Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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 */
17 /* loading of CSS style sheets using the network APIs */
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/MemoryReporting.h"
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"
54 #ifdef MOZ_XUL
55 #include "nsXULPrototypeCache.h"
56 #endif
58 #include "nsIMediaList.h"
59 #include "nsIDOMStyleSheet.h"
60 #include "nsError.h"
62 #include "nsIChannelPolicy.h"
63 #include "nsIContentSecurityPolicy.h"
64 #include "nsCycleCollectionParticipant.h"
66 #include "mozilla/dom/EncodingUtils.h"
67 using mozilla::dom::EncodingUtils;
69 using namespace mozilla::dom;
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 */
99 namespace mozilla {
100 namespace css {
102 /*********************************************
103 * Data needed to properly load a stylesheet *
104 *********************************************/
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);
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);
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);
141 already_AddRefed<nsIURI> GetReferrerURI();
143 void ScheduleLoadEventIfNeeded(nsresult aStatus);
145 NS_DECL_ISUPPORTS
146 NS_DECL_NSIRUNNABLE
147 NS_DECL_NSITHREADOBSERVER
148 NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
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
154 // Title needed to pull datas out of the pending datas table when
155 // the preferred title is changed
156 nsString mTitle;
158 // Charset we decided to use for the sheet
159 nsCString mCharset;
161 // URI we're loading. Null for inline sheets
162 nsCOMPtr<nsIURI> mURI;
164 // Should be 1 for non-inline sheets.
165 uint32_t mLineNumber;
167 // The sheet we're loading data for
168 nsRefPtr<nsCSSStyleSheet> mSheet;
170 // Linked list of datas for the same URI as us
171 SheetLoadData* mNext; // strong ref
173 // Load data for the sheet that @import-ed us if we were @import-ed
174 // during the parse
175 SheetLoadData* mParentData; // strong ref
177 // Number of sheets we @import-ed that are still loading
178 uint32_t mPendingChildren;
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;
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;
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;
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;
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;
208 // mWasAlternate is true if the sheet was an alternate when the load data was
209 // created.
210 bool mWasAlternate : 1;
212 // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed
213 // in the loaded sheet.
214 bool mAllowUnsafeRules : 1;
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;
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;
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;
229 // The observer that wishes to be notified of load completion
230 nsCOMPtr<nsICSSLoaderObserver> mObserver;
232 // The principal that identifies who started loading us.
233 nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
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;
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;
245 private:
246 void FireLoadEvent(nsIThreadInternal* aThread);
247 };
249 #ifdef MOZ_LOGGING
250 // #define FORCE_PR_LOG /* Allow logging in the release build */
251 #endif /* MOZ_LOGGING */
252 #include "prlog.h"
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 */
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)
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()
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
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
302 /********************************
303 * SheetLoadData implementation *
304 ********************************/
305 NS_IMPL_ISUPPORTS(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable,
306 nsIThreadObserver)
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 }
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 }
378 NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
379 "Shouldn't use system principal for async loads");
380 }
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);
415 NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
416 "Shouldn't use system principal for async loads");
417 }
419 SheetLoadData::~SheetLoadData()
420 {
421 NS_RELEASE(mLoader);
422 NS_IF_RELEASE(mParentData);
423 NS_IF_RELEASE(mNext);
424 }
426 NS_IMETHODIMP
427 SheetLoadData::Run()
428 {
429 mLoader->HandleLoadEvent(this);
430 return NS_OK;
431 }
433 NS_IMETHODIMP
434 SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
435 {
436 return NS_OK;
437 }
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 }
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 }
461 void
462 SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
463 {
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);
470 // Now fire the event
471 nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
472 NS_ASSERTION(node, "How did that happen???");
474 nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
475 node,
476 NS_SUCCEEDED(mStatus) ?
477 NS_LITERAL_STRING("load") :
478 NS_LITERAL_STRING("error"),
479 false, false);
481 // And unblock onload
482 if (mLoader->mDocument) {
483 mLoader->mDocument->UnblockOnload(true);
484 }
485 }
487 void
488 SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
489 {
490 if (!mOwningElement) {
491 return;
492 }
494 mStatus = aStatus;
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 }
506 /*************************
507 * Loader Implementation *
508 *************************/
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 }
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 }
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 }
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 }
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");
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 }
576 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
577 return PL_DHASH_REMOVE;
578 }
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
596 mPreferredSheet = aTitle;
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);
603 mDatasToNotifyOn += arr.Length();
604 for (uint32_t i = 0; i < arr.Length(); ++i) {
605 --mDatasToNotifyOn;
606 LoadSheet(arr[i], eSheetNeedsParser);
607 }
608 }
610 return NS_OK;
611 }
613 static const char kCharsetSym[] = "@charset \"";
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;
623 if (strncmp(aStyleSheetData,
624 kCharsetSym,
625 sizeof(kCharsetSym) - 1)) {
626 return false;
627 }
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 }
642 // Did not see end quote or semicolon
643 aCharset.Truncate();
644 return false;
645 }
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");
656 LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
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
666 aCharset.Truncate();
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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");
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 }
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 }
815 if (NS_FAILED(aStatus)) {
816 LOG_WARN((" Load failed: status 0x%x", aStatus));
817 mLoader->SheetComplete(this, aStatus);
818 return NS_OK;
819 }
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 }
829 nsCOMPtr<nsIURI> originalURI;
830 channel->GetOriginalURI(getter_AddRefs(originalURI));
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));
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 }
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 }
858 if (NS_FAILED(result)) {
859 LOG_WARN((" Couldn't get principal"));
860 mLoader->SheetComplete(this, result);
861 return NS_OK;
862 }
864 mSheet->SetPrincipal(principal);
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 }
879 nsAutoCString contentType;
880 if (channel) {
881 channel->GetContentType(contentType);
882 }
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.
889 bool validType = contentType.EqualsLiteral("text/css") ||
890 contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
891 contentType.IsEmpty();
893 if (!validType) {
894 const char *errorMessage;
895 uint32_t errorFlag;
896 bool sameOrigin = true;
898 if (mLoaderPrincipal) {
899 bool subsumed;
900 result = mLoaderPrincipal->Subsumes(principal, &subsumed);
901 if (NS_FAILED(result) || !subsumed) {
902 sameOrigin = false;
903 }
904 }
906 if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
907 errorMessage = "MimeNotCssWarn";
908 errorFlag = nsIScriptError::warningFlag;
909 } else {
910 errorMessage = "MimeNotCss";
911 errorFlag = nsIScriptError::errorFlag;
912 }
914 nsAutoCString spec;
915 channelURI->GetSpec(spec);
917 const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec);
918 const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
919 const char16_t *strings[] = { specUTF16.get(), ctypeUTF16.get() };
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);
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 }
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);
942 bool completed;
943 result = mLoader->ParseSheet(aBuffer, this, completed);
944 NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
945 return result;
946 }
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 }
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 }
968 return !aTitle.Equals(mPreferredSheet);
969 }
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 }
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 }
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"));
1016 nsresult rv;
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 }
1028 LOG((" Passed security check"));
1030 // Check with content policy
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());
1043 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
1044 LOG((" Load blocked by content policy"));
1045 return NS_ERROR_CONTENT_BLOCKED;
1046 }
1047 }
1049 return NS_OK;
1050 }
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!");
1076 if (!mSheets) {
1077 mSheets = new Sheets();
1078 }
1080 *aSheet = nullptr;
1081 aSheetState = eSheetStateUnknown;
1083 // Check the alternate state before doing anything else, because it
1084 // can mess with our hashtables.
1085 *aIsAlternate = IsAlternate(aTitle, aHasAlternateRel);
1087 if (aURI) {
1088 aSheetState = eSheetComplete;
1089 nsRefPtr<nsCSSStyleSheet> sheet;
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
1104 bool fromCompleteSheets = false;
1105 if (!sheet) {
1106 // Then our per-document complete sheets.
1107 URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode);
1109 mSheets->mCompleteSheets.Get(&key, getter_AddRefs(sheet));
1110 LOG((" From completed: %p", sheet.get()));
1112 fromCompleteSheets = !!sheet;
1113 }
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");
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 }
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()));
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 }
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()));
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 }
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");
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 }
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 }
1213 nsRefPtr<nsCSSStyleSheet> sheet = new nsCSSStyleSheet(aCORSMode);
1214 sheet->SetURIs(sheetURI, originalURI, baseURI);
1215 sheet.forget(aSheet);
1216 }
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]));
1222 return NS_OK;
1223 }
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!");
1240 nsRefPtr<nsMediaList> mediaList(aMediaList);
1242 if (!aMediaString.IsEmpty()) {
1243 NS_ASSERTION(!aMediaList,
1244 "must not provide both aMediaString and aMediaList");
1245 mediaList = new nsMediaList();
1247 nsCSSParser mediumParser(this);
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 }
1254 aSheet->SetMedia(mediaList);
1256 aSheet->SetTitle(aTitle);
1257 aSheet->SetEnabled(! isAlternate);
1258 aSheet->SetScopeElement(aScopeElement);
1259 }
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");
1283 // XXX Need to cancel pending sheet loads for this element, if any
1285 int32_t sheetCount = aDocument->GetNumberOfStyleSheets();
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 }
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 }
1315 nsCOMPtr<nsINode> sheetOwnerNode = do_QueryInterface(sheetOwner);
1316 NS_ASSERTION(aLinkingContent != sheetOwnerNode,
1317 "Why do we still have our old sheet?");
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 }
1327 ++insertionPoint; // adjust the index to the spot we want to insert in
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 }
1337 aDocument->BeginUpdate(UPDATE_STYLE);
1338 aDocument->InsertStyleSheetAt(aSheet, insertionPoint);
1339 aDocument->EndUpdate(UPDATE_STYLE);
1340 LOG((" Inserting into document at position %d", insertionPoint));
1342 return NS_OK;
1343 }
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?");
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);
1368 aParentSheet->AppendStyleSheet(aSheet);
1369 aParentRule->SetSheet(aSheet); // This sets the ownerRule on the sheet
1371 LOG((" Inserting into parent sheet"));
1372 // LOG((" Inserting into parent sheet at position %d", insertionPoint));
1374 return NS_OK;
1375 }
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.");
1397 LOG_URI(" Load from: '%s'", aLoadData->mURI);
1399 nsresult rv = NS_OK;
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 }
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");
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 }
1425 if (mDocument) {
1426 mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
1427 nsINetworkSeer::LEARN_LOAD_SUBRESOURCE,
1428 mDocument);
1429 }
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 }
1443 NS_ASSERTION(channel, "NS_OpenURI lied?");
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"));
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 }
1459 SheetLoadData* existingData = nullptr;
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 }
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
1483 #ifdef DEBUG
1484 SheetLoadData* removedData;
1485 NS_ASSERTION(mSheets->mPendingDatas.Get(&key, &removedData) &&
1486 removedData == existingData,
1487 "Bad pending table.");
1488 #endif
1490 mSheets->mPendingDatas.Remove(&key);
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 }
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 }
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);
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 }
1535 nsCOMPtr<nsIHttpChannelInternal>
1536 internalHttpChannel(do_QueryInterface(channel));
1537 if (internalHttpChannel)
1538 internalHttpChannel->SetLoadAsBlocking(!aLoadData->mWasAlternate);
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);
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 }
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"));
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 }
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 }
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 }
1614 if (mDocument) {
1615 mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
1616 nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument);
1617 }
1619 rv = channel->AsyncOpen(channelListener, nullptr);
1621 #ifdef DEBUG
1622 mSyncCallback = false;
1623 #endif
1625 if (NS_FAILED(rv)) {
1626 LOG_ERROR((" Failed to create stream loader"));
1627 SheetComplete(aLoadData, rv);
1628 return rv;
1629 }
1631 mSheets->mLoadingDatas.Put(&key, aLoadData);
1632 aLoadData->mIsLoading = true;
1634 return NS_OK;
1635 }
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");
1652 aCompleted = false;
1654 nsCSSParser parser(this, aLoadData->mSheet);
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);
1666 if (NS_FAILED(rv)) {
1667 LOG_ERROR((" Low-level error in parser!"));
1668 SheetComplete(aLoadData, rv);
1669 return rv;
1670 }
1672 NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad,
1673 "Sync load has leftover pending children!");
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.
1683 return NS_OK;
1684 }
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"));
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);
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;
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 }
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 }
1730 if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) {
1731 LOG((" No more loading sheets; starting alternates"));
1732 StartAlternateLoads();
1733 }
1734 }
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.");
1745 LOG(("Load completed, status: 0x%x", aStatus));
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
1762 mSheets->mLoadingDatas.Remove(&key);
1763 aLoadData->mIsLoading = false;
1764 }
1765 }
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);
1784 // On append failure, just press on. We'll fail to notify the observer,
1785 // but not much we can do about that....
1786 }
1788 NS_ASSERTION(!data->mParentData ||
1789 data->mParentData->mPendingChildren != 0,
1790 "Broken pending child count on our parent");
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 }
1803 data = data->mNext;
1804 }
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 }
1849 NS_RELEASE(aLoadData); // this will release parents and siblings and all that
1850 }
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?");
1866 *aCompleted = true;
1868 if (!mEnabled) {
1869 LOG_WARN((" Not enabled"));
1870 return NS_ERROR_NOT_AVAILABLE;
1871 }
1873 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
1875 nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
1876 NS_ASSERTION(owningElement, "Element is not a style linking element!");
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");
1889 LOG((" Sheet is alternate: %d", *aIsAlternate));
1891 PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate);
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 }
1902 SheetLoadData* data = new SheetLoadData(this, aTitle, nullptr, sheet,
1903 owningElement, *aIsAlternate,
1904 aObserver, nullptr);
1906 // We never actually load this, so just set its principal directly
1907 sheet->SetPrincipal(aElement->NodePrincipal());
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);
1915 // If aCompleted is true, |data| may well be deleted by now.
1916 if (!*aCompleted) {
1917 data->mMustNotify = true;
1918 }
1919 return rv;
1920 }
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?");
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));
1941 if (!mEnabled) {
1942 LOG_WARN((" Not enabled"));
1943 return NS_ERROR_NOT_AVAILABLE;
1944 }
1946 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
1948 nsIPrincipal* principal =
1949 aElement ? aElement->NodePrincipal() : mDocument->NodePrincipal();
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;
1958 LOG((" Passed load check"));
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);
1967 LOG((" Sheet is alternate: %d", *aIsAlternate));
1969 PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate);
1971 rv = InsertSheetInDoc(sheet, aElement, mDocument);
1972 NS_ENSURE_SUCCESS(rv, rv);
1974 nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
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 }
1985 return NS_OK;
1986 }
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);
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);
2002 data->mMustNotify = true;
2003 return NS_OK;
2004 }
2006 // Load completion will free the data
2007 rv = LoadSheet(data, state);
2008 NS_ENSURE_SUCCESS(rv, rv);
2010 data->mMustNotify = true;
2011 return rv;
2012 }
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 }
2024 bool equal;
2025 if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) {
2026 return true;
2027 }
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 }
2038 aData = aData->mNext;
2039 }
2041 return false;
2042 }
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");
2054 if (!mEnabled) {
2055 LOG_WARN((" Not enabled"));
2056 return NS_ERROR_NOT_AVAILABLE;
2057 }
2059 LOG_URI(" Child uri: '%s'", aURL);
2061 nsCOMPtr<nsIDOMNode> owningNode;
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!?
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);
2076 topSheet->GetOwnerNode(getter_AddRefs(owningNode));
2077 }
2079 nsISupports* context = owningNode;
2080 if (!context) {
2081 context = mDocument;
2082 }
2084 nsIPrincipal* principal = aParentSheet->Principal();
2085 nsresult rv = CheckLoadAllowed(principal, aURL, context);
2086 if (NS_FAILED(rv)) return rv;
2088 LOG((" Passed load check"));
2090 SheetLoadData* parentData = nullptr;
2091 nsCOMPtr<nsICSSLoaderObserver> observer;
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 }
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 }
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);
2126 PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate);
2128 rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
2129 NS_ENSURE_SUCCESS(rv, rv);
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 }
2139 SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
2140 observer, principal);
2142 NS_ADDREF(data);
2143 bool syncLoad = data->mSyncLoad;
2145 // Load completion will release the data
2146 rv = LoadSheet(data, state);
2147 NS_ENSURE_SUCCESS(rv, rv);
2149 // If syncLoad is true, |data| will be deleted by now.
2150 if (!syncLoad) {
2151 data->mMustNotify = true;
2152 }
2153 return rv;
2154 }
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 }
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 }
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 }
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?");
2210 LOG_URI(" Non-document sheet uri: '%s'", aURL);
2212 if (aSheet) {
2213 *aSheet = nullptr;
2214 }
2216 if (!mEnabled) {
2217 LOG_WARN((" Not enabled"));
2218 return NS_ERROR_NOT_AVAILABLE;
2219 }
2221 nsresult rv = CheckLoadAllowed(aOriginPrincipal, aURL, mDocument);
2222 if (NS_FAILED(rv)) {
2223 return rv;
2224 }
2226 StyleSheetState state;
2227 bool isAlternate;
2228 nsRefPtr<nsCSSStyleSheet> sheet;
2229 bool syncLoad = (aObserver == nullptr);
2230 const nsSubstring& empty = EmptyString();
2232 rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aCORSMode, syncLoad, false,
2233 empty, state, &isAlternate, getter_AddRefs(sheet));
2234 NS_ENSURE_SUCCESS(rv, rv);
2236 PrepareSheet(sheet, empty, empty, nullptr, nullptr, isAlternate);
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 }
2249 SheetLoadData* data =
2250 new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules,
2251 aUseSystemPrincipal, aCharset, aObserver,
2252 aOriginPrincipal);
2254 NS_ADDREF(data);
2255 rv = LoadSheet(data, state);
2256 NS_ENSURE_SUCCESS(rv, rv);
2258 if (aSheet) {
2259 sheet.swap(*aSheet);
2260 }
2261 if (aObserver) {
2262 data->mMustNotify = true;
2263 }
2265 return rv;
2266 }
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");
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);
2290 if (!mPostedEvents.AppendElement(evt)) {
2291 return NS_ERROR_OUT_OF_MEMORY;
2292 }
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 }
2304 // We want to notify the observer for this data.
2305 evt->mMustNotify = true;
2306 evt->mSheetAlreadyComplete = true;
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 }
2315 return rv;
2316 }
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");
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);
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 }
2338 if (mDocument) {
2339 mDocument->UnblockOnload(true);
2340 }
2341 }
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");
2351 aData->mIsLoading = false; // we will handle the removal right here
2352 aData->mIsCancelled = true;
2354 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
2356 return PL_DHASH_REMOVE;
2357 }
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());
2368 if (pendingCount) {
2369 mSheets->mPendingDatas.Enumerate(StopLoadingSheetCallback, &arr);
2370 }
2371 if (loadingCount) {
2372 mSheets->mLoadingDatas.Enumerate(StopLoadingSheetCallback, &arr);
2373 }
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();
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 }
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 }
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 }
2418 return NS_ERROR_OUT_OF_MEMORY;
2419 }
2421 void
2422 Loader::RemoveObserver(nsICSSLoaderObserver* aObserver)
2423 {
2424 mObservers.RemoveElement(aObserver);
2425 }
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 }
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);
2443 mDatasToNotifyOn += arr.Length();
2444 for (uint32_t i = 0; i < arr.Length(); ++i) {
2445 --mDatasToNotifyOn;
2446 LoadSheet(arr[i], eSheetNeedsParser);
2447 }
2448 }
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 }
2462 void
2463 Loader::TraverseCachedSheets(nsCycleCollectionTraversalCallback& cb)
2464 {
2465 if (mSheets) {
2466 mSheets->mCompleteSheets.EnumerateRead(TraverseSheet, &cb);
2467 }
2468 }
2470 void
2471 Loader::UnlinkCachedSheets()
2472 {
2473 if (mSheets) {
2474 mSheets->mCompleteSheets.Clear();
2475 }
2476 }
2478 struct SheetMemoryCounter {
2479 size_t size;
2480 mozilla::MallocSizeOf mallocSizeOf;
2481 };
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 }
2499 size_t
2500 Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
2501 {
2502 size_t s = aMallocSizeOf(this);
2504 if (mSheets) {
2505 s += mSheets->mCompleteSheets.SizeOfExcludingThis(CountSheetMemory, aMallocSizeOf);
2506 }
2507 s += mObservers.SizeOfExcludingThis(aMallocSizeOf);
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
2520 return s;
2521 }
2523 } // namespace css
2524 } // namespace mozilla