Bumping manifests a=b2g-bump
[gecko.git] / layout / style / Loader.cpp
blob7dc0655ed31eb3cb457d3a8ce88a8e715503298d
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
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/.
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
13 * Date Modified by Description of modification
14 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
17 /* loading of CSS style sheets using the network APIs */
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/LoadInfo.h"
21 #include "mozilla/MemoryReporting.h"
23 #include "mozilla/css/Loader.h"
24 #include "nsIRunnable.h"
25 #include "nsIUnicharStreamLoader.h"
26 #include "nsSyncLoadService.h"
27 #include "nsCOMPtr.h"
28 #include "nsString.h"
29 #include "nsIContent.h"
30 #include "nsIDocument.h"
31 #include "nsIDOMNode.h"
32 #include "nsIDOMDocument.h"
33 #include "nsIURI.h"
34 #include "nsNetUtil.h"
35 #include "nsContentUtils.h"
36 #include "nsIScriptSecurityManager.h"
37 #include "nsContentPolicyUtils.h"
38 #include "nsIHttpChannel.h"
39 #include "nsIClassOfService.h"
40 #include "nsIScriptError.h"
41 #include "nsMimeTypes.h"
42 #include "nsIStyleSheetLinkingElement.h"
43 #include "nsICSSLoaderObserver.h"
44 #include "nsCSSParser.h"
45 #include "mozilla/CSSStyleSheet.h"
46 #include "mozilla/css/ImportRule.h"
47 #include "nsThreadUtils.h"
48 #include "nsGkAtoms.h"
49 #include "nsIThreadInternal.h"
50 #include "nsCORSListenerProxy.h"
51 #include "nsINetworkPredictor.h"
52 #include "mozilla/dom/ShadowRoot.h"
53 #include "mozilla/dom/URL.h"
55 #ifdef MOZ_XUL
56 #include "nsXULPrototypeCache.h"
57 #endif
59 #include "nsIMediaList.h"
60 #include "nsIDOMStyleSheet.h"
61 #include "nsError.h"
63 #include "nsIContentSecurityPolicy.h"
65 #include "mozilla/dom/EncodingUtils.h"
66 using mozilla::dom::EncodingUtils;
68 using namespace mozilla::dom;
70 /**
71 * OVERALL ARCHITECTURE
73 * The CSS Loader gets requests to load various sorts of style sheets:
74 * inline style from <style> elements, linked style, @import-ed child
75 * sheets, non-document sheets. The loader handles the following tasks:
77 * 1) Checking whether the load is allowed: CheckLoadAllowed()
78 * 2) Creation of the actual style sheet objects: CreateSheet()
79 * 3) setting of the right media, title, enabled state, etc on the
80 * sheet: PrepareSheet()
81 * 4) Insertion of the sheet in the proper cascade order:
82 * InsertSheetInDoc() and InsertChildSheet()
83 * 5) Load of the sheet: LoadSheet()
84 * 6) Parsing of the sheet: ParseSheet()
85 * 7) Cleanup: SheetComplete()
87 * The detailed documentation for these functions is found with the
88 * function implementations.
90 * The following helper object is used:
91 * SheetLoadData -- a small class that is used to store all the
92 * information needed for the loading of a sheet;
93 * this class handles listening for the stream
94 * loader completion and also handles charset
95 * determination.
98 namespace mozilla {
99 namespace css {
101 /*********************************************
102 * Data needed to properly load a stylesheet *
103 *********************************************/
105 class SheetLoadData : public nsIRunnable,
106 public nsIUnicharStreamLoaderObserver,
107 public nsIThreadObserver
109 protected:
110 virtual ~SheetLoadData(void);
112 public:
113 // Data for loading a sheet linked from a document
114 SheetLoadData(Loader* aLoader,
115 const nsSubstring& aTitle,
116 nsIURI* aURI,
117 CSSStyleSheet* aSheet,
118 nsIStyleSheetLinkingElement* aOwningElement,
119 bool aIsAlternate,
120 nsICSSLoaderObserver* aObserver,
121 nsIPrincipal* aLoaderPrincipal,
122 nsINode* aRequestingNode);
124 // Data for loading a sheet linked from an @import rule
125 SheetLoadData(Loader* aLoader,
126 nsIURI* aURI,
127 CSSStyleSheet* aSheet,
128 SheetLoadData* aParentData,
129 nsICSSLoaderObserver* aObserver,
130 nsIPrincipal* aLoaderPrincipal,
131 nsINode* aRequestingNode);
133 // Data for loading a non-document sheet
134 SheetLoadData(Loader* aLoader,
135 nsIURI* aURI,
136 CSSStyleSheet* aSheet,
137 bool aSyncLoad,
138 bool aAllowUnsafeRules,
139 bool aUseSystemPrincipal,
140 const nsCString& aCharset,
141 nsICSSLoaderObserver* aObserver,
142 nsIPrincipal* aLoaderPrincipal,
143 nsINode* aRequestingNode);
145 already_AddRefed<nsIURI> GetReferrerURI();
147 void ScheduleLoadEventIfNeeded(nsresult aStatus);
149 NS_DECL_ISUPPORTS
150 NS_DECL_NSIRUNNABLE
151 NS_DECL_NSITHREADOBSERVER
152 NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
154 // Hold a ref to the CSSLoader so we can call back to it to let it
155 // know the load finished
156 nsRefPtr<Loader> mLoader;
158 // Title needed to pull datas out of the pending datas table when
159 // the preferred title is changed
160 nsString mTitle;
162 // Charset we decided to use for the sheet
163 nsCString mCharset;
165 // URI we're loading. Null for inline sheets
166 nsCOMPtr<nsIURI> mURI;
168 // Should be 1 for non-inline sheets.
169 uint32_t mLineNumber;
171 // The sheet we're loading data for
172 nsRefPtr<CSSStyleSheet> mSheet;
174 // Linked list of datas for the same URI as us
175 SheetLoadData* mNext; // strong ref
177 // Load data for the sheet that @import-ed us if we were @import-ed
178 // during the parse
179 nsRefPtr<SheetLoadData> mParentData;
181 // Number of sheets we @import-ed that are still loading
182 uint32_t mPendingChildren;
184 // mSyncLoad is true when the load needs to be synchronous -- right
185 // now only for LoadSheetSync and children of sync loads.
186 bool mSyncLoad : 1;
188 // mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
189 // LoadSheet or an @import from such a sheet. Non-document sheet loads can
190 // proceed even if we have no document.
191 bool mIsNonDocumentSheet : 1;
193 // mIsLoading is true from the moment we are placed in the loader's
194 // "loading datas" table (right after the async channel is opened)
195 // to the moment we are removed from said table (due to the load
196 // completing or being cancelled).
197 bool mIsLoading : 1;
199 // mIsCancelled is set to true when a sheet load is stopped by
200 // Stop() or StopLoadingSheet() (which was removed in Bug 556446).
201 // SheetLoadData::OnStreamComplete() checks this to avoid parsing
202 // sheets that have been cancelled and such.
203 bool mIsCancelled : 1;
205 // mMustNotify is true if the load data is being loaded async and
206 // the original function call that started the load has returned.
207 // This applies only to observer notifications; load/error events
208 // are fired for any SheetLoadData that has a non-null
209 // mOwningElement.
210 bool mMustNotify : 1;
212 // mWasAlternate is true if the sheet was an alternate when the load data was
213 // created.
214 bool mWasAlternate : 1;
216 // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed
217 // in the loaded sheet.
218 bool mAllowUnsafeRules : 1;
220 // mUseSystemPrincipal is true if the system principal should be used for
221 // this sheet, no matter what the channel principal is. Only true for sync
222 // loads.
223 bool mUseSystemPrincipal : 1;
225 // If true, this SheetLoadData is being used as a way to handle
226 // async observer notification for an already-complete sheet.
227 bool mSheetAlreadyComplete : 1;
229 // This is the element that imported the sheet. Needed to get the
230 // charset set on it and to fire load/error events.
231 nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
233 // The observer that wishes to be notified of load completion
234 nsCOMPtr<nsICSSLoaderObserver> mObserver;
236 // The principal that identifies who started loading us.
237 nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
239 // The node that identifies who started loading us.
240 nsCOMPtr<nsINode> mRequestingNode;
242 // The charset to use if the transport and sheet don't indicate one.
243 // May be empty. Must be empty if mOwningElement is non-null.
244 nsCString mCharsetHint;
246 // The status our load ended up with; this determines whether we
247 // should fire error events or load events. This gets initialized
248 // by ScheduleLoadEventIfNeeded, and is only used after that has
249 // been called.
250 nsresult mStatus;
252 private:
253 void FireLoadEvent(nsIThreadInternal* aThread);
256 #include "prlog.h"
258 #ifdef PR_LOGGING
259 static PRLogModuleInfo *
260 GetLoaderLog()
262 static PRLogModuleInfo *sLog;
263 if (!sLog)
264 sLog = PR_NewLogModule("nsCSSLoader");
265 return sLog;
267 #endif /* PR_LOGGING */
269 #define LOG_FORCE(args) PR_LOG(GetLoaderLog(), PR_LOG_ALWAYS, args)
270 #define LOG_ERROR(args) PR_LOG(GetLoaderLog(), PR_LOG_ERROR, args)
271 #define LOG_WARN(args) PR_LOG(GetLoaderLog(), PR_LOG_WARNING, args)
272 #define LOG_DEBUG(args) PR_LOG(GetLoaderLog(), PR_LOG_DEBUG, args)
273 #define LOG(args) LOG_DEBUG(args)
275 #define LOG_FORCE_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ALWAYS)
276 #define LOG_ERROR_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ERROR)
277 #define LOG_WARN_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_WARNING)
278 #define LOG_DEBUG_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_DEBUG)
279 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
281 #ifdef PR_LOGGING
282 #define LOG_URI(format, uri) \
283 PR_BEGIN_MACRO \
284 NS_ASSERTION(uri, "Logging null uri"); \
285 if (LOG_ENABLED()) { \
286 nsAutoCString _logURISpec; \
287 uri->GetSpec(_logURISpec); \
288 LOG((format, _logURISpec.get())); \
290 PR_END_MACRO
291 #else // PR_LOGGING
292 #define LOG_URI(format, uri)
293 #endif // PR_LOGGING
295 // And some convenience strings...
296 #ifdef PR_LOGGING
297 static const char* const gStateStrings[] = {
298 "eSheetStateUnknown",
299 "eSheetNeedsParser",
300 "eSheetPending",
301 "eSheetLoading",
302 "eSheetComplete"
304 #endif
306 /********************************
307 * SheetLoadData implementation *
308 ********************************/
309 NS_IMPL_ISUPPORTS(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable,
310 nsIThreadObserver)
312 SheetLoadData::SheetLoadData(Loader* aLoader,
313 const nsSubstring& aTitle,
314 nsIURI* aURI,
315 CSSStyleSheet* aSheet,
316 nsIStyleSheetLinkingElement* aOwningElement,
317 bool aIsAlternate,
318 nsICSSLoaderObserver* aObserver,
319 nsIPrincipal* aLoaderPrincipal,
320 nsINode* aRequestingNode)
321 : mLoader(aLoader),
322 mTitle(aTitle),
323 mURI(aURI),
324 mLineNumber(1),
325 mSheet(aSheet),
326 mNext(nullptr),
327 mPendingChildren(0),
328 mSyncLoad(false),
329 mIsNonDocumentSheet(false),
330 mIsLoading(false),
331 mIsCancelled(false),
332 mMustNotify(false),
333 mWasAlternate(aIsAlternate),
334 mAllowUnsafeRules(false),
335 mUseSystemPrincipal(false),
336 mSheetAlreadyComplete(false),
337 mOwningElement(aOwningElement),
338 mObserver(aObserver),
339 mLoaderPrincipal(aLoaderPrincipal),
340 mRequestingNode(aRequestingNode)
342 NS_PRECONDITION(mLoader, "Must have a loader!");
345 SheetLoadData::SheetLoadData(Loader* aLoader,
346 nsIURI* aURI,
347 CSSStyleSheet* aSheet,
348 SheetLoadData* aParentData,
349 nsICSSLoaderObserver* aObserver,
350 nsIPrincipal* aLoaderPrincipal,
351 nsINode* aRequestingNode)
352 : mLoader(aLoader),
353 mURI(aURI),
354 mLineNumber(1),
355 mSheet(aSheet),
356 mNext(nullptr),
357 mParentData(aParentData),
358 mPendingChildren(0),
359 mSyncLoad(false),
360 mIsNonDocumentSheet(false),
361 mIsLoading(false),
362 mIsCancelled(false),
363 mMustNotify(false),
364 mWasAlternate(false),
365 mAllowUnsafeRules(false),
366 mUseSystemPrincipal(false),
367 mSheetAlreadyComplete(false),
368 mOwningElement(nullptr),
369 mObserver(aObserver),
370 mLoaderPrincipal(aLoaderPrincipal),
371 mRequestingNode(aRequestingNode)
373 NS_PRECONDITION(mLoader, "Must have a loader!");
374 if (mParentData) {
375 mSyncLoad = mParentData->mSyncLoad;
376 mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
377 mAllowUnsafeRules = mParentData->mAllowUnsafeRules;
378 mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
379 ++(mParentData->mPendingChildren);
382 NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
383 "Shouldn't use system principal for async loads");
386 SheetLoadData::SheetLoadData(Loader* aLoader,
387 nsIURI* aURI,
388 CSSStyleSheet* aSheet,
389 bool aSyncLoad,
390 bool aAllowUnsafeRules,
391 bool aUseSystemPrincipal,
392 const nsCString& aCharset,
393 nsICSSLoaderObserver* aObserver,
394 nsIPrincipal* aLoaderPrincipal,
395 nsINode* aRequestingNode)
396 : mLoader(aLoader),
397 mURI(aURI),
398 mLineNumber(1),
399 mSheet(aSheet),
400 mNext(nullptr),
401 mPendingChildren(0),
402 mSyncLoad(aSyncLoad),
403 mIsNonDocumentSheet(true),
404 mIsLoading(false),
405 mIsCancelled(false),
406 mMustNotify(false),
407 mWasAlternate(false),
408 mAllowUnsafeRules(aAllowUnsafeRules),
409 mUseSystemPrincipal(aUseSystemPrincipal),
410 mSheetAlreadyComplete(false),
411 mOwningElement(nullptr),
412 mObserver(aObserver),
413 mLoaderPrincipal(aLoaderPrincipal),
414 mRequestingNode(aRequestingNode),
415 mCharsetHint(aCharset)
417 NS_PRECONDITION(mLoader, "Must have a loader!");
419 NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
420 "Shouldn't use system principal for async loads");
423 SheetLoadData::~SheetLoadData()
425 NS_IF_RELEASE(mNext);
428 NS_IMETHODIMP
429 SheetLoadData::Run()
431 mLoader->HandleLoadEvent(this);
432 return NS_OK;
435 NS_IMETHODIMP
436 SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
438 return NS_OK;
441 NS_IMETHODIMP
442 SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
443 bool aMayWait,
444 uint32_t aRecursionDepth)
446 // We want to fire our load even before or after event processing,
447 // whichever comes first.
448 FireLoadEvent(aThread);
449 return NS_OK;
452 NS_IMETHODIMP
453 SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
454 uint32_t aRecursionDepth,
455 bool aEventWasProcessed)
457 // We want to fire our load even before or after event processing,
458 // whichever comes first.
459 FireLoadEvent(aThread);
460 return NS_OK;
463 void
464 SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
467 // First remove ourselves as a thread observer. But we need to keep
468 // ourselves alive while doing that!
469 nsRefPtr<SheetLoadData> kungFuDeathGrip(this);
470 aThread->RemoveObserver(this);
472 // Now fire the event
473 nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
474 NS_ASSERTION(node, "How did that happen???");
476 nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
477 node,
478 NS_SUCCEEDED(mStatus) ?
479 NS_LITERAL_STRING("load") :
480 NS_LITERAL_STRING("error"),
481 false, false);
483 // And unblock onload
484 if (mLoader->mDocument) {
485 mLoader->mDocument->UnblockOnload(true);
489 void
490 SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
492 if (!mOwningElement) {
493 return;
496 mStatus = aStatus;
498 nsCOMPtr<nsIThread> thread = do_GetMainThread();
499 nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
500 if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
501 // Make sure to block onload here
502 if (mLoader->mDocument) {
503 mLoader->mDocument->BlockOnload();
508 /*************************
509 * Loader Implementation *
510 *************************/
512 Loader::Loader(void)
513 : mDocument(nullptr)
514 , mDatasToNotifyOn(0)
515 , mCompatMode(eCompatibility_FullStandards)
516 , mEnabled(true)
517 #ifdef DEBUG
518 , mSyncCallback(false)
519 #endif
523 Loader::Loader(nsIDocument* aDocument)
524 : mDocument(aDocument)
525 , mDatasToNotifyOn(0)
526 , mCompatMode(eCompatibility_FullStandards)
527 , mEnabled(true)
528 #ifdef DEBUG
529 , mSyncCallback(false)
530 #endif
532 // We can just use the preferred set, since there are no sheets in the
533 // document yet (if there are, how did they get there? _we_ load the sheets!)
534 // and hence the selected set makes no sense at this time.
535 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
536 if (domDoc) {
537 domDoc->GetPreferredStyleSheetSet(mPreferredSheet);
541 Loader::~Loader()
543 NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0,
544 "How did we get destroyed when there are loading data?");
545 NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0,
546 "How did we get destroyed when there are pending data?");
547 // Note: no real need to revoke our stylesheet loaded events -- they
548 // hold strong references to us, so if we're going away that means
549 // they're all done.
552 void
553 Loader::DropDocumentReference(void)
555 mDocument = nullptr;
556 // Flush out pending datas just so we don't leak by accident. These
557 // loads should short-circuit through the mDocument check in
558 // LoadSheet and just end up in SheetComplete immediately
559 if (mSheets) {
560 StartAlternateLoads();
564 static PLDHashOperator
565 CollectNonAlternates(URIPrincipalReferrerPolicyAndCORSModeHashKey *aKey,
566 SheetLoadData* &aData,
567 void* aClosure)
569 NS_PRECONDITION(aData, "Must have a data");
570 NS_PRECONDITION(aClosure, "Must have an array");
572 // Note that we don't want to affect what the selected style set is,
573 // so use true for aHasAlternateRel.
574 if (aData->mLoader->IsAlternate(aData->mTitle, true)) {
575 return PL_DHASH_NEXT;
578 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
579 return PL_DHASH_REMOVE;
582 nsresult
583 Loader::SetPreferredSheet(const nsAString& aTitle)
585 #ifdef DEBUG
586 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
587 if (doc) {
588 nsAutoString currentPreferred;
589 doc->GetLastStyleSheetSet(currentPreferred);
590 if (DOMStringIsNull(currentPreferred)) {
591 doc->GetPreferredStyleSheetSet(currentPreferred);
593 NS_ASSERTION(currentPreferred.Equals(aTitle),
594 "Unexpected argument to SetPreferredSheet");
596 #endif
598 mPreferredSheet = aTitle;
600 // start any pending alternates that aren't alternates anymore
601 if (mSheets) {
602 LoadDataArray arr(mSheets->mPendingDatas.Count());
603 mSheets->mPendingDatas.Enumerate(CollectNonAlternates, &arr);
605 mDatasToNotifyOn += arr.Length();
606 for (uint32_t i = 0; i < arr.Length(); ++i) {
607 --mDatasToNotifyOn;
608 LoadSheet(arr[i], eSheetNeedsParser);
612 return NS_OK;
615 static const char kCharsetSym[] = "@charset \"";
617 static bool GetCharsetFromData(const char* aStyleSheetData,
618 uint32_t aDataLength,
619 nsACString& aCharset)
621 aCharset.Truncate();
622 if (aDataLength <= sizeof(kCharsetSym) - 1)
623 return false;
625 if (strncmp(aStyleSheetData,
626 kCharsetSym,
627 sizeof(kCharsetSym) - 1)) {
628 return false;
631 for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
632 char c = aStyleSheetData[i];
633 if (c == '"') {
634 ++i;
635 if (i < aDataLength && aStyleSheetData[i] == ';') {
636 return true;
638 // fail
639 break;
641 aCharset.Append(c);
644 // Did not see end quote or semicolon
645 aCharset.Truncate();
646 return false;
649 NS_IMETHODIMP
650 SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
651 nsISupports* aContext,
652 nsACString const& aSegment,
653 nsACString& aCharset)
655 NS_PRECONDITION(!mOwningElement || mCharsetHint.IsEmpty(),
656 "Can't have element _and_ charset hint");
658 LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
660 // The precedence is (per CSS3 Syntax 2012-11-08 ED):
661 // BOM
662 // Channel
663 // @charset rule
664 // charset attribute on the referrer
665 // encoding of the referrer
666 // UTF-8
668 aCharset.Truncate();
670 if (nsContentUtils::CheckForBOM((const unsigned char*)aSegment.BeginReading(),
671 aSegment.Length(),
672 aCharset)) {
673 // aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8"
674 // which will swallow the BOM.
675 mCharset.Assign(aCharset);
676 #ifdef PR_LOGGING
677 LOG((" Setting from BOM to: %s", PromiseFlatCString(aCharset).get()));
678 #endif
679 return NS_OK;
682 nsCOMPtr<nsIChannel> channel;
683 nsAutoCString specified;
684 aLoader->GetChannel(getter_AddRefs(channel));
685 if (channel) {
686 channel->GetContentCharset(specified);
687 if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
688 mCharset.Assign(aCharset);
689 #ifdef PR_LOGGING
690 LOG((" Setting from HTTP to: %s", PromiseFlatCString(aCharset).get()));
691 #endif
692 return NS_OK;
696 if (GetCharsetFromData(aSegment.BeginReading(),
697 aSegment.Length(),
698 specified)) {
699 if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
700 // FindEncodingForLabel currently never returns UTF-16LE but will
701 // probably change to never return UTF-16 instead, so check both here
702 // to avoid relying on the exact behavior.
703 if (aCharset.EqualsLiteral("UTF-16") ||
704 aCharset.EqualsLiteral("UTF-16BE") ||
705 aCharset.EqualsLiteral("UTF-16LE")) {
706 // Be consistent with HTML <meta> handling in face of impossibility.
707 // When the @charset rule itself evidently was not UTF-16-encoded,
708 // it saying UTF-16 has to be a lie.
709 aCharset.AssignLiteral("UTF-8");
711 mCharset.Assign(aCharset);
712 #ifdef PR_LOGGING
713 LOG((" Setting from @charset rule to: %s",
714 PromiseFlatCString(aCharset).get()));
715 #endif
716 return NS_OK;
720 // Now try the charset on the <link> or processing instruction
721 // that loaded us
722 if (mOwningElement) {
723 nsAutoString specified16;
724 mOwningElement->GetCharset(specified16);
725 if (EncodingUtils::FindEncodingForLabel(specified16, aCharset)) {
726 mCharset.Assign(aCharset);
727 #ifdef PR_LOGGING
728 LOG((" Setting from charset attribute to: %s",
729 PromiseFlatCString(aCharset).get()));
730 #endif
731 return NS_OK;
735 // In the preload case, the value of the charset attribute on <link> comes
736 // in via mCharsetHint instead.
737 if (EncodingUtils::FindEncodingForLabel(mCharsetHint, aCharset)) {
738 mCharset.Assign(aCharset);
739 #ifdef PR_LOGGING
740 LOG((" Setting from charset attribute (preload case) to: %s",
741 PromiseFlatCString(aCharset).get()));
742 #endif
743 return NS_OK;
746 // Try charset from the parent stylesheet.
747 if (mParentData) {
748 aCharset = mParentData->mCharset;
749 if (!aCharset.IsEmpty()) {
750 mCharset.Assign(aCharset);
751 #ifdef PR_LOGGING
752 LOG((" Setting from parent sheet to: %s",
753 PromiseFlatCString(aCharset).get()));
754 #endif
755 return NS_OK;
759 if (mLoader->mDocument) {
760 // no useful data on charset. Try the document charset.
761 aCharset = mLoader->mDocument->GetDocumentCharacterSet();
762 MOZ_ASSERT(!aCharset.IsEmpty());
763 mCharset.Assign(aCharset);
764 #ifdef PR_LOGGING
765 LOG((" Setting from document to: %s", PromiseFlatCString(aCharset).get()));
766 #endif
767 return NS_OK;
770 aCharset.AssignLiteral("UTF-8");
771 mCharset = aCharset;
772 #ifdef PR_LOGGING
773 LOG((" Setting from default to: %s", PromiseFlatCString(aCharset).get()));
774 #endif
775 return NS_OK;
778 already_AddRefed<nsIURI>
779 SheetLoadData::GetReferrerURI()
781 nsCOMPtr<nsIURI> uri;
782 if (mParentData)
783 uri = mParentData->mSheet->GetSheetURI();
784 if (!uri && mLoader->mDocument)
785 uri = mLoader->mDocument->GetDocumentURI();
786 return uri.forget();
790 * Here we need to check that the load did not give us an http error
791 * page and check the mimetype on the channel to make sure we're not
792 * loading non-text/css data in standards mode.
794 NS_IMETHODIMP
795 SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
796 nsISupports* aContext,
797 nsresult aStatus,
798 const nsAString& aBuffer)
800 LOG(("SheetLoadData::OnStreamComplete"));
801 NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
803 if (mIsCancelled) {
804 // Just return. Don't call SheetComplete -- it's already been
805 // called and calling it again will lead to an extra NS_RELEASE on
806 // this data and a likely crash.
807 return NS_OK;
810 if (!mLoader->mDocument && !mIsNonDocumentSheet) {
811 // Sorry, we don't care about this load anymore
812 LOG_WARN((" No document and not non-document sheet; dropping load"));
813 mLoader->SheetComplete(this, NS_BINDING_ABORTED);
814 return NS_OK;
817 if (NS_FAILED(aStatus)) {
818 LOG_WARN((" Load failed: status 0x%x", aStatus));
819 // Handle sheet not loading error because source was a tracking URL.
820 // We make a note of this sheet node by including it in a dedicated
821 // array of blocked tracking nodes under its parent document.
823 // Multiple sheet load instances might be tied to this request,
824 // we annotate each one linked to a valid owning element (node).
825 if (aStatus == NS_ERROR_TRACKING_URI) {
826 nsIDocument* doc = mLoader->GetDocument();
827 if (doc) {
828 for (SheetLoadData* data = this; data; data = data->mNext) {
829 // mOwningElement may be null but AddBlockTrackingNode can cope
830 nsCOMPtr<nsIContent> content = do_QueryInterface(data->mOwningElement);
831 doc->AddBlockedTrackingNode(content);
835 mLoader->SheetComplete(this, aStatus);
836 return NS_OK;
839 nsCOMPtr<nsIChannel> channel;
840 nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
841 if (NS_FAILED(result)) {
842 LOG_WARN((" No channel from loader"));
843 mLoader->SheetComplete(this, result);
844 return NS_OK;
847 nsCOMPtr<nsIURI> originalURI;
848 channel->GetOriginalURI(getter_AddRefs(originalURI));
850 // If the channel's original URI is "chrome:", we want that, since
851 // the observer code in nsXULPrototypeCache depends on chrome stylesheets
852 // having a chrome URI. (Whether or not chrome stylesheets come through
853 // this codepath seems nondeterministic.)
854 // Otherwise we want the potentially-HTTP-redirected URI.
855 nsCOMPtr<nsIURI> channelURI;
856 NS_GetFinalChannelURI(channel, getter_AddRefs(channelURI));
858 if (!channelURI || !originalURI) {
859 NS_ERROR("Someone just violated the nsIRequest contract");
860 LOG_WARN((" Channel without a URI. Bad!"));
861 mLoader->SheetComplete(this, NS_ERROR_UNEXPECTED);
862 return NS_OK;
865 nsCOMPtr<nsIPrincipal> principal;
866 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
867 result = NS_ERROR_NOT_AVAILABLE;
868 if (secMan) { // Could be null if we already shut down
869 if (mUseSystemPrincipal) {
870 result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
871 } else {
872 result = secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
876 if (NS_FAILED(result)) {
877 LOG_WARN((" Couldn't get principal"));
878 mLoader->SheetComplete(this, result);
879 return NS_OK;
882 mSheet->SetPrincipal(principal);
884 // If it's an HTTP channel, we want to make sure this is not an
885 // error document we got.
886 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
887 if (httpChannel) {
888 bool requestSucceeded;
889 result = httpChannel->GetRequestSucceeded(&requestSucceeded);
890 if (NS_SUCCEEDED(result) && !requestSucceeded) {
891 LOG((" Load returned an error page"));
892 mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
893 return NS_OK;
897 nsAutoCString contentType;
898 if (channel) {
899 channel->GetContentType(contentType);
902 // In standards mode, a style sheet must have one of these MIME
903 // types to be processed at all. In quirks mode, we accept any
904 // MIME type, but only if the style sheet is same-origin with the
905 // requesting document or parent sheet. See bug 524223.
907 bool validType = contentType.EqualsLiteral("text/css") ||
908 contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
909 contentType.IsEmpty();
911 if (!validType) {
912 const char *errorMessage;
913 uint32_t errorFlag;
914 bool sameOrigin = true;
916 if (mLoaderPrincipal) {
917 bool subsumed;
918 result = mLoaderPrincipal->Subsumes(principal, &subsumed);
919 if (NS_FAILED(result) || !subsumed) {
920 sameOrigin = false;
924 if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
925 errorMessage = "MimeNotCssWarn";
926 errorFlag = nsIScriptError::warningFlag;
927 } else {
928 errorMessage = "MimeNotCss";
929 errorFlag = nsIScriptError::errorFlag;
932 nsAutoCString spec;
933 channelURI->GetSpec(spec);
935 const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec);
936 const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
937 const char16_t *strings[] = { specUTF16.get(), ctypeUTF16.get() };
939 nsCOMPtr<nsIURI> referrer = GetReferrerURI();
940 nsContentUtils::ReportToConsole(errorFlag,
941 NS_LITERAL_CSTRING("CSS Loader"),
942 mLoader->mDocument,
943 nsContentUtils::eCSS_PROPERTIES,
944 errorMessage,
945 strings, ArrayLength(strings),
946 referrer);
948 if (errorFlag == nsIScriptError::errorFlag) {
949 LOG_WARN((" Ignoring sheet with improper MIME type %s",
950 contentType.get()));
951 mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
952 return NS_OK;
956 // Enough to set the URIs on mSheet, since any sibling datas we have share
957 // the same mInner as mSheet and will thus get the same URI.
958 mSheet->SetURIs(channelURI, originalURI, channelURI);
960 bool completed;
961 result = mLoader->ParseSheet(aBuffer, this, completed);
962 NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
963 return result;
966 bool
967 Loader::IsAlternate(const nsAString& aTitle, bool aHasAlternateRel)
969 // A sheet is alternate if it has a nonempty title that doesn't match the
970 // currently selected style set. But if there _is_ no currently selected
971 // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
972 // is nonempty, we should select the style set corresponding to aTitle, since
973 // that's a preferred sheet.
974 if (aTitle.IsEmpty()) {
975 return false;
978 if (!aHasAlternateRel && mDocument && mPreferredSheet.IsEmpty()) {
979 // There's no preferred set yet, and we now have a sheet with a title.
980 // Make that be the preferred set.
981 mDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, aTitle);
982 // We're definitely not an alternate
983 return false;
986 return !aTitle.Equals(mPreferredSheet);
989 /* static */ PLDHashOperator
990 Loader::RemoveEntriesWithURI(URIPrincipalReferrerPolicyAndCORSModeHashKey* aKey,
991 nsRefPtr<CSSStyleSheet>& aSheet,
992 void* aUserData)
994 nsIURI* obsoleteURI = static_cast<nsIURI*>(aUserData);
995 nsIURI* sheetURI = aKey->GetURI();
996 bool areEqual;
997 nsresult rv = sheetURI->Equals(obsoleteURI, &areEqual);
998 if (NS_SUCCEEDED(rv) && areEqual) {
999 return PL_DHASH_REMOVE;
1001 return PL_DHASH_NEXT;
1004 nsresult
1005 Loader::ObsoleteSheet(nsIURI* aURI)
1007 if (!mSheets) {
1008 return NS_OK;
1010 if (!aURI) {
1011 return NS_ERROR_INVALID_ARG;
1013 mSheets->mCompleteSheets.Enumerate(RemoveEntriesWithURI, aURI);
1014 return NS_OK;
1018 * CheckLoadAllowed will return success if the load is allowed,
1019 * failure otherwise.
1021 * @param aSourcePrincipal the principal of the node or document or parent
1022 * sheet loading the sheet
1023 * @param aTargetURI the uri of the sheet to be loaded
1024 * @param aContext the node owning the sheet. This is the element or document
1025 * owning the stylesheet (possibly indirectly, for child sheets)
1027 nsresult
1028 Loader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
1029 nsIURI* aTargetURI,
1030 nsISupports* aContext)
1032 LOG(("css::Loader::CheckLoadAllowed"));
1034 nsresult rv;
1036 if (aSourcePrincipal) {
1037 // Check with the security manager
1038 nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
1039 rv =
1040 secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
1041 nsIScriptSecurityManager::ALLOW_CHROME);
1042 if (NS_FAILED(rv)) { // failure is normal here; don't warn
1043 return rv;
1046 LOG((" Passed security check"));
1048 // Check with content policy
1050 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1051 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
1052 aTargetURI,
1053 aSourcePrincipal,
1054 aContext,
1055 NS_LITERAL_CSTRING("text/css"),
1056 nullptr, //extra param
1057 &shouldLoad,
1058 nsContentUtils::GetContentPolicy(),
1059 nsContentUtils::GetSecurityManager());
1061 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
1062 LOG((" Load blocked by content policy"));
1063 return NS_ERROR_CONTENT_BLOCKED;
1067 return NS_OK;
1071 * CreateSheet() creates a CSSStyleSheet object for the given URI,
1072 * if any. If there is no URI given, we just create a new style sheet
1073 * object. Otherwise, we check for an existing style sheet object for
1074 * that uri in various caches and clone it if we find it. Cloned
1075 * sheets will have the title/media/enabled state of the sheet they
1076 * are clones off; make sure to call PrepareSheet() on the result of
1077 * CreateSheet().
1079 nsresult
1080 Loader::CreateSheet(nsIURI* aURI,
1081 nsIContent* aLinkingContent,
1082 nsIPrincipal* aLoaderPrincipal,
1083 CORSMode aCORSMode,
1084 ReferrerPolicy aReferrerPolicy,
1085 bool aSyncLoad,
1086 bool aHasAlternateRel,
1087 const nsAString& aTitle,
1088 StyleSheetState& aSheetState,
1089 bool *aIsAlternate,
1090 CSSStyleSheet** aSheet)
1092 LOG(("css::Loader::CreateSheet"));
1093 NS_PRECONDITION(aSheet, "Null out param!");
1095 if (!mSheets) {
1096 mSheets = new Sheets();
1099 *aSheet = nullptr;
1100 aSheetState = eSheetStateUnknown;
1102 // Check the alternate state before doing anything else, because it
1103 // can mess with our hashtables.
1104 *aIsAlternate = IsAlternate(aTitle, aHasAlternateRel);
1106 if (aURI) {
1107 aSheetState = eSheetComplete;
1108 nsRefPtr<CSSStyleSheet> sheet;
1110 // First, the XUL cache
1111 #ifdef MOZ_XUL
1112 if (IsChromeURI(aURI)) {
1113 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1114 if (cache) {
1115 if (cache->IsEnabled()) {
1116 sheet = cache->GetStyleSheet(aURI);
1117 LOG((" From XUL cache: %p", sheet.get()));
1121 #endif
1123 bool fromCompleteSheets = false;
1124 if (!sheet) {
1125 // Then our per-document complete sheets.
1126 URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
1128 mSheets->mCompleteSheets.Get(&key, getter_AddRefs(sheet));
1129 LOG((" From completed: %p", sheet.get()));
1131 fromCompleteSheets = !!sheet;
1134 if (sheet) {
1135 // This sheet came from the XUL cache or our per-document hashtable; it
1136 // better be a complete sheet.
1137 NS_ASSERTION(sheet->IsComplete(),
1138 "Sheet thinks it's not complete while we think it is");
1140 // Make sure it hasn't been modified; if it has, we can't use it
1141 if (sheet->IsModified()) {
1142 LOG((" Not cloning completed sheet %p because it's been modified",
1143 sheet.get()));
1144 sheet = nullptr;
1145 fromCompleteSheets = false;
1149 // Then loading sheets
1150 if (!sheet && !aSyncLoad) {
1151 aSheetState = eSheetLoading;
1152 SheetLoadData* loadData = nullptr;
1153 URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
1154 mSheets->mLoadingDatas.Get(&key, &loadData);
1155 if (loadData) {
1156 sheet = loadData->mSheet;
1157 LOG((" From loading: %p", sheet.get()));
1159 #ifdef DEBUG
1160 bool debugEqual;
1161 NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
1162 (aLoaderPrincipal && loadData->mLoaderPrincipal &&
1163 NS_SUCCEEDED(aLoaderPrincipal->
1164 Equals(loadData->mLoaderPrincipal,
1165 &debugEqual)) && debugEqual),
1166 "Principals should be the same");
1167 #endif
1170 // Then alternate sheets
1171 if (!sheet) {
1172 aSheetState = eSheetPending;
1173 loadData = nullptr;
1174 mSheets->mPendingDatas.Get(&key, &loadData);
1175 if (loadData) {
1176 sheet = loadData->mSheet;
1177 LOG((" From pending: %p", sheet.get()));
1179 #ifdef DEBUG
1180 bool debugEqual;
1181 NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
1182 (aLoaderPrincipal && loadData->mLoaderPrincipal &&
1183 NS_SUCCEEDED(aLoaderPrincipal->
1184 Equals(loadData->mLoaderPrincipal,
1185 &debugEqual)) && debugEqual),
1186 "Principals should be the same");
1187 #endif
1192 if (sheet) {
1193 // The sheet we have now should be either incomplete or unmodified
1194 NS_ASSERTION(!sheet->IsModified() || !sheet->IsComplete(),
1195 "Unexpected modified complete sheet");
1196 NS_ASSERTION(sheet->IsComplete() || aSheetState != eSheetComplete,
1197 "Sheet thinks it's not complete while we think it is");
1199 *aSheet = sheet->Clone(nullptr, nullptr, nullptr, nullptr).take();
1200 if (*aSheet && fromCompleteSheets &&
1201 !sheet->GetOwnerNode() && !sheet->GetParentSheet()) {
1202 // The sheet we're cloning isn't actually referenced by
1203 // anyone. Replace it in the cache, so that if our CSSOM is
1204 // later modified we don't end up with two copies of our inner
1205 // hanging around.
1206 URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
1207 NS_ASSERTION((*aSheet)->IsComplete(),
1208 "Should only be caching complete sheets");
1209 mSheets->mCompleteSheets.Put(&key, *aSheet);
1214 if (!*aSheet) {
1215 aSheetState = eSheetNeedsParser;
1216 nsIURI *sheetURI;
1217 nsCOMPtr<nsIURI> baseURI;
1218 nsIURI* originalURI;
1219 if (!aURI) {
1220 // Inline style. Use the document's base URL so that @import in
1221 // the inline sheet picks up the right base.
1222 NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?");
1223 baseURI = aLinkingContent->GetBaseURI();
1224 sheetURI = aLinkingContent->OwnerDoc()->GetDocumentURI();
1225 originalURI = nullptr;
1226 } else {
1227 baseURI = aURI;
1228 sheetURI = aURI;
1229 originalURI = aURI;
1232 nsRefPtr<CSSStyleSheet> sheet = new CSSStyleSheet(aCORSMode, aReferrerPolicy);
1233 sheet->SetURIs(sheetURI, originalURI, baseURI);
1234 sheet.forget(aSheet);
1237 NS_ASSERTION(*aSheet, "We should have a sheet by now!");
1238 NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
1239 LOG((" State: %s", gStateStrings[aSheetState]));
1241 return NS_OK;
1245 * PrepareSheet() handles setting the media and title on the sheet, as
1246 * well as setting the enabled state based on the title and whether
1247 * the sheet had "alternate" in its rel.
1249 void
1250 Loader::PrepareSheet(CSSStyleSheet* aSheet,
1251 const nsSubstring& aTitle,
1252 const nsSubstring& aMediaString,
1253 nsMediaList* aMediaList,
1254 Element* aScopeElement,
1255 bool isAlternate)
1257 NS_PRECONDITION(aSheet, "Must have a sheet!");
1259 nsRefPtr<nsMediaList> mediaList(aMediaList);
1261 if (!aMediaString.IsEmpty()) {
1262 NS_ASSERTION(!aMediaList,
1263 "must not provide both aMediaString and aMediaList");
1264 mediaList = new nsMediaList();
1266 nsCSSParser mediumParser(this);
1268 // We have aMediaString only when linked from link elements, style
1269 // elements, or PIs, so pass true.
1270 mediumParser.ParseMediaList(aMediaString, nullptr, 0, mediaList, true);
1273 aSheet->SetMedia(mediaList);
1275 aSheet->SetTitle(aTitle);
1276 aSheet->SetEnabled(! isAlternate);
1277 aSheet->SetScopeElement(aScopeElement);
1281 * InsertSheetInDoc handles ordering of sheets in the document. Here
1282 * we have two types of sheets -- those with linking elements and
1283 * those without. The latter are loaded by Link: headers.
1284 * The following constraints are observed:
1285 * 1) Any sheet with a linking element comes after all sheets without
1286 * linking elements
1287 * 2) Sheets without linking elements are inserted in the order in
1288 * which the inserting requests come in, since all of these are
1289 * inserted during header data processing in the content sink
1290 * 3) Sheets with linking elements are ordered based on document order
1291 * as determined by CompareDocumentPosition.
1293 nsresult
1294 Loader::InsertSheetInDoc(CSSStyleSheet* aSheet,
1295 nsIContent* aLinkingContent,
1296 nsIDocument* aDocument)
1298 LOG(("css::Loader::InsertSheetInDoc"));
1299 NS_PRECONDITION(aSheet, "Nothing to insert");
1300 NS_PRECONDITION(aDocument, "Must have a document to insert into");
1302 // XXX Need to cancel pending sheet loads for this element, if any
1304 int32_t sheetCount = aDocument->GetNumberOfStyleSheets();
1307 * Start the walk at the _end_ of the list, since in the typical
1308 * case we'll just want to append anyway. We want to break out of
1309 * the loop when insertionPoint points to just before the index we
1310 * want to insert at. In other words, when we leave the loop
1311 * insertionPoint is the index of the stylesheet that immediately
1312 * precedes the one we're inserting.
1314 int32_t insertionPoint;
1315 for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) {
1316 nsIStyleSheet *curSheet = aDocument->GetStyleSheetAt(insertionPoint);
1317 NS_ASSERTION(curSheet, "There must be a sheet here!");
1318 nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(curSheet);
1319 NS_ASSERTION(domSheet, "All the \"normal\" sheets implement nsIDOMStyleSheet");
1320 nsCOMPtr<nsIDOMNode> sheetOwner;
1321 domSheet->GetOwnerNode(getter_AddRefs(sheetOwner));
1322 if (sheetOwner && !aLinkingContent) {
1323 // Keep moving; all sheets with a sheetOwner come after all
1324 // sheets without a linkingNode
1325 continue;
1328 if (!sheetOwner) {
1329 // Aha! The current sheet has no sheet owner, so we want to
1330 // insert after it no matter whether we have a linkingNode
1331 break;
1334 nsCOMPtr<nsINode> sheetOwnerNode = do_QueryInterface(sheetOwner);
1335 NS_ASSERTION(aLinkingContent != sheetOwnerNode,
1336 "Why do we still have our old sheet?");
1338 // Have to compare
1339 if (nsContentUtils::PositionIsBefore(sheetOwnerNode, aLinkingContent)) {
1340 // The current sheet comes before us, and it better be the first
1341 // such, because now we break
1342 break;
1346 ++insertionPoint; // adjust the index to the spot we want to insert in
1348 // XXX <meta> elements do not implement nsIStyleSheetLinkingElement;
1349 // need to fix this for them to be ordered correctly.
1350 nsCOMPtr<nsIStyleSheetLinkingElement>
1351 linkingElement = do_QueryInterface(aLinkingContent);
1352 if (linkingElement) {
1353 linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
1356 aDocument->BeginUpdate(UPDATE_STYLE);
1357 aDocument->InsertStyleSheetAt(aSheet, insertionPoint);
1358 aDocument->EndUpdate(UPDATE_STYLE);
1359 LOG((" Inserting into document at position %d", insertionPoint));
1361 return NS_OK;
1365 * InsertChildSheet handles ordering of @import-ed sheet in their
1366 * parent sheets. Here we want to just insert based on order of the
1367 * @import rules that imported the sheets. In theory we can't just
1368 * append to the end because the CSSOM can insert @import rules. In
1369 * practice, we get the call to load the child sheet before the CSSOM
1370 * has finished inserting the @import rule, so we have no idea where
1371 * to put it anyway. So just append for now.
1373 nsresult
1374 Loader::InsertChildSheet(CSSStyleSheet* aSheet,
1375 CSSStyleSheet* aParentSheet,
1376 ImportRule* aParentRule)
1378 LOG(("css::Loader::InsertChildSheet"));
1379 NS_PRECONDITION(aSheet, "Nothing to insert");
1380 NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
1381 NS_PRECONDITION(aParentSheet, "How did we get imported?");
1383 // child sheets should always start out enabled, even if they got
1384 // cloned off of top-level sheets which were disabled
1385 aSheet->SetEnabled(true);
1387 aParentSheet->AppendStyleSheet(aSheet);
1388 aParentRule->SetSheet(aSheet); // This sets the ownerRule on the sheet
1390 LOG((" Inserting into parent sheet"));
1391 // LOG((" Inserting into parent sheet at position %d", insertionPoint));
1393 return NS_OK;
1397 * LoadSheet handles the actual load of a sheet. If the load is
1398 * supposed to be synchronous it just opens a channel synchronously
1399 * using the given uri, wraps the resulting stream in a converter
1400 * stream and calls ParseSheet. Otherwise it tries to look for an
1401 * existing load for this URI and piggyback on it. Failing all that,
1402 * a new load is kicked off asynchronously.
1404 nsresult
1405 Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
1407 LOG(("css::Loader::LoadSheet"));
1408 NS_PRECONDITION(aLoadData, "Need a load data");
1409 NS_PRECONDITION(aLoadData->mURI, "Need a URI to load");
1410 NS_PRECONDITION(aLoadData->mSheet, "Need a sheet to load into");
1411 NS_PRECONDITION(aSheetState != eSheetComplete, "Why bother?");
1412 NS_PRECONDITION(!aLoadData->mUseSystemPrincipal || aLoadData->mSyncLoad,
1413 "Shouldn't use system principal for async loads");
1414 NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
1416 LOG_URI(" Load from: '%s'", aLoadData->mURI);
1418 nsresult rv = NS_OK;
1420 if (!mDocument && !aLoadData->mIsNonDocumentSheet) {
1421 // No point starting the load; just release all the data and such.
1422 LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1423 SheetComplete(aLoadData, NS_BINDING_ABORTED);
1424 return NS_BINDING_ABORTED;
1427 bool inherit = false;
1428 nsIPrincipal* triggeringPrincipal = aLoadData->mLoaderPrincipal;
1429 if (triggeringPrincipal) {
1430 rv = NS_URIChainHasFlags(aLoadData->mURI,
1431 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
1432 &inherit);
1433 inherit =
1434 ((NS_SUCCEEDED(rv) && inherit) ||
1435 (nsContentUtils::URIIsLocalFile(aLoadData->mURI) &&
1436 NS_SUCCEEDED(aLoadData->mLoaderPrincipal->
1437 CheckMayLoad(aLoadData->mURI, false, false))));
1439 else {
1440 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
1443 if (aLoadData->mSyncLoad) {
1444 LOG((" Synchronous load"));
1445 NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
1446 NS_ASSERTION(aSheetState == eSheetNeedsParser,
1447 "Sync loads can't reuse existing async loads");
1449 // Create a nsIUnicharStreamLoader instance to which we will feed
1450 // the data from the sync load. Do this before creating the
1451 // channel to make error recovery simpler.
1452 nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
1453 rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
1454 if (NS_FAILED(rv)) {
1455 LOG_ERROR((" Failed to create stream loader for sync load"));
1456 SheetComplete(aLoadData, rv);
1457 return rv;
1460 if (mDocument) {
1461 mozilla::net::PredictorLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
1462 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1463 mDocument);
1466 // Just load it
1467 nsCOMPtr<nsIChannel> channel;
1468 // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1469 // a node and a principal.
1470 // This is because of a case where the node is the document being styled and
1471 // the principal is the stylesheet (perhaps from a different origin) that is
1472 // applying the styles.
1473 if (aLoadData->mRequestingNode) {
1474 rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
1475 aLoadData->mURI,
1476 aLoadData->mRequestingNode,
1477 triggeringPrincipal,
1478 nsILoadInfo::SEC_NORMAL,
1479 nsIContentPolicy::TYPE_OTHER);
1481 else {
1482 // either we are loading something inside a document, in which case
1483 // we should always have a requestingNode, or we are loading something
1484 // outside a document, in which case the triggeringPrincipal
1485 // should always be the systemPrincipal.
1486 MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(triggeringPrincipal));
1487 rv = NS_NewChannel(getter_AddRefs(channel),
1488 aLoadData->mURI,
1489 triggeringPrincipal,
1490 nsILoadInfo::SEC_NORMAL,
1491 nsIContentPolicy::TYPE_OTHER);
1493 NS_ENSURE_SUCCESS(rv, rv);
1495 nsCOMPtr<nsIInputStream> stream;
1496 rv = channel->Open(getter_AddRefs(stream));
1498 if (NS_FAILED(rv)) {
1499 LOG_ERROR((" Failed to open URI synchronously"));
1500 SheetComplete(aLoadData, rv);
1501 return rv;
1504 // Force UA sheets to be UTF-8.
1505 // XXX this is only necessary because the default in
1506 // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1507 channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
1509 // Manually feed the streamloader the contents of the stream.
1510 // This will call back into OnStreamComplete
1511 // and thence to ParseSheet. Regardless of whether this fails,
1512 // SheetComplete has been called.
1513 return nsSyncLoadService::PushSyncStreamToListener(stream,
1514 streamLoader,
1515 channel);
1518 SheetLoadData* existingData = nullptr;
1520 URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData->mURI,
1521 aLoadData->mLoaderPrincipal,
1522 aLoadData->mSheet->GetCORSMode(),
1523 aLoadData->mSheet->GetReferrerPolicy());
1524 if (aSheetState == eSheetLoading) {
1525 mSheets->mLoadingDatas.Get(&key, &existingData);
1526 NS_ASSERTION(existingData, "CreateSheet lied about the state");
1528 else if (aSheetState == eSheetPending){
1529 mSheets->mPendingDatas.Get(&key, &existingData);
1530 NS_ASSERTION(existingData, "CreateSheet lied about the state");
1533 if (existingData) {
1534 LOG((" Glomming on to existing load"));
1535 SheetLoadData* data = existingData;
1536 while (data->mNext) {
1537 data = data->mNext;
1539 data->mNext = aLoadData; // transfer ownership
1540 if (aSheetState == eSheetPending && !aLoadData->mWasAlternate) {
1541 // Kick the load off; someone cares about it right away
1543 #ifdef DEBUG
1544 SheetLoadData* removedData;
1545 NS_ASSERTION(mSheets->mPendingDatas.Get(&key, &removedData) &&
1546 removedData == existingData,
1547 "Bad pending table.");
1548 #endif
1550 mSheets->mPendingDatas.Remove(&key);
1552 LOG((" Forcing load of pending data"));
1553 return LoadSheet(existingData, eSheetNeedsParser);
1555 // All done here; once the load completes we'll be marked complete
1556 // automatically
1557 return NS_OK;
1560 #ifdef DEBUG
1561 mSyncCallback = true;
1562 #endif
1563 nsCOMPtr<nsILoadGroup> loadGroup;
1564 if (mDocument) {
1565 loadGroup = mDocument->GetDocumentLoadGroup();
1566 NS_ASSERTION(loadGroup,
1567 "No loadgroup for stylesheet; onload will fire early");
1570 nsLoadFlags securityFlags = nsILoadInfo::SEC_NORMAL;
1571 if (inherit) {
1572 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
1575 nsCOMPtr<nsIChannel> channel;
1576 // Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1577 // and a principal. This is because of a case where the node is the document
1578 // being styled and the principal is the stylesheet (perhaps from a different
1579 // origin) that is applying the styles.
1580 if (aLoadData->mRequestingNode) {
1581 rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
1582 aLoadData->mURI,
1583 aLoadData->mRequestingNode,
1584 triggeringPrincipal,
1585 securityFlags,
1586 nsIContentPolicy::TYPE_STYLESHEET,
1587 loadGroup,
1588 nullptr, // aCallbacks
1589 nsIChannel::LOAD_NORMAL |
1590 nsIChannel::LOAD_CLASSIFY_URI);
1592 else {
1593 // either we are loading something inside a document, in which case
1594 // we should always have a requestingNode, or we are loading something
1595 // outside a document, in which case the triggeringPrincipal
1596 // should always be the systemPrincipal.
1597 MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(triggeringPrincipal));
1598 rv = NS_NewChannel(getter_AddRefs(channel),
1599 aLoadData->mURI,
1600 triggeringPrincipal,
1601 securityFlags,
1602 nsIContentPolicy::TYPE_STYLESHEET,
1603 loadGroup,
1604 nullptr, // aCallbacks
1605 nsIChannel::LOAD_NORMAL |
1606 nsIChannel::LOAD_CLASSIFY_URI);
1609 if (NS_FAILED(rv)) {
1610 #ifdef DEBUG
1611 mSyncCallback = false;
1612 #endif
1613 LOG_ERROR((" Failed to create channel"));
1614 SheetComplete(aLoadData, rv);
1615 return rv;
1618 if (!aLoadData->mWasAlternate) {
1619 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
1620 if (cos) {
1621 cos->AddClassFlags(nsIClassOfService::Leader);
1625 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
1626 if (httpChannel) {
1627 // send a minimal Accept header for text/css
1628 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
1629 NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
1630 false);
1631 nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
1632 if (referrerURI)
1633 httpChannel->SetReferrerWithPolicy(referrerURI,
1634 aLoadData->mSheet->GetReferrerPolicy());
1636 // Set the initiator type
1637 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
1638 if (timedChannel) {
1639 if (aLoadData->mParentData) {
1640 timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
1641 } else {
1642 timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
1647 // Now tell the channel we expect text/css data back.... We do
1648 // this before opening it, so it's only treated as a hint.
1649 channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
1651 // We don't have to hold on to the stream loader. The ownership
1652 // model is: Necko owns the stream loader, which owns the load data,
1653 // which owns us
1654 nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
1655 rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
1656 if (NS_FAILED(rv)) {
1657 #ifdef DEBUG
1658 mSyncCallback = false;
1659 #endif
1660 LOG_ERROR((" Failed to create stream loader"));
1661 SheetComplete(aLoadData, rv);
1662 return rv;
1665 nsCOMPtr<nsIStreamListener> channelListener;
1666 CORSMode ourCORSMode = aLoadData->mSheet->GetCORSMode();
1667 if (ourCORSMode != CORS_NONE) {
1668 bool withCredentials = (ourCORSMode == CORS_USE_CREDENTIALS);
1669 LOG((" Doing CORS-enabled load; credentials %d", withCredentials));
1670 nsRefPtr<nsCORSListenerProxy> corsListener =
1671 new nsCORSListenerProxy(streamLoader, aLoadData->mLoaderPrincipal,
1672 withCredentials);
1673 rv = corsListener->Init(channel);
1674 if (NS_FAILED(rv)) {
1675 #ifdef DEBUG
1676 mSyncCallback = false;
1677 #endif
1678 LOG_ERROR((" Initial CORS check failed"));
1679 SheetComplete(aLoadData, rv);
1680 return rv;
1682 channelListener = corsListener;
1683 } else {
1684 channelListener = streamLoader;
1687 if (mDocument) {
1688 mozilla::net::PredictorLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
1689 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1690 mDocument);
1693 rv = channel->AsyncOpen(channelListener, nullptr);
1695 #ifdef DEBUG
1696 mSyncCallback = false;
1697 #endif
1699 if (NS_FAILED(rv)) {
1700 LOG_ERROR((" Failed to create stream loader"));
1701 SheetComplete(aLoadData, rv);
1702 return rv;
1705 mSheets->mLoadingDatas.Put(&key, aLoadData);
1706 aLoadData->mIsLoading = true;
1708 return NS_OK;
1712 * ParseSheet handles parsing the data stream. The main idea here is
1713 * to push the current load data onto the parse stack before letting
1714 * the CSS parser at the data stream. That lets us handle @import
1715 * correctly.
1717 nsresult
1718 Loader::ParseSheet(const nsAString& aInput,
1719 SheetLoadData* aLoadData,
1720 bool& aCompleted)
1722 LOG(("css::Loader::ParseSheet"));
1723 NS_PRECONDITION(aLoadData, "Must have load data");
1724 NS_PRECONDITION(aLoadData->mSheet, "Must have sheet to parse into");
1726 aCompleted = false;
1728 nsCSSParser parser(this, aLoadData->mSheet);
1730 // Push our load data on the stack so any kids can pick it up
1731 mParsingDatas.AppendElement(aLoadData);
1732 nsIURI* sheetURI = aLoadData->mSheet->GetSheetURI();
1733 nsIURI* baseURI = aLoadData->mSheet->GetBaseURI();
1734 nsresult rv = parser.ParseSheet(aInput, sheetURI, baseURI,
1735 aLoadData->mSheet->Principal(),
1736 aLoadData->mLineNumber,
1737 aLoadData->mAllowUnsafeRules);
1738 mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
1740 if (NS_FAILED(rv)) {
1741 LOG_ERROR((" Low-level error in parser!"));
1742 SheetComplete(aLoadData, rv);
1743 return rv;
1746 NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad,
1747 "Sync load has leftover pending children!");
1749 if (aLoadData->mPendingChildren == 0) {
1750 LOG((" No pending kids from parse"));
1751 aCompleted = true;
1752 SheetComplete(aLoadData, NS_OK);
1754 // Otherwise, the children are holding strong refs to the data and
1755 // will call SheetComplete() on it when they complete.
1757 return NS_OK;
1761 * SheetComplete is the do-it-all cleanup function. It removes the
1762 * load data from the "loading" hashtable, adds the sheet to the
1763 * "completed" hashtable, massages the XUL cache, handles siblings of
1764 * the load data (other loads for the same URI), handles unblocking
1765 * blocked parent loads as needed, and most importantly calls
1766 * NS_RELEASE on the load data to destroy the whole mess.
1768 void
1769 Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
1771 LOG(("css::Loader::SheetComplete"));
1773 // 8 is probably big enough for all our common cases. It's not likely that
1774 // imports will nest more than 8 deep, and multiple sheets with the same URI
1775 // are rare.
1776 nsAutoTArray<nsRefPtr<SheetLoadData>, 8> datasToNotify;
1777 DoSheetComplete(aLoadData, aStatus, datasToNotify);
1779 // Now it's safe to go ahead and notify observers
1780 uint32_t count = datasToNotify.Length();
1781 mDatasToNotifyOn += count;
1782 for (uint32_t i = 0; i < count; ++i) {
1783 --mDatasToNotifyOn;
1785 SheetLoadData* data = datasToNotify[i];
1786 NS_ASSERTION(data && data->mMustNotify, "How did this data get here?");
1787 if (data->mObserver) {
1788 LOG((" Notifying observer 0x%x for data 0x%x. wasAlternate: %d",
1789 data->mObserver.get(), data, data->mWasAlternate));
1790 data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate,
1791 aStatus);
1794 nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers);
1795 nsCOMPtr<nsICSSLoaderObserver> obs;
1796 while (iter.HasMore()) {
1797 obs = iter.GetNext();
1798 LOG((" Notifying global observer 0x%x for data 0x%s. wasAlternate: %d",
1799 obs.get(), data, data->mWasAlternate));
1800 obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus);
1804 if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) {
1805 LOG((" No more loading sheets; starting alternates"));
1806 StartAlternateLoads();
1810 void
1811 Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
1812 LoadDataArray& aDatasToNotify)
1814 LOG(("css::Loader::DoSheetComplete"));
1815 NS_PRECONDITION(aLoadData, "Must have a load data!");
1816 NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
1817 NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
1819 LOG(("Load completed, status: 0x%x", aStatus));
1821 // Twiddle the hashtables
1822 if (aLoadData->mURI) {
1823 LOG_URI(" Finished loading: '%s'", aLoadData->mURI);
1824 // Remove the data from the list of loading datas
1825 if (aLoadData->mIsLoading) {
1826 URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData->mURI,
1827 aLoadData->mLoaderPrincipal,
1828 aLoadData->mSheet->GetCORSMode(),
1829 aLoadData->mSheet->GetReferrerPolicy());
1830 #ifdef DEBUG
1831 SheetLoadData *loadingData;
1832 NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) &&
1833 loadingData == aLoadData,
1834 "Bad loading table");
1835 #endif
1837 mSheets->mLoadingDatas.Remove(&key);
1838 aLoadData->mIsLoading = false;
1842 // Go through and deal with the whole linked list.
1843 SheetLoadData* data = aLoadData;
1844 while (data) {
1845 if (!data->mSheetAlreadyComplete) {
1846 // If mSheetAlreadyComplete, then the sheet could well be modified between
1847 // when we posted the async call to SheetComplete and now, since the sheet
1848 // was page-accessible during that whole time.
1849 NS_ABORT_IF_FALSE(!data->mSheet->IsModified(),
1850 "should not get marked modified during parsing");
1851 data->mSheet->SetComplete();
1852 data->ScheduleLoadEventIfNeeded(aStatus);
1854 if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
1855 // Don't notify here so we don't trigger script. Remember the
1856 // info we need to notify, then do it later when it's safe.
1857 aDatasToNotify.AppendElement(data);
1859 // On append failure, just press on. We'll fail to notify the observer,
1860 // but not much we can do about that....
1863 NS_ASSERTION(!data->mParentData ||
1864 data->mParentData->mPendingChildren != 0,
1865 "Broken pending child count on our parent");
1867 // If we have a parent, our parent is no longer being parsed, and
1868 // we are the last pending child, then our load completion
1869 // completes the parent too. Note that the parent _can_ still be
1870 // being parsed (eg if the child (us) failed to open the channel
1871 // or some such).
1872 if (data->mParentData &&
1873 --(data->mParentData->mPendingChildren) == 0 &&
1874 !mParsingDatas.Contains(data->mParentData)) {
1875 DoSheetComplete(data->mParentData, aStatus, aDatasToNotify);
1878 data = data->mNext;
1881 // Now that it's marked complete, put the sheet in our cache.
1882 // If we ever start doing this for failure aStatus, we'll need to
1883 // adjust the PostLoadEvent code that thinks anything already
1884 // complete must have loaded succesfully.
1885 if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
1886 // Pick our sheet to cache carefully. Ideally, we want to cache
1887 // one of the sheets that will be kept alive by a document or
1888 // parent sheet anyway, so that if someone then accesses it via
1889 // CSSOM we won't have extra clones of the inner lying around.
1890 data = aLoadData;
1891 CSSStyleSheet* sheet = aLoadData->mSheet;
1892 while (data) {
1893 if (data->mSheet->GetParentSheet() || data->mSheet->GetOwnerNode()) {
1894 sheet = data->mSheet;
1895 break;
1897 data = data->mNext;
1899 #ifdef MOZ_XUL
1900 if (IsChromeURI(aLoadData->mURI)) {
1901 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1902 if (cache && cache->IsEnabled()) {
1903 if (!cache->GetStyleSheet(aLoadData->mURI)) {
1904 LOG((" Putting sheet in XUL prototype cache"));
1905 NS_ASSERTION(sheet->IsComplete(),
1906 "Should only be caching complete sheets");
1907 cache->PutStyleSheet(sheet);
1911 else {
1912 #endif
1913 URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData->mURI,
1914 aLoadData->mLoaderPrincipal,
1915 aLoadData->mSheet->GetCORSMode(),
1916 aLoadData->mSheet->GetReferrerPolicy());
1917 NS_ASSERTION(sheet->IsComplete(),
1918 "Should only be caching complete sheets");
1919 mSheets->mCompleteSheets.Put(&key, sheet);
1920 #ifdef MOZ_XUL
1922 #endif
1925 NS_RELEASE(aLoadData); // this will release parents and siblings and all that
1928 nsresult
1929 Loader::LoadInlineStyle(nsIContent* aElement,
1930 const nsAString& aBuffer,
1931 uint32_t aLineNumber,
1932 const nsAString& aTitle,
1933 const nsAString& aMedia,
1934 Element* aScopeElement,
1935 nsICSSLoaderObserver* aObserver,
1936 bool* aCompleted,
1937 bool* aIsAlternate)
1939 LOG(("css::Loader::LoadInlineStyle"));
1940 NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
1942 *aCompleted = true;
1944 if (!mEnabled) {
1945 LOG_WARN((" Not enabled"));
1946 return NS_ERROR_NOT_AVAILABLE;
1949 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
1951 nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
1952 NS_ASSERTION(owningElement, "Element is not a style linking element!");
1954 // Since we're not planning to load a URI, no need to hand a principal to the
1955 // load data or to CreateSheet(). Also, OK to use CORS_NONE for the CORS
1956 // mode and mDocument's ReferrerPolicy.
1957 StyleSheetState state;
1958 nsRefPtr<CSSStyleSheet> sheet;
1959 nsresult rv = CreateSheet(nullptr, aElement, nullptr, CORS_NONE,
1960 mDocument->GetReferrerPolicy(), false, false,
1961 aTitle, state, aIsAlternate, getter_AddRefs(sheet));
1962 NS_ENSURE_SUCCESS(rv, rv);
1963 NS_ASSERTION(state == eSheetNeedsParser,
1964 "Inline sheets should not be cached");
1966 LOG((" Sheet is alternate: %d", *aIsAlternate));
1968 PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate);
1970 if (aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
1971 ShadowRoot* containingShadow = aElement->GetContainingShadow();
1972 MOZ_ASSERT(containingShadow);
1973 containingShadow->InsertSheet(sheet, aElement);
1974 } else {
1975 rv = InsertSheetInDoc(sheet, aElement, mDocument);
1976 NS_ENSURE_SUCCESS(rv, rv);
1979 SheetLoadData* data = new SheetLoadData(this, aTitle, nullptr, sheet,
1980 owningElement, *aIsAlternate,
1981 aObserver, nullptr, static_cast<nsINode*>(aElement));
1983 // We never actually load this, so just set its principal directly
1984 sheet->SetPrincipal(aElement->NodePrincipal());
1986 NS_ADDREF(data);
1987 data->mLineNumber = aLineNumber;
1988 // Parse completion releases the load data
1989 rv = ParseSheet(aBuffer, data, *aCompleted);
1990 NS_ENSURE_SUCCESS(rv, rv);
1992 // If aCompleted is true, |data| may well be deleted by now.
1993 if (!*aCompleted) {
1994 data->mMustNotify = true;
1996 return rv;
1999 nsresult
2000 Loader::LoadStyleLink(nsIContent* aElement,
2001 nsIURI* aURL,
2002 const nsAString& aTitle,
2003 const nsAString& aMedia,
2004 bool aHasAlternateRel,
2005 CORSMode aCORSMode,
2006 ReferrerPolicy aReferrerPolicy,
2007 nsICSSLoaderObserver* aObserver,
2008 bool* aIsAlternate)
2010 LOG(("css::Loader::LoadStyleLink"));
2011 NS_PRECONDITION(aURL, "Must have URL to load");
2012 NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
2014 LOG_URI(" Link uri: '%s'", aURL);
2015 LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle).get()));
2016 LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia).get()));
2017 LOG((" Link alternate rel: %d", aHasAlternateRel));
2019 if (!mEnabled) {
2020 LOG_WARN((" Not enabled"));
2021 return NS_ERROR_NOT_AVAILABLE;
2024 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
2026 nsIPrincipal* principal =
2027 aElement ? aElement->NodePrincipal() : mDocument->NodePrincipal();
2029 nsISupports* context = aElement;
2030 if (!context) {
2031 context = mDocument;
2033 nsresult rv = CheckLoadAllowed(principal, aURL, context);
2034 if (NS_FAILED(rv)) return rv;
2036 LOG((" Passed load check"));
2038 StyleSheetState state;
2039 nsRefPtr<CSSStyleSheet> sheet;
2040 rv = CreateSheet(aURL, aElement, principal, aCORSMode,
2041 aReferrerPolicy, false,
2042 aHasAlternateRel, aTitle, state, aIsAlternate,
2043 getter_AddRefs(sheet));
2044 NS_ENSURE_SUCCESS(rv, rv);
2046 LOG((" Sheet is alternate: %d", *aIsAlternate));
2048 PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate);
2050 rv = InsertSheetInDoc(sheet, aElement, mDocument);
2051 NS_ENSURE_SUCCESS(rv, rv);
2053 nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
2055 if (state == eSheetComplete) {
2056 LOG((" Sheet already complete: 0x%p",
2057 static_cast<void*>(sheet.get())));
2058 if (aObserver || !mObservers.IsEmpty() || owningElement) {
2059 rv = PostLoadEvent(aURL, sheet, aObserver, *aIsAlternate,
2060 owningElement);
2061 return rv;
2064 return NS_OK;
2067 // Now we need to actually load it
2068 nsCOMPtr<nsINode> requestingNode = do_QueryInterface(context);
2069 SheetLoadData* data = new SheetLoadData(this, aTitle, aURL, sheet,
2070 owningElement, *aIsAlternate,
2071 aObserver, principal, requestingNode);
2072 NS_ADDREF(data);
2074 // If we have to parse and it's an alternate non-inline, defer it
2075 if (aURL && state == eSheetNeedsParser && mSheets->mLoadingDatas.Count() != 0 &&
2076 *aIsAlternate) {
2077 LOG((" Deferring alternate sheet load"));
2078 URIPrincipalReferrerPolicyAndCORSModeHashKey key(data->mURI,
2079 data->mLoaderPrincipal,
2080 data->mSheet->GetCORSMode(),
2081 data->mSheet->GetReferrerPolicy());
2082 mSheets->mPendingDatas.Put(&key, data);
2084 data->mMustNotify = true;
2085 return NS_OK;
2088 // Load completion will free the data
2089 rv = LoadSheet(data, state);
2090 NS_ENSURE_SUCCESS(rv, rv);
2092 data->mMustNotify = true;
2093 return rv;
2096 static bool
2097 HaveAncestorDataWithURI(SheetLoadData *aData, nsIURI *aURI)
2099 if (!aData->mURI) {
2100 // Inline style; this won't have any ancestors
2101 NS_ABORT_IF_FALSE(!aData->mParentData,
2102 "How does inline style have a parent?");
2103 return false;
2106 bool equal;
2107 if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) {
2108 return true;
2111 // Datas down the mNext chain have the same URI as aData, so we
2112 // don't have to compare to them. But they might have different
2113 // parents, and we have to check all of those.
2114 while (aData) {
2115 if (aData->mParentData &&
2116 HaveAncestorDataWithURI(aData->mParentData, aURI)) {
2117 return true;
2120 aData = aData->mNext;
2123 return false;
2126 nsresult
2127 Loader::LoadChildSheet(CSSStyleSheet* aParentSheet,
2128 nsIURI* aURL,
2129 nsMediaList* aMedia,
2130 ImportRule* aParentRule)
2132 LOG(("css::Loader::LoadChildSheet"));
2133 NS_PRECONDITION(aURL, "Must have a URI to load");
2134 NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
2136 if (!mEnabled) {
2137 LOG_WARN((" Not enabled"));
2138 return NS_ERROR_NOT_AVAILABLE;
2141 LOG_URI(" Child uri: '%s'", aURL);
2143 nsCOMPtr<nsIDOMNode> owningNode;
2145 // check for an owning document: if none, don't bother walking up the parent
2146 // sheets
2147 if (aParentSheet->GetOwningDocument()) {
2148 nsCOMPtr<nsIDOMStyleSheet> nextParentSheet(aParentSheet);
2149 NS_ENSURE_TRUE(nextParentSheet, NS_ERROR_FAILURE); //Not a stylesheet!?
2151 nsCOMPtr<nsIDOMStyleSheet> topSheet;
2152 //traverse our way to the top-most sheet
2153 do {
2154 topSheet.swap(nextParentSheet);
2155 topSheet->GetParentStyleSheet(getter_AddRefs(nextParentSheet));
2156 } while (nextParentSheet);
2158 topSheet->GetOwnerNode(getter_AddRefs(owningNode));
2161 nsISupports* context = owningNode;
2162 if (!context) {
2163 context = mDocument;
2166 nsIPrincipal* principal = aParentSheet->Principal();
2167 nsresult rv = CheckLoadAllowed(principal, aURL, context);
2168 if (NS_FAILED(rv)) return rv;
2170 LOG((" Passed load check"));
2172 SheetLoadData* parentData = nullptr;
2173 nsCOMPtr<nsICSSLoaderObserver> observer;
2175 int32_t count = mParsingDatas.Length();
2176 if (count > 0) {
2177 LOG((" Have a parent load"));
2178 parentData = mParsingDatas.ElementAt(count - 1);
2179 // Check for cycles
2180 if (HaveAncestorDataWithURI(parentData, aURL)) {
2181 // Houston, we have a loop, blow off this child and pretend this never
2182 // happened
2183 LOG_ERROR((" @import cycle detected, dropping load"));
2184 return NS_OK;
2187 NS_ASSERTION(parentData->mSheet == aParentSheet,
2188 "Unexpected call to LoadChildSheet");
2189 } else {
2190 LOG((" No parent load; must be CSSOM"));
2191 // No parent load data, so the sheet will need to be notified when
2192 // we finish, if it can be, if we do the load asynchronously.
2193 observer = aParentSheet;
2196 // Now that we know it's safe to load this (passes security check and not a
2197 // loop) do so.
2198 nsRefPtr<CSSStyleSheet> sheet;
2199 bool isAlternate;
2200 StyleSheetState state;
2201 const nsSubstring& empty = EmptyString();
2202 // For now, use CORS_NONE for child sheets
2203 rv = CreateSheet(aURL, nullptr, principal, CORS_NONE,
2204 aParentSheet->GetReferrerPolicy(),
2205 parentData ? parentData->mSyncLoad : false,
2206 false, empty, state, &isAlternate, getter_AddRefs(sheet));
2207 NS_ENSURE_SUCCESS(rv, rv);
2209 PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate);
2211 rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
2212 NS_ENSURE_SUCCESS(rv, rv);
2214 if (state == eSheetComplete) {
2215 LOG((" Sheet already complete"));
2216 // We're completely done. No need to notify, even, since the
2217 // @import rule addition/modification will trigger the right style
2218 // changes automatically.
2219 return NS_OK;
2222 nsCOMPtr<nsINode> requestingNode = do_QueryInterface(context);
2223 SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
2224 observer, principal, requestingNode);
2226 NS_ADDREF(data);
2227 bool syncLoad = data->mSyncLoad;
2229 // Load completion will release the data
2230 rv = LoadSheet(data, state);
2231 NS_ENSURE_SUCCESS(rv, rv);
2233 // If syncLoad is true, |data| will be deleted by now.
2234 if (!syncLoad) {
2235 data->mMustNotify = true;
2237 return rv;
2240 nsresult
2241 Loader::LoadSheetSync(nsIURI* aURL, bool aAllowUnsafeRules,
2242 bool aUseSystemPrincipal,
2243 CSSStyleSheet** aSheet)
2245 LOG(("css::Loader::LoadSheetSync"));
2246 return InternalLoadNonDocumentSheet(aURL, aAllowUnsafeRules,
2247 aUseSystemPrincipal, nullptr,
2248 EmptyCString(), aSheet, nullptr);
2251 nsresult
2252 Loader::LoadSheet(nsIURI* aURL,
2253 nsIPrincipal* aOriginPrincipal,
2254 const nsCString& aCharset,
2255 nsICSSLoaderObserver* aObserver,
2256 CSSStyleSheet** aSheet)
2258 LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
2259 NS_PRECONDITION(aSheet, "aSheet is null");
2260 return InternalLoadNonDocumentSheet(aURL, false, false,
2261 aOriginPrincipal, aCharset,
2262 aSheet, aObserver);
2265 nsresult
2266 Loader::LoadSheet(nsIURI* aURL,
2267 nsIPrincipal* aOriginPrincipal,
2268 const nsCString& aCharset,
2269 nsICSSLoaderObserver* aObserver,
2270 CORSMode aCORSMode,
2271 ReferrerPolicy aReferrerPolicy)
2273 LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2274 return InternalLoadNonDocumentSheet(aURL, false, false,
2275 aOriginPrincipal, aCharset,
2276 nullptr, aObserver, aCORSMode,
2277 aReferrerPolicy);
2280 nsresult
2281 Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
2282 bool aAllowUnsafeRules,
2283 bool aUseSystemPrincipal,
2284 nsIPrincipal* aOriginPrincipal,
2285 const nsCString& aCharset,
2286 CSSStyleSheet** aSheet,
2287 nsICSSLoaderObserver* aObserver,
2288 CORSMode aCORSMode,
2289 ReferrerPolicy aReferrerPolicy)
2291 NS_PRECONDITION(aURL, "Must have a URI to load");
2292 NS_PRECONDITION(aSheet || aObserver, "Sheet and observer can't both be null");
2293 NS_PRECONDITION(!aUseSystemPrincipal || !aObserver,
2294 "Shouldn't load system-principal sheets async");
2295 NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
2297 LOG_URI(" Non-document sheet uri: '%s'", aURL);
2299 if (aSheet) {
2300 *aSheet = nullptr;
2303 if (!mEnabled) {
2304 LOG_WARN((" Not enabled"));
2305 return NS_ERROR_NOT_AVAILABLE;
2308 nsresult rv = CheckLoadAllowed(aOriginPrincipal, aURL, mDocument);
2309 if (NS_FAILED(rv)) {
2310 return rv;
2313 StyleSheetState state;
2314 bool isAlternate;
2315 nsRefPtr<CSSStyleSheet> sheet;
2316 bool syncLoad = (aObserver == nullptr);
2317 const nsSubstring& empty = EmptyString();
2319 rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aCORSMode,
2320 aReferrerPolicy, syncLoad, false,
2321 empty, state, &isAlternate, getter_AddRefs(sheet));
2322 NS_ENSURE_SUCCESS(rv, rv);
2324 PrepareSheet(sheet, empty, empty, nullptr, nullptr, isAlternate);
2326 if (state == eSheetComplete) {
2327 LOG((" Sheet already complete"));
2328 if (aObserver || !mObservers.IsEmpty()) {
2329 rv = PostLoadEvent(aURL, sheet, aObserver, false, nullptr);
2331 if (aSheet) {
2332 sheet.swap(*aSheet);
2334 return rv;
2337 SheetLoadData* data =
2338 new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules,
2339 aUseSystemPrincipal, aCharset, aObserver,
2340 aOriginPrincipal, mDocument);
2342 NS_ADDREF(data);
2343 rv = LoadSheet(data, state);
2344 NS_ENSURE_SUCCESS(rv, rv);
2346 if (aSheet) {
2347 sheet.swap(*aSheet);
2349 if (aObserver) {
2350 data->mMustNotify = true;
2353 return rv;
2356 nsresult
2357 Loader::PostLoadEvent(nsIURI* aURI,
2358 CSSStyleSheet* aSheet,
2359 nsICSSLoaderObserver* aObserver,
2360 bool aWasAlternate,
2361 nsIStyleSheetLinkingElement* aElement)
2363 LOG(("css::Loader::PostLoadEvent"));
2364 NS_PRECONDITION(aSheet, "Must have sheet");
2365 NS_PRECONDITION(aObserver || !mObservers.IsEmpty() || aElement,
2366 "Must have observer or element");
2368 nsRefPtr<SheetLoadData> evt =
2369 new SheetLoadData(this, EmptyString(), // title doesn't matter here
2370 aURI,
2371 aSheet,
2372 aElement,
2373 aWasAlternate,
2374 aObserver,
2375 nullptr,
2376 mDocument);
2377 NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
2379 if (!mPostedEvents.AppendElement(evt)) {
2380 return NS_ERROR_OUT_OF_MEMORY;
2383 nsresult rv = NS_DispatchToCurrentThread(evt);
2384 if (NS_FAILED(rv)) {
2385 NS_WARNING("failed to dispatch stylesheet load event");
2386 mPostedEvents.RemoveElement(evt);
2387 } else {
2388 // We'll unblock onload when we handle the event.
2389 if (mDocument) {
2390 mDocument->BlockOnload();
2393 // We want to notify the observer for this data.
2394 evt->mMustNotify = true;
2395 evt->mSheetAlreadyComplete = true;
2397 // If we get to this code, aSheet loaded correctly at some point, so
2398 // we can just use NS_OK for the status. Note that we do this here
2399 // and not from inside our SheetComplete so that we don't end up
2400 // running the load event async.
2401 evt->ScheduleLoadEventIfNeeded(NS_OK);
2404 return rv;
2407 void
2408 Loader::HandleLoadEvent(SheetLoadData* aEvent)
2410 // XXXbz can't assert this yet.... May not have an observer because
2411 // we're unblocking the parser
2412 // NS_ASSERTION(aEvent->mObserver, "Must have observer");
2413 NS_ASSERTION(aEvent->mSheet, "Must have sheet");
2415 // Very important: this needs to come before the SheetComplete call
2416 // below, so that HasPendingLoads() will test true as needed under
2417 // notifications we send from that SheetComplete call.
2418 mPostedEvents.RemoveElement(aEvent);
2420 if (!aEvent->mIsCancelled) {
2421 // SheetComplete will call Release(), so give it a reference to do
2422 // that with.
2423 NS_ADDREF(aEvent);
2424 SheetComplete(aEvent, NS_OK);
2427 if (mDocument) {
2428 mDocument->UnblockOnload(true);
2432 static PLDHashOperator
2433 StopLoadingSheetCallback(URIPrincipalReferrerPolicyAndCORSModeHashKey* aKey,
2434 SheetLoadData*& aData,
2435 void* aClosure)
2437 NS_PRECONDITION(aData, "Must have a data!");
2438 NS_PRECONDITION(aClosure, "Must have a loader");
2440 aData->mIsLoading = false; // we will handle the removal right here
2441 aData->mIsCancelled = true;
2443 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
2445 return PL_DHASH_REMOVE;
2448 nsresult
2449 Loader::Stop()
2451 uint32_t pendingCount =
2452 mSheets ? mSheets->mPendingDatas.Count() : 0;
2453 uint32_t loadingCount =
2454 mSheets ? mSheets->mLoadingDatas.Count() : 0;
2455 LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length());
2457 if (pendingCount) {
2458 mSheets->mPendingDatas.Enumerate(StopLoadingSheetCallback, &arr);
2460 if (loadingCount) {
2461 mSheets->mLoadingDatas.Enumerate(StopLoadingSheetCallback, &arr);
2464 uint32_t i;
2465 for (i = 0; i < mPostedEvents.Length(); ++i) {
2466 SheetLoadData* data = mPostedEvents[i];
2467 data->mIsCancelled = true;
2468 if (arr.AppendElement(data)) {
2469 // SheetComplete() calls Release(), so give this an extra ref.
2470 NS_ADDREF(data);
2472 #ifdef DEBUG
2473 else {
2474 NS_NOTREACHED("We preallocated this memory... shouldn't really fail, "
2475 "except we never check that preallocation succeeds.");
2477 #endif
2479 mPostedEvents.Clear();
2481 mDatasToNotifyOn += arr.Length();
2482 for (i = 0; i < arr.Length(); ++i) {
2483 --mDatasToNotifyOn;
2484 SheetComplete(arr[i], NS_BINDING_ABORTED);
2486 return NS_OK;
2489 bool
2490 Loader::HasPendingLoads()
2492 return
2493 (mSheets && mSheets->mLoadingDatas.Count() != 0) ||
2494 (mSheets && mSheets->mPendingDatas.Count() != 0) ||
2495 mPostedEvents.Length() != 0 ||
2496 mDatasToNotifyOn != 0;
2499 nsresult
2500 Loader::AddObserver(nsICSSLoaderObserver* aObserver)
2502 NS_PRECONDITION(aObserver, "Must have observer");
2503 if (mObservers.AppendElementUnlessExists(aObserver)) {
2504 return NS_OK;
2507 return NS_ERROR_OUT_OF_MEMORY;
2510 void
2511 Loader::RemoveObserver(nsICSSLoaderObserver* aObserver)
2513 mObservers.RemoveElement(aObserver);
2516 static PLDHashOperator
2517 CollectLoadDatas(URIPrincipalReferrerPolicyAndCORSModeHashKey *aKey,
2518 SheetLoadData* &aData,
2519 void* aClosure)
2521 static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
2522 return PL_DHASH_REMOVE;
2525 void
2526 Loader::StartAlternateLoads()
2528 NS_PRECONDITION(mSheets, "Don't call me!");
2529 LoadDataArray arr(mSheets->mPendingDatas.Count());
2530 mSheets->mPendingDatas.Enumerate(CollectLoadDatas, &arr);
2532 mDatasToNotifyOn += arr.Length();
2533 for (uint32_t i = 0; i < arr.Length(); ++i) {
2534 --mDatasToNotifyOn;
2535 LoadSheet(arr[i], eSheetNeedsParser);
2539 static PLDHashOperator
2540 TraverseSheet(URIPrincipalReferrerPolicyAndCORSModeHashKey*,
2541 CSSStyleSheet* aSheet,
2542 void* aClosure)
2544 nsCycleCollectionTraversalCallback* cb =
2545 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
2546 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "Sheet cache nsCSSLoader");
2547 cb->NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIStyleSheet*, aSheet));
2548 return PL_DHASH_NEXT;
2551 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
2553 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
2554 if (tmp->mSheets) {
2555 tmp->mSheets->mCompleteSheets.EnumerateRead(TraverseSheet, &cb);
2557 nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>>::ForwardIterator
2558 it(tmp->mObservers);
2559 while (it.HasMore()) {
2560 ImplCycleCollectionTraverse(cb, it.GetNext(),
2561 "mozilla::css::Loader.mObservers");
2563 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2565 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader)
2566 if (tmp->mSheets) {
2567 tmp->mSheets->mCompleteSheets.Clear();
2569 tmp->mObservers.Clear();
2570 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2572 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Loader, AddRef)
2573 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Loader, Release)
2575 struct SheetMemoryCounter {
2576 size_t size;
2577 mozilla::MallocSizeOf mallocSizeOf;
2580 static size_t
2581 CountSheetMemory(URIPrincipalReferrerPolicyAndCORSModeHashKey* /* unused */,
2582 const nsRefPtr<CSSStyleSheet>& aSheet,
2583 mozilla::MallocSizeOf aMallocSizeOf,
2584 void* /* unused */)
2586 // If aSheet has a parent, then its parent will report it so we don't
2587 // have to worry about it here.
2588 // Likewise, if aSheet has an owning node, then the document that
2589 // node is in will report it.
2590 if (aSheet->GetOwnerNode() || aSheet->GetParentSheet()) {
2591 return 0;
2593 return aSheet->SizeOfIncludingThis(aMallocSizeOf);
2596 size_t
2597 Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
2599 size_t s = aMallocSizeOf(this);
2601 if (mSheets) {
2602 s += mSheets->mCompleteSheets.SizeOfExcludingThis(CountSheetMemory, aMallocSizeOf);
2604 s += mObservers.SizeOfExcludingThis(aMallocSizeOf);
2606 // Measurement of the following members may be added later if DMD finds it is
2607 // worthwhile:
2608 // - mLoadingDatas: transient, and should be small
2609 // - mPendingDatas: transient, and should be small
2610 // - mParsingDatas: transient, and should be small
2611 // - mPostedEvents: transient, and should be small
2613 // The following members aren't measured:
2614 // - mDocument, because it's a weak backpointer
2615 // - mPreferredSheet, because it can be a shared string
2617 return s;
2620 } // namespace css
2621 } // namespace mozilla