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