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"
29 #include "nsIContent.h"
30 #include "nsIDocument.h"
31 #include "nsIDOMNode.h"
32 #include "nsIDOMDocument.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"
56 #include "nsXULPrototypeCache.h"
59 #include "nsIMediaList.h"
60 #include "nsIDOMStyleSheet.h"
63 #include "nsIContentSecurityPolicy.h"
65 #include "mozilla/dom/EncodingUtils.h"
66 using mozilla::dom::EncodingUtils
;
68 using namespace mozilla::dom
;
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
101 /*********************************************
102 * Data needed to properly load a stylesheet *
103 *********************************************/
105 class SheetLoadData
: public nsIRunnable
,
106 public nsIUnicharStreamLoaderObserver
,
107 public nsIThreadObserver
110 virtual ~SheetLoadData(void);
113 // Data for loading a sheet linked from a document
114 SheetLoadData(Loader
* aLoader
,
115 const nsSubstring
& aTitle
,
117 CSSStyleSheet
* aSheet
,
118 nsIStyleSheetLinkingElement
* aOwningElement
,
120 nsICSSLoaderObserver
* aObserver
,
121 nsIPrincipal
* aLoaderPrincipal
,
122 nsINode
* aRequestingNode
);
124 // Data for loading a sheet linked from an @import rule
125 SheetLoadData(Loader
* aLoader
,
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
,
136 CSSStyleSheet
* aSheet
,
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
);
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
162 // Charset we decided to use for the sheet
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
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.
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).
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
210 bool mMustNotify
: 1;
212 // mWasAlternate is true if the sheet was an alternate when the load data was
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
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
253 void FireLoadEvent(nsIThreadInternal
* aThread
);
259 static PRLogModuleInfo
*
262 static PRLogModuleInfo
*sLog
;
264 sLog
= PR_NewLogModule("nsCSSLoader");
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()
282 #define LOG_URI(format, uri) \
284 NS_ASSERTION(uri, "Logging null uri"); \
285 if (LOG_ENABLED()) { \
286 nsAutoCString _logURISpec; \
287 uri->GetSpec(_logURISpec); \
288 LOG((format, _logURISpec.get())); \
292 #define LOG_URI(format, uri)
295 // And some convenience strings...
297 static const char* const gStateStrings
[] = {
298 "eSheetStateUnknown",
306 /********************************
307 * SheetLoadData implementation *
308 ********************************/
309 NS_IMPL_ISUPPORTS(SheetLoadData
, nsIUnicharStreamLoaderObserver
, nsIRunnable
,
312 SheetLoadData::SheetLoadData(Loader
* aLoader
,
313 const nsSubstring
& aTitle
,
315 CSSStyleSheet
* aSheet
,
316 nsIStyleSheetLinkingElement
* aOwningElement
,
318 nsICSSLoaderObserver
* aObserver
,
319 nsIPrincipal
* aLoaderPrincipal
,
320 nsINode
* aRequestingNode
)
329 mIsNonDocumentSheet(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
,
347 CSSStyleSheet
* aSheet
,
348 SheetLoadData
* aParentData
,
349 nsICSSLoaderObserver
* aObserver
,
350 nsIPrincipal
* aLoaderPrincipal
,
351 nsINode
* aRequestingNode
)
357 mParentData(aParentData
),
360 mIsNonDocumentSheet(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!");
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
,
388 CSSStyleSheet
* aSheet
,
390 bool aAllowUnsafeRules
,
391 bool aUseSystemPrincipal
,
392 const nsCString
& aCharset
,
393 nsICSSLoaderObserver
* aObserver
,
394 nsIPrincipal
* aLoaderPrincipal
,
395 nsINode
* aRequestingNode
)
402 mSyncLoad(aSyncLoad
),
403 mIsNonDocumentSheet(true),
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
);
431 mLoader
->HandleLoadEvent(this);
436 SheetLoadData::OnDispatchedEvent(nsIThreadInternal
* aThread
)
442 SheetLoadData::OnProcessNextEvent(nsIThreadInternal
* aThread
,
444 uint32_t aRecursionDepth
)
446 // We want to fire our load even before or after event processing,
447 // whichever comes first.
448 FireLoadEvent(aThread
);
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
);
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(),
478 NS_SUCCEEDED(mStatus
) ?
479 NS_LITERAL_STRING("load") :
480 NS_LITERAL_STRING("error"),
483 // And unblock onload
484 if (mLoader
->mDocument
) {
485 mLoader
->mDocument
->UnblockOnload(true);
490 SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus
)
492 if (!mOwningElement
) {
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 *************************/
514 , mDatasToNotifyOn(0)
515 , mCompatMode(eCompatibility_FullStandards
)
518 , mSyncCallback(false)
523 Loader::Loader(nsIDocument
* aDocument
)
524 : mDocument(aDocument
)
525 , mDatasToNotifyOn(0)
526 , mCompatMode(eCompatibility_FullStandards
)
529 , mSyncCallback(false)
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
);
537 domDoc
->GetPreferredStyleSheetSet(mPreferredSheet
);
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
553 Loader::DropDocumentReference(void)
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
560 StartAlternateLoads();
564 static PLDHashOperator
565 CollectNonAlternates(URIPrincipalReferrerPolicyAndCORSModeHashKey
*aKey
,
566 SheetLoadData
* &aData
,
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
;
583 Loader::SetPreferredSheet(const nsAString
& aTitle
)
586 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryInterface(mDocument
);
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");
598 mPreferredSheet
= aTitle
;
600 // start any pending alternates that aren't alternates anymore
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
) {
608 LoadSheet(arr
[i
], eSheetNeedsParser
);
615 static const char kCharsetSym
[] = "@charset \"";
617 static bool GetCharsetFromData(const char* aStyleSheetData
,
618 uint32_t aDataLength
,
619 nsACString
& aCharset
)
622 if (aDataLength
<= sizeof(kCharsetSym
) - 1)
625 if (strncmp(aStyleSheetData
,
627 sizeof(kCharsetSym
) - 1)) {
631 for (uint32_t i
= sizeof(kCharsetSym
) - 1; i
< aDataLength
; ++i
) {
632 char c
= aStyleSheetData
[i
];
635 if (i
< aDataLength
&& aStyleSheetData
[i
] == ';') {
644 // Did not see end quote or semicolon
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):
664 // charset attribute on the referrer
665 // encoding of the referrer
670 if (nsContentUtils::CheckForBOM((const unsigned char*)aSegment
.BeginReading(),
673 // aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8"
674 // which will swallow the BOM.
675 mCharset
.Assign(aCharset
);
677 LOG((" Setting from BOM to: %s", PromiseFlatCString(aCharset
).get()));
682 nsCOMPtr
<nsIChannel
> channel
;
683 nsAutoCString specified
;
684 aLoader
->GetChannel(getter_AddRefs(channel
));
686 channel
->GetContentCharset(specified
);
687 if (EncodingUtils::FindEncodingForLabel(specified
, aCharset
)) {
688 mCharset
.Assign(aCharset
);
690 LOG((" Setting from HTTP to: %s", PromiseFlatCString(aCharset
).get()));
696 if (GetCharsetFromData(aSegment
.BeginReading(),
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
);
713 LOG((" Setting from @charset rule to: %s",
714 PromiseFlatCString(aCharset
).get()));
720 // Now try the charset on the <link> or processing instruction
722 if (mOwningElement
) {
723 nsAutoString specified16
;
724 mOwningElement
->GetCharset(specified16
);
725 if (EncodingUtils::FindEncodingForLabel(specified16
, aCharset
)) {
726 mCharset
.Assign(aCharset
);
728 LOG((" Setting from charset attribute to: %s",
729 PromiseFlatCString(aCharset
).get()));
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
);
740 LOG((" Setting from charset attribute (preload case) to: %s",
741 PromiseFlatCString(aCharset
).get()));
746 // Try charset from the parent stylesheet.
748 aCharset
= mParentData
->mCharset
;
749 if (!aCharset
.IsEmpty()) {
750 mCharset
.Assign(aCharset
);
752 LOG((" Setting from parent sheet to: %s",
753 PromiseFlatCString(aCharset
).get()));
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
);
765 LOG((" Setting from document to: %s", PromiseFlatCString(aCharset
).get()));
770 aCharset
.AssignLiteral("UTF-8");
773 LOG((" Setting from default to: %s", PromiseFlatCString(aCharset
).get()));
778 already_AddRefed
<nsIURI
>
779 SheetLoadData::GetReferrerURI()
781 nsCOMPtr
<nsIURI
> uri
;
783 uri
= mParentData
->mSheet
->GetSheetURI();
784 if (!uri
&& mLoader
->mDocument
)
785 uri
= mLoader
->mDocument
->GetDocumentURI();
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.
795 SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader
* aLoader
,
796 nsISupports
* aContext
,
798 const nsAString
& aBuffer
)
800 LOG(("SheetLoadData::OnStreamComplete"));
801 NS_ASSERTION(!mLoader
->mSyncCallback
, "Synchronous callback from necko");
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.
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
);
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();
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
);
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
);
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
);
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
));
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
);
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
));
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
);
897 nsAutoCString contentType
;
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();
912 const char *errorMessage
;
914 bool sameOrigin
= true;
916 if (mLoaderPrincipal
) {
918 result
= mLoaderPrincipal
->Subsumes(principal
, &subsumed
);
919 if (NS_FAILED(result
) || !subsumed
) {
924 if (sameOrigin
&& mLoader
->mCompatMode
== eCompatibility_NavQuirks
) {
925 errorMessage
= "MimeNotCssWarn";
926 errorFlag
= nsIScriptError::warningFlag
;
928 errorMessage
= "MimeNotCss";
929 errorFlag
= nsIScriptError::errorFlag
;
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"),
943 nsContentUtils::eCSS_PROPERTIES
,
945 strings
, ArrayLength(strings
),
948 if (errorFlag
== nsIScriptError::errorFlag
) {
949 LOG_WARN((" Ignoring sheet with improper MIME type %s",
951 mLoader
->SheetComplete(this, NS_ERROR_NOT_AVAILABLE
);
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
);
961 result
= mLoader
->ParseSheet(aBuffer
, this, completed
);
962 NS_ASSERTION(completed
|| !mSyncLoad
, "sync load did not complete");
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()) {
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
986 return !aTitle
.Equals(mPreferredSheet
);
989 /* static */ PLDHashOperator
990 Loader::RemoveEntriesWithURI(URIPrincipalReferrerPolicyAndCORSModeHashKey
* aKey
,
991 nsRefPtr
<CSSStyleSheet
>& aSheet
,
994 nsIURI
* obsoleteURI
= static_cast<nsIURI
*>(aUserData
);
995 nsIURI
* sheetURI
= aKey
->GetURI();
997 nsresult rv
= sheetURI
->Equals(obsoleteURI
, &areEqual
);
998 if (NS_SUCCEEDED(rv
) && areEqual
) {
999 return PL_DHASH_REMOVE
;
1001 return PL_DHASH_NEXT
;
1005 Loader::ObsoleteSheet(nsIURI
* aURI
)
1011 return NS_ERROR_INVALID_ARG
;
1013 mSheets
->mCompleteSheets
.Enumerate(RemoveEntriesWithURI
, aURI
);
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)
1028 Loader::CheckLoadAllowed(nsIPrincipal
* aSourcePrincipal
,
1030 nsISupports
* aContext
)
1032 LOG(("css::Loader::CheckLoadAllowed"));
1036 if (aSourcePrincipal
) {
1037 // Check with the security manager
1038 nsIScriptSecurityManager
*secMan
= nsContentUtils::GetSecurityManager();
1040 secMan
->CheckLoadURIWithPrincipal(aSourcePrincipal
, aTargetURI
,
1041 nsIScriptSecurityManager::ALLOW_CHROME
);
1042 if (NS_FAILED(rv
)) { // failure is normal here; don't warn
1046 LOG((" Passed security check"));
1048 // Check with content policy
1050 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
1051 rv
= NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET
,
1055 NS_LITERAL_CSTRING("text/css"),
1056 nullptr, //extra param
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
;
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
1080 Loader::CreateSheet(nsIURI
* aURI
,
1081 nsIContent
* aLinkingContent
,
1082 nsIPrincipal
* aLoaderPrincipal
,
1084 ReferrerPolicy aReferrerPolicy
,
1086 bool aHasAlternateRel
,
1087 const nsAString
& aTitle
,
1088 StyleSheetState
& aSheetState
,
1090 CSSStyleSheet
** aSheet
)
1092 LOG(("css::Loader::CreateSheet"));
1093 NS_PRECONDITION(aSheet
, "Null out param!");
1096 mSheets
= new Sheets();
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
);
1107 aSheetState
= eSheetComplete
;
1108 nsRefPtr
<CSSStyleSheet
> sheet
;
1110 // First, the XUL cache
1112 if (IsChromeURI(aURI
)) {
1113 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
1115 if (cache
->IsEnabled()) {
1116 sheet
= cache
->GetStyleSheet(aURI
);
1117 LOG((" From XUL cache: %p", sheet
.get()));
1123 bool fromCompleteSheets
= false;
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
;
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",
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
);
1156 sheet
= loadData
->mSheet
;
1157 LOG((" From loading: %p", sheet
.get()));
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");
1170 // Then alternate sheets
1172 aSheetState
= eSheetPending
;
1174 mSheets
->mPendingDatas
.Get(&key
, &loadData
);
1176 sheet
= loadData
->mSheet
;
1177 LOG((" From pending: %p", sheet
.get()));
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");
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
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
);
1215 aSheetState
= eSheetNeedsParser
;
1217 nsCOMPtr
<nsIURI
> baseURI
;
1218 nsIURI
* originalURI
;
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;
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
]));
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.
1250 Loader::PrepareSheet(CSSStyleSheet
* aSheet
,
1251 const nsSubstring
& aTitle
,
1252 const nsSubstring
& aMediaString
,
1253 nsMediaList
* aMediaList
,
1254 Element
* aScopeElement
,
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
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.
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
1329 // Aha! The current sheet has no sheet owner, so we want to
1330 // insert after it no matter whether we have a linkingNode
1334 nsCOMPtr
<nsINode
> sheetOwnerNode
= do_QueryInterface(sheetOwner
);
1335 NS_ASSERTION(aLinkingContent
!= sheetOwnerNode
,
1336 "Why do we still have our old sheet?");
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
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
));
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.
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));
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.
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
,
1434 ((NS_SUCCEEDED(rv
) && inherit
) ||
1435 (nsContentUtils::URIIsLocalFile(aLoadData
->mURI
) &&
1436 NS_SUCCEEDED(aLoadData
->mLoaderPrincipal
->
1437 CheckMayLoad(aLoadData
->mURI
, false, false))));
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
);
1461 mozilla::net::PredictorLearn(aLoadData
->mURI
, mDocument
->GetDocumentURI(),
1462 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
,
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
),
1476 aLoadData
->mRequestingNode
,
1477 triggeringPrincipal
,
1478 nsILoadInfo::SEC_NORMAL
,
1479 nsIContentPolicy::TYPE_OTHER
);
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
),
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
);
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
,
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");
1534 LOG((" Glomming on to existing load"));
1535 SheetLoadData
* data
= existingData
;
1536 while (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
1544 SheetLoadData
* removedData
;
1545 NS_ASSERTION(mSheets
->mPendingDatas
.Get(&key
, &removedData
) &&
1546 removedData
== existingData
,
1547 "Bad pending table.");
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
1561 mSyncCallback
= true;
1563 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1565 loadGroup
= mDocument
->GetDocumentLoadGroup();
1566 NS_ASSERTION(loadGroup
,
1567 "No loadgroup for stylesheet; onload will fire early");
1570 nsLoadFlags securityFlags
= nsILoadInfo::SEC_NORMAL
;
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
),
1583 aLoadData
->mRequestingNode
,
1584 triggeringPrincipal
,
1586 nsIContentPolicy::TYPE_STYLESHEET
,
1588 nullptr, // aCallbacks
1589 nsIChannel::LOAD_NORMAL
|
1590 nsIChannel::LOAD_CLASSIFY_URI
);
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
),
1600 triggeringPrincipal
,
1602 nsIContentPolicy::TYPE_STYLESHEET
,
1604 nullptr, // aCallbacks
1605 nsIChannel::LOAD_NORMAL
|
1606 nsIChannel::LOAD_CLASSIFY_URI
);
1609 if (NS_FAILED(rv
)) {
1611 mSyncCallback
= false;
1613 LOG_ERROR((" Failed to create channel"));
1614 SheetComplete(aLoadData
, rv
);
1618 if (!aLoadData
->mWasAlternate
) {
1619 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(channel
));
1621 cos
->AddClassFlags(nsIClassOfService::Leader
);
1625 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
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"),
1631 nsCOMPtr
<nsIURI
> referrerURI
= aLoadData
->GetReferrerURI();
1633 httpChannel
->SetReferrerWithPolicy(referrerURI
,
1634 aLoadData
->mSheet
->GetReferrerPolicy());
1636 // Set the initiator type
1637 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
1639 if (aLoadData
->mParentData
) {
1640 timedChannel
->SetInitiatorType(NS_LITERAL_STRING("css"));
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,
1654 nsCOMPtr
<nsIUnicharStreamLoader
> streamLoader
;
1655 rv
= NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader
), aLoadData
);
1656 if (NS_FAILED(rv
)) {
1658 mSyncCallback
= false;
1660 LOG_ERROR((" Failed to create stream loader"));
1661 SheetComplete(aLoadData
, 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
,
1673 rv
= corsListener
->Init(channel
);
1674 if (NS_FAILED(rv
)) {
1676 mSyncCallback
= false;
1678 LOG_ERROR((" Initial CORS check failed"));
1679 SheetComplete(aLoadData
, rv
);
1682 channelListener
= corsListener
;
1684 channelListener
= streamLoader
;
1688 mozilla::net::PredictorLearn(aLoadData
->mURI
, mDocument
->GetDocumentURI(),
1689 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
,
1693 rv
= channel
->AsyncOpen(channelListener
, nullptr);
1696 mSyncCallback
= false;
1699 if (NS_FAILED(rv
)) {
1700 LOG_ERROR((" Failed to create stream loader"));
1701 SheetComplete(aLoadData
, rv
);
1705 mSheets
->mLoadingDatas
.Put(&key
, aLoadData
);
1706 aLoadData
->mIsLoading
= true;
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
1718 Loader::ParseSheet(const nsAString
& aInput
,
1719 SheetLoadData
* aLoadData
,
1722 LOG(("css::Loader::ParseSheet"));
1723 NS_PRECONDITION(aLoadData
, "Must have load data");
1724 NS_PRECONDITION(aLoadData
->mSheet
, "Must have sheet to parse into");
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
);
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"));
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.
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.
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
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
) {
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
,
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();
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());
1831 SheetLoadData
*loadingData
;
1832 NS_ASSERTION(mSheets
->mLoadingDatas
.Get(&key
, &loadingData
) &&
1833 loadingData
== aLoadData
,
1834 "Bad loading table");
1837 mSheets
->mLoadingDatas
.Remove(&key
);
1838 aLoadData
->mIsLoading
= false;
1842 // Go through and deal with the whole linked list.
1843 SheetLoadData
* data
= aLoadData
;
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
1872 if (data
->mParentData
&&
1873 --(data
->mParentData
->mPendingChildren
) == 0 &&
1874 !mParsingDatas
.Contains(data
->mParentData
)) {
1875 DoSheetComplete(data
->mParentData
, aStatus
, aDatasToNotify
);
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.
1891 CSSStyleSheet
* sheet
= aLoadData
->mSheet
;
1893 if (data
->mSheet
->GetParentSheet() || data
->mSheet
->GetOwnerNode()) {
1894 sheet
= data
->mSheet
;
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
);
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
);
1925 NS_RELEASE(aLoadData
); // this will release parents and siblings and all that
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
,
1939 LOG(("css::Loader::LoadInlineStyle"));
1940 NS_ASSERTION(mParsingDatas
.Length() == 0, "We're in the middle of a parse?");
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
);
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());
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.
1994 data
->mMustNotify
= true;
2000 Loader::LoadStyleLink(nsIContent
* aElement
,
2002 const nsAString
& aTitle
,
2003 const nsAString
& aMedia
,
2004 bool aHasAlternateRel
,
2006 ReferrerPolicy aReferrerPolicy
,
2007 nsICSSLoaderObserver
* aObserver
,
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
));
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
;
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
,
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
);
2074 // If we have to parse and it's an alternate non-inline, defer it
2075 if (aURL
&& state
== eSheetNeedsParser
&& mSheets
->mLoadingDatas
.Count() != 0 &&
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;
2088 // Load completion will free the data
2089 rv
= LoadSheet(data
, state
);
2090 NS_ENSURE_SUCCESS(rv
, rv
);
2092 data
->mMustNotify
= true;
2097 HaveAncestorDataWithURI(SheetLoadData
*aData
, nsIURI
*aURI
)
2100 // Inline style; this won't have any ancestors
2101 NS_ABORT_IF_FALSE(!aData
->mParentData
,
2102 "How does inline style have a parent?");
2107 if (NS_FAILED(aData
->mURI
->Equals(aURI
, &equal
)) || equal
) {
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.
2115 if (aData
->mParentData
&&
2116 HaveAncestorDataWithURI(aData
->mParentData
, aURI
)) {
2120 aData
= aData
->mNext
;
2127 Loader::LoadChildSheet(CSSStyleSheet
* aParentSheet
,
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");
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
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
2154 topSheet
.swap(nextParentSheet
);
2155 topSheet
->GetParentStyleSheet(getter_AddRefs(nextParentSheet
));
2156 } while (nextParentSheet
);
2158 topSheet
->GetOwnerNode(getter_AddRefs(owningNode
));
2161 nsISupports
* context
= owningNode
;
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();
2177 LOG((" Have a parent load"));
2178 parentData
= mParsingDatas
.ElementAt(count
- 1);
2180 if (HaveAncestorDataWithURI(parentData
, aURL
)) {
2181 // Houston, we have a loop, blow off this child and pretend this never
2183 LOG_ERROR((" @import cycle detected, dropping load"));
2187 NS_ASSERTION(parentData
->mSheet
== aParentSheet
,
2188 "Unexpected call to LoadChildSheet");
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
2198 nsRefPtr
<CSSStyleSheet
> sheet
;
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.
2222 nsCOMPtr
<nsINode
> requestingNode
= do_QueryInterface(context
);
2223 SheetLoadData
* data
= new SheetLoadData(this, aURL
, sheet
, parentData
,
2224 observer
, principal
, requestingNode
);
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.
2235 data
->mMustNotify
= true;
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);
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
,
2266 Loader::LoadSheet(nsIURI
* aURL
,
2267 nsIPrincipal
* aOriginPrincipal
,
2268 const nsCString
& aCharset
,
2269 nsICSSLoaderObserver
* aObserver
,
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
,
2281 Loader::InternalLoadNonDocumentSheet(nsIURI
* aURL
,
2282 bool aAllowUnsafeRules
,
2283 bool aUseSystemPrincipal
,
2284 nsIPrincipal
* aOriginPrincipal
,
2285 const nsCString
& aCharset
,
2286 CSSStyleSheet
** aSheet
,
2287 nsICSSLoaderObserver
* aObserver
,
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
);
2304 LOG_WARN((" Not enabled"));
2305 return NS_ERROR_NOT_AVAILABLE
;
2308 nsresult rv
= CheckLoadAllowed(aOriginPrincipal
, aURL
, mDocument
);
2309 if (NS_FAILED(rv
)) {
2313 StyleSheetState state
;
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);
2332 sheet
.swap(*aSheet
);
2337 SheetLoadData
* data
=
2338 new SheetLoadData(this, aURL
, sheet
, syncLoad
, aAllowUnsafeRules
,
2339 aUseSystemPrincipal
, aCharset
, aObserver
,
2340 aOriginPrincipal
, mDocument
);
2343 rv
= LoadSheet(data
, state
);
2344 NS_ENSURE_SUCCESS(rv
, rv
);
2347 sheet
.swap(*aSheet
);
2350 data
->mMustNotify
= true;
2357 Loader::PostLoadEvent(nsIURI
* aURI
,
2358 CSSStyleSheet
* aSheet
,
2359 nsICSSLoaderObserver
* aObserver
,
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
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
);
2388 // We'll unblock onload when we handle the event.
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
);
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
2424 SheetComplete(aEvent
, NS_OK
);
2428 mDocument
->UnblockOnload(true);
2432 static PLDHashOperator
2433 StopLoadingSheetCallback(URIPrincipalReferrerPolicyAndCORSModeHashKey
* aKey
,
2434 SheetLoadData
*& aData
,
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
;
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());
2458 mSheets
->mPendingDatas
.Enumerate(StopLoadingSheetCallback
, &arr
);
2461 mSheets
->mLoadingDatas
.Enumerate(StopLoadingSheetCallback
, &arr
);
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.
2474 NS_NOTREACHED("We preallocated this memory... shouldn't really fail, "
2475 "except we never check that preallocation succeeds.");
2479 mPostedEvents
.Clear();
2481 mDatasToNotifyOn
+= arr
.Length();
2482 for (i
= 0; i
< arr
.Length(); ++i
) {
2484 SheetComplete(arr
[i
], NS_BINDING_ABORTED
);
2490 Loader::HasPendingLoads()
2493 (mSheets
&& mSheets
->mLoadingDatas
.Count() != 0) ||
2494 (mSheets
&& mSheets
->mPendingDatas
.Count() != 0) ||
2495 mPostedEvents
.Length() != 0 ||
2496 mDatasToNotifyOn
!= 0;
2500 Loader::AddObserver(nsICSSLoaderObserver
* aObserver
)
2502 NS_PRECONDITION(aObserver
, "Must have observer");
2503 if (mObservers
.AppendElementUnlessExists(aObserver
)) {
2507 return NS_ERROR_OUT_OF_MEMORY
;
2511 Loader::RemoveObserver(nsICSSLoaderObserver
* aObserver
)
2513 mObservers
.RemoveElement(aObserver
);
2516 static PLDHashOperator
2517 CollectLoadDatas(URIPrincipalReferrerPolicyAndCORSModeHashKey
*aKey
,
2518 SheetLoadData
* &aData
,
2521 static_cast<Loader::LoadDataArray
*>(aClosure
)->AppendElement(aData
);
2522 return PL_DHASH_REMOVE
;
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
) {
2535 LoadSheet(arr
[i
], eSheetNeedsParser
);
2539 static PLDHashOperator
2540 TraverseSheet(URIPrincipalReferrerPolicyAndCORSModeHashKey
*,
2541 CSSStyleSheet
* aSheet
,
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
)
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
)
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
{
2577 mozilla::MallocSizeOf mallocSizeOf
;
2581 CountSheetMemory(URIPrincipalReferrerPolicyAndCORSModeHashKey
* /* unused */,
2582 const nsRefPtr
<CSSStyleSheet
>& aSheet
,
2583 mozilla::MallocSizeOf aMallocSizeOf
,
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()) {
2593 return aSheet
->SizeOfIncludingThis(aMallocSizeOf
);
2597 Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const
2599 size_t s
= aMallocSizeOf(this);
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
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
2621 } // namespace mozilla