Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsTArray.h"
8 : #include "nsString.h"
9 : #include "nsIStyleRuleProcessor.h"
10 : #include "nsIDocument.h"
11 : #include "nsIContent.h"
12 : #include "nsIPresShell.h"
13 : #include "nsXBLService.h"
14 : #include "nsIServiceManager.h"
15 : #include "nsXBLResourceLoader.h"
16 : #include "nsXBLPrototypeResources.h"
17 : #include "nsIDocumentObserver.h"
18 : #include "imgILoader.h"
19 : #include "imgRequestProxy.h"
20 : #include "mozilla/StyleSheet.h"
21 : #include "mozilla/StyleSheetInlines.h"
22 : #include "mozilla/css/Loader.h"
23 : #include "nsIURI.h"
24 : #include "nsNetUtil.h"
25 : #include "nsGkAtoms.h"
26 : #include "nsFrameManager.h"
27 : #include "nsStyleContext.h"
28 : #include "nsXBLPrototypeBinding.h"
29 : #include "nsCSSRuleProcessor.h"
30 : #include "nsContentUtils.h"
31 : #include "nsStyleSet.h"
32 : #include "nsIScriptSecurityManager.h"
33 :
34 : using namespace mozilla;
35 :
36 0 : NS_IMPL_CYCLE_COLLECTION(nsXBLResourceLoader, mBoundElements)
37 :
38 30 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLResourceLoader)
39 0 : NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
40 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
41 0 : NS_INTERFACE_MAP_END
42 :
43 30 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLResourceLoader)
44 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLResourceLoader)
45 :
46 : struct nsXBLResource
47 : {
48 : nsXBLResource* mNext;
49 : nsIAtom* mType;
50 : nsString mSrc;
51 :
52 39 : nsXBLResource(nsIAtom* aType, const nsAString& aSrc)
53 39 : {
54 39 : MOZ_COUNT_CTOR(nsXBLResource);
55 39 : mNext = nullptr;
56 39 : mType = aType;
57 39 : mSrc = aSrc;
58 39 : }
59 :
60 32 : ~nsXBLResource()
61 32 : {
62 32 : MOZ_COUNT_DTOR(nsXBLResource);
63 41 : NS_CONTENT_DELETE_LIST_MEMBER(nsXBLResource, this, mNext);
64 32 : }
65 : };
66 :
67 30 : nsXBLResourceLoader::nsXBLResourceLoader(nsXBLPrototypeBinding* aBinding,
68 30 : nsXBLPrototypeResources* aResources)
69 : :mBinding(aBinding),
70 : mResources(aResources),
71 : mResourceList(nullptr),
72 : mLastResource(nullptr),
73 : mLoadingResources(false),
74 : mInLoadResourcesFunc(false),
75 30 : mPendingSheets(0)
76 : {
77 30 : }
78 :
79 0 : nsXBLResourceLoader::~nsXBLResourceLoader()
80 : {
81 0 : delete mResourceList;
82 0 : }
83 :
84 : bool
85 164 : nsXBLResourceLoader::LoadResources(nsIContent* aBoundElement)
86 : {
87 164 : mInLoadResourcesFunc = true;
88 :
89 164 : if (mLoadingResources) {
90 141 : mInLoadResourcesFunc = false;
91 141 : return mPendingSheets == 0;
92 : }
93 :
94 23 : mLoadingResources = true;
95 :
96 : // Declare our loaders.
97 46 : nsCOMPtr<nsIDocument> doc = mBinding->XBLDocumentInfo()->GetDocument();
98 23 : mBoundDocument = aBoundElement->OwnerDoc();
99 :
100 23 : mozilla::css::Loader* cssLoader = doc->CSSLoader();
101 23 : MOZ_ASSERT(cssLoader->GetDocument() &&
102 : cssLoader->GetDocument()->GetStyleBackendType()
103 : == mBoundDocument->GetStyleBackendType(),
104 : "The style backends of the loader and bound document are mismatched!");
105 :
106 23 : nsIURI *docURL = doc->GetDocumentURI();
107 23 : nsIPrincipal* docPrincipal = doc->NodePrincipal();
108 :
109 46 : nsCOMPtr<nsIURI> url;
110 :
111 55 : for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) {
112 32 : if (curr->mSrc.IsEmpty())
113 0 : continue;
114 :
115 32 : if (NS_FAILED(NS_NewURI(getter_AddRefs(url), curr->mSrc,
116 : doc->GetDocumentCharacterSet(), docURL)))
117 0 : continue;
118 :
119 32 : if (curr->mType == nsGkAtoms::image) {
120 : // Now kick off the image load...
121 : // Passing nullptr for pretty much everything -- cause we don't care!
122 : // XXX: initialDocumentURI is nullptr!
123 0 : RefPtr<imgRequestProxy> req;
124 0 : nsContentUtils::LoadImage(url, doc, doc, docPrincipal, docURL,
125 : doc->GetReferrerPolicy(), nullptr,
126 0 : nsIRequest::LOAD_BACKGROUND, EmptyString(),
127 0 : getter_AddRefs(req));
128 : }
129 32 : else if (curr->mType == nsGkAtoms::stylesheet) {
130 : // Kick off the load of the stylesheet.
131 :
132 : // Always load chrome synchronously
133 : // XXXbz should that still do a content policy check?
134 : bool chrome;
135 : nsresult rv;
136 32 : if (NS_SUCCEEDED(url->SchemeIs("chrome", &chrome)) && chrome)
137 : {
138 32 : rv = nsContentUtils::GetSecurityManager()->
139 32 : CheckLoadURIWithPrincipal(docPrincipal, url,
140 64 : nsIScriptSecurityManager::ALLOW_CHROME);
141 32 : if (NS_SUCCEEDED(rv)) {
142 64 : RefPtr<StyleSheet> sheet;
143 32 : rv = cssLoader->LoadSheetSync(url, &sheet);
144 32 : NS_ASSERTION(NS_SUCCEEDED(rv), "Load failed!!!");
145 32 : if (NS_SUCCEEDED(rv))
146 : {
147 32 : rv = StyleSheetLoaded(sheet, false, NS_OK);
148 32 : NS_ASSERTION(NS_SUCCEEDED(rv), "Processing the style sheet failed!!!");
149 : }
150 : }
151 : }
152 : else
153 : {
154 0 : rv = cssLoader->LoadSheet(url, false, docPrincipal, EmptyCString(), this);
155 0 : if (NS_SUCCEEDED(rv))
156 0 : ++mPendingSheets;
157 : }
158 : }
159 : }
160 :
161 23 : mInLoadResourcesFunc = false;
162 :
163 : // Destroy our resource list.
164 23 : delete mResourceList;
165 23 : mResourceList = nullptr;
166 :
167 23 : return mPendingSheets == 0;
168 : }
169 :
170 : // nsICSSLoaderObserver
171 : NS_IMETHODIMP
172 32 : nsXBLResourceLoader::StyleSheetLoaded(StyleSheet* aSheet,
173 : bool aWasAlternate,
174 : nsresult aStatus)
175 : {
176 32 : if (!mResources) {
177 : // Our resources got destroyed -- just bail out
178 0 : return NS_OK;
179 : }
180 :
181 32 : mResources->AppendStyleSheet(aSheet);
182 :
183 32 : if (!mInLoadResourcesFunc)
184 0 : mPendingSheets--;
185 :
186 32 : if (mPendingSheets == 0) {
187 : // All stylesheets are loaded.
188 32 : if (aSheet->IsGecko()) {
189 32 : mResources->GatherRuleProcessor();
190 : } else {
191 0 : mResources->ComputeServoStyleSet(
192 0 : mBoundDocument->GetShell()->GetPresContext());
193 : }
194 :
195 : // XXX Check for mPendingScripts when scripts also come online.
196 32 : if (!mInLoadResourcesFunc)
197 0 : NotifyBoundElements();
198 : }
199 32 : return NS_OK;
200 : }
201 :
202 : void
203 39 : nsXBLResourceLoader::AddResource(nsIAtom* aResourceType, const nsAString& aSrc)
204 : {
205 39 : nsXBLResource* res = new nsXBLResource(aResourceType, aSrc);
206 39 : if (!mResourceList)
207 30 : mResourceList = res;
208 : else
209 9 : mLastResource->mNext = res;
210 :
211 39 : mLastResource = res;
212 39 : }
213 :
214 : void
215 0 : nsXBLResourceLoader::AddResourceListener(nsIContent* aBoundElement)
216 : {
217 0 : if (aBoundElement) {
218 0 : mBoundElements.AppendObject(aBoundElement);
219 : }
220 0 : }
221 :
222 : void
223 0 : nsXBLResourceLoader::NotifyBoundElements()
224 : {
225 0 : nsXBLService* xblService = nsXBLService::GetInstance();
226 0 : if (!xblService)
227 0 : return;
228 :
229 0 : nsIURI* bindingURI = mBinding->BindingURI();
230 :
231 0 : uint32_t eltCount = mBoundElements.Count();
232 0 : for (uint32_t j = 0; j < eltCount; j++) {
233 0 : nsCOMPtr<nsIContent> content = mBoundElements.ObjectAt(j);
234 0 : MOZ_ASSERT(content->IsElement());
235 :
236 0 : bool ready = false;
237 0 : xblService->BindingReady(content, bindingURI, &ready);
238 :
239 0 : if (ready) {
240 : // We need the document to flush out frame construction and
241 : // such, so we want to use the current document.
242 0 : nsIDocument* doc = content->GetUncomposedDoc();
243 :
244 0 : if (doc) {
245 : // Flush first to make sure we can get the frame for content
246 0 : doc->FlushPendingNotifications(FlushType::Frames);
247 :
248 : // If |content| is (in addition to having binding |mBinding|)
249 : // also a descendant of another element with binding |mBinding|,
250 : // then we might have just constructed it due to the
251 : // notification of its parent. (We can know about both if the
252 : // binding loads were triggered from the DOM rather than frame
253 : // construction.) So we have to check both whether the element
254 : // has a primary frame and whether it's in the undisplayed map
255 : // before sending a ContentInserted notification, or bad things
256 : // will happen.
257 0 : nsIPresShell *shell = doc->GetShell();
258 0 : if (shell) {
259 0 : nsIFrame* childFrame = content->GetPrimaryFrame();
260 0 : if (!childFrame) {
261 : // Check to see if it's in the undisplayed content map, or the
262 : // display: contents map.
263 : nsStyleContext* sc =
264 0 : shell->FrameManager()->GetUndisplayedContent(content);
265 :
266 0 : if (!sc) {
267 0 : sc = shell->FrameManager()->GetDisplayContentsStyleFor(content);
268 : }
269 :
270 0 : if (!sc) {
271 0 : if (shell->StyleSet()->IsServo()) {
272 : // Ensure the element has servo data so that
273 : // nsChangeHint_ReconstructFrame posted by
274 : // PostRecreateFramesFor() is recognized.
275 0 : shell->StyleSet()->GetAsServo()->StyleNewlyBoundElement(
276 0 : content->AsElement());
277 : }
278 0 : shell->PostRecreateFramesFor(content->AsElement());
279 : }
280 : }
281 : }
282 :
283 : // Flush again
284 : // XXXbz why is this needed?
285 0 : doc->FlushPendingNotifications(FlushType::ContentAndNotify);
286 : }
287 : }
288 : }
289 :
290 : // Clear out the whole array.
291 0 : mBoundElements.Clear();
292 :
293 : // Delete ourselves.
294 0 : mResources->ClearLoader();
295 : }
296 :
297 : nsresult
298 0 : nsXBLResourceLoader::Write(nsIObjectOutputStream* aStream)
299 : {
300 : nsresult rv;
301 :
302 0 : for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) {
303 0 : if (curr->mType == nsGkAtoms::image)
304 0 : rv = aStream->Write8(XBLBinding_Serialize_Image);
305 0 : else if (curr->mType == nsGkAtoms::stylesheet)
306 0 : rv = aStream->Write8(XBLBinding_Serialize_Stylesheet);
307 : else
308 0 : continue;
309 :
310 0 : NS_ENSURE_SUCCESS(rv, rv);
311 :
312 0 : rv = aStream->WriteWStringZ(curr->mSrc.get());
313 0 : NS_ENSURE_SUCCESS(rv, rv);
314 : }
315 :
316 0 : return NS_OK;
317 : }
|