|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * vim: ft=cpp tw=78 sw=2 et ts=2 |
|
3 * |
|
4 * This Source Code Form is subject to the terms of the Mozilla Public |
|
5 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
7 * |
|
8 * This Original Code has been modified by IBM Corporation. |
|
9 * Modifications made by IBM described herein are Copyright (c) |
|
10 * International Business Machines Corporation, 2000. Modifications |
|
11 * to Mozilla code or documentation identified per MPL Section 3.3 |
|
12 * |
|
13 * Date Modified by Description of modification |
|
14 * 04/20/2000 IBM Corp. OS/2 VisualAge build. |
|
15 */ |
|
16 |
|
17 /* loading of CSS style sheets using the network APIs */ |
|
18 |
|
19 #include "mozilla/ArrayUtils.h" |
|
20 #include "mozilla/MemoryReporting.h" |
|
21 |
|
22 #include "mozilla/css/Loader.h" |
|
23 #include "nsIRunnable.h" |
|
24 #include "nsIUnicharStreamLoader.h" |
|
25 #include "nsSyncLoadService.h" |
|
26 #include "nsCOMPtr.h" |
|
27 #include "nsString.h" |
|
28 #include "nsIContent.h" |
|
29 #include "nsIDocument.h" |
|
30 #include "nsIDOMNode.h" |
|
31 #include "nsIDOMDocument.h" |
|
32 #include "nsIURI.h" |
|
33 #include "nsNetUtil.h" |
|
34 #include "nsContentUtils.h" |
|
35 #include "nsIScriptSecurityManager.h" |
|
36 #include "nsContentPolicyUtils.h" |
|
37 #include "nsIHttpChannel.h" |
|
38 #include "nsIHttpChannelInternal.h" |
|
39 #include "nsIScriptError.h" |
|
40 #include "nsMimeTypes.h" |
|
41 #include "nsCSSStyleSheet.h" |
|
42 #include "nsIStyleSheetLinkingElement.h" |
|
43 #include "nsICSSLoaderObserver.h" |
|
44 #include "nsCSSParser.h" |
|
45 #include "mozilla/css/ImportRule.h" |
|
46 #include "nsThreadUtils.h" |
|
47 #include "nsGkAtoms.h" |
|
48 #include "nsIThreadInternal.h" |
|
49 #include "nsCrossSiteListenerProxy.h" |
|
50 #include "nsINetworkSeer.h" |
|
51 #include "mozilla/dom/ShadowRoot.h" |
|
52 #include "mozilla/dom/URL.h" |
|
53 |
|
54 #ifdef MOZ_XUL |
|
55 #include "nsXULPrototypeCache.h" |
|
56 #endif |
|
57 |
|
58 #include "nsIMediaList.h" |
|
59 #include "nsIDOMStyleSheet.h" |
|
60 #include "nsError.h" |
|
61 |
|
62 #include "nsIChannelPolicy.h" |
|
63 #include "nsIContentSecurityPolicy.h" |
|
64 #include "nsCycleCollectionParticipant.h" |
|
65 |
|
66 #include "mozilla/dom/EncodingUtils.h" |
|
67 using mozilla::dom::EncodingUtils; |
|
68 |
|
69 using namespace mozilla::dom; |
|
70 |
|
71 /** |
|
72 * OVERALL ARCHITECTURE |
|
73 * |
|
74 * The CSS Loader gets requests to load various sorts of style sheets: |
|
75 * inline style from <style> elements, linked style, @import-ed child |
|
76 * sheets, non-document sheets. The loader handles the following tasks: |
|
77 * |
|
78 * 1) Checking whether the load is allowed: CheckLoadAllowed() |
|
79 * 2) Creation of the actual style sheet objects: CreateSheet() |
|
80 * 3) setting of the right media, title, enabled state, etc on the |
|
81 * sheet: PrepareSheet() |
|
82 * 4) Insertion of the sheet in the proper cascade order: |
|
83 * InsertSheetInDoc() and InsertChildSheet() |
|
84 * 5) Load of the sheet: LoadSheet() |
|
85 * 6) Parsing of the sheet: ParseSheet() |
|
86 * 7) Cleanup: SheetComplete() |
|
87 * |
|
88 * The detailed documentation for these functions is found with the |
|
89 * function implementations. |
|
90 * |
|
91 * The following helper object is used: |
|
92 * SheetLoadData -- a small class that is used to store all the |
|
93 * information needed for the loading of a sheet; |
|
94 * this class handles listening for the stream |
|
95 * loader completion and also handles charset |
|
96 * determination. |
|
97 */ |
|
98 |
|
99 namespace mozilla { |
|
100 namespace css { |
|
101 |
|
102 /********************************************* |
|
103 * Data needed to properly load a stylesheet * |
|
104 *********************************************/ |
|
105 |
|
106 class SheetLoadData : public nsIRunnable, |
|
107 public nsIUnicharStreamLoaderObserver, |
|
108 public nsIThreadObserver |
|
109 { |
|
110 public: |
|
111 virtual ~SheetLoadData(void); |
|
112 // Data for loading a sheet linked from a document |
|
113 SheetLoadData(Loader* aLoader, |
|
114 const nsSubstring& aTitle, |
|
115 nsIURI* aURI, |
|
116 nsCSSStyleSheet* aSheet, |
|
117 nsIStyleSheetLinkingElement* aOwningElement, |
|
118 bool aIsAlternate, |
|
119 nsICSSLoaderObserver* aObserver, |
|
120 nsIPrincipal* aLoaderPrincipal); |
|
121 |
|
122 // Data for loading a sheet linked from an @import rule |
|
123 SheetLoadData(Loader* aLoader, |
|
124 nsIURI* aURI, |
|
125 nsCSSStyleSheet* aSheet, |
|
126 SheetLoadData* aParentData, |
|
127 nsICSSLoaderObserver* aObserver, |
|
128 nsIPrincipal* aLoaderPrincipal); |
|
129 |
|
130 // Data for loading a non-document sheet |
|
131 SheetLoadData(Loader* aLoader, |
|
132 nsIURI* aURI, |
|
133 nsCSSStyleSheet* aSheet, |
|
134 bool aSyncLoad, |
|
135 bool aAllowUnsafeRules, |
|
136 bool aUseSystemPrincipal, |
|
137 const nsCString& aCharset, |
|
138 nsICSSLoaderObserver* aObserver, |
|
139 nsIPrincipal* aLoaderPrincipal); |
|
140 |
|
141 already_AddRefed<nsIURI> GetReferrerURI(); |
|
142 |
|
143 void ScheduleLoadEventIfNeeded(nsresult aStatus); |
|
144 |
|
145 NS_DECL_ISUPPORTS |
|
146 NS_DECL_NSIRUNNABLE |
|
147 NS_DECL_NSITHREADOBSERVER |
|
148 NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER |
|
149 |
|
150 // Hold a ref to the CSSLoader so we can call back to it to let it |
|
151 // know the load finished |
|
152 Loader* mLoader; // strong ref |
|
153 |
|
154 // Title needed to pull datas out of the pending datas table when |
|
155 // the preferred title is changed |
|
156 nsString mTitle; |
|
157 |
|
158 // Charset we decided to use for the sheet |
|
159 nsCString mCharset; |
|
160 |
|
161 // URI we're loading. Null for inline sheets |
|
162 nsCOMPtr<nsIURI> mURI; |
|
163 |
|
164 // Should be 1 for non-inline sheets. |
|
165 uint32_t mLineNumber; |
|
166 |
|
167 // The sheet we're loading data for |
|
168 nsRefPtr<nsCSSStyleSheet> mSheet; |
|
169 |
|
170 // Linked list of datas for the same URI as us |
|
171 SheetLoadData* mNext; // strong ref |
|
172 |
|
173 // Load data for the sheet that @import-ed us if we were @import-ed |
|
174 // during the parse |
|
175 SheetLoadData* mParentData; // strong ref |
|
176 |
|
177 // Number of sheets we @import-ed that are still loading |
|
178 uint32_t mPendingChildren; |
|
179 |
|
180 // mSyncLoad is true when the load needs to be synchronous -- right |
|
181 // now only for LoadSheetSync and children of sync loads. |
|
182 bool mSyncLoad : 1; |
|
183 |
|
184 // mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or |
|
185 // LoadSheet or an @import from such a sheet. Non-document sheet loads can |
|
186 // proceed even if we have no document. |
|
187 bool mIsNonDocumentSheet : 1; |
|
188 |
|
189 // mIsLoading is true from the moment we are placed in the loader's |
|
190 // "loading datas" table (right after the async channel is opened) |
|
191 // to the moment we are removed from said table (due to the load |
|
192 // completing or being cancelled). |
|
193 bool mIsLoading : 1; |
|
194 |
|
195 // mIsCancelled is set to true when a sheet load is stopped by |
|
196 // Stop() or StopLoadingSheet() (which was removed in Bug 556446). |
|
197 // SheetLoadData::OnStreamComplete() checks this to avoid parsing |
|
198 // sheets that have been cancelled and such. |
|
199 bool mIsCancelled : 1; |
|
200 |
|
201 // mMustNotify is true if the load data is being loaded async and |
|
202 // the original function call that started the load has returned. |
|
203 // This applies only to observer notifications; load/error events |
|
204 // are fired for any SheetLoadData that has a non-null |
|
205 // mOwningElement. |
|
206 bool mMustNotify : 1; |
|
207 |
|
208 // mWasAlternate is true if the sheet was an alternate when the load data was |
|
209 // created. |
|
210 bool mWasAlternate : 1; |
|
211 |
|
212 // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed |
|
213 // in the loaded sheet. |
|
214 bool mAllowUnsafeRules : 1; |
|
215 |
|
216 // mUseSystemPrincipal is true if the system principal should be used for |
|
217 // this sheet, no matter what the channel principal is. Only true for sync |
|
218 // loads. |
|
219 bool mUseSystemPrincipal : 1; |
|
220 |
|
221 // If true, this SheetLoadData is being used as a way to handle |
|
222 // async observer notification for an already-complete sheet. |
|
223 bool mSheetAlreadyComplete : 1; |
|
224 |
|
225 // This is the element that imported the sheet. Needed to get the |
|
226 // charset set on it and to fire load/error events. |
|
227 nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement; |
|
228 |
|
229 // The observer that wishes to be notified of load completion |
|
230 nsCOMPtr<nsICSSLoaderObserver> mObserver; |
|
231 |
|
232 // The principal that identifies who started loading us. |
|
233 nsCOMPtr<nsIPrincipal> mLoaderPrincipal; |
|
234 |
|
235 // The charset to use if the transport and sheet don't indicate one. |
|
236 // May be empty. Must be empty if mOwningElement is non-null. |
|
237 nsCString mCharsetHint; |
|
238 |
|
239 // The status our load ended up with; this determines whether we |
|
240 // should fire error events or load events. This gets initialized |
|
241 // by ScheduleLoadEventIfNeeded, and is only used after that has |
|
242 // been called. |
|
243 nsresult mStatus; |
|
244 |
|
245 private: |
|
246 void FireLoadEvent(nsIThreadInternal* aThread); |
|
247 }; |
|
248 |
|
249 #ifdef MOZ_LOGGING |
|
250 // #define FORCE_PR_LOG /* Allow logging in the release build */ |
|
251 #endif /* MOZ_LOGGING */ |
|
252 #include "prlog.h" |
|
253 |
|
254 #ifdef PR_LOGGING |
|
255 static PRLogModuleInfo * |
|
256 GetLoaderLog() |
|
257 { |
|
258 static PRLogModuleInfo *sLog; |
|
259 if (!sLog) |
|
260 sLog = PR_NewLogModule("nsCSSLoader"); |
|
261 return sLog; |
|
262 } |
|
263 #endif /* PR_LOGGING */ |
|
264 |
|
265 #define LOG_FORCE(args) PR_LOG(GetLoaderLog(), PR_LOG_ALWAYS, args) |
|
266 #define LOG_ERROR(args) PR_LOG(GetLoaderLog(), PR_LOG_ERROR, args) |
|
267 #define LOG_WARN(args) PR_LOG(GetLoaderLog(), PR_LOG_WARNING, args) |
|
268 #define LOG_DEBUG(args) PR_LOG(GetLoaderLog(), PR_LOG_DEBUG, args) |
|
269 #define LOG(args) LOG_DEBUG(args) |
|
270 |
|
271 #define LOG_FORCE_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ALWAYS) |
|
272 #define LOG_ERROR_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ERROR) |
|
273 #define LOG_WARN_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_WARNING) |
|
274 #define LOG_DEBUG_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_DEBUG) |
|
275 #define LOG_ENABLED() LOG_DEBUG_ENABLED() |
|
276 |
|
277 #ifdef PR_LOGGING |
|
278 #define LOG_URI(format, uri) \ |
|
279 PR_BEGIN_MACRO \ |
|
280 NS_ASSERTION(uri, "Logging null uri"); \ |
|
281 if (LOG_ENABLED()) { \ |
|
282 nsAutoCString _logURISpec; \ |
|
283 uri->GetSpec(_logURISpec); \ |
|
284 LOG((format, _logURISpec.get())); \ |
|
285 } \ |
|
286 PR_END_MACRO |
|
287 #else // PR_LOGGING |
|
288 #define LOG_URI(format, uri) |
|
289 #endif // PR_LOGGING |
|
290 |
|
291 // And some convenience strings... |
|
292 #ifdef PR_LOGGING |
|
293 static const char* const gStateStrings[] = { |
|
294 "eSheetStateUnknown", |
|
295 "eSheetNeedsParser", |
|
296 "eSheetPending", |
|
297 "eSheetLoading", |
|
298 "eSheetComplete" |
|
299 }; |
|
300 #endif |
|
301 |
|
302 /******************************** |
|
303 * SheetLoadData implementation * |
|
304 ********************************/ |
|
305 NS_IMPL_ISUPPORTS(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable, |
|
306 nsIThreadObserver) |
|
307 |
|
308 SheetLoadData::SheetLoadData(Loader* aLoader, |
|
309 const nsSubstring& aTitle, |
|
310 nsIURI* aURI, |
|
311 nsCSSStyleSheet* aSheet, |
|
312 nsIStyleSheetLinkingElement* aOwningElement, |
|
313 bool aIsAlternate, |
|
314 nsICSSLoaderObserver* aObserver, |
|
315 nsIPrincipal* aLoaderPrincipal) |
|
316 : mLoader(aLoader), |
|
317 mTitle(aTitle), |
|
318 mURI(aURI), |
|
319 mLineNumber(1), |
|
320 mSheet(aSheet), |
|
321 mNext(nullptr), |
|
322 mParentData(nullptr), |
|
323 mPendingChildren(0), |
|
324 mSyncLoad(false), |
|
325 mIsNonDocumentSheet(false), |
|
326 mIsLoading(false), |
|
327 mIsCancelled(false), |
|
328 mMustNotify(false), |
|
329 mWasAlternate(aIsAlternate), |
|
330 mAllowUnsafeRules(false), |
|
331 mUseSystemPrincipal(false), |
|
332 mSheetAlreadyComplete(false), |
|
333 mOwningElement(aOwningElement), |
|
334 mObserver(aObserver), |
|
335 mLoaderPrincipal(aLoaderPrincipal) |
|
336 { |
|
337 NS_PRECONDITION(mLoader, "Must have a loader!"); |
|
338 NS_ADDREF(mLoader); |
|
339 } |
|
340 |
|
341 SheetLoadData::SheetLoadData(Loader* aLoader, |
|
342 nsIURI* aURI, |
|
343 nsCSSStyleSheet* aSheet, |
|
344 SheetLoadData* aParentData, |
|
345 nsICSSLoaderObserver* aObserver, |
|
346 nsIPrincipal* aLoaderPrincipal) |
|
347 : mLoader(aLoader), |
|
348 mURI(aURI), |
|
349 mLineNumber(1), |
|
350 mSheet(aSheet), |
|
351 mNext(nullptr), |
|
352 mParentData(aParentData), |
|
353 mPendingChildren(0), |
|
354 mSyncLoad(false), |
|
355 mIsNonDocumentSheet(false), |
|
356 mIsLoading(false), |
|
357 mIsCancelled(false), |
|
358 mMustNotify(false), |
|
359 mWasAlternate(false), |
|
360 mAllowUnsafeRules(false), |
|
361 mUseSystemPrincipal(false), |
|
362 mSheetAlreadyComplete(false), |
|
363 mOwningElement(nullptr), |
|
364 mObserver(aObserver), |
|
365 mLoaderPrincipal(aLoaderPrincipal) |
|
366 { |
|
367 NS_PRECONDITION(mLoader, "Must have a loader!"); |
|
368 NS_ADDREF(mLoader); |
|
369 if (mParentData) { |
|
370 NS_ADDREF(mParentData); |
|
371 mSyncLoad = mParentData->mSyncLoad; |
|
372 mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet; |
|
373 mAllowUnsafeRules = mParentData->mAllowUnsafeRules; |
|
374 mUseSystemPrincipal = mParentData->mUseSystemPrincipal; |
|
375 ++(mParentData->mPendingChildren); |
|
376 } |
|
377 |
|
378 NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad, |
|
379 "Shouldn't use system principal for async loads"); |
|
380 } |
|
381 |
|
382 SheetLoadData::SheetLoadData(Loader* aLoader, |
|
383 nsIURI* aURI, |
|
384 nsCSSStyleSheet* aSheet, |
|
385 bool aSyncLoad, |
|
386 bool aAllowUnsafeRules, |
|
387 bool aUseSystemPrincipal, |
|
388 const nsCString& aCharset, |
|
389 nsICSSLoaderObserver* aObserver, |
|
390 nsIPrincipal* aLoaderPrincipal) |
|
391 : mLoader(aLoader), |
|
392 mURI(aURI), |
|
393 mLineNumber(1), |
|
394 mSheet(aSheet), |
|
395 mNext(nullptr), |
|
396 mParentData(nullptr), |
|
397 mPendingChildren(0), |
|
398 mSyncLoad(aSyncLoad), |
|
399 mIsNonDocumentSheet(true), |
|
400 mIsLoading(false), |
|
401 mIsCancelled(false), |
|
402 mMustNotify(false), |
|
403 mWasAlternate(false), |
|
404 mAllowUnsafeRules(aAllowUnsafeRules), |
|
405 mUseSystemPrincipal(aUseSystemPrincipal), |
|
406 mSheetAlreadyComplete(false), |
|
407 mOwningElement(nullptr), |
|
408 mObserver(aObserver), |
|
409 mLoaderPrincipal(aLoaderPrincipal), |
|
410 mCharsetHint(aCharset) |
|
411 { |
|
412 NS_PRECONDITION(mLoader, "Must have a loader!"); |
|
413 NS_ADDREF(mLoader); |
|
414 |
|
415 NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad, |
|
416 "Shouldn't use system principal for async loads"); |
|
417 } |
|
418 |
|
419 SheetLoadData::~SheetLoadData() |
|
420 { |
|
421 NS_RELEASE(mLoader); |
|
422 NS_IF_RELEASE(mParentData); |
|
423 NS_IF_RELEASE(mNext); |
|
424 } |
|
425 |
|
426 NS_IMETHODIMP |
|
427 SheetLoadData::Run() |
|
428 { |
|
429 mLoader->HandleLoadEvent(this); |
|
430 return NS_OK; |
|
431 } |
|
432 |
|
433 NS_IMETHODIMP |
|
434 SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread) |
|
435 { |
|
436 return NS_OK; |
|
437 } |
|
438 |
|
439 NS_IMETHODIMP |
|
440 SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread, |
|
441 bool aMayWait, |
|
442 uint32_t aRecursionDepth) |
|
443 { |
|
444 // We want to fire our load even before or after event processing, |
|
445 // whichever comes first. |
|
446 FireLoadEvent(aThread); |
|
447 return NS_OK; |
|
448 } |
|
449 |
|
450 NS_IMETHODIMP |
|
451 SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread, |
|
452 uint32_t aRecursionDepth, |
|
453 bool aEventWasProcessed) |
|
454 { |
|
455 // We want to fire our load even before or after event processing, |
|
456 // whichever comes first. |
|
457 FireLoadEvent(aThread); |
|
458 return NS_OK; |
|
459 } |
|
460 |
|
461 void |
|
462 SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread) |
|
463 { |
|
464 |
|
465 // First remove ourselves as a thread observer. But we need to keep |
|
466 // ourselves alive while doing that! |
|
467 nsRefPtr<SheetLoadData> kungFuDeathGrip(this); |
|
468 aThread->RemoveObserver(this); |
|
469 |
|
470 // Now fire the event |
|
471 nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement); |
|
472 NS_ASSERTION(node, "How did that happen???"); |
|
473 |
|
474 nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(), |
|
475 node, |
|
476 NS_SUCCEEDED(mStatus) ? |
|
477 NS_LITERAL_STRING("load") : |
|
478 NS_LITERAL_STRING("error"), |
|
479 false, false); |
|
480 |
|
481 // And unblock onload |
|
482 if (mLoader->mDocument) { |
|
483 mLoader->mDocument->UnblockOnload(true); |
|
484 } |
|
485 } |
|
486 |
|
487 void |
|
488 SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus) |
|
489 { |
|
490 if (!mOwningElement) { |
|
491 return; |
|
492 } |
|
493 |
|
494 mStatus = aStatus; |
|
495 |
|
496 nsCOMPtr<nsIThread> thread = do_GetMainThread(); |
|
497 nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread); |
|
498 if (NS_SUCCEEDED(internalThread->AddObserver(this))) { |
|
499 // Make sure to block onload here |
|
500 if (mLoader->mDocument) { |
|
501 mLoader->mDocument->BlockOnload(); |
|
502 } |
|
503 } |
|
504 } |
|
505 |
|
506 /************************* |
|
507 * Loader Implementation * |
|
508 *************************/ |
|
509 |
|
510 Loader::Loader(void) |
|
511 : mDocument(nullptr) |
|
512 , mDatasToNotifyOn(0) |
|
513 , mCompatMode(eCompatibility_FullStandards) |
|
514 , mEnabled(true) |
|
515 #ifdef DEBUG |
|
516 , mSyncCallback(false) |
|
517 #endif |
|
518 { |
|
519 } |
|
520 |
|
521 Loader::Loader(nsIDocument* aDocument) |
|
522 : mDocument(aDocument) |
|
523 , mDatasToNotifyOn(0) |
|
524 , mCompatMode(eCompatibility_FullStandards) |
|
525 , mEnabled(true) |
|
526 #ifdef DEBUG |
|
527 , mSyncCallback(false) |
|
528 #endif |
|
529 { |
|
530 // We can just use the preferred set, since there are no sheets in the |
|
531 // document yet (if there are, how did they get there? _we_ load the sheets!) |
|
532 // and hence the selected set makes no sense at this time. |
|
533 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument); |
|
534 if (domDoc) { |
|
535 domDoc->GetPreferredStyleSheetSet(mPreferredSheet); |
|
536 } |
|
537 } |
|
538 |
|
539 Loader::~Loader() |
|
540 { |
|
541 NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0, |
|
542 "How did we get destroyed when there are loading data?"); |
|
543 NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0, |
|
544 "How did we get destroyed when there are pending data?"); |
|
545 // Note: no real need to revoke our stylesheet loaded events -- they |
|
546 // hold strong references to us, so if we're going away that means |
|
547 // they're all done. |
|
548 } |
|
549 |
|
550 void |
|
551 Loader::DropDocumentReference(void) |
|
552 { |
|
553 mDocument = nullptr; |
|
554 // Flush out pending datas just so we don't leak by accident. These |
|
555 // loads should short-circuit through the mDocument check in |
|
556 // LoadSheet and just end up in SheetComplete immediately |
|
557 if (mSheets) { |
|
558 StartAlternateLoads(); |
|
559 } |
|
560 } |
|
561 |
|
562 static PLDHashOperator |
|
563 CollectNonAlternates(URIPrincipalAndCORSModeHashKey *aKey, |
|
564 SheetLoadData* &aData, |
|
565 void* aClosure) |
|
566 { |
|
567 NS_PRECONDITION(aData, "Must have a data"); |
|
568 NS_PRECONDITION(aClosure, "Must have an array"); |
|
569 |
|
570 // Note that we don't want to affect what the selected style set is, |
|
571 // so use true for aHasAlternateRel. |
|
572 if (aData->mLoader->IsAlternate(aData->mTitle, true)) { |
|
573 return PL_DHASH_NEXT; |
|
574 } |
|
575 |
|
576 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData); |
|
577 return PL_DHASH_REMOVE; |
|
578 } |
|
579 |
|
580 nsresult |
|
581 Loader::SetPreferredSheet(const nsAString& aTitle) |
|
582 { |
|
583 #ifdef DEBUG |
|
584 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument); |
|
585 if (doc) { |
|
586 nsAutoString currentPreferred; |
|
587 doc->GetLastStyleSheetSet(currentPreferred); |
|
588 if (DOMStringIsNull(currentPreferred)) { |
|
589 doc->GetPreferredStyleSheetSet(currentPreferred); |
|
590 } |
|
591 NS_ASSERTION(currentPreferred.Equals(aTitle), |
|
592 "Unexpected argument to SetPreferredSheet"); |
|
593 } |
|
594 #endif |
|
595 |
|
596 mPreferredSheet = aTitle; |
|
597 |
|
598 // start any pending alternates that aren't alternates anymore |
|
599 if (mSheets) { |
|
600 LoadDataArray arr(mSheets->mPendingDatas.Count()); |
|
601 mSheets->mPendingDatas.Enumerate(CollectNonAlternates, &arr); |
|
602 |
|
603 mDatasToNotifyOn += arr.Length(); |
|
604 for (uint32_t i = 0; i < arr.Length(); ++i) { |
|
605 --mDatasToNotifyOn; |
|
606 LoadSheet(arr[i], eSheetNeedsParser); |
|
607 } |
|
608 } |
|
609 |
|
610 return NS_OK; |
|
611 } |
|
612 |
|
613 static const char kCharsetSym[] = "@charset \""; |
|
614 |
|
615 static bool GetCharsetFromData(const char* aStyleSheetData, |
|
616 uint32_t aDataLength, |
|
617 nsACString& aCharset) |
|
618 { |
|
619 aCharset.Truncate(); |
|
620 if (aDataLength <= sizeof(kCharsetSym) - 1) |
|
621 return false; |
|
622 |
|
623 if (strncmp(aStyleSheetData, |
|
624 kCharsetSym, |
|
625 sizeof(kCharsetSym) - 1)) { |
|
626 return false; |
|
627 } |
|
628 |
|
629 for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) { |
|
630 char c = aStyleSheetData[i]; |
|
631 if (c == '"') { |
|
632 ++i; |
|
633 if (i < aDataLength && aStyleSheetData[i] == ';') { |
|
634 return true; |
|
635 } |
|
636 // fail |
|
637 break; |
|
638 } |
|
639 aCharset.Append(c); |
|
640 } |
|
641 |
|
642 // Did not see end quote or semicolon |
|
643 aCharset.Truncate(); |
|
644 return false; |
|
645 } |
|
646 |
|
647 NS_IMETHODIMP |
|
648 SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader, |
|
649 nsISupports* aContext, |
|
650 nsACString const& aSegment, |
|
651 nsACString& aCharset) |
|
652 { |
|
653 NS_PRECONDITION(!mOwningElement || mCharsetHint.IsEmpty(), |
|
654 "Can't have element _and_ charset hint"); |
|
655 |
|
656 LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI); |
|
657 |
|
658 // The precedence is (per CSS3 Syntax 2012-11-08 ED): |
|
659 // BOM |
|
660 // Channel |
|
661 // @charset rule |
|
662 // charset attribute on the referrer |
|
663 // encoding of the referrer |
|
664 // UTF-8 |
|
665 |
|
666 aCharset.Truncate(); |
|
667 |
|
668 if (nsContentUtils::CheckForBOM((const unsigned char*)aSegment.BeginReading(), |
|
669 aSegment.Length(), |
|
670 aCharset)) { |
|
671 // aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8" |
|
672 // which will swallow the BOM. |
|
673 mCharset.Assign(aCharset); |
|
674 #ifdef PR_LOGGING |
|
675 LOG((" Setting from BOM to: %s", PromiseFlatCString(aCharset).get())); |
|
676 #endif |
|
677 return NS_OK; |
|
678 } |
|
679 |
|
680 nsCOMPtr<nsIChannel> channel; |
|
681 nsAutoCString specified; |
|
682 aLoader->GetChannel(getter_AddRefs(channel)); |
|
683 if (channel) { |
|
684 channel->GetContentCharset(specified); |
|
685 if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) { |
|
686 mCharset.Assign(aCharset); |
|
687 #ifdef PR_LOGGING |
|
688 LOG((" Setting from HTTP to: %s", PromiseFlatCString(aCharset).get())); |
|
689 #endif |
|
690 return NS_OK; |
|
691 } |
|
692 } |
|
693 |
|
694 if (GetCharsetFromData(aSegment.BeginReading(), |
|
695 aSegment.Length(), |
|
696 specified)) { |
|
697 if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) { |
|
698 // FindEncodingForLabel currently never returns UTF-16LE but will |
|
699 // probably change to never return UTF-16 instead, so check both here |
|
700 // to avoid relying on the exact behavior. |
|
701 if (aCharset.EqualsLiteral("UTF-16") || |
|
702 aCharset.EqualsLiteral("UTF-16BE") || |
|
703 aCharset.EqualsLiteral("UTF-16LE")) { |
|
704 // Be consistent with HTML <meta> handling in face of impossibility. |
|
705 // When the @charset rule itself evidently was not UTF-16-encoded, |
|
706 // it saying UTF-16 has to be a lie. |
|
707 aCharset.AssignLiteral("UTF-8"); |
|
708 } |
|
709 mCharset.Assign(aCharset); |
|
710 #ifdef PR_LOGGING |
|
711 LOG((" Setting from @charset rule to: %s", |
|
712 PromiseFlatCString(aCharset).get())); |
|
713 #endif |
|
714 return NS_OK; |
|
715 } |
|
716 } |
|
717 |
|
718 // Now try the charset on the <link> or processing instruction |
|
719 // that loaded us |
|
720 if (mOwningElement) { |
|
721 nsAutoString specified16; |
|
722 mOwningElement->GetCharset(specified16); |
|
723 if (EncodingUtils::FindEncodingForLabel(specified16, aCharset)) { |
|
724 mCharset.Assign(aCharset); |
|
725 #ifdef PR_LOGGING |
|
726 LOG((" Setting from charset attribute to: %s", |
|
727 PromiseFlatCString(aCharset).get())); |
|
728 #endif |
|
729 return NS_OK; |
|
730 } |
|
731 } |
|
732 |
|
733 // In the preload case, the value of the charset attribute on <link> comes |
|
734 // in via mCharsetHint instead. |
|
735 if (EncodingUtils::FindEncodingForLabel(mCharsetHint, aCharset)) { |
|
736 mCharset.Assign(aCharset); |
|
737 #ifdef PR_LOGGING |
|
738 LOG((" Setting from charset attribute (preload case) to: %s", |
|
739 PromiseFlatCString(aCharset).get())); |
|
740 #endif |
|
741 return NS_OK; |
|
742 } |
|
743 |
|
744 // Try charset from the parent stylesheet. |
|
745 if (mParentData) { |
|
746 aCharset = mParentData->mCharset; |
|
747 if (!aCharset.IsEmpty()) { |
|
748 mCharset.Assign(aCharset); |
|
749 #ifdef PR_LOGGING |
|
750 LOG((" Setting from parent sheet to: %s", |
|
751 PromiseFlatCString(aCharset).get())); |
|
752 #endif |
|
753 return NS_OK; |
|
754 } |
|
755 } |
|
756 |
|
757 if (mLoader->mDocument) { |
|
758 // no useful data on charset. Try the document charset. |
|
759 aCharset = mLoader->mDocument->GetDocumentCharacterSet(); |
|
760 MOZ_ASSERT(!aCharset.IsEmpty()); |
|
761 mCharset.Assign(aCharset); |
|
762 #ifdef PR_LOGGING |
|
763 LOG((" Setting from document to: %s", PromiseFlatCString(aCharset).get())); |
|
764 #endif |
|
765 return NS_OK; |
|
766 } |
|
767 |
|
768 aCharset.AssignLiteral("UTF-8"); |
|
769 mCharset = aCharset; |
|
770 #ifdef PR_LOGGING |
|
771 LOG((" Setting from default to: %s", PromiseFlatCString(aCharset).get())); |
|
772 #endif |
|
773 return NS_OK; |
|
774 } |
|
775 |
|
776 already_AddRefed<nsIURI> |
|
777 SheetLoadData::GetReferrerURI() |
|
778 { |
|
779 nsCOMPtr<nsIURI> uri; |
|
780 if (mParentData) |
|
781 uri = mParentData->mSheet->GetSheetURI(); |
|
782 if (!uri && mLoader->mDocument) |
|
783 uri = mLoader->mDocument->GetDocumentURI(); |
|
784 return uri.forget(); |
|
785 } |
|
786 |
|
787 /* |
|
788 * Here we need to check that the load did not give us an http error |
|
789 * page and check the mimetype on the channel to make sure we're not |
|
790 * loading non-text/css data in standards mode. |
|
791 */ |
|
792 NS_IMETHODIMP |
|
793 SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader, |
|
794 nsISupports* aContext, |
|
795 nsresult aStatus, |
|
796 const nsAString& aBuffer) |
|
797 { |
|
798 LOG(("SheetLoadData::OnStreamComplete")); |
|
799 NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko"); |
|
800 |
|
801 if (mIsCancelled) { |
|
802 // Just return. Don't call SheetComplete -- it's already been |
|
803 // called and calling it again will lead to an extra NS_RELEASE on |
|
804 // this data and a likely crash. |
|
805 return NS_OK; |
|
806 } |
|
807 |
|
808 if (!mLoader->mDocument && !mIsNonDocumentSheet) { |
|
809 // Sorry, we don't care about this load anymore |
|
810 LOG_WARN((" No document and not non-document sheet; dropping load")); |
|
811 mLoader->SheetComplete(this, NS_BINDING_ABORTED); |
|
812 return NS_OK; |
|
813 } |
|
814 |
|
815 if (NS_FAILED(aStatus)) { |
|
816 LOG_WARN((" Load failed: status 0x%x", aStatus)); |
|
817 mLoader->SheetComplete(this, aStatus); |
|
818 return NS_OK; |
|
819 } |
|
820 |
|
821 nsCOMPtr<nsIChannel> channel; |
|
822 nsresult result = aLoader->GetChannel(getter_AddRefs(channel)); |
|
823 if (NS_FAILED(result)) { |
|
824 LOG_WARN((" No channel from loader")); |
|
825 mLoader->SheetComplete(this, result); |
|
826 return NS_OK; |
|
827 } |
|
828 |
|
829 nsCOMPtr<nsIURI> originalURI; |
|
830 channel->GetOriginalURI(getter_AddRefs(originalURI)); |
|
831 |
|
832 // If the channel's original URI is "chrome:", we want that, since |
|
833 // the observer code in nsXULPrototypeCache depends on chrome stylesheets |
|
834 // having a chrome URI. (Whether or not chrome stylesheets come through |
|
835 // this codepath seems nondeterministic.) |
|
836 // Otherwise we want the potentially-HTTP-redirected URI. |
|
837 nsCOMPtr<nsIURI> channelURI; |
|
838 NS_GetFinalChannelURI(channel, getter_AddRefs(channelURI)); |
|
839 |
|
840 if (!channelURI || !originalURI) { |
|
841 NS_ERROR("Someone just violated the nsIRequest contract"); |
|
842 LOG_WARN((" Channel without a URI. Bad!")); |
|
843 mLoader->SheetComplete(this, NS_ERROR_UNEXPECTED); |
|
844 return NS_OK; |
|
845 } |
|
846 |
|
847 nsCOMPtr<nsIPrincipal> principal; |
|
848 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
|
849 result = NS_ERROR_NOT_AVAILABLE; |
|
850 if (secMan) { // Could be null if we already shut down |
|
851 if (mUseSystemPrincipal) { |
|
852 result = secMan->GetSystemPrincipal(getter_AddRefs(principal)); |
|
853 } else { |
|
854 result = secMan->GetChannelPrincipal(channel, getter_AddRefs(principal)); |
|
855 } |
|
856 } |
|
857 |
|
858 if (NS_FAILED(result)) { |
|
859 LOG_WARN((" Couldn't get principal")); |
|
860 mLoader->SheetComplete(this, result); |
|
861 return NS_OK; |
|
862 } |
|
863 |
|
864 mSheet->SetPrincipal(principal); |
|
865 |
|
866 // If it's an HTTP channel, we want to make sure this is not an |
|
867 // error document we got. |
|
868 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); |
|
869 if (httpChannel) { |
|
870 bool requestSucceeded; |
|
871 result = httpChannel->GetRequestSucceeded(&requestSucceeded); |
|
872 if (NS_SUCCEEDED(result) && !requestSucceeded) { |
|
873 LOG((" Load returned an error page")); |
|
874 mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE); |
|
875 return NS_OK; |
|
876 } |
|
877 } |
|
878 |
|
879 nsAutoCString contentType; |
|
880 if (channel) { |
|
881 channel->GetContentType(contentType); |
|
882 } |
|
883 |
|
884 // In standards mode, a style sheet must have one of these MIME |
|
885 // types to be processed at all. In quirks mode, we accept any |
|
886 // MIME type, but only if the style sheet is same-origin with the |
|
887 // requesting document or parent sheet. See bug 524223. |
|
888 |
|
889 bool validType = contentType.EqualsLiteral("text/css") || |
|
890 contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) || |
|
891 contentType.IsEmpty(); |
|
892 |
|
893 if (!validType) { |
|
894 const char *errorMessage; |
|
895 uint32_t errorFlag; |
|
896 bool sameOrigin = true; |
|
897 |
|
898 if (mLoaderPrincipal) { |
|
899 bool subsumed; |
|
900 result = mLoaderPrincipal->Subsumes(principal, &subsumed); |
|
901 if (NS_FAILED(result) || !subsumed) { |
|
902 sameOrigin = false; |
|
903 } |
|
904 } |
|
905 |
|
906 if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) { |
|
907 errorMessage = "MimeNotCssWarn"; |
|
908 errorFlag = nsIScriptError::warningFlag; |
|
909 } else { |
|
910 errorMessage = "MimeNotCss"; |
|
911 errorFlag = nsIScriptError::errorFlag; |
|
912 } |
|
913 |
|
914 nsAutoCString spec; |
|
915 channelURI->GetSpec(spec); |
|
916 |
|
917 const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec); |
|
918 const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType); |
|
919 const char16_t *strings[] = { specUTF16.get(), ctypeUTF16.get() }; |
|
920 |
|
921 nsCOMPtr<nsIURI> referrer = GetReferrerURI(); |
|
922 nsContentUtils::ReportToConsole(errorFlag, |
|
923 NS_LITERAL_CSTRING("CSS Loader"), |
|
924 mLoader->mDocument, |
|
925 nsContentUtils::eCSS_PROPERTIES, |
|
926 errorMessage, |
|
927 strings, ArrayLength(strings), |
|
928 referrer); |
|
929 |
|
930 if (errorFlag == nsIScriptError::errorFlag) { |
|
931 LOG_WARN((" Ignoring sheet with improper MIME type %s", |
|
932 contentType.get())); |
|
933 mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE); |
|
934 return NS_OK; |
|
935 } |
|
936 } |
|
937 |
|
938 // Enough to set the URIs on mSheet, since any sibling datas we have share |
|
939 // the same mInner as mSheet and will thus get the same URI. |
|
940 mSheet->SetURIs(channelURI, originalURI, channelURI); |
|
941 |
|
942 bool completed; |
|
943 result = mLoader->ParseSheet(aBuffer, this, completed); |
|
944 NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete"); |
|
945 return result; |
|
946 } |
|
947 |
|
948 bool |
|
949 Loader::IsAlternate(const nsAString& aTitle, bool aHasAlternateRel) |
|
950 { |
|
951 // A sheet is alternate if it has a nonempty title that doesn't match the |
|
952 // currently selected style set. But if there _is_ no currently selected |
|
953 // style set, the sheet wasn't marked as an alternate explicitly, and aTitle |
|
954 // is nonempty, we should select the style set corresponding to aTitle, since |
|
955 // that's a preferred sheet. |
|
956 if (aTitle.IsEmpty()) { |
|
957 return false; |
|
958 } |
|
959 |
|
960 if (!aHasAlternateRel && mDocument && mPreferredSheet.IsEmpty()) { |
|
961 // There's no preferred set yet, and we now have a sheet with a title. |
|
962 // Make that be the preferred set. |
|
963 mDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, aTitle); |
|
964 // We're definitely not an alternate |
|
965 return false; |
|
966 } |
|
967 |
|
968 return !aTitle.Equals(mPreferredSheet); |
|
969 } |
|
970 |
|
971 /* static */ PLDHashOperator |
|
972 Loader::RemoveEntriesWithURI(URIPrincipalAndCORSModeHashKey* aKey, |
|
973 nsRefPtr<nsCSSStyleSheet> &aSheet, |
|
974 void* aUserData) |
|
975 { |
|
976 nsIURI* obsoleteURI = static_cast<nsIURI*>(aUserData); |
|
977 nsIURI* sheetURI = aKey->GetURI(); |
|
978 bool areEqual; |
|
979 nsresult rv = sheetURI->Equals(obsoleteURI, &areEqual); |
|
980 if (NS_SUCCEEDED(rv) && areEqual) { |
|
981 return PL_DHASH_REMOVE; |
|
982 } |
|
983 return PL_DHASH_NEXT; |
|
984 } |
|
985 |
|
986 nsresult |
|
987 Loader::ObsoleteSheet(nsIURI* aURI) |
|
988 { |
|
989 if (!mSheets) { |
|
990 return NS_OK; |
|
991 } |
|
992 if (!aURI) { |
|
993 return NS_ERROR_INVALID_ARG; |
|
994 } |
|
995 mSheets->mCompleteSheets.Enumerate(RemoveEntriesWithURI, aURI); |
|
996 return NS_OK; |
|
997 } |
|
998 |
|
999 /** |
|
1000 * CheckLoadAllowed will return success if the load is allowed, |
|
1001 * failure otherwise. |
|
1002 * |
|
1003 * @param aSourcePrincipal the principal of the node or document or parent |
|
1004 * sheet loading the sheet |
|
1005 * @param aTargetURI the uri of the sheet to be loaded |
|
1006 * @param aContext the node owning the sheet. This is the element or document |
|
1007 * owning the stylesheet (possibly indirectly, for child sheets) |
|
1008 */ |
|
1009 nsresult |
|
1010 Loader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal, |
|
1011 nsIURI* aTargetURI, |
|
1012 nsISupports* aContext) |
|
1013 { |
|
1014 LOG(("css::Loader::CheckLoadAllowed")); |
|
1015 |
|
1016 nsresult rv; |
|
1017 |
|
1018 if (aSourcePrincipal) { |
|
1019 // Check with the security manager |
|
1020 nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager(); |
|
1021 rv = |
|
1022 secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI, |
|
1023 nsIScriptSecurityManager::ALLOW_CHROME); |
|
1024 if (NS_FAILED(rv)) { // failure is normal here; don't warn |
|
1025 return rv; |
|
1026 } |
|
1027 |
|
1028 LOG((" Passed security check")); |
|
1029 |
|
1030 // Check with content policy |
|
1031 |
|
1032 int16_t shouldLoad = nsIContentPolicy::ACCEPT; |
|
1033 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET, |
|
1034 aTargetURI, |
|
1035 aSourcePrincipal, |
|
1036 aContext, |
|
1037 NS_LITERAL_CSTRING("text/css"), |
|
1038 nullptr, //extra param |
|
1039 &shouldLoad, |
|
1040 nsContentUtils::GetContentPolicy(), |
|
1041 nsContentUtils::GetSecurityManager()); |
|
1042 |
|
1043 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { |
|
1044 LOG((" Load blocked by content policy")); |
|
1045 return NS_ERROR_CONTENT_BLOCKED; |
|
1046 } |
|
1047 } |
|
1048 |
|
1049 return NS_OK; |
|
1050 } |
|
1051 |
|
1052 /** |
|
1053 * CreateSheet() creates an nsCSSStyleSheet object for the given URI, |
|
1054 * if any. If there is no URI given, we just create a new style sheet |
|
1055 * object. Otherwise, we check for an existing style sheet object for |
|
1056 * that uri in various caches and clone it if we find it. Cloned |
|
1057 * sheets will have the title/media/enabled state of the sheet they |
|
1058 * are clones off; make sure to call PrepareSheet() on the result of |
|
1059 * CreateSheet(). |
|
1060 */ |
|
1061 nsresult |
|
1062 Loader::CreateSheet(nsIURI* aURI, |
|
1063 nsIContent* aLinkingContent, |
|
1064 nsIPrincipal* aLoaderPrincipal, |
|
1065 CORSMode aCORSMode, |
|
1066 bool aSyncLoad, |
|
1067 bool aHasAlternateRel, |
|
1068 const nsAString& aTitle, |
|
1069 StyleSheetState& aSheetState, |
|
1070 bool *aIsAlternate, |
|
1071 nsCSSStyleSheet** aSheet) |
|
1072 { |
|
1073 LOG(("css::Loader::CreateSheet")); |
|
1074 NS_PRECONDITION(aSheet, "Null out param!"); |
|
1075 |
|
1076 if (!mSheets) { |
|
1077 mSheets = new Sheets(); |
|
1078 } |
|
1079 |
|
1080 *aSheet = nullptr; |
|
1081 aSheetState = eSheetStateUnknown; |
|
1082 |
|
1083 // Check the alternate state before doing anything else, because it |
|
1084 // can mess with our hashtables. |
|
1085 *aIsAlternate = IsAlternate(aTitle, aHasAlternateRel); |
|
1086 |
|
1087 if (aURI) { |
|
1088 aSheetState = eSheetComplete; |
|
1089 nsRefPtr<nsCSSStyleSheet> sheet; |
|
1090 |
|
1091 // First, the XUL cache |
|
1092 #ifdef MOZ_XUL |
|
1093 if (IsChromeURI(aURI)) { |
|
1094 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); |
|
1095 if (cache) { |
|
1096 if (cache->IsEnabled()) { |
|
1097 sheet = cache->GetStyleSheet(aURI); |
|
1098 LOG((" From XUL cache: %p", sheet.get())); |
|
1099 } |
|
1100 } |
|
1101 } |
|
1102 #endif |
|
1103 |
|
1104 bool fromCompleteSheets = false; |
|
1105 if (!sheet) { |
|
1106 // Then our per-document complete sheets. |
|
1107 URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode); |
|
1108 |
|
1109 mSheets->mCompleteSheets.Get(&key, getter_AddRefs(sheet)); |
|
1110 LOG((" From completed: %p", sheet.get())); |
|
1111 |
|
1112 fromCompleteSheets = !!sheet; |
|
1113 } |
|
1114 |
|
1115 if (sheet) { |
|
1116 // This sheet came from the XUL cache or our per-document hashtable; it |
|
1117 // better be a complete sheet. |
|
1118 NS_ASSERTION(sheet->IsComplete(), |
|
1119 "Sheet thinks it's not complete while we think it is"); |
|
1120 |
|
1121 // Make sure it hasn't been modified; if it has, we can't use it |
|
1122 if (sheet->IsModified()) { |
|
1123 LOG((" Not cloning completed sheet %p because it's been modified", |
|
1124 sheet.get())); |
|
1125 sheet = nullptr; |
|
1126 fromCompleteSheets = false; |
|
1127 } |
|
1128 } |
|
1129 |
|
1130 // Then loading sheets |
|
1131 if (!sheet && !aSyncLoad) { |
|
1132 aSheetState = eSheetLoading; |
|
1133 SheetLoadData* loadData = nullptr; |
|
1134 URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode); |
|
1135 mSheets->mLoadingDatas.Get(&key, &loadData); |
|
1136 if (loadData) { |
|
1137 sheet = loadData->mSheet; |
|
1138 LOG((" From loading: %p", sheet.get())); |
|
1139 |
|
1140 #ifdef DEBUG |
|
1141 bool debugEqual; |
|
1142 NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) || |
|
1143 (aLoaderPrincipal && loadData->mLoaderPrincipal && |
|
1144 NS_SUCCEEDED(aLoaderPrincipal-> |
|
1145 Equals(loadData->mLoaderPrincipal, |
|
1146 &debugEqual)) && debugEqual), |
|
1147 "Principals should be the same"); |
|
1148 #endif |
|
1149 } |
|
1150 |
|
1151 // Then alternate sheets |
|
1152 if (!sheet) { |
|
1153 aSheetState = eSheetPending; |
|
1154 loadData = nullptr; |
|
1155 mSheets->mPendingDatas.Get(&key, &loadData); |
|
1156 if (loadData) { |
|
1157 sheet = loadData->mSheet; |
|
1158 LOG((" From pending: %p", sheet.get())); |
|
1159 |
|
1160 #ifdef DEBUG |
|
1161 bool debugEqual; |
|
1162 NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) || |
|
1163 (aLoaderPrincipal && loadData->mLoaderPrincipal && |
|
1164 NS_SUCCEEDED(aLoaderPrincipal-> |
|
1165 Equals(loadData->mLoaderPrincipal, |
|
1166 &debugEqual)) && debugEqual), |
|
1167 "Principals should be the same"); |
|
1168 #endif |
|
1169 } |
|
1170 } |
|
1171 } |
|
1172 |
|
1173 if (sheet) { |
|
1174 // The sheet we have now should be either incomplete or unmodified |
|
1175 NS_ASSERTION(!sheet->IsModified() || !sheet->IsComplete(), |
|
1176 "Unexpected modified complete sheet"); |
|
1177 NS_ASSERTION(sheet->IsComplete() || aSheetState != eSheetComplete, |
|
1178 "Sheet thinks it's not complete while we think it is"); |
|
1179 |
|
1180 *aSheet = sheet->Clone(nullptr, nullptr, nullptr, nullptr).take(); |
|
1181 if (*aSheet && fromCompleteSheets && |
|
1182 !sheet->GetOwnerNode() && !sheet->GetParentSheet()) { |
|
1183 // The sheet we're cloning isn't actually referenced by |
|
1184 // anyone. Replace it in the cache, so that if our CSSOM is |
|
1185 // later modified we don't end up with two copies of our inner |
|
1186 // hanging around. |
|
1187 URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode); |
|
1188 NS_ASSERTION((*aSheet)->IsComplete(), |
|
1189 "Should only be caching complete sheets"); |
|
1190 mSheets->mCompleteSheets.Put(&key, *aSheet); |
|
1191 } |
|
1192 } |
|
1193 } |
|
1194 |
|
1195 if (!*aSheet) { |
|
1196 aSheetState = eSheetNeedsParser; |
|
1197 nsIURI *sheetURI; |
|
1198 nsCOMPtr<nsIURI> baseURI; |
|
1199 nsIURI* originalURI; |
|
1200 if (!aURI) { |
|
1201 // Inline style. Use the document's base URL so that @import in |
|
1202 // the inline sheet picks up the right base. |
|
1203 NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?"); |
|
1204 baseURI = aLinkingContent->GetBaseURI(); |
|
1205 sheetURI = aLinkingContent->GetDocument()->GetDocumentURI(); |
|
1206 originalURI = nullptr; |
|
1207 } else { |
|
1208 baseURI = aURI; |
|
1209 sheetURI = aURI; |
|
1210 originalURI = aURI; |
|
1211 } |
|
1212 |
|
1213 nsRefPtr<nsCSSStyleSheet> sheet = new nsCSSStyleSheet(aCORSMode); |
|
1214 sheet->SetURIs(sheetURI, originalURI, baseURI); |
|
1215 sheet.forget(aSheet); |
|
1216 } |
|
1217 |
|
1218 NS_ASSERTION(*aSheet, "We should have a sheet by now!"); |
|
1219 NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!"); |
|
1220 LOG((" State: %s", gStateStrings[aSheetState])); |
|
1221 |
|
1222 return NS_OK; |
|
1223 } |
|
1224 |
|
1225 /** |
|
1226 * PrepareSheet() handles setting the media and title on the sheet, as |
|
1227 * well as setting the enabled state based on the title and whether |
|
1228 * the sheet had "alternate" in its rel. |
|
1229 */ |
|
1230 void |
|
1231 Loader::PrepareSheet(nsCSSStyleSheet* aSheet, |
|
1232 const nsSubstring& aTitle, |
|
1233 const nsSubstring& aMediaString, |
|
1234 nsMediaList* aMediaList, |
|
1235 Element* aScopeElement, |
|
1236 bool isAlternate) |
|
1237 { |
|
1238 NS_PRECONDITION(aSheet, "Must have a sheet!"); |
|
1239 |
|
1240 nsRefPtr<nsMediaList> mediaList(aMediaList); |
|
1241 |
|
1242 if (!aMediaString.IsEmpty()) { |
|
1243 NS_ASSERTION(!aMediaList, |
|
1244 "must not provide both aMediaString and aMediaList"); |
|
1245 mediaList = new nsMediaList(); |
|
1246 |
|
1247 nsCSSParser mediumParser(this); |
|
1248 |
|
1249 // We have aMediaString only when linked from link elements, style |
|
1250 // elements, or PIs, so pass true. |
|
1251 mediumParser.ParseMediaList(aMediaString, nullptr, 0, mediaList, true); |
|
1252 } |
|
1253 |
|
1254 aSheet->SetMedia(mediaList); |
|
1255 |
|
1256 aSheet->SetTitle(aTitle); |
|
1257 aSheet->SetEnabled(! isAlternate); |
|
1258 aSheet->SetScopeElement(aScopeElement); |
|
1259 } |
|
1260 |
|
1261 /** |
|
1262 * InsertSheetInDoc handles ordering of sheets in the document. Here |
|
1263 * we have two types of sheets -- those with linking elements and |
|
1264 * those without. The latter are loaded by Link: headers. |
|
1265 * The following constraints are observed: |
|
1266 * 1) Any sheet with a linking element comes after all sheets without |
|
1267 * linking elements |
|
1268 * 2) Sheets without linking elements are inserted in the order in |
|
1269 * which the inserting requests come in, since all of these are |
|
1270 * inserted during header data processing in the content sink |
|
1271 * 3) Sheets with linking elements are ordered based on document order |
|
1272 * as determined by CompareDocumentPosition. |
|
1273 */ |
|
1274 nsresult |
|
1275 Loader::InsertSheetInDoc(nsCSSStyleSheet* aSheet, |
|
1276 nsIContent* aLinkingContent, |
|
1277 nsIDocument* aDocument) |
|
1278 { |
|
1279 LOG(("css::Loader::InsertSheetInDoc")); |
|
1280 NS_PRECONDITION(aSheet, "Nothing to insert"); |
|
1281 NS_PRECONDITION(aDocument, "Must have a document to insert into"); |
|
1282 |
|
1283 // XXX Need to cancel pending sheet loads for this element, if any |
|
1284 |
|
1285 int32_t sheetCount = aDocument->GetNumberOfStyleSheets(); |
|
1286 |
|
1287 /* |
|
1288 * Start the walk at the _end_ of the list, since in the typical |
|
1289 * case we'll just want to append anyway. We want to break out of |
|
1290 * the loop when insertionPoint points to just before the index we |
|
1291 * want to insert at. In other words, when we leave the loop |
|
1292 * insertionPoint is the index of the stylesheet that immediately |
|
1293 * precedes the one we're inserting. |
|
1294 */ |
|
1295 int32_t insertionPoint; |
|
1296 for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) { |
|
1297 nsIStyleSheet *curSheet = aDocument->GetStyleSheetAt(insertionPoint); |
|
1298 NS_ASSERTION(curSheet, "There must be a sheet here!"); |
|
1299 nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(curSheet); |
|
1300 NS_ASSERTION(domSheet, "All the \"normal\" sheets implement nsIDOMStyleSheet"); |
|
1301 nsCOMPtr<nsIDOMNode> sheetOwner; |
|
1302 domSheet->GetOwnerNode(getter_AddRefs(sheetOwner)); |
|
1303 if (sheetOwner && !aLinkingContent) { |
|
1304 // Keep moving; all sheets with a sheetOwner come after all |
|
1305 // sheets without a linkingNode |
|
1306 continue; |
|
1307 } |
|
1308 |
|
1309 if (!sheetOwner) { |
|
1310 // Aha! The current sheet has no sheet owner, so we want to |
|
1311 // insert after it no matter whether we have a linkingNode |
|
1312 break; |
|
1313 } |
|
1314 |
|
1315 nsCOMPtr<nsINode> sheetOwnerNode = do_QueryInterface(sheetOwner); |
|
1316 NS_ASSERTION(aLinkingContent != sheetOwnerNode, |
|
1317 "Why do we still have our old sheet?"); |
|
1318 |
|
1319 // Have to compare |
|
1320 if (nsContentUtils::PositionIsBefore(sheetOwnerNode, aLinkingContent)) { |
|
1321 // The current sheet comes before us, and it better be the first |
|
1322 // such, because now we break |
|
1323 break; |
|
1324 } |
|
1325 } |
|
1326 |
|
1327 ++insertionPoint; // adjust the index to the spot we want to insert in |
|
1328 |
|
1329 // XXX <meta> elements do not implement nsIStyleSheetLinkingElement; |
|
1330 // need to fix this for them to be ordered correctly. |
|
1331 nsCOMPtr<nsIStyleSheetLinkingElement> |
|
1332 linkingElement = do_QueryInterface(aLinkingContent); |
|
1333 if (linkingElement) { |
|
1334 linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet |
|
1335 } |
|
1336 |
|
1337 aDocument->BeginUpdate(UPDATE_STYLE); |
|
1338 aDocument->InsertStyleSheetAt(aSheet, insertionPoint); |
|
1339 aDocument->EndUpdate(UPDATE_STYLE); |
|
1340 LOG((" Inserting into document at position %d", insertionPoint)); |
|
1341 |
|
1342 return NS_OK; |
|
1343 } |
|
1344 |
|
1345 /** |
|
1346 * InsertChildSheet handles ordering of @import-ed sheet in their |
|
1347 * parent sheets. Here we want to just insert based on order of the |
|
1348 * @import rules that imported the sheets. In theory we can't just |
|
1349 * append to the end because the CSSOM can insert @import rules. In |
|
1350 * practice, we get the call to load the child sheet before the CSSOM |
|
1351 * has finished inserting the @import rule, so we have no idea where |
|
1352 * to put it anyway. So just append for now. |
|
1353 */ |
|
1354 nsresult |
|
1355 Loader::InsertChildSheet(nsCSSStyleSheet* aSheet, |
|
1356 nsCSSStyleSheet* aParentSheet, |
|
1357 ImportRule* aParentRule) |
|
1358 { |
|
1359 LOG(("css::Loader::InsertChildSheet")); |
|
1360 NS_PRECONDITION(aSheet, "Nothing to insert"); |
|
1361 NS_PRECONDITION(aParentSheet, "Need a parent to insert into"); |
|
1362 NS_PRECONDITION(aParentSheet, "How did we get imported?"); |
|
1363 |
|
1364 // child sheets should always start out enabled, even if they got |
|
1365 // cloned off of top-level sheets which were disabled |
|
1366 aSheet->SetEnabled(true); |
|
1367 |
|
1368 aParentSheet->AppendStyleSheet(aSheet); |
|
1369 aParentRule->SetSheet(aSheet); // This sets the ownerRule on the sheet |
|
1370 |
|
1371 LOG((" Inserting into parent sheet")); |
|
1372 // LOG((" Inserting into parent sheet at position %d", insertionPoint)); |
|
1373 |
|
1374 return NS_OK; |
|
1375 } |
|
1376 |
|
1377 /** |
|
1378 * LoadSheet handles the actual load of a sheet. If the load is |
|
1379 * supposed to be synchronous it just opens a channel synchronously |
|
1380 * using the given uri, wraps the resulting stream in a converter |
|
1381 * stream and calls ParseSheet. Otherwise it tries to look for an |
|
1382 * existing load for this URI and piggyback on it. Failing all that, |
|
1383 * a new load is kicked off asynchronously. |
|
1384 */ |
|
1385 nsresult |
|
1386 Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState) |
|
1387 { |
|
1388 LOG(("css::Loader::LoadSheet")); |
|
1389 NS_PRECONDITION(aLoadData, "Need a load data"); |
|
1390 NS_PRECONDITION(aLoadData->mURI, "Need a URI to load"); |
|
1391 NS_PRECONDITION(aLoadData->mSheet, "Need a sheet to load into"); |
|
1392 NS_PRECONDITION(aSheetState != eSheetComplete, "Why bother?"); |
|
1393 NS_PRECONDITION(!aLoadData->mUseSystemPrincipal || aLoadData->mSyncLoad, |
|
1394 "Shouldn't use system principal for async loads"); |
|
1395 NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now."); |
|
1396 |
|
1397 LOG_URI(" Load from: '%s'", aLoadData->mURI); |
|
1398 |
|
1399 nsresult rv = NS_OK; |
|
1400 |
|
1401 if (!mDocument && !aLoadData->mIsNonDocumentSheet) { |
|
1402 // No point starting the load; just release all the data and such. |
|
1403 LOG_WARN((" No document and not non-document sheet; pre-dropping load")); |
|
1404 SheetComplete(aLoadData, NS_BINDING_ABORTED); |
|
1405 return NS_BINDING_ABORTED; |
|
1406 } |
|
1407 |
|
1408 if (aLoadData->mSyncLoad) { |
|
1409 LOG((" Synchronous load")); |
|
1410 NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?"); |
|
1411 NS_ASSERTION(aSheetState == eSheetNeedsParser, |
|
1412 "Sync loads can't reuse existing async loads"); |
|
1413 |
|
1414 // Create a nsIUnicharStreamLoader instance to which we will feed |
|
1415 // the data from the sync load. Do this before creating the |
|
1416 // channel to make error recovery simpler. |
|
1417 nsCOMPtr<nsIUnicharStreamLoader> streamLoader; |
|
1418 rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData); |
|
1419 if (NS_FAILED(rv)) { |
|
1420 LOG_ERROR((" Failed to create stream loader for sync load")); |
|
1421 SheetComplete(aLoadData, rv); |
|
1422 return rv; |
|
1423 } |
|
1424 |
|
1425 if (mDocument) { |
|
1426 mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), |
|
1427 nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, |
|
1428 mDocument); |
|
1429 } |
|
1430 |
|
1431 // Just load it |
|
1432 nsCOMPtr<nsIInputStream> stream; |
|
1433 nsCOMPtr<nsIChannel> channel; |
|
1434 rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI, nullptr, |
|
1435 nullptr, nullptr, nsIRequest::LOAD_NORMAL, |
|
1436 getter_AddRefs(channel)); |
|
1437 if (NS_FAILED(rv)) { |
|
1438 LOG_ERROR((" Failed to open URI synchronously")); |
|
1439 SheetComplete(aLoadData, rv); |
|
1440 return rv; |
|
1441 } |
|
1442 |
|
1443 NS_ASSERTION(channel, "NS_OpenURI lied?"); |
|
1444 |
|
1445 // Force UA sheets to be UTF-8. |
|
1446 // XXX this is only necessary because the default in |
|
1447 // SheetLoadData::OnDetermineCharset is wrong (bug 521039). |
|
1448 channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8")); |
|
1449 |
|
1450 // Manually feed the streamloader the contents of the stream we |
|
1451 // got from NS_OpenURI. This will call back into OnStreamComplete |
|
1452 // and thence to ParseSheet. Regardless of whether this fails, |
|
1453 // SheetComplete has been called. |
|
1454 return nsSyncLoadService::PushSyncStreamToListener(stream, |
|
1455 streamLoader, |
|
1456 channel); |
|
1457 } |
|
1458 |
|
1459 SheetLoadData* existingData = nullptr; |
|
1460 |
|
1461 URIPrincipalAndCORSModeHashKey key(aLoadData->mURI, |
|
1462 aLoadData->mLoaderPrincipal, |
|
1463 aLoadData->mSheet->GetCORSMode()); |
|
1464 if (aSheetState == eSheetLoading) { |
|
1465 mSheets->mLoadingDatas.Get(&key, &existingData); |
|
1466 NS_ASSERTION(existingData, "CreateSheet lied about the state"); |
|
1467 } |
|
1468 else if (aSheetState == eSheetPending){ |
|
1469 mSheets->mPendingDatas.Get(&key, &existingData); |
|
1470 NS_ASSERTION(existingData, "CreateSheet lied about the state"); |
|
1471 } |
|
1472 |
|
1473 if (existingData) { |
|
1474 LOG((" Glomming on to existing load")); |
|
1475 SheetLoadData* data = existingData; |
|
1476 while (data->mNext) { |
|
1477 data = data->mNext; |
|
1478 } |
|
1479 data->mNext = aLoadData; // transfer ownership |
|
1480 if (aSheetState == eSheetPending && !aLoadData->mWasAlternate) { |
|
1481 // Kick the load off; someone cares about it right away |
|
1482 |
|
1483 #ifdef DEBUG |
|
1484 SheetLoadData* removedData; |
|
1485 NS_ASSERTION(mSheets->mPendingDatas.Get(&key, &removedData) && |
|
1486 removedData == existingData, |
|
1487 "Bad pending table."); |
|
1488 #endif |
|
1489 |
|
1490 mSheets->mPendingDatas.Remove(&key); |
|
1491 |
|
1492 LOG((" Forcing load of pending data")); |
|
1493 return LoadSheet(existingData, eSheetNeedsParser); |
|
1494 } |
|
1495 // All done here; once the load completes we'll be marked complete |
|
1496 // automatically |
|
1497 return NS_OK; |
|
1498 } |
|
1499 |
|
1500 #ifdef DEBUG |
|
1501 mSyncCallback = true; |
|
1502 #endif |
|
1503 nsCOMPtr<nsILoadGroup> loadGroup; |
|
1504 // Content Security Policy information to pass into channel |
|
1505 nsCOMPtr<nsIChannelPolicy> channelPolicy; |
|
1506 if (mDocument) { |
|
1507 loadGroup = mDocument->GetDocumentLoadGroup(); |
|
1508 NS_ASSERTION(loadGroup, |
|
1509 "No loadgroup for stylesheet; onload will fire early"); |
|
1510 nsCOMPtr<nsIContentSecurityPolicy> csp; |
|
1511 rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp)); |
|
1512 NS_ENSURE_SUCCESS(rv, rv); |
|
1513 if (csp) { |
|
1514 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1"); |
|
1515 channelPolicy->SetContentSecurityPolicy(csp); |
|
1516 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_STYLESHEET); |
|
1517 } |
|
1518 } |
|
1519 |
|
1520 nsCOMPtr<nsIChannel> channel; |
|
1521 rv = NS_NewChannel(getter_AddRefs(channel), |
|
1522 aLoadData->mURI, nullptr, loadGroup, nullptr, |
|
1523 nsIChannel::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI, |
|
1524 channelPolicy); |
|
1525 |
|
1526 if (NS_FAILED(rv)) { |
|
1527 #ifdef DEBUG |
|
1528 mSyncCallback = false; |
|
1529 #endif |
|
1530 LOG_ERROR((" Failed to create channel")); |
|
1531 SheetComplete(aLoadData, rv); |
|
1532 return rv; |
|
1533 } |
|
1534 |
|
1535 nsCOMPtr<nsIHttpChannelInternal> |
|
1536 internalHttpChannel(do_QueryInterface(channel)); |
|
1537 if (internalHttpChannel) |
|
1538 internalHttpChannel->SetLoadAsBlocking(!aLoadData->mWasAlternate); |
|
1539 |
|
1540 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); |
|
1541 if (httpChannel) { |
|
1542 // send a minimal Accept header for text/css |
|
1543 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), |
|
1544 NS_LITERAL_CSTRING("text/css,*/*;q=0.1"), |
|
1545 false); |
|
1546 nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI(); |
|
1547 if (referrerURI) |
|
1548 httpChannel->SetReferrer(referrerURI); |
|
1549 |
|
1550 // Set the initiator type |
|
1551 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel)); |
|
1552 if (timedChannel) { |
|
1553 if (aLoadData->mParentData) { |
|
1554 timedChannel->SetInitiatorType(NS_LITERAL_STRING("css")); |
|
1555 } else { |
|
1556 timedChannel->SetInitiatorType(NS_LITERAL_STRING("link")); |
|
1557 } |
|
1558 } |
|
1559 } |
|
1560 |
|
1561 // Now tell the channel we expect text/css data back.... We do |
|
1562 // this before opening it, so it's only treated as a hint. |
|
1563 channel->SetContentType(NS_LITERAL_CSTRING("text/css")); |
|
1564 |
|
1565 if (aLoadData->mLoaderPrincipal) { |
|
1566 bool inherit; |
|
1567 rv = NS_URIChainHasFlags(aLoadData->mURI, |
|
1568 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, |
|
1569 &inherit); |
|
1570 if ((NS_SUCCEEDED(rv) && inherit) || |
|
1571 (nsContentUtils::URIIsLocalFile(aLoadData->mURI) && |
|
1572 NS_SUCCEEDED(aLoadData->mLoaderPrincipal-> |
|
1573 CheckMayLoad(aLoadData->mURI, false, false)))) { |
|
1574 channel->SetOwner(aLoadData->mLoaderPrincipal); |
|
1575 } |
|
1576 } |
|
1577 |
|
1578 // We don't have to hold on to the stream loader. The ownership |
|
1579 // model is: Necko owns the stream loader, which owns the load data, |
|
1580 // which owns us |
|
1581 nsCOMPtr<nsIUnicharStreamLoader> streamLoader; |
|
1582 rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData); |
|
1583 if (NS_FAILED(rv)) { |
|
1584 #ifdef DEBUG |
|
1585 mSyncCallback = false; |
|
1586 #endif |
|
1587 LOG_ERROR((" Failed to create stream loader")); |
|
1588 SheetComplete(aLoadData, rv); |
|
1589 return rv; |
|
1590 } |
|
1591 |
|
1592 nsCOMPtr<nsIStreamListener> channelListener; |
|
1593 CORSMode ourCORSMode = aLoadData->mSheet->GetCORSMode(); |
|
1594 if (ourCORSMode != CORS_NONE) { |
|
1595 bool withCredentials = (ourCORSMode == CORS_USE_CREDENTIALS); |
|
1596 LOG((" Doing CORS-enabled load; credentials %d", withCredentials)); |
|
1597 nsRefPtr<nsCORSListenerProxy> corsListener = |
|
1598 new nsCORSListenerProxy(streamLoader, aLoadData->mLoaderPrincipal, |
|
1599 withCredentials); |
|
1600 rv = corsListener->Init(channel); |
|
1601 if (NS_FAILED(rv)) { |
|
1602 #ifdef DEBUG |
|
1603 mSyncCallback = false; |
|
1604 #endif |
|
1605 LOG_ERROR((" Initial CORS check failed")); |
|
1606 SheetComplete(aLoadData, rv); |
|
1607 return rv; |
|
1608 } |
|
1609 channelListener = corsListener; |
|
1610 } else { |
|
1611 channelListener = streamLoader; |
|
1612 } |
|
1613 |
|
1614 if (mDocument) { |
|
1615 mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), |
|
1616 nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument); |
|
1617 } |
|
1618 |
|
1619 rv = channel->AsyncOpen(channelListener, nullptr); |
|
1620 |
|
1621 #ifdef DEBUG |
|
1622 mSyncCallback = false; |
|
1623 #endif |
|
1624 |
|
1625 if (NS_FAILED(rv)) { |
|
1626 LOG_ERROR((" Failed to create stream loader")); |
|
1627 SheetComplete(aLoadData, rv); |
|
1628 return rv; |
|
1629 } |
|
1630 |
|
1631 mSheets->mLoadingDatas.Put(&key, aLoadData); |
|
1632 aLoadData->mIsLoading = true; |
|
1633 |
|
1634 return NS_OK; |
|
1635 } |
|
1636 |
|
1637 /** |
|
1638 * ParseSheet handles parsing the data stream. The main idea here is |
|
1639 * to push the current load data onto the parse stack before letting |
|
1640 * the CSS parser at the data stream. That lets us handle @import |
|
1641 * correctly. |
|
1642 */ |
|
1643 nsresult |
|
1644 Loader::ParseSheet(const nsAString& aInput, |
|
1645 SheetLoadData* aLoadData, |
|
1646 bool& aCompleted) |
|
1647 { |
|
1648 LOG(("css::Loader::ParseSheet")); |
|
1649 NS_PRECONDITION(aLoadData, "Must have load data"); |
|
1650 NS_PRECONDITION(aLoadData->mSheet, "Must have sheet to parse into"); |
|
1651 |
|
1652 aCompleted = false; |
|
1653 |
|
1654 nsCSSParser parser(this, aLoadData->mSheet); |
|
1655 |
|
1656 // Push our load data on the stack so any kids can pick it up |
|
1657 mParsingDatas.AppendElement(aLoadData); |
|
1658 nsIURI* sheetURI = aLoadData->mSheet->GetSheetURI(); |
|
1659 nsIURI* baseURI = aLoadData->mSheet->GetBaseURI(); |
|
1660 nsresult rv = parser.ParseSheet(aInput, sheetURI, baseURI, |
|
1661 aLoadData->mSheet->Principal(), |
|
1662 aLoadData->mLineNumber, |
|
1663 aLoadData->mAllowUnsafeRules); |
|
1664 mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1); |
|
1665 |
|
1666 if (NS_FAILED(rv)) { |
|
1667 LOG_ERROR((" Low-level error in parser!")); |
|
1668 SheetComplete(aLoadData, rv); |
|
1669 return rv; |
|
1670 } |
|
1671 |
|
1672 NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad, |
|
1673 "Sync load has leftover pending children!"); |
|
1674 |
|
1675 if (aLoadData->mPendingChildren == 0) { |
|
1676 LOG((" No pending kids from parse")); |
|
1677 aCompleted = true; |
|
1678 SheetComplete(aLoadData, NS_OK); |
|
1679 } |
|
1680 // Otherwise, the children are holding strong refs to the data and |
|
1681 // will call SheetComplete() on it when they complete. |
|
1682 |
|
1683 return NS_OK; |
|
1684 } |
|
1685 |
|
1686 /** |
|
1687 * SheetComplete is the do-it-all cleanup function. It removes the |
|
1688 * load data from the "loading" hashtable, adds the sheet to the |
|
1689 * "completed" hashtable, massages the XUL cache, handles siblings of |
|
1690 * the load data (other loads for the same URI), handles unblocking |
|
1691 * blocked parent loads as needed, and most importantly calls |
|
1692 * NS_RELEASE on the load data to destroy the whole mess. |
|
1693 */ |
|
1694 void |
|
1695 Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus) |
|
1696 { |
|
1697 LOG(("css::Loader::SheetComplete")); |
|
1698 |
|
1699 // 8 is probably big enough for all our common cases. It's not likely that |
|
1700 // imports will nest more than 8 deep, and multiple sheets with the same URI |
|
1701 // are rare. |
|
1702 nsAutoTArray<nsRefPtr<SheetLoadData>, 8> datasToNotify; |
|
1703 DoSheetComplete(aLoadData, aStatus, datasToNotify); |
|
1704 |
|
1705 // Now it's safe to go ahead and notify observers |
|
1706 uint32_t count = datasToNotify.Length(); |
|
1707 mDatasToNotifyOn += count; |
|
1708 for (uint32_t i = 0; i < count; ++i) { |
|
1709 --mDatasToNotifyOn; |
|
1710 |
|
1711 SheetLoadData* data = datasToNotify[i]; |
|
1712 NS_ASSERTION(data && data->mMustNotify, "How did this data get here?"); |
|
1713 if (data->mObserver) { |
|
1714 LOG((" Notifying observer 0x%x for data 0x%x. wasAlternate: %d", |
|
1715 data->mObserver.get(), data, data->mWasAlternate)); |
|
1716 data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate, |
|
1717 aStatus); |
|
1718 } |
|
1719 |
|
1720 nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers); |
|
1721 nsCOMPtr<nsICSSLoaderObserver> obs; |
|
1722 while (iter.HasMore()) { |
|
1723 obs = iter.GetNext(); |
|
1724 LOG((" Notifying global observer 0x%x for data 0x%s. wasAlternate: %d", |
|
1725 obs.get(), data, data->mWasAlternate)); |
|
1726 obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus); |
|
1727 } |
|
1728 } |
|
1729 |
|
1730 if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) { |
|
1731 LOG((" No more loading sheets; starting alternates")); |
|
1732 StartAlternateLoads(); |
|
1733 } |
|
1734 } |
|
1735 |
|
1736 void |
|
1737 Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus, |
|
1738 LoadDataArray& aDatasToNotify) |
|
1739 { |
|
1740 LOG(("css::Loader::DoSheetComplete")); |
|
1741 NS_PRECONDITION(aLoadData, "Must have a load data!"); |
|
1742 NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet"); |
|
1743 NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now."); |
|
1744 |
|
1745 LOG(("Load completed, status: 0x%x", aStatus)); |
|
1746 |
|
1747 // Twiddle the hashtables |
|
1748 if (aLoadData->mURI) { |
|
1749 LOG_URI(" Finished loading: '%s'", aLoadData->mURI); |
|
1750 // Remove the data from the list of loading datas |
|
1751 if (aLoadData->mIsLoading) { |
|
1752 URIPrincipalAndCORSModeHashKey key(aLoadData->mURI, |
|
1753 aLoadData->mLoaderPrincipal, |
|
1754 aLoadData->mSheet->GetCORSMode()); |
|
1755 #ifdef DEBUG |
|
1756 SheetLoadData *loadingData; |
|
1757 NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) && |
|
1758 loadingData == aLoadData, |
|
1759 "Bad loading table"); |
|
1760 #endif |
|
1761 |
|
1762 mSheets->mLoadingDatas.Remove(&key); |
|
1763 aLoadData->mIsLoading = false; |
|
1764 } |
|
1765 } |
|
1766 |
|
1767 // Go through and deal with the whole linked list. |
|
1768 SheetLoadData* data = aLoadData; |
|
1769 while (data) { |
|
1770 if (!data->mSheetAlreadyComplete) { |
|
1771 // If mSheetAlreadyComplete, then the sheet could well be modified between |
|
1772 // when we posted the async call to SheetComplete and now, since the sheet |
|
1773 // was page-accessible during that whole time. |
|
1774 NS_ABORT_IF_FALSE(!data->mSheet->IsModified(), |
|
1775 "should not get marked modified during parsing"); |
|
1776 data->mSheet->SetComplete(); |
|
1777 data->ScheduleLoadEventIfNeeded(aStatus); |
|
1778 } |
|
1779 if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) { |
|
1780 // Don't notify here so we don't trigger script. Remember the |
|
1781 // info we need to notify, then do it later when it's safe. |
|
1782 aDatasToNotify.AppendElement(data); |
|
1783 |
|
1784 // On append failure, just press on. We'll fail to notify the observer, |
|
1785 // but not much we can do about that.... |
|
1786 } |
|
1787 |
|
1788 NS_ASSERTION(!data->mParentData || |
|
1789 data->mParentData->mPendingChildren != 0, |
|
1790 "Broken pending child count on our parent"); |
|
1791 |
|
1792 // If we have a parent, our parent is no longer being parsed, and |
|
1793 // we are the last pending child, then our load completion |
|
1794 // completes the parent too. Note that the parent _can_ still be |
|
1795 // being parsed (eg if the child (us) failed to open the channel |
|
1796 // or some such). |
|
1797 if (data->mParentData && |
|
1798 --(data->mParentData->mPendingChildren) == 0 && |
|
1799 !mParsingDatas.Contains(data->mParentData)) { |
|
1800 DoSheetComplete(data->mParentData, aStatus, aDatasToNotify); |
|
1801 } |
|
1802 |
|
1803 data = data->mNext; |
|
1804 } |
|
1805 |
|
1806 // Now that it's marked complete, put the sheet in our cache. |
|
1807 // If we ever start doing this for failure aStatus, we'll need to |
|
1808 // adjust the PostLoadEvent code that thinks anything already |
|
1809 // complete must have loaded succesfully. |
|
1810 if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) { |
|
1811 // Pick our sheet to cache carefully. Ideally, we want to cache |
|
1812 // one of the sheets that will be kept alive by a document or |
|
1813 // parent sheet anyway, so that if someone then accesses it via |
|
1814 // CSSOM we won't have extra clones of the inner lying around. |
|
1815 data = aLoadData; |
|
1816 nsCSSStyleSheet* sheet = aLoadData->mSheet; |
|
1817 while (data) { |
|
1818 if (data->mSheet->GetParentSheet() || data->mSheet->GetOwnerNode()) { |
|
1819 sheet = data->mSheet; |
|
1820 break; |
|
1821 } |
|
1822 data = data->mNext; |
|
1823 } |
|
1824 #ifdef MOZ_XUL |
|
1825 if (IsChromeURI(aLoadData->mURI)) { |
|
1826 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); |
|
1827 if (cache && cache->IsEnabled()) { |
|
1828 if (!cache->GetStyleSheet(aLoadData->mURI)) { |
|
1829 LOG((" Putting sheet in XUL prototype cache")); |
|
1830 NS_ASSERTION(sheet->IsComplete(), |
|
1831 "Should only be caching complete sheets"); |
|
1832 cache->PutStyleSheet(sheet); |
|
1833 } |
|
1834 } |
|
1835 } |
|
1836 else { |
|
1837 #endif |
|
1838 URIPrincipalAndCORSModeHashKey key(aLoadData->mURI, |
|
1839 aLoadData->mLoaderPrincipal, |
|
1840 aLoadData->mSheet->GetCORSMode()); |
|
1841 NS_ASSERTION(sheet->IsComplete(), |
|
1842 "Should only be caching complete sheets"); |
|
1843 mSheets->mCompleteSheets.Put(&key, sheet); |
|
1844 #ifdef MOZ_XUL |
|
1845 } |
|
1846 #endif |
|
1847 } |
|
1848 |
|
1849 NS_RELEASE(aLoadData); // this will release parents and siblings and all that |
|
1850 } |
|
1851 |
|
1852 nsresult |
|
1853 Loader::LoadInlineStyle(nsIContent* aElement, |
|
1854 const nsAString& aBuffer, |
|
1855 uint32_t aLineNumber, |
|
1856 const nsAString& aTitle, |
|
1857 const nsAString& aMedia, |
|
1858 Element* aScopeElement, |
|
1859 nsICSSLoaderObserver* aObserver, |
|
1860 bool* aCompleted, |
|
1861 bool* aIsAlternate) |
|
1862 { |
|
1863 LOG(("css::Loader::LoadInlineStyle")); |
|
1864 NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?"); |
|
1865 |
|
1866 *aCompleted = true; |
|
1867 |
|
1868 if (!mEnabled) { |
|
1869 LOG_WARN((" Not enabled")); |
|
1870 return NS_ERROR_NOT_AVAILABLE; |
|
1871 } |
|
1872 |
|
1873 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED); |
|
1874 |
|
1875 nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement)); |
|
1876 NS_ASSERTION(owningElement, "Element is not a style linking element!"); |
|
1877 |
|
1878 // Since we're not planning to load a URI, no need to hand a principal to the |
|
1879 // load data or to CreateSheet(). Also, OK to use CORS_NONE for the CORS |
|
1880 // mode. |
|
1881 StyleSheetState state; |
|
1882 nsRefPtr<nsCSSStyleSheet> sheet; |
|
1883 nsresult rv = CreateSheet(nullptr, aElement, nullptr, CORS_NONE, false, false, |
|
1884 aTitle, state, aIsAlternate, getter_AddRefs(sheet)); |
|
1885 NS_ENSURE_SUCCESS(rv, rv); |
|
1886 NS_ASSERTION(state == eSheetNeedsParser, |
|
1887 "Inline sheets should not be cached"); |
|
1888 |
|
1889 LOG((" Sheet is alternate: %d", *aIsAlternate)); |
|
1890 |
|
1891 PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate); |
|
1892 |
|
1893 if (aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) { |
|
1894 ShadowRoot* containingShadow = aElement->GetContainingShadow(); |
|
1895 MOZ_ASSERT(containingShadow); |
|
1896 containingShadow->InsertSheet(sheet, aElement); |
|
1897 } else { |
|
1898 rv = InsertSheetInDoc(sheet, aElement, mDocument); |
|
1899 NS_ENSURE_SUCCESS(rv, rv); |
|
1900 } |
|
1901 |
|
1902 SheetLoadData* data = new SheetLoadData(this, aTitle, nullptr, sheet, |
|
1903 owningElement, *aIsAlternate, |
|
1904 aObserver, nullptr); |
|
1905 |
|
1906 // We never actually load this, so just set its principal directly |
|
1907 sheet->SetPrincipal(aElement->NodePrincipal()); |
|
1908 |
|
1909 NS_ADDREF(data); |
|
1910 data->mLineNumber = aLineNumber; |
|
1911 // Parse completion releases the load data |
|
1912 rv = ParseSheet(aBuffer, data, *aCompleted); |
|
1913 NS_ENSURE_SUCCESS(rv, rv); |
|
1914 |
|
1915 // If aCompleted is true, |data| may well be deleted by now. |
|
1916 if (!*aCompleted) { |
|
1917 data->mMustNotify = true; |
|
1918 } |
|
1919 return rv; |
|
1920 } |
|
1921 |
|
1922 nsresult |
|
1923 Loader::LoadStyleLink(nsIContent* aElement, |
|
1924 nsIURI* aURL, |
|
1925 const nsAString& aTitle, |
|
1926 const nsAString& aMedia, |
|
1927 bool aHasAlternateRel, |
|
1928 CORSMode aCORSMode, |
|
1929 nsICSSLoaderObserver* aObserver, |
|
1930 bool* aIsAlternate) |
|
1931 { |
|
1932 LOG(("css::Loader::LoadStyleLink")); |
|
1933 NS_PRECONDITION(aURL, "Must have URL to load"); |
|
1934 NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?"); |
|
1935 |
|
1936 LOG_URI(" Link uri: '%s'", aURL); |
|
1937 LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle).get())); |
|
1938 LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia).get())); |
|
1939 LOG((" Link alternate rel: %d", aHasAlternateRel)); |
|
1940 |
|
1941 if (!mEnabled) { |
|
1942 LOG_WARN((" Not enabled")); |
|
1943 return NS_ERROR_NOT_AVAILABLE; |
|
1944 } |
|
1945 |
|
1946 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED); |
|
1947 |
|
1948 nsIPrincipal* principal = |
|
1949 aElement ? aElement->NodePrincipal() : mDocument->NodePrincipal(); |
|
1950 |
|
1951 nsISupports* context = aElement; |
|
1952 if (!context) { |
|
1953 context = mDocument; |
|
1954 } |
|
1955 nsresult rv = CheckLoadAllowed(principal, aURL, context); |
|
1956 if (NS_FAILED(rv)) return rv; |
|
1957 |
|
1958 LOG((" Passed load check")); |
|
1959 |
|
1960 StyleSheetState state; |
|
1961 nsRefPtr<nsCSSStyleSheet> sheet; |
|
1962 rv = CreateSheet(aURL, aElement, principal, aCORSMode, false, |
|
1963 aHasAlternateRel, aTitle, state, aIsAlternate, |
|
1964 getter_AddRefs(sheet)); |
|
1965 NS_ENSURE_SUCCESS(rv, rv); |
|
1966 |
|
1967 LOG((" Sheet is alternate: %d", *aIsAlternate)); |
|
1968 |
|
1969 PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate); |
|
1970 |
|
1971 rv = InsertSheetInDoc(sheet, aElement, mDocument); |
|
1972 NS_ENSURE_SUCCESS(rv, rv); |
|
1973 |
|
1974 nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement)); |
|
1975 |
|
1976 if (state == eSheetComplete) { |
|
1977 LOG((" Sheet already complete: 0x%p", |
|
1978 static_cast<void*>(sheet.get()))); |
|
1979 if (aObserver || !mObservers.IsEmpty() || owningElement) { |
|
1980 rv = PostLoadEvent(aURL, sheet, aObserver, *aIsAlternate, |
|
1981 owningElement); |
|
1982 return rv; |
|
1983 } |
|
1984 |
|
1985 return NS_OK; |
|
1986 } |
|
1987 |
|
1988 // Now we need to actually load it |
|
1989 SheetLoadData* data = new SheetLoadData(this, aTitle, aURL, sheet, |
|
1990 owningElement, *aIsAlternate, |
|
1991 aObserver, principal); |
|
1992 NS_ADDREF(data); |
|
1993 |
|
1994 // If we have to parse and it's an alternate non-inline, defer it |
|
1995 if (aURL && state == eSheetNeedsParser && mSheets->mLoadingDatas.Count() != 0 && |
|
1996 *aIsAlternate) { |
|
1997 LOG((" Deferring alternate sheet load")); |
|
1998 URIPrincipalAndCORSModeHashKey key(data->mURI, data->mLoaderPrincipal, |
|
1999 data->mSheet->GetCORSMode()); |
|
2000 mSheets->mPendingDatas.Put(&key, data); |
|
2001 |
|
2002 data->mMustNotify = true; |
|
2003 return NS_OK; |
|
2004 } |
|
2005 |
|
2006 // Load completion will free the data |
|
2007 rv = LoadSheet(data, state); |
|
2008 NS_ENSURE_SUCCESS(rv, rv); |
|
2009 |
|
2010 data->mMustNotify = true; |
|
2011 return rv; |
|
2012 } |
|
2013 |
|
2014 static bool |
|
2015 HaveAncestorDataWithURI(SheetLoadData *aData, nsIURI *aURI) |
|
2016 { |
|
2017 if (!aData->mURI) { |
|
2018 // Inline style; this won't have any ancestors |
|
2019 NS_ABORT_IF_FALSE(!aData->mParentData, |
|
2020 "How does inline style have a parent?"); |
|
2021 return false; |
|
2022 } |
|
2023 |
|
2024 bool equal; |
|
2025 if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) { |
|
2026 return true; |
|
2027 } |
|
2028 |
|
2029 // Datas down the mNext chain have the same URI as aData, so we |
|
2030 // don't have to compare to them. But they might have different |
|
2031 // parents, and we have to check all of those. |
|
2032 while (aData) { |
|
2033 if (aData->mParentData && |
|
2034 HaveAncestorDataWithURI(aData->mParentData, aURI)) { |
|
2035 return true; |
|
2036 } |
|
2037 |
|
2038 aData = aData->mNext; |
|
2039 } |
|
2040 |
|
2041 return false; |
|
2042 } |
|
2043 |
|
2044 nsresult |
|
2045 Loader::LoadChildSheet(nsCSSStyleSheet* aParentSheet, |
|
2046 nsIURI* aURL, |
|
2047 nsMediaList* aMedia, |
|
2048 ImportRule* aParentRule) |
|
2049 { |
|
2050 LOG(("css::Loader::LoadChildSheet")); |
|
2051 NS_PRECONDITION(aURL, "Must have a URI to load"); |
|
2052 NS_PRECONDITION(aParentSheet, "Must have a parent sheet"); |
|
2053 |
|
2054 if (!mEnabled) { |
|
2055 LOG_WARN((" Not enabled")); |
|
2056 return NS_ERROR_NOT_AVAILABLE; |
|
2057 } |
|
2058 |
|
2059 LOG_URI(" Child uri: '%s'", aURL); |
|
2060 |
|
2061 nsCOMPtr<nsIDOMNode> owningNode; |
|
2062 |
|
2063 // check for an owning document: if none, don't bother walking up the parent |
|
2064 // sheets |
|
2065 if (aParentSheet->GetOwningDocument()) { |
|
2066 nsCOMPtr<nsIDOMStyleSheet> nextParentSheet(aParentSheet); |
|
2067 NS_ENSURE_TRUE(nextParentSheet, NS_ERROR_FAILURE); //Not a stylesheet!? |
|
2068 |
|
2069 nsCOMPtr<nsIDOMStyleSheet> topSheet; |
|
2070 //traverse our way to the top-most sheet |
|
2071 do { |
|
2072 topSheet.swap(nextParentSheet); |
|
2073 topSheet->GetParentStyleSheet(getter_AddRefs(nextParentSheet)); |
|
2074 } while (nextParentSheet); |
|
2075 |
|
2076 topSheet->GetOwnerNode(getter_AddRefs(owningNode)); |
|
2077 } |
|
2078 |
|
2079 nsISupports* context = owningNode; |
|
2080 if (!context) { |
|
2081 context = mDocument; |
|
2082 } |
|
2083 |
|
2084 nsIPrincipal* principal = aParentSheet->Principal(); |
|
2085 nsresult rv = CheckLoadAllowed(principal, aURL, context); |
|
2086 if (NS_FAILED(rv)) return rv; |
|
2087 |
|
2088 LOG((" Passed load check")); |
|
2089 |
|
2090 SheetLoadData* parentData = nullptr; |
|
2091 nsCOMPtr<nsICSSLoaderObserver> observer; |
|
2092 |
|
2093 int32_t count = mParsingDatas.Length(); |
|
2094 if (count > 0) { |
|
2095 LOG((" Have a parent load")); |
|
2096 parentData = mParsingDatas.ElementAt(count - 1); |
|
2097 // Check for cycles |
|
2098 if (HaveAncestorDataWithURI(parentData, aURL)) { |
|
2099 // Houston, we have a loop, blow off this child and pretend this never |
|
2100 // happened |
|
2101 LOG_ERROR((" @import cycle detected, dropping load")); |
|
2102 return NS_OK; |
|
2103 } |
|
2104 |
|
2105 NS_ASSERTION(parentData->mSheet == aParentSheet, |
|
2106 "Unexpected call to LoadChildSheet"); |
|
2107 } else { |
|
2108 LOG((" No parent load; must be CSSOM")); |
|
2109 // No parent load data, so the sheet will need to be notified when |
|
2110 // we finish, if it can be, if we do the load asynchronously. |
|
2111 observer = aParentSheet; |
|
2112 } |
|
2113 |
|
2114 // Now that we know it's safe to load this (passes security check and not a |
|
2115 // loop) do so. |
|
2116 nsRefPtr<nsCSSStyleSheet> sheet; |
|
2117 bool isAlternate; |
|
2118 StyleSheetState state; |
|
2119 const nsSubstring& empty = EmptyString(); |
|
2120 // For now, use CORS_NONE for child sheets |
|
2121 rv = CreateSheet(aURL, nullptr, principal, CORS_NONE, |
|
2122 parentData ? parentData->mSyncLoad : false, |
|
2123 false, empty, state, &isAlternate, getter_AddRefs(sheet)); |
|
2124 NS_ENSURE_SUCCESS(rv, rv); |
|
2125 |
|
2126 PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate); |
|
2127 |
|
2128 rv = InsertChildSheet(sheet, aParentSheet, aParentRule); |
|
2129 NS_ENSURE_SUCCESS(rv, rv); |
|
2130 |
|
2131 if (state == eSheetComplete) { |
|
2132 LOG((" Sheet already complete")); |
|
2133 // We're completely done. No need to notify, even, since the |
|
2134 // @import rule addition/modification will trigger the right style |
|
2135 // changes automatically. |
|
2136 return NS_OK; |
|
2137 } |
|
2138 |
|
2139 SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData, |
|
2140 observer, principal); |
|
2141 |
|
2142 NS_ADDREF(data); |
|
2143 bool syncLoad = data->mSyncLoad; |
|
2144 |
|
2145 // Load completion will release the data |
|
2146 rv = LoadSheet(data, state); |
|
2147 NS_ENSURE_SUCCESS(rv, rv); |
|
2148 |
|
2149 // If syncLoad is true, |data| will be deleted by now. |
|
2150 if (!syncLoad) { |
|
2151 data->mMustNotify = true; |
|
2152 } |
|
2153 return rv; |
|
2154 } |
|
2155 |
|
2156 nsresult |
|
2157 Loader::LoadSheetSync(nsIURI* aURL, bool aAllowUnsafeRules, |
|
2158 bool aUseSystemPrincipal, |
|
2159 nsCSSStyleSheet** aSheet) |
|
2160 { |
|
2161 LOG(("css::Loader::LoadSheetSync")); |
|
2162 return InternalLoadNonDocumentSheet(aURL, aAllowUnsafeRules, |
|
2163 aUseSystemPrincipal, nullptr, |
|
2164 EmptyCString(), aSheet, nullptr); |
|
2165 } |
|
2166 |
|
2167 nsresult |
|
2168 Loader::LoadSheet(nsIURI* aURL, |
|
2169 nsIPrincipal* aOriginPrincipal, |
|
2170 const nsCString& aCharset, |
|
2171 nsICSSLoaderObserver* aObserver, |
|
2172 nsCSSStyleSheet** aSheet) |
|
2173 { |
|
2174 LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call")); |
|
2175 NS_PRECONDITION(aSheet, "aSheet is null"); |
|
2176 return InternalLoadNonDocumentSheet(aURL, false, false, |
|
2177 aOriginPrincipal, aCharset, |
|
2178 aSheet, aObserver); |
|
2179 } |
|
2180 |
|
2181 nsresult |
|
2182 Loader::LoadSheet(nsIURI* aURL, |
|
2183 nsIPrincipal* aOriginPrincipal, |
|
2184 const nsCString& aCharset, |
|
2185 nsICSSLoaderObserver* aObserver, |
|
2186 CORSMode aCORSMode) |
|
2187 { |
|
2188 LOG(("css::Loader::LoadSheet(aURL, aObserver) api call")); |
|
2189 return InternalLoadNonDocumentSheet(aURL, false, false, |
|
2190 aOriginPrincipal, aCharset, |
|
2191 nullptr, aObserver, aCORSMode); |
|
2192 } |
|
2193 |
|
2194 nsresult |
|
2195 Loader::InternalLoadNonDocumentSheet(nsIURI* aURL, |
|
2196 bool aAllowUnsafeRules, |
|
2197 bool aUseSystemPrincipal, |
|
2198 nsIPrincipal* aOriginPrincipal, |
|
2199 const nsCString& aCharset, |
|
2200 nsCSSStyleSheet** aSheet, |
|
2201 nsICSSLoaderObserver* aObserver, |
|
2202 CORSMode aCORSMode) |
|
2203 { |
|
2204 NS_PRECONDITION(aURL, "Must have a URI to load"); |
|
2205 NS_PRECONDITION(aSheet || aObserver, "Sheet and observer can't both be null"); |
|
2206 NS_PRECONDITION(!aUseSystemPrincipal || !aObserver, |
|
2207 "Shouldn't load system-principal sheets async"); |
|
2208 NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?"); |
|
2209 |
|
2210 LOG_URI(" Non-document sheet uri: '%s'", aURL); |
|
2211 |
|
2212 if (aSheet) { |
|
2213 *aSheet = nullptr; |
|
2214 } |
|
2215 |
|
2216 if (!mEnabled) { |
|
2217 LOG_WARN((" Not enabled")); |
|
2218 return NS_ERROR_NOT_AVAILABLE; |
|
2219 } |
|
2220 |
|
2221 nsresult rv = CheckLoadAllowed(aOriginPrincipal, aURL, mDocument); |
|
2222 if (NS_FAILED(rv)) { |
|
2223 return rv; |
|
2224 } |
|
2225 |
|
2226 StyleSheetState state; |
|
2227 bool isAlternate; |
|
2228 nsRefPtr<nsCSSStyleSheet> sheet; |
|
2229 bool syncLoad = (aObserver == nullptr); |
|
2230 const nsSubstring& empty = EmptyString(); |
|
2231 |
|
2232 rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aCORSMode, syncLoad, false, |
|
2233 empty, state, &isAlternate, getter_AddRefs(sheet)); |
|
2234 NS_ENSURE_SUCCESS(rv, rv); |
|
2235 |
|
2236 PrepareSheet(sheet, empty, empty, nullptr, nullptr, isAlternate); |
|
2237 |
|
2238 if (state == eSheetComplete) { |
|
2239 LOG((" Sheet already complete")); |
|
2240 if (aObserver || !mObservers.IsEmpty()) { |
|
2241 rv = PostLoadEvent(aURL, sheet, aObserver, false, nullptr); |
|
2242 } |
|
2243 if (aSheet) { |
|
2244 sheet.swap(*aSheet); |
|
2245 } |
|
2246 return rv; |
|
2247 } |
|
2248 |
|
2249 SheetLoadData* data = |
|
2250 new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules, |
|
2251 aUseSystemPrincipal, aCharset, aObserver, |
|
2252 aOriginPrincipal); |
|
2253 |
|
2254 NS_ADDREF(data); |
|
2255 rv = LoadSheet(data, state); |
|
2256 NS_ENSURE_SUCCESS(rv, rv); |
|
2257 |
|
2258 if (aSheet) { |
|
2259 sheet.swap(*aSheet); |
|
2260 } |
|
2261 if (aObserver) { |
|
2262 data->mMustNotify = true; |
|
2263 } |
|
2264 |
|
2265 return rv; |
|
2266 } |
|
2267 |
|
2268 nsresult |
|
2269 Loader::PostLoadEvent(nsIURI* aURI, |
|
2270 nsCSSStyleSheet* aSheet, |
|
2271 nsICSSLoaderObserver* aObserver, |
|
2272 bool aWasAlternate, |
|
2273 nsIStyleSheetLinkingElement* aElement) |
|
2274 { |
|
2275 LOG(("css::Loader::PostLoadEvent")); |
|
2276 NS_PRECONDITION(aSheet, "Must have sheet"); |
|
2277 NS_PRECONDITION(aObserver || !mObservers.IsEmpty() || aElement, |
|
2278 "Must have observer or element"); |
|
2279 |
|
2280 nsRefPtr<SheetLoadData> evt = |
|
2281 new SheetLoadData(this, EmptyString(), // title doesn't matter here |
|
2282 aURI, |
|
2283 aSheet, |
|
2284 aElement, |
|
2285 aWasAlternate, |
|
2286 aObserver, |
|
2287 nullptr); |
|
2288 NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY); |
|
2289 |
|
2290 if (!mPostedEvents.AppendElement(evt)) { |
|
2291 return NS_ERROR_OUT_OF_MEMORY; |
|
2292 } |
|
2293 |
|
2294 nsresult rv = NS_DispatchToCurrentThread(evt); |
|
2295 if (NS_FAILED(rv)) { |
|
2296 NS_WARNING("failed to dispatch stylesheet load event"); |
|
2297 mPostedEvents.RemoveElement(evt); |
|
2298 } else { |
|
2299 // We'll unblock onload when we handle the event. |
|
2300 if (mDocument) { |
|
2301 mDocument->BlockOnload(); |
|
2302 } |
|
2303 |
|
2304 // We want to notify the observer for this data. |
|
2305 evt->mMustNotify = true; |
|
2306 evt->mSheetAlreadyComplete = true; |
|
2307 |
|
2308 // If we get to this code, aSheet loaded correctly at some point, so |
|
2309 // we can just use NS_OK for the status. Note that we do this here |
|
2310 // and not from inside our SheetComplete so that we don't end up |
|
2311 // running the load event async. |
|
2312 evt->ScheduleLoadEventIfNeeded(NS_OK); |
|
2313 } |
|
2314 |
|
2315 return rv; |
|
2316 } |
|
2317 |
|
2318 void |
|
2319 Loader::HandleLoadEvent(SheetLoadData* aEvent) |
|
2320 { |
|
2321 // XXXbz can't assert this yet.... May not have an observer because |
|
2322 // we're unblocking the parser |
|
2323 // NS_ASSERTION(aEvent->mObserver, "Must have observer"); |
|
2324 NS_ASSERTION(aEvent->mSheet, "Must have sheet"); |
|
2325 |
|
2326 // Very important: this needs to come before the SheetComplete call |
|
2327 // below, so that HasPendingLoads() will test true as needed under |
|
2328 // notifications we send from that SheetComplete call. |
|
2329 mPostedEvents.RemoveElement(aEvent); |
|
2330 |
|
2331 if (!aEvent->mIsCancelled) { |
|
2332 // SheetComplete will call Release(), so give it a reference to do |
|
2333 // that with. |
|
2334 NS_ADDREF(aEvent); |
|
2335 SheetComplete(aEvent, NS_OK); |
|
2336 } |
|
2337 |
|
2338 if (mDocument) { |
|
2339 mDocument->UnblockOnload(true); |
|
2340 } |
|
2341 } |
|
2342 |
|
2343 static PLDHashOperator |
|
2344 StopLoadingSheetCallback(URIPrincipalAndCORSModeHashKey* aKey, |
|
2345 SheetLoadData*& aData, |
|
2346 void* aClosure) |
|
2347 { |
|
2348 NS_PRECONDITION(aData, "Must have a data!"); |
|
2349 NS_PRECONDITION(aClosure, "Must have a loader"); |
|
2350 |
|
2351 aData->mIsLoading = false; // we will handle the removal right here |
|
2352 aData->mIsCancelled = true; |
|
2353 |
|
2354 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData); |
|
2355 |
|
2356 return PL_DHASH_REMOVE; |
|
2357 } |
|
2358 |
|
2359 nsresult |
|
2360 Loader::Stop() |
|
2361 { |
|
2362 uint32_t pendingCount = |
|
2363 mSheets ? mSheets->mPendingDatas.Count() : 0; |
|
2364 uint32_t loadingCount = |
|
2365 mSheets ? mSheets->mLoadingDatas.Count() : 0; |
|
2366 LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length()); |
|
2367 |
|
2368 if (pendingCount) { |
|
2369 mSheets->mPendingDatas.Enumerate(StopLoadingSheetCallback, &arr); |
|
2370 } |
|
2371 if (loadingCount) { |
|
2372 mSheets->mLoadingDatas.Enumerate(StopLoadingSheetCallback, &arr); |
|
2373 } |
|
2374 |
|
2375 uint32_t i; |
|
2376 for (i = 0; i < mPostedEvents.Length(); ++i) { |
|
2377 SheetLoadData* data = mPostedEvents[i]; |
|
2378 data->mIsCancelled = true; |
|
2379 if (arr.AppendElement(data)) { |
|
2380 // SheetComplete() calls Release(), so give this an extra ref. |
|
2381 NS_ADDREF(data); |
|
2382 } |
|
2383 #ifdef DEBUG |
|
2384 else { |
|
2385 NS_NOTREACHED("We preallocated this memory... shouldn't really fail, " |
|
2386 "except we never check that preallocation succeeds."); |
|
2387 } |
|
2388 #endif |
|
2389 } |
|
2390 mPostedEvents.Clear(); |
|
2391 |
|
2392 mDatasToNotifyOn += arr.Length(); |
|
2393 for (i = 0; i < arr.Length(); ++i) { |
|
2394 --mDatasToNotifyOn; |
|
2395 SheetComplete(arr[i], NS_BINDING_ABORTED); |
|
2396 } |
|
2397 return NS_OK; |
|
2398 } |
|
2399 |
|
2400 bool |
|
2401 Loader::HasPendingLoads() |
|
2402 { |
|
2403 return |
|
2404 (mSheets && mSheets->mLoadingDatas.Count() != 0) || |
|
2405 (mSheets && mSheets->mPendingDatas.Count() != 0) || |
|
2406 mPostedEvents.Length() != 0 || |
|
2407 mDatasToNotifyOn != 0; |
|
2408 } |
|
2409 |
|
2410 nsresult |
|
2411 Loader::AddObserver(nsICSSLoaderObserver* aObserver) |
|
2412 { |
|
2413 NS_PRECONDITION(aObserver, "Must have observer"); |
|
2414 if (mObservers.AppendElementUnlessExists(aObserver)) { |
|
2415 return NS_OK; |
|
2416 } |
|
2417 |
|
2418 return NS_ERROR_OUT_OF_MEMORY; |
|
2419 } |
|
2420 |
|
2421 void |
|
2422 Loader::RemoveObserver(nsICSSLoaderObserver* aObserver) |
|
2423 { |
|
2424 mObservers.RemoveElement(aObserver); |
|
2425 } |
|
2426 |
|
2427 static PLDHashOperator |
|
2428 CollectLoadDatas(URIPrincipalAndCORSModeHashKey *aKey, |
|
2429 SheetLoadData* &aData, |
|
2430 void* aClosure) |
|
2431 { |
|
2432 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData); |
|
2433 return PL_DHASH_REMOVE; |
|
2434 } |
|
2435 |
|
2436 void |
|
2437 Loader::StartAlternateLoads() |
|
2438 { |
|
2439 NS_PRECONDITION(mSheets, "Don't call me!"); |
|
2440 LoadDataArray arr(mSheets->mPendingDatas.Count()); |
|
2441 mSheets->mPendingDatas.Enumerate(CollectLoadDatas, &arr); |
|
2442 |
|
2443 mDatasToNotifyOn += arr.Length(); |
|
2444 for (uint32_t i = 0; i < arr.Length(); ++i) { |
|
2445 --mDatasToNotifyOn; |
|
2446 LoadSheet(arr[i], eSheetNeedsParser); |
|
2447 } |
|
2448 } |
|
2449 |
|
2450 static PLDHashOperator |
|
2451 TraverseSheet(URIPrincipalAndCORSModeHashKey*, |
|
2452 nsCSSStyleSheet* aSheet, |
|
2453 void* aClosure) |
|
2454 { |
|
2455 nsCycleCollectionTraversalCallback* cb = |
|
2456 static_cast<nsCycleCollectionTraversalCallback*>(aClosure); |
|
2457 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "Sheet cache nsCSSLoader"); |
|
2458 cb->NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIStyleSheet*, aSheet)); |
|
2459 return PL_DHASH_NEXT; |
|
2460 } |
|
2461 |
|
2462 void |
|
2463 Loader::TraverseCachedSheets(nsCycleCollectionTraversalCallback& cb) |
|
2464 { |
|
2465 if (mSheets) { |
|
2466 mSheets->mCompleteSheets.EnumerateRead(TraverseSheet, &cb); |
|
2467 } |
|
2468 } |
|
2469 |
|
2470 void |
|
2471 Loader::UnlinkCachedSheets() |
|
2472 { |
|
2473 if (mSheets) { |
|
2474 mSheets->mCompleteSheets.Clear(); |
|
2475 } |
|
2476 } |
|
2477 |
|
2478 struct SheetMemoryCounter { |
|
2479 size_t size; |
|
2480 mozilla::MallocSizeOf mallocSizeOf; |
|
2481 }; |
|
2482 |
|
2483 static size_t |
|
2484 CountSheetMemory(URIPrincipalAndCORSModeHashKey* /* unused */, |
|
2485 const nsRefPtr<nsCSSStyleSheet>& aSheet, |
|
2486 mozilla::MallocSizeOf aMallocSizeOf, |
|
2487 void* /* unused */) |
|
2488 { |
|
2489 // If aSheet has a parent, then its parent will report it so we don't |
|
2490 // have to worry about it here. |
|
2491 // Likewise, if aSheet has an owning node, then the document that |
|
2492 // node is in will report it. |
|
2493 if (aSheet->GetOwnerNode() || aSheet->GetParentSheet()) { |
|
2494 return 0; |
|
2495 } |
|
2496 return aSheet->SizeOfIncludingThis(aMallocSizeOf); |
|
2497 } |
|
2498 |
|
2499 size_t |
|
2500 Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
2501 { |
|
2502 size_t s = aMallocSizeOf(this); |
|
2503 |
|
2504 if (mSheets) { |
|
2505 s += mSheets->mCompleteSheets.SizeOfExcludingThis(CountSheetMemory, aMallocSizeOf); |
|
2506 } |
|
2507 s += mObservers.SizeOfExcludingThis(aMallocSizeOf); |
|
2508 |
|
2509 // Measurement of the following members may be added later if DMD finds it is |
|
2510 // worthwhile: |
|
2511 // - mLoadingDatas: transient, and should be small |
|
2512 // - mPendingDatas: transient, and should be small |
|
2513 // - mParsingDatas: transient, and should be small |
|
2514 // - mPostedEvents: transient, and should be small |
|
2515 // |
|
2516 // The following members aren't measured: |
|
2517 // - mDocument, because it's a weak backpointer |
|
2518 // - mPreferredSheet, because it can be a shared string |
|
2519 |
|
2520 return s; |
|
2521 } |
|
2522 |
|
2523 } // namespace css |
|
2524 } // namespace mozilla |