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 "nsIHttpChannelInternal.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 "nsCrossSiteListenerProxy.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 "nsIChannelPolicy.h"
64 #include "nsIContentSecurityPolicy.h"
66 #include "mozilla/dom/EncodingUtils.h"
67 using mozilla::dom::EncodingUtils
;
69 using namespace mozilla::dom
;
72 * OVERALL ARCHITECTURE
74 * The CSS Loader gets requests to load various sorts of style sheets:
75 * inline style from <style> elements, linked style, @import-ed child
76 * sheets, non-document sheets. The loader handles the following tasks:
78 * 1) Checking whether the load is allowed: CheckLoadAllowed()
79 * 2) Creation of the actual style sheet objects: CreateSheet()
80 * 3) setting of the right media, title, enabled state, etc on the
81 * sheet: PrepareSheet()
82 * 4) Insertion of the sheet in the proper cascade order:
83 * InsertSheetInDoc() and InsertChildSheet()
84 * 5) Load of the sheet: LoadSheet()
85 * 6) Parsing of the sheet: ParseSheet()
86 * 7) Cleanup: SheetComplete()
88 * The detailed documentation for these functions is found with the
89 * function implementations.
91 * The following helper object is used:
92 * SheetLoadData -- a small class that is used to store all the
93 * information needed for the loading of a sheet;
94 * this class handles listening for the stream
95 * loader completion and also handles charset
102 /*********************************************
103 * Data needed to properly load a stylesheet *
104 *********************************************/
106 class SheetLoadData
: public nsIRunnable
,
107 public nsIUnicharStreamLoaderObserver
,
108 public nsIThreadObserver
111 virtual ~SheetLoadData(void);
114 // Data for loading a sheet linked from a document
115 SheetLoadData(Loader
* aLoader
,
116 const nsSubstring
& aTitle
,
118 CSSStyleSheet
* aSheet
,
119 nsIStyleSheetLinkingElement
* aOwningElement
,
121 nsICSSLoaderObserver
* aObserver
,
122 nsIPrincipal
* aLoaderPrincipal
);
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
);
132 // Data for loading a non-document sheet
133 SheetLoadData(Loader
* aLoader
,
135 CSSStyleSheet
* aSheet
,
137 bool aAllowUnsafeRules
,
138 bool aUseSystemPrincipal
,
139 const nsCString
& aCharset
,
140 nsICSSLoaderObserver
* aObserver
,
141 nsIPrincipal
* aLoaderPrincipal
);
143 already_AddRefed
<nsIURI
> GetReferrerURI();
145 void ScheduleLoadEventIfNeeded(nsresult aStatus
);
149 NS_DECL_NSITHREADOBSERVER
150 NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
152 // Hold a ref to the CSSLoader so we can call back to it to let it
153 // know the load finished
154 nsRefPtr
<Loader
> mLoader
;
156 // Title needed to pull datas out of the pending datas table when
157 // the preferred title is changed
160 // Charset we decided to use for the sheet
163 // URI we're loading. Null for inline sheets
164 nsCOMPtr
<nsIURI
> mURI
;
166 // Should be 1 for non-inline sheets.
167 uint32_t mLineNumber
;
169 // The sheet we're loading data for
170 nsRefPtr
<CSSStyleSheet
> mSheet
;
172 // Linked list of datas for the same URI as us
173 SheetLoadData
* mNext
; // strong ref
175 // Load data for the sheet that @import-ed us if we were @import-ed
177 nsRefPtr
<SheetLoadData
> mParentData
;
179 // Number of sheets we @import-ed that are still loading
180 uint32_t mPendingChildren
;
182 // mSyncLoad is true when the load needs to be synchronous -- right
183 // now only for LoadSheetSync and children of sync loads.
186 // mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
187 // LoadSheet or an @import from such a sheet. Non-document sheet loads can
188 // proceed even if we have no document.
189 bool mIsNonDocumentSheet
: 1;
191 // mIsLoading is true from the moment we are placed in the loader's
192 // "loading datas" table (right after the async channel is opened)
193 // to the moment we are removed from said table (due to the load
194 // completing or being cancelled).
197 // mIsCancelled is set to true when a sheet load is stopped by
198 // Stop() or StopLoadingSheet() (which was removed in Bug 556446).
199 // SheetLoadData::OnStreamComplete() checks this to avoid parsing
200 // sheets that have been cancelled and such.
201 bool mIsCancelled
: 1;
203 // mMustNotify is true if the load data is being loaded async and
204 // the original function call that started the load has returned.
205 // This applies only to observer notifications; load/error events
206 // are fired for any SheetLoadData that has a non-null
208 bool mMustNotify
: 1;
210 // mWasAlternate is true if the sheet was an alternate when the load data was
212 bool mWasAlternate
: 1;
214 // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed
215 // in the loaded sheet.
216 bool mAllowUnsafeRules
: 1;
218 // mUseSystemPrincipal is true if the system principal should be used for
219 // this sheet, no matter what the channel principal is. Only true for sync
221 bool mUseSystemPrincipal
: 1;
223 // If true, this SheetLoadData is being used as a way to handle
224 // async observer notification for an already-complete sheet.
225 bool mSheetAlreadyComplete
: 1;
227 // This is the element that imported the sheet. Needed to get the
228 // charset set on it and to fire load/error events.
229 nsCOMPtr
<nsIStyleSheetLinkingElement
> mOwningElement
;
231 // The observer that wishes to be notified of load completion
232 nsCOMPtr
<nsICSSLoaderObserver
> mObserver
;
234 // The principal that identifies who started loading us.
235 nsCOMPtr
<nsIPrincipal
> mLoaderPrincipal
;
237 // The charset to use if the transport and sheet don't indicate one.
238 // May be empty. Must be empty if mOwningElement is non-null.
239 nsCString mCharsetHint
;
241 // The status our load ended up with; this determines whether we
242 // should fire error events or load events. This gets initialized
243 // by ScheduleLoadEventIfNeeded, and is only used after that has
248 void FireLoadEvent(nsIThreadInternal
* aThread
);
252 // #define FORCE_PR_LOG /* Allow logging in the release build */
253 #endif /* MOZ_LOGGING */
257 static PRLogModuleInfo
*
260 static PRLogModuleInfo
*sLog
;
262 sLog
= PR_NewLogModule("nsCSSLoader");
265 #endif /* PR_LOGGING */
267 #define LOG_FORCE(args) PR_LOG(GetLoaderLog(), PR_LOG_ALWAYS, args)
268 #define LOG_ERROR(args) PR_LOG(GetLoaderLog(), PR_LOG_ERROR, args)
269 #define LOG_WARN(args) PR_LOG(GetLoaderLog(), PR_LOG_WARNING, args)
270 #define LOG_DEBUG(args) PR_LOG(GetLoaderLog(), PR_LOG_DEBUG, args)
271 #define LOG(args) LOG_DEBUG(args)
273 #define LOG_FORCE_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ALWAYS)
274 #define LOG_ERROR_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ERROR)
275 #define LOG_WARN_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_WARNING)
276 #define LOG_DEBUG_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_DEBUG)
277 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
280 #define LOG_URI(format, uri) \
282 NS_ASSERTION(uri, "Logging null uri"); \
283 if (LOG_ENABLED()) { \
284 nsAutoCString _logURISpec; \
285 uri->GetSpec(_logURISpec); \
286 LOG((format, _logURISpec.get())); \
290 #define LOG_URI(format, uri)
293 // And some convenience strings...
295 static const char* const gStateStrings
[] = {
296 "eSheetStateUnknown",
304 /********************************
305 * SheetLoadData implementation *
306 ********************************/
307 NS_IMPL_ISUPPORTS(SheetLoadData
, nsIUnicharStreamLoaderObserver
, nsIRunnable
,
310 SheetLoadData::SheetLoadData(Loader
* aLoader
,
311 const nsSubstring
& aTitle
,
313 CSSStyleSheet
* aSheet
,
314 nsIStyleSheetLinkingElement
* aOwningElement
,
316 nsICSSLoaderObserver
* aObserver
,
317 nsIPrincipal
* aLoaderPrincipal
)
326 mIsNonDocumentSheet(false),
330 mWasAlternate(aIsAlternate
),
331 mAllowUnsafeRules(false),
332 mUseSystemPrincipal(false),
333 mSheetAlreadyComplete(false),
334 mOwningElement(aOwningElement
),
335 mObserver(aObserver
),
336 mLoaderPrincipal(aLoaderPrincipal
)
338 NS_PRECONDITION(mLoader
, "Must have a loader!");
341 SheetLoadData::SheetLoadData(Loader
* aLoader
,
343 CSSStyleSheet
* aSheet
,
344 SheetLoadData
* aParentData
,
345 nsICSSLoaderObserver
* aObserver
,
346 nsIPrincipal
* aLoaderPrincipal
)
352 mParentData(aParentData
),
355 mIsNonDocumentSheet(false),
359 mWasAlternate(false),
360 mAllowUnsafeRules(false),
361 mUseSystemPrincipal(false),
362 mSheetAlreadyComplete(false),
363 mOwningElement(nullptr),
364 mObserver(aObserver
),
365 mLoaderPrincipal(aLoaderPrincipal
)
367 NS_PRECONDITION(mLoader
, "Must have a loader!");
369 mSyncLoad
= mParentData
->mSyncLoad
;
370 mIsNonDocumentSheet
= mParentData
->mIsNonDocumentSheet
;
371 mAllowUnsafeRules
= mParentData
->mAllowUnsafeRules
;
372 mUseSystemPrincipal
= mParentData
->mUseSystemPrincipal
;
373 ++(mParentData
->mPendingChildren
);
376 NS_POSTCONDITION(!mUseSystemPrincipal
|| mSyncLoad
,
377 "Shouldn't use system principal for async loads");
380 SheetLoadData::SheetLoadData(Loader
* aLoader
,
382 CSSStyleSheet
* aSheet
,
384 bool aAllowUnsafeRules
,
385 bool aUseSystemPrincipal
,
386 const nsCString
& aCharset
,
387 nsICSSLoaderObserver
* aObserver
,
388 nsIPrincipal
* aLoaderPrincipal
)
395 mSyncLoad(aSyncLoad
),
396 mIsNonDocumentSheet(true),
400 mWasAlternate(false),
401 mAllowUnsafeRules(aAllowUnsafeRules
),
402 mUseSystemPrincipal(aUseSystemPrincipal
),
403 mSheetAlreadyComplete(false),
404 mOwningElement(nullptr),
405 mObserver(aObserver
),
406 mLoaderPrincipal(aLoaderPrincipal
),
407 mCharsetHint(aCharset
)
409 NS_PRECONDITION(mLoader
, "Must have a loader!");
411 NS_POSTCONDITION(!mUseSystemPrincipal
|| mSyncLoad
,
412 "Shouldn't use system principal for async loads");
415 SheetLoadData::~SheetLoadData()
417 NS_IF_RELEASE(mNext
);
423 mLoader
->HandleLoadEvent(this);
428 SheetLoadData::OnDispatchedEvent(nsIThreadInternal
* aThread
)
434 SheetLoadData::OnProcessNextEvent(nsIThreadInternal
* aThread
,
436 uint32_t aRecursionDepth
)
438 // We want to fire our load even before or after event processing,
439 // whichever comes first.
440 FireLoadEvent(aThread
);
445 SheetLoadData::AfterProcessNextEvent(nsIThreadInternal
* aThread
,
446 uint32_t aRecursionDepth
,
447 bool aEventWasProcessed
)
449 // We want to fire our load even before or after event processing,
450 // whichever comes first.
451 FireLoadEvent(aThread
);
456 SheetLoadData::FireLoadEvent(nsIThreadInternal
* aThread
)
459 // First remove ourselves as a thread observer. But we need to keep
460 // ourselves alive while doing that!
461 nsRefPtr
<SheetLoadData
> kungFuDeathGrip(this);
462 aThread
->RemoveObserver(this);
464 // Now fire the event
465 nsCOMPtr
<nsINode
> node
= do_QueryInterface(mOwningElement
);
466 NS_ASSERTION(node
, "How did that happen???");
468 nsContentUtils::DispatchTrustedEvent(node
->OwnerDoc(),
470 NS_SUCCEEDED(mStatus
) ?
471 NS_LITERAL_STRING("load") :
472 NS_LITERAL_STRING("error"),
475 // And unblock onload
476 if (mLoader
->mDocument
) {
477 mLoader
->mDocument
->UnblockOnload(true);
482 SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus
)
484 if (!mOwningElement
) {
490 nsCOMPtr
<nsIThread
> thread
= do_GetMainThread();
491 nsCOMPtr
<nsIThreadInternal
> internalThread
= do_QueryInterface(thread
);
492 if (NS_SUCCEEDED(internalThread
->AddObserver(this))) {
493 // Make sure to block onload here
494 if (mLoader
->mDocument
) {
495 mLoader
->mDocument
->BlockOnload();
500 /*************************
501 * Loader Implementation *
502 *************************/
506 , mDatasToNotifyOn(0)
507 , mCompatMode(eCompatibility_FullStandards
)
510 , mSyncCallback(false)
515 Loader::Loader(nsIDocument
* aDocument
)
516 : mDocument(aDocument
)
517 , mDatasToNotifyOn(0)
518 , mCompatMode(eCompatibility_FullStandards
)
521 , mSyncCallback(false)
524 // We can just use the preferred set, since there are no sheets in the
525 // document yet (if there are, how did they get there? _we_ load the sheets!)
526 // and hence the selected set makes no sense at this time.
527 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(mDocument
);
529 domDoc
->GetPreferredStyleSheetSet(mPreferredSheet
);
535 NS_ASSERTION(!mSheets
|| mSheets
->mLoadingDatas
.Count() == 0,
536 "How did we get destroyed when there are loading data?");
537 NS_ASSERTION(!mSheets
|| mSheets
->mPendingDatas
.Count() == 0,
538 "How did we get destroyed when there are pending data?");
539 // Note: no real need to revoke our stylesheet loaded events -- they
540 // hold strong references to us, so if we're going away that means
545 Loader::DropDocumentReference(void)
548 // Flush out pending datas just so we don't leak by accident. These
549 // loads should short-circuit through the mDocument check in
550 // LoadSheet and just end up in SheetComplete immediately
552 StartAlternateLoads();
556 static PLDHashOperator
557 CollectNonAlternates(URIPrincipalAndCORSModeHashKey
*aKey
,
558 SheetLoadData
* &aData
,
561 NS_PRECONDITION(aData
, "Must have a data");
562 NS_PRECONDITION(aClosure
, "Must have an array");
564 // Note that we don't want to affect what the selected style set is,
565 // so use true for aHasAlternateRel.
566 if (aData
->mLoader
->IsAlternate(aData
->mTitle
, true)) {
567 return PL_DHASH_NEXT
;
570 static_cast<Loader::LoadDataArray
*>(aClosure
)->AppendElement(aData
);
571 return PL_DHASH_REMOVE
;
575 Loader::SetPreferredSheet(const nsAString
& aTitle
)
578 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryInterface(mDocument
);
580 nsAutoString currentPreferred
;
581 doc
->GetLastStyleSheetSet(currentPreferred
);
582 if (DOMStringIsNull(currentPreferred
)) {
583 doc
->GetPreferredStyleSheetSet(currentPreferred
);
585 NS_ASSERTION(currentPreferred
.Equals(aTitle
),
586 "Unexpected argument to SetPreferredSheet");
590 mPreferredSheet
= aTitle
;
592 // start any pending alternates that aren't alternates anymore
594 LoadDataArray
arr(mSheets
->mPendingDatas
.Count());
595 mSheets
->mPendingDatas
.Enumerate(CollectNonAlternates
, &arr
);
597 mDatasToNotifyOn
+= arr
.Length();
598 for (uint32_t i
= 0; i
< arr
.Length(); ++i
) {
600 LoadSheet(arr
[i
], eSheetNeedsParser
);
607 static const char kCharsetSym
[] = "@charset \"";
609 static bool GetCharsetFromData(const char* aStyleSheetData
,
610 uint32_t aDataLength
,
611 nsACString
& aCharset
)
614 if (aDataLength
<= sizeof(kCharsetSym
) - 1)
617 if (strncmp(aStyleSheetData
,
619 sizeof(kCharsetSym
) - 1)) {
623 for (uint32_t i
= sizeof(kCharsetSym
) - 1; i
< aDataLength
; ++i
) {
624 char c
= aStyleSheetData
[i
];
627 if (i
< aDataLength
&& aStyleSheetData
[i
] == ';') {
636 // Did not see end quote or semicolon
642 SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader
* aLoader
,
643 nsISupports
* aContext
,
644 nsACString
const& aSegment
,
645 nsACString
& aCharset
)
647 NS_PRECONDITION(!mOwningElement
|| mCharsetHint
.IsEmpty(),
648 "Can't have element _and_ charset hint");
650 LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI
);
652 // The precedence is (per CSS3 Syntax 2012-11-08 ED):
656 // charset attribute on the referrer
657 // encoding of the referrer
662 if (nsContentUtils::CheckForBOM((const unsigned char*)aSegment
.BeginReading(),
665 // aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8"
666 // which will swallow the BOM.
667 mCharset
.Assign(aCharset
);
669 LOG((" Setting from BOM to: %s", PromiseFlatCString(aCharset
).get()));
674 nsCOMPtr
<nsIChannel
> channel
;
675 nsAutoCString specified
;
676 aLoader
->GetChannel(getter_AddRefs(channel
));
678 channel
->GetContentCharset(specified
);
679 if (EncodingUtils::FindEncodingForLabel(specified
, aCharset
)) {
680 mCharset
.Assign(aCharset
);
682 LOG((" Setting from HTTP to: %s", PromiseFlatCString(aCharset
).get()));
688 if (GetCharsetFromData(aSegment
.BeginReading(),
691 if (EncodingUtils::FindEncodingForLabel(specified
, aCharset
)) {
692 // FindEncodingForLabel currently never returns UTF-16LE but will
693 // probably change to never return UTF-16 instead, so check both here
694 // to avoid relying on the exact behavior.
695 if (aCharset
.EqualsLiteral("UTF-16") ||
696 aCharset
.EqualsLiteral("UTF-16BE") ||
697 aCharset
.EqualsLiteral("UTF-16LE")) {
698 // Be consistent with HTML <meta> handling in face of impossibility.
699 // When the @charset rule itself evidently was not UTF-16-encoded,
700 // it saying UTF-16 has to be a lie.
701 aCharset
.AssignLiteral("UTF-8");
703 mCharset
.Assign(aCharset
);
705 LOG((" Setting from @charset rule to: %s",
706 PromiseFlatCString(aCharset
).get()));
712 // Now try the charset on the <link> or processing instruction
714 if (mOwningElement
) {
715 nsAutoString specified16
;
716 mOwningElement
->GetCharset(specified16
);
717 if (EncodingUtils::FindEncodingForLabel(specified16
, aCharset
)) {
718 mCharset
.Assign(aCharset
);
720 LOG((" Setting from charset attribute to: %s",
721 PromiseFlatCString(aCharset
).get()));
727 // In the preload case, the value of the charset attribute on <link> comes
728 // in via mCharsetHint instead.
729 if (EncodingUtils::FindEncodingForLabel(mCharsetHint
, aCharset
)) {
730 mCharset
.Assign(aCharset
);
732 LOG((" Setting from charset attribute (preload case) to: %s",
733 PromiseFlatCString(aCharset
).get()));
738 // Try charset from the parent stylesheet.
740 aCharset
= mParentData
->mCharset
;
741 if (!aCharset
.IsEmpty()) {
742 mCharset
.Assign(aCharset
);
744 LOG((" Setting from parent sheet to: %s",
745 PromiseFlatCString(aCharset
).get()));
751 if (mLoader
->mDocument
) {
752 // no useful data on charset. Try the document charset.
753 aCharset
= mLoader
->mDocument
->GetDocumentCharacterSet();
754 MOZ_ASSERT(!aCharset
.IsEmpty());
755 mCharset
.Assign(aCharset
);
757 LOG((" Setting from document to: %s", PromiseFlatCString(aCharset
).get()));
762 aCharset
.AssignLiteral("UTF-8");
765 LOG((" Setting from default to: %s", PromiseFlatCString(aCharset
).get()));
770 already_AddRefed
<nsIURI
>
771 SheetLoadData::GetReferrerURI()
773 nsCOMPtr
<nsIURI
> uri
;
775 uri
= mParentData
->mSheet
->GetSheetURI();
776 if (!uri
&& mLoader
->mDocument
)
777 uri
= mLoader
->mDocument
->GetDocumentURI();
782 * Here we need to check that the load did not give us an http error
783 * page and check the mimetype on the channel to make sure we're not
784 * loading non-text/css data in standards mode.
787 SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader
* aLoader
,
788 nsISupports
* aContext
,
790 const nsAString
& aBuffer
)
792 LOG(("SheetLoadData::OnStreamComplete"));
793 NS_ASSERTION(!mLoader
->mSyncCallback
, "Synchronous callback from necko");
796 // Just return. Don't call SheetComplete -- it's already been
797 // called and calling it again will lead to an extra NS_RELEASE on
798 // this data and a likely crash.
802 if (!mLoader
->mDocument
&& !mIsNonDocumentSheet
) {
803 // Sorry, we don't care about this load anymore
804 LOG_WARN((" No document and not non-document sheet; dropping load"));
805 mLoader
->SheetComplete(this, NS_BINDING_ABORTED
);
809 if (NS_FAILED(aStatus
)) {
810 LOG_WARN((" Load failed: status 0x%x", aStatus
));
811 // Handle sheet not loading error because source was a tracking URL.
812 // We make a note of this sheet node by including it in a dedicated
813 // array of blocked tracking nodes under its parent document.
815 // Multiple sheet load instances might be tied to this request,
816 // we annotate each one linked to a valid owning element (node).
817 if (aStatus
== NS_ERROR_TRACKING_URI
) {
818 nsIDocument
* doc
= mLoader
->GetDocument();
820 for (SheetLoadData
* data
= this; data
; data
= data
->mNext
) {
821 // mOwningElement may be null but AddBlockTrackingNode can cope
822 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(data
->mOwningElement
);
823 doc
->AddBlockedTrackingNode(content
);
827 mLoader
->SheetComplete(this, aStatus
);
831 nsCOMPtr
<nsIChannel
> channel
;
832 nsresult result
= aLoader
->GetChannel(getter_AddRefs(channel
));
833 if (NS_FAILED(result
)) {
834 LOG_WARN((" No channel from loader"));
835 mLoader
->SheetComplete(this, result
);
839 nsCOMPtr
<nsIURI
> originalURI
;
840 channel
->GetOriginalURI(getter_AddRefs(originalURI
));
842 // If the channel's original URI is "chrome:", we want that, since
843 // the observer code in nsXULPrototypeCache depends on chrome stylesheets
844 // having a chrome URI. (Whether or not chrome stylesheets come through
845 // this codepath seems nondeterministic.)
846 // Otherwise we want the potentially-HTTP-redirected URI.
847 nsCOMPtr
<nsIURI
> channelURI
;
848 NS_GetFinalChannelURI(channel
, getter_AddRefs(channelURI
));
850 if (!channelURI
|| !originalURI
) {
851 NS_ERROR("Someone just violated the nsIRequest contract");
852 LOG_WARN((" Channel without a URI. Bad!"));
853 mLoader
->SheetComplete(this, NS_ERROR_UNEXPECTED
);
857 nsCOMPtr
<nsIPrincipal
> principal
;
858 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
859 result
= NS_ERROR_NOT_AVAILABLE
;
860 if (secMan
) { // Could be null if we already shut down
861 if (mUseSystemPrincipal
) {
862 result
= secMan
->GetSystemPrincipal(getter_AddRefs(principal
));
864 result
= secMan
->GetChannelPrincipal(channel
, getter_AddRefs(principal
));
868 if (NS_FAILED(result
)) {
869 LOG_WARN((" Couldn't get principal"));
870 mLoader
->SheetComplete(this, result
);
874 mSheet
->SetPrincipal(principal
);
876 // If it's an HTTP channel, we want to make sure this is not an
877 // error document we got.
878 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
880 bool requestSucceeded
;
881 result
= httpChannel
->GetRequestSucceeded(&requestSucceeded
);
882 if (NS_SUCCEEDED(result
) && !requestSucceeded
) {
883 LOG((" Load returned an error page"));
884 mLoader
->SheetComplete(this, NS_ERROR_NOT_AVAILABLE
);
889 nsAutoCString contentType
;
891 channel
->GetContentType(contentType
);
894 // In standards mode, a style sheet must have one of these MIME
895 // types to be processed at all. In quirks mode, we accept any
896 // MIME type, but only if the style sheet is same-origin with the
897 // requesting document or parent sheet. See bug 524223.
899 bool validType
= contentType
.EqualsLiteral("text/css") ||
900 contentType
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
) ||
901 contentType
.IsEmpty();
904 const char *errorMessage
;
906 bool sameOrigin
= true;
908 if (mLoaderPrincipal
) {
910 result
= mLoaderPrincipal
->Subsumes(principal
, &subsumed
);
911 if (NS_FAILED(result
) || !subsumed
) {
916 if (sameOrigin
&& mLoader
->mCompatMode
== eCompatibility_NavQuirks
) {
917 errorMessage
= "MimeNotCssWarn";
918 errorFlag
= nsIScriptError::warningFlag
;
920 errorMessage
= "MimeNotCss";
921 errorFlag
= nsIScriptError::errorFlag
;
925 channelURI
->GetSpec(spec
);
927 const nsAFlatString
& specUTF16
= NS_ConvertUTF8toUTF16(spec
);
928 const nsAFlatString
& ctypeUTF16
= NS_ConvertASCIItoUTF16(contentType
);
929 const char16_t
*strings
[] = { specUTF16
.get(), ctypeUTF16
.get() };
931 nsCOMPtr
<nsIURI
> referrer
= GetReferrerURI();
932 nsContentUtils::ReportToConsole(errorFlag
,
933 NS_LITERAL_CSTRING("CSS Loader"),
935 nsContentUtils::eCSS_PROPERTIES
,
937 strings
, ArrayLength(strings
),
940 if (errorFlag
== nsIScriptError::errorFlag
) {
941 LOG_WARN((" Ignoring sheet with improper MIME type %s",
943 mLoader
->SheetComplete(this, NS_ERROR_NOT_AVAILABLE
);
948 // Enough to set the URIs on mSheet, since any sibling datas we have share
949 // the same mInner as mSheet and will thus get the same URI.
950 mSheet
->SetURIs(channelURI
, originalURI
, channelURI
);
953 result
= mLoader
->ParseSheet(aBuffer
, this, completed
);
954 NS_ASSERTION(completed
|| !mSyncLoad
, "sync load did not complete");
959 Loader::IsAlternate(const nsAString
& aTitle
, bool aHasAlternateRel
)
961 // A sheet is alternate if it has a nonempty title that doesn't match the
962 // currently selected style set. But if there _is_ no currently selected
963 // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
964 // is nonempty, we should select the style set corresponding to aTitle, since
965 // that's a preferred sheet.
966 if (aTitle
.IsEmpty()) {
970 if (!aHasAlternateRel
&& mDocument
&& mPreferredSheet
.IsEmpty()) {
971 // There's no preferred set yet, and we now have a sheet with a title.
972 // Make that be the preferred set.
973 mDocument
->SetHeaderData(nsGkAtoms::headerDefaultStyle
, aTitle
);
974 // We're definitely not an alternate
978 return !aTitle
.Equals(mPreferredSheet
);
981 /* static */ PLDHashOperator
982 Loader::RemoveEntriesWithURI(URIPrincipalAndCORSModeHashKey
* aKey
,
983 nsRefPtr
<CSSStyleSheet
>& aSheet
,
986 nsIURI
* obsoleteURI
= static_cast<nsIURI
*>(aUserData
);
987 nsIURI
* sheetURI
= aKey
->GetURI();
989 nsresult rv
= sheetURI
->Equals(obsoleteURI
, &areEqual
);
990 if (NS_SUCCEEDED(rv
) && areEqual
) {
991 return PL_DHASH_REMOVE
;
993 return PL_DHASH_NEXT
;
997 Loader::ObsoleteSheet(nsIURI
* aURI
)
1003 return NS_ERROR_INVALID_ARG
;
1005 mSheets
->mCompleteSheets
.Enumerate(RemoveEntriesWithURI
, aURI
);
1010 * CheckLoadAllowed will return success if the load is allowed,
1011 * failure otherwise.
1013 * @param aSourcePrincipal the principal of the node or document or parent
1014 * sheet loading the sheet
1015 * @param aTargetURI the uri of the sheet to be loaded
1016 * @param aContext the node owning the sheet. This is the element or document
1017 * owning the stylesheet (possibly indirectly, for child sheets)
1020 Loader::CheckLoadAllowed(nsIPrincipal
* aSourcePrincipal
,
1022 nsISupports
* aContext
)
1024 LOG(("css::Loader::CheckLoadAllowed"));
1028 if (aSourcePrincipal
) {
1029 // Check with the security manager
1030 nsIScriptSecurityManager
*secMan
= nsContentUtils::GetSecurityManager();
1032 secMan
->CheckLoadURIWithPrincipal(aSourcePrincipal
, aTargetURI
,
1033 nsIScriptSecurityManager::ALLOW_CHROME
);
1034 if (NS_FAILED(rv
)) { // failure is normal here; don't warn
1038 LOG((" Passed security check"));
1040 // Check with content policy
1042 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
1043 rv
= NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET
,
1047 NS_LITERAL_CSTRING("text/css"),
1048 nullptr, //extra param
1050 nsContentUtils::GetContentPolicy(),
1051 nsContentUtils::GetSecurityManager());
1053 if (NS_FAILED(rv
) || NS_CP_REJECTED(shouldLoad
)) {
1054 LOG((" Load blocked by content policy"));
1055 return NS_ERROR_CONTENT_BLOCKED
;
1063 * CreateSheet() creates a CSSStyleSheet object for the given URI,
1064 * if any. If there is no URI given, we just create a new style sheet
1065 * object. Otherwise, we check for an existing style sheet object for
1066 * that uri in various caches and clone it if we find it. Cloned
1067 * sheets will have the title/media/enabled state of the sheet they
1068 * are clones off; make sure to call PrepareSheet() on the result of
1072 Loader::CreateSheet(nsIURI
* aURI
,
1073 nsIContent
* aLinkingContent
,
1074 nsIPrincipal
* aLoaderPrincipal
,
1077 bool aHasAlternateRel
,
1078 const nsAString
& aTitle
,
1079 StyleSheetState
& aSheetState
,
1081 CSSStyleSheet
** aSheet
)
1083 LOG(("css::Loader::CreateSheet"));
1084 NS_PRECONDITION(aSheet
, "Null out param!");
1087 mSheets
= new Sheets();
1091 aSheetState
= eSheetStateUnknown
;
1093 // Check the alternate state before doing anything else, because it
1094 // can mess with our hashtables.
1095 *aIsAlternate
= IsAlternate(aTitle
, aHasAlternateRel
);
1098 aSheetState
= eSheetComplete
;
1099 nsRefPtr
<CSSStyleSheet
> sheet
;
1101 // First, the XUL cache
1103 if (IsChromeURI(aURI
)) {
1104 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
1106 if (cache
->IsEnabled()) {
1107 sheet
= cache
->GetStyleSheet(aURI
);
1108 LOG((" From XUL cache: %p", sheet
.get()));
1114 bool fromCompleteSheets
= false;
1116 // Then our per-document complete sheets.
1117 URIPrincipalAndCORSModeHashKey
key(aURI
, aLoaderPrincipal
, aCORSMode
);
1119 mSheets
->mCompleteSheets
.Get(&key
, getter_AddRefs(sheet
));
1120 LOG((" From completed: %p", sheet
.get()));
1122 fromCompleteSheets
= !!sheet
;
1126 // This sheet came from the XUL cache or our per-document hashtable; it
1127 // better be a complete sheet.
1128 NS_ASSERTION(sheet
->IsComplete(),
1129 "Sheet thinks it's not complete while we think it is");
1131 // Make sure it hasn't been modified; if it has, we can't use it
1132 if (sheet
->IsModified()) {
1133 LOG((" Not cloning completed sheet %p because it's been modified",
1136 fromCompleteSheets
= false;
1140 // Then loading sheets
1141 if (!sheet
&& !aSyncLoad
) {
1142 aSheetState
= eSheetLoading
;
1143 SheetLoadData
* loadData
= nullptr;
1144 URIPrincipalAndCORSModeHashKey
key(aURI
, aLoaderPrincipal
, aCORSMode
);
1145 mSheets
->mLoadingDatas
.Get(&key
, &loadData
);
1147 sheet
= loadData
->mSheet
;
1148 LOG((" From loading: %p", sheet
.get()));
1152 NS_ASSERTION((!aLoaderPrincipal
&& !loadData
->mLoaderPrincipal
) ||
1153 (aLoaderPrincipal
&& loadData
->mLoaderPrincipal
&&
1154 NS_SUCCEEDED(aLoaderPrincipal
->
1155 Equals(loadData
->mLoaderPrincipal
,
1156 &debugEqual
)) && debugEqual
),
1157 "Principals should be the same");
1161 // Then alternate sheets
1163 aSheetState
= eSheetPending
;
1165 mSheets
->mPendingDatas
.Get(&key
, &loadData
);
1167 sheet
= loadData
->mSheet
;
1168 LOG((" From pending: %p", sheet
.get()));
1172 NS_ASSERTION((!aLoaderPrincipal
&& !loadData
->mLoaderPrincipal
) ||
1173 (aLoaderPrincipal
&& loadData
->mLoaderPrincipal
&&
1174 NS_SUCCEEDED(aLoaderPrincipal
->
1175 Equals(loadData
->mLoaderPrincipal
,
1176 &debugEqual
)) && debugEqual
),
1177 "Principals should be the same");
1184 // The sheet we have now should be either incomplete or unmodified
1185 NS_ASSERTION(!sheet
->IsModified() || !sheet
->IsComplete(),
1186 "Unexpected modified complete sheet");
1187 NS_ASSERTION(sheet
->IsComplete() || aSheetState
!= eSheetComplete
,
1188 "Sheet thinks it's not complete while we think it is");
1190 *aSheet
= sheet
->Clone(nullptr, nullptr, nullptr, nullptr).take();
1191 if (*aSheet
&& fromCompleteSheets
&&
1192 !sheet
->GetOwnerNode() && !sheet
->GetParentSheet()) {
1193 // The sheet we're cloning isn't actually referenced by
1194 // anyone. Replace it in the cache, so that if our CSSOM is
1195 // later modified we don't end up with two copies of our inner
1197 URIPrincipalAndCORSModeHashKey
key(aURI
, aLoaderPrincipal
, aCORSMode
);
1198 NS_ASSERTION((*aSheet
)->IsComplete(),
1199 "Should only be caching complete sheets");
1200 mSheets
->mCompleteSheets
.Put(&key
, *aSheet
);
1206 aSheetState
= eSheetNeedsParser
;
1208 nsCOMPtr
<nsIURI
> baseURI
;
1209 nsIURI
* originalURI
;
1211 // Inline style. Use the document's base URL so that @import in
1212 // the inline sheet picks up the right base.
1213 NS_ASSERTION(aLinkingContent
, "Inline stylesheet without linking content?");
1214 baseURI
= aLinkingContent
->GetBaseURI();
1215 sheetURI
= aLinkingContent
->OwnerDoc()->GetDocumentURI();
1216 originalURI
= nullptr;
1223 nsRefPtr
<CSSStyleSheet
> sheet
= new CSSStyleSheet(aCORSMode
);
1224 sheet
->SetURIs(sheetURI
, originalURI
, baseURI
);
1225 sheet
.forget(aSheet
);
1228 NS_ASSERTION(*aSheet
, "We should have a sheet by now!");
1229 NS_ASSERTION(aSheetState
!= eSheetStateUnknown
, "Have to set a state!");
1230 LOG((" State: %s", gStateStrings
[aSheetState
]));
1236 * PrepareSheet() handles setting the media and title on the sheet, as
1237 * well as setting the enabled state based on the title and whether
1238 * the sheet had "alternate" in its rel.
1241 Loader::PrepareSheet(CSSStyleSheet
* aSheet
,
1242 const nsSubstring
& aTitle
,
1243 const nsSubstring
& aMediaString
,
1244 nsMediaList
* aMediaList
,
1245 Element
* aScopeElement
,
1248 NS_PRECONDITION(aSheet
, "Must have a sheet!");
1250 nsRefPtr
<nsMediaList
> mediaList(aMediaList
);
1252 if (!aMediaString
.IsEmpty()) {
1253 NS_ASSERTION(!aMediaList
,
1254 "must not provide both aMediaString and aMediaList");
1255 mediaList
= new nsMediaList();
1257 nsCSSParser
mediumParser(this);
1259 // We have aMediaString only when linked from link elements, style
1260 // elements, or PIs, so pass true.
1261 mediumParser
.ParseMediaList(aMediaString
, nullptr, 0, mediaList
, true);
1264 aSheet
->SetMedia(mediaList
);
1266 aSheet
->SetTitle(aTitle
);
1267 aSheet
->SetEnabled(! isAlternate
);
1268 aSheet
->SetScopeElement(aScopeElement
);
1272 * InsertSheetInDoc handles ordering of sheets in the document. Here
1273 * we have two types of sheets -- those with linking elements and
1274 * those without. The latter are loaded by Link: headers.
1275 * The following constraints are observed:
1276 * 1) Any sheet with a linking element comes after all sheets without
1278 * 2) Sheets without linking elements are inserted in the order in
1279 * which the inserting requests come in, since all of these are
1280 * inserted during header data processing in the content sink
1281 * 3) Sheets with linking elements are ordered based on document order
1282 * as determined by CompareDocumentPosition.
1285 Loader::InsertSheetInDoc(CSSStyleSheet
* aSheet
,
1286 nsIContent
* aLinkingContent
,
1287 nsIDocument
* aDocument
)
1289 LOG(("css::Loader::InsertSheetInDoc"));
1290 NS_PRECONDITION(aSheet
, "Nothing to insert");
1291 NS_PRECONDITION(aDocument
, "Must have a document to insert into");
1293 // XXX Need to cancel pending sheet loads for this element, if any
1295 int32_t sheetCount
= aDocument
->GetNumberOfStyleSheets();
1298 * Start the walk at the _end_ of the list, since in the typical
1299 * case we'll just want to append anyway. We want to break out of
1300 * the loop when insertionPoint points to just before the index we
1301 * want to insert at. In other words, when we leave the loop
1302 * insertionPoint is the index of the stylesheet that immediately
1303 * precedes the one we're inserting.
1305 int32_t insertionPoint
;
1306 for (insertionPoint
= sheetCount
- 1; insertionPoint
>= 0; --insertionPoint
) {
1307 nsIStyleSheet
*curSheet
= aDocument
->GetStyleSheetAt(insertionPoint
);
1308 NS_ASSERTION(curSheet
, "There must be a sheet here!");
1309 nsCOMPtr
<nsIDOMStyleSheet
> domSheet
= do_QueryInterface(curSheet
);
1310 NS_ASSERTION(domSheet
, "All the \"normal\" sheets implement nsIDOMStyleSheet");
1311 nsCOMPtr
<nsIDOMNode
> sheetOwner
;
1312 domSheet
->GetOwnerNode(getter_AddRefs(sheetOwner
));
1313 if (sheetOwner
&& !aLinkingContent
) {
1314 // Keep moving; all sheets with a sheetOwner come after all
1315 // sheets without a linkingNode
1320 // Aha! The current sheet has no sheet owner, so we want to
1321 // insert after it no matter whether we have a linkingNode
1325 nsCOMPtr
<nsINode
> sheetOwnerNode
= do_QueryInterface(sheetOwner
);
1326 NS_ASSERTION(aLinkingContent
!= sheetOwnerNode
,
1327 "Why do we still have our old sheet?");
1330 if (nsContentUtils::PositionIsBefore(sheetOwnerNode
, aLinkingContent
)) {
1331 // The current sheet comes before us, and it better be the first
1332 // such, because now we break
1337 ++insertionPoint
; // adjust the index to the spot we want to insert in
1339 // XXX <meta> elements do not implement nsIStyleSheetLinkingElement;
1340 // need to fix this for them to be ordered correctly.
1341 nsCOMPtr
<nsIStyleSheetLinkingElement
>
1342 linkingElement
= do_QueryInterface(aLinkingContent
);
1343 if (linkingElement
) {
1344 linkingElement
->SetStyleSheet(aSheet
); // This sets the ownerNode on the sheet
1347 aDocument
->BeginUpdate(UPDATE_STYLE
);
1348 aDocument
->InsertStyleSheetAt(aSheet
, insertionPoint
);
1349 aDocument
->EndUpdate(UPDATE_STYLE
);
1350 LOG((" Inserting into document at position %d", insertionPoint
));
1356 * InsertChildSheet handles ordering of @import-ed sheet in their
1357 * parent sheets. Here we want to just insert based on order of the
1358 * @import rules that imported the sheets. In theory we can't just
1359 * append to the end because the CSSOM can insert @import rules. In
1360 * practice, we get the call to load the child sheet before the CSSOM
1361 * has finished inserting the @import rule, so we have no idea where
1362 * to put it anyway. So just append for now.
1365 Loader::InsertChildSheet(CSSStyleSheet
* aSheet
,
1366 CSSStyleSheet
* aParentSheet
,
1367 ImportRule
* aParentRule
)
1369 LOG(("css::Loader::InsertChildSheet"));
1370 NS_PRECONDITION(aSheet
, "Nothing to insert");
1371 NS_PRECONDITION(aParentSheet
, "Need a parent to insert into");
1372 NS_PRECONDITION(aParentSheet
, "How did we get imported?");
1374 // child sheets should always start out enabled, even if they got
1375 // cloned off of top-level sheets which were disabled
1376 aSheet
->SetEnabled(true);
1378 aParentSheet
->AppendStyleSheet(aSheet
);
1379 aParentRule
->SetSheet(aSheet
); // This sets the ownerRule on the sheet
1381 LOG((" Inserting into parent sheet"));
1382 // LOG((" Inserting into parent sheet at position %d", insertionPoint));
1388 * LoadSheet handles the actual load of a sheet. If the load is
1389 * supposed to be synchronous it just opens a channel synchronously
1390 * using the given uri, wraps the resulting stream in a converter
1391 * stream and calls ParseSheet. Otherwise it tries to look for an
1392 * existing load for this URI and piggyback on it. Failing all that,
1393 * a new load is kicked off asynchronously.
1396 Loader::LoadSheet(SheetLoadData
* aLoadData
, StyleSheetState aSheetState
)
1398 LOG(("css::Loader::LoadSheet"));
1399 NS_PRECONDITION(aLoadData
, "Need a load data");
1400 NS_PRECONDITION(aLoadData
->mURI
, "Need a URI to load");
1401 NS_PRECONDITION(aLoadData
->mSheet
, "Need a sheet to load into");
1402 NS_PRECONDITION(aSheetState
!= eSheetComplete
, "Why bother?");
1403 NS_PRECONDITION(!aLoadData
->mUseSystemPrincipal
|| aLoadData
->mSyncLoad
,
1404 "Shouldn't use system principal for async loads");
1405 NS_ASSERTION(mSheets
, "mLoadingDatas should be initialized by now.");
1407 LOG_URI(" Load from: '%s'", aLoadData
->mURI
);
1409 nsresult rv
= NS_OK
;
1411 if (!mDocument
&& !aLoadData
->mIsNonDocumentSheet
) {
1412 // No point starting the load; just release all the data and such.
1413 LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1414 SheetComplete(aLoadData
, NS_BINDING_ABORTED
);
1415 return NS_BINDING_ABORTED
;
1418 if (aLoadData
->mSyncLoad
) {
1419 LOG((" Synchronous load"));
1420 NS_ASSERTION(!aLoadData
->mObserver
, "Observer for a sync load?");
1421 NS_ASSERTION(aSheetState
== eSheetNeedsParser
,
1422 "Sync loads can't reuse existing async loads");
1424 // Create a nsIUnicharStreamLoader instance to which we will feed
1425 // the data from the sync load. Do this before creating the
1426 // channel to make error recovery simpler.
1427 nsCOMPtr
<nsIUnicharStreamLoader
> streamLoader
;
1428 rv
= NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader
), aLoadData
);
1429 if (NS_FAILED(rv
)) {
1430 LOG_ERROR((" Failed to create stream loader for sync load"));
1431 SheetComplete(aLoadData
, rv
);
1436 mozilla::net::PredictorLearn(aLoadData
->mURI
, mDocument
->GetDocumentURI(),
1437 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
,
1442 nsCOMPtr
<nsIInputStream
> stream
;
1443 nsCOMPtr
<nsIChannel
> channel
;
1444 rv
= NS_OpenURI(getter_AddRefs(stream
), aLoadData
->mURI
, nullptr,
1445 nullptr, nullptr, nsIRequest::LOAD_NORMAL
,
1446 getter_AddRefs(channel
));
1447 if (NS_FAILED(rv
)) {
1448 LOG_ERROR((" Failed to open URI synchronously"));
1449 SheetComplete(aLoadData
, rv
);
1453 NS_ASSERTION(channel
, "NS_OpenURI lied?");
1455 // Force UA sheets to be UTF-8.
1456 // XXX this is only necessary because the default in
1457 // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1458 channel
->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
1460 // Manually feed the streamloader the contents of the stream we
1461 // got from NS_OpenURI. This will call back into OnStreamComplete
1462 // and thence to ParseSheet. Regardless of whether this fails,
1463 // SheetComplete has been called.
1464 return nsSyncLoadService::PushSyncStreamToListener(stream
,
1469 SheetLoadData
* existingData
= nullptr;
1471 URIPrincipalAndCORSModeHashKey
key(aLoadData
->mURI
,
1472 aLoadData
->mLoaderPrincipal
,
1473 aLoadData
->mSheet
->GetCORSMode());
1474 if (aSheetState
== eSheetLoading
) {
1475 mSheets
->mLoadingDatas
.Get(&key
, &existingData
);
1476 NS_ASSERTION(existingData
, "CreateSheet lied about the state");
1478 else if (aSheetState
== eSheetPending
){
1479 mSheets
->mPendingDatas
.Get(&key
, &existingData
);
1480 NS_ASSERTION(existingData
, "CreateSheet lied about the state");
1484 LOG((" Glomming on to existing load"));
1485 SheetLoadData
* data
= existingData
;
1486 while (data
->mNext
) {
1489 data
->mNext
= aLoadData
; // transfer ownership
1490 if (aSheetState
== eSheetPending
&& !aLoadData
->mWasAlternate
) {
1491 // Kick the load off; someone cares about it right away
1494 SheetLoadData
* removedData
;
1495 NS_ASSERTION(mSheets
->mPendingDatas
.Get(&key
, &removedData
) &&
1496 removedData
== existingData
,
1497 "Bad pending table.");
1500 mSheets
->mPendingDatas
.Remove(&key
);
1502 LOG((" Forcing load of pending data"));
1503 return LoadSheet(existingData
, eSheetNeedsParser
);
1505 // All done here; once the load completes we'll be marked complete
1511 mSyncCallback
= true;
1513 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1514 // Content Security Policy information to pass into channel
1515 nsCOMPtr
<nsIChannelPolicy
> channelPolicy
;
1517 loadGroup
= mDocument
->GetDocumentLoadGroup();
1518 NS_ASSERTION(loadGroup
,
1519 "No loadgroup for stylesheet; onload will fire early");
1520 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
1521 rv
= mDocument
->NodePrincipal()->GetCsp(getter_AddRefs(csp
));
1522 NS_ENSURE_SUCCESS(rv
, rv
);
1524 channelPolicy
= do_CreateInstance("@mozilla.org/nschannelpolicy;1");
1525 channelPolicy
->SetContentSecurityPolicy(csp
);
1526 channelPolicy
->SetLoadType(nsIContentPolicy::TYPE_STYLESHEET
);
1530 nsCOMPtr
<nsIChannel
> channel
;
1531 rv
= NS_NewChannel(getter_AddRefs(channel
),
1532 aLoadData
->mURI
, nullptr, loadGroup
, nullptr,
1533 nsIChannel::LOAD_NORMAL
| nsIChannel::LOAD_CLASSIFY_URI
,
1536 if (NS_FAILED(rv
)) {
1538 mSyncCallback
= false;
1540 LOG_ERROR((" Failed to create channel"));
1541 SheetComplete(aLoadData
, rv
);
1545 nsCOMPtr
<nsIHttpChannelInternal
>
1546 internalHttpChannel(do_QueryInterface(channel
));
1547 if (internalHttpChannel
)
1548 internalHttpChannel
->SetLoadAsBlocking(!aLoadData
->mWasAlternate
);
1550 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
1552 // send a minimal Accept header for text/css
1553 httpChannel
->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
1554 NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
1556 nsCOMPtr
<nsIURI
> referrerURI
= aLoadData
->GetReferrerURI();
1558 httpChannel
->SetReferrer(referrerURI
);
1560 // Set the initiator type
1561 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
1563 if (aLoadData
->mParentData
) {
1564 timedChannel
->SetInitiatorType(NS_LITERAL_STRING("css"));
1566 timedChannel
->SetInitiatorType(NS_LITERAL_STRING("link"));
1571 // Now tell the channel we expect text/css data back.... We do
1572 // this before opening it, so it's only treated as a hint.
1573 channel
->SetContentType(NS_LITERAL_CSTRING("text/css"));
1575 if (aLoadData
->mLoaderPrincipal
) {
1577 rv
= NS_URIChainHasFlags(aLoadData
->mURI
,
1578 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT
,
1581 ((NS_SUCCEEDED(rv
) && inherit
) ||
1582 (nsContentUtils::URIIsLocalFile(aLoadData
->mURI
) &&
1583 NS_SUCCEEDED(aLoadData
->mLoaderPrincipal
->
1584 CheckMayLoad(aLoadData
->mURI
, false, false))));
1585 nsCOMPtr
<nsILoadInfo
> loadInfo
=
1586 new LoadInfo(aLoadData
->mLoaderPrincipal
,
1588 LoadInfo::eInheritPrincipal
: LoadInfo::eDontInheritPrincipal
,
1589 LoadInfo::eNotSandboxed
);
1590 channel
->SetLoadInfo(loadInfo
);
1593 // We don't have to hold on to the stream loader. The ownership
1594 // model is: Necko owns the stream loader, which owns the load data,
1596 nsCOMPtr
<nsIUnicharStreamLoader
> streamLoader
;
1597 rv
= NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader
), aLoadData
);
1598 if (NS_FAILED(rv
)) {
1600 mSyncCallback
= false;
1602 LOG_ERROR((" Failed to create stream loader"));
1603 SheetComplete(aLoadData
, rv
);
1607 nsCOMPtr
<nsIStreamListener
> channelListener
;
1608 CORSMode ourCORSMode
= aLoadData
->mSheet
->GetCORSMode();
1609 if (ourCORSMode
!= CORS_NONE
) {
1610 bool withCredentials
= (ourCORSMode
== CORS_USE_CREDENTIALS
);
1611 LOG((" Doing CORS-enabled load; credentials %d", withCredentials
));
1612 nsRefPtr
<nsCORSListenerProxy
> corsListener
=
1613 new nsCORSListenerProxy(streamLoader
, aLoadData
->mLoaderPrincipal
,
1615 rv
= corsListener
->Init(channel
);
1616 if (NS_FAILED(rv
)) {
1618 mSyncCallback
= false;
1620 LOG_ERROR((" Initial CORS check failed"));
1621 SheetComplete(aLoadData
, rv
);
1624 channelListener
= corsListener
;
1626 channelListener
= streamLoader
;
1630 mozilla::net::PredictorLearn(aLoadData
->mURI
, mDocument
->GetDocumentURI(),
1631 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
,
1635 rv
= channel
->AsyncOpen(channelListener
, nullptr);
1638 mSyncCallback
= false;
1641 if (NS_FAILED(rv
)) {
1642 LOG_ERROR((" Failed to create stream loader"));
1643 SheetComplete(aLoadData
, rv
);
1647 mSheets
->mLoadingDatas
.Put(&key
, aLoadData
);
1648 aLoadData
->mIsLoading
= true;
1654 * ParseSheet handles parsing the data stream. The main idea here is
1655 * to push the current load data onto the parse stack before letting
1656 * the CSS parser at the data stream. That lets us handle @import
1660 Loader::ParseSheet(const nsAString
& aInput
,
1661 SheetLoadData
* aLoadData
,
1664 LOG(("css::Loader::ParseSheet"));
1665 NS_PRECONDITION(aLoadData
, "Must have load data");
1666 NS_PRECONDITION(aLoadData
->mSheet
, "Must have sheet to parse into");
1670 nsCSSParser
parser(this, aLoadData
->mSheet
);
1672 // Push our load data on the stack so any kids can pick it up
1673 mParsingDatas
.AppendElement(aLoadData
);
1674 nsIURI
* sheetURI
= aLoadData
->mSheet
->GetSheetURI();
1675 nsIURI
* baseURI
= aLoadData
->mSheet
->GetBaseURI();
1676 nsresult rv
= parser
.ParseSheet(aInput
, sheetURI
, baseURI
,
1677 aLoadData
->mSheet
->Principal(),
1678 aLoadData
->mLineNumber
,
1679 aLoadData
->mAllowUnsafeRules
);
1680 mParsingDatas
.RemoveElementAt(mParsingDatas
.Length() - 1);
1682 if (NS_FAILED(rv
)) {
1683 LOG_ERROR((" Low-level error in parser!"));
1684 SheetComplete(aLoadData
, rv
);
1688 NS_ASSERTION(aLoadData
->mPendingChildren
== 0 || !aLoadData
->mSyncLoad
,
1689 "Sync load has leftover pending children!");
1691 if (aLoadData
->mPendingChildren
== 0) {
1692 LOG((" No pending kids from parse"));
1694 SheetComplete(aLoadData
, NS_OK
);
1696 // Otherwise, the children are holding strong refs to the data and
1697 // will call SheetComplete() on it when they complete.
1703 * SheetComplete is the do-it-all cleanup function. It removes the
1704 * load data from the "loading" hashtable, adds the sheet to the
1705 * "completed" hashtable, massages the XUL cache, handles siblings of
1706 * the load data (other loads for the same URI), handles unblocking
1707 * blocked parent loads as needed, and most importantly calls
1708 * NS_RELEASE on the load data to destroy the whole mess.
1711 Loader::SheetComplete(SheetLoadData
* aLoadData
, nsresult aStatus
)
1713 LOG(("css::Loader::SheetComplete"));
1715 // 8 is probably big enough for all our common cases. It's not likely that
1716 // imports will nest more than 8 deep, and multiple sheets with the same URI
1718 nsAutoTArray
<nsRefPtr
<SheetLoadData
>, 8> datasToNotify
;
1719 DoSheetComplete(aLoadData
, aStatus
, datasToNotify
);
1721 // Now it's safe to go ahead and notify observers
1722 uint32_t count
= datasToNotify
.Length();
1723 mDatasToNotifyOn
+= count
;
1724 for (uint32_t i
= 0; i
< count
; ++i
) {
1727 SheetLoadData
* data
= datasToNotify
[i
];
1728 NS_ASSERTION(data
&& data
->mMustNotify
, "How did this data get here?");
1729 if (data
->mObserver
) {
1730 LOG((" Notifying observer 0x%x for data 0x%x. wasAlternate: %d",
1731 data
->mObserver
.get(), data
, data
->mWasAlternate
));
1732 data
->mObserver
->StyleSheetLoaded(data
->mSheet
, data
->mWasAlternate
,
1736 nsTObserverArray
<nsCOMPtr
<nsICSSLoaderObserver
> >::ForwardIterator
iter(mObservers
);
1737 nsCOMPtr
<nsICSSLoaderObserver
> obs
;
1738 while (iter
.HasMore()) {
1739 obs
= iter
.GetNext();
1740 LOG((" Notifying global observer 0x%x for data 0x%s. wasAlternate: %d",
1741 obs
.get(), data
, data
->mWasAlternate
));
1742 obs
->StyleSheetLoaded(data
->mSheet
, data
->mWasAlternate
, aStatus
);
1746 if (mSheets
->mLoadingDatas
.Count() == 0 && mSheets
->mPendingDatas
.Count() > 0) {
1747 LOG((" No more loading sheets; starting alternates"));
1748 StartAlternateLoads();
1753 Loader::DoSheetComplete(SheetLoadData
* aLoadData
, nsresult aStatus
,
1754 LoadDataArray
& aDatasToNotify
)
1756 LOG(("css::Loader::DoSheetComplete"));
1757 NS_PRECONDITION(aLoadData
, "Must have a load data!");
1758 NS_PRECONDITION(aLoadData
->mSheet
, "Must have a sheet");
1759 NS_ASSERTION(mSheets
, "mLoadingDatas should be initialized by now.");
1761 LOG(("Load completed, status: 0x%x", aStatus
));
1763 // Twiddle the hashtables
1764 if (aLoadData
->mURI
) {
1765 LOG_URI(" Finished loading: '%s'", aLoadData
->mURI
);
1766 // Remove the data from the list of loading datas
1767 if (aLoadData
->mIsLoading
) {
1768 URIPrincipalAndCORSModeHashKey
key(aLoadData
->mURI
,
1769 aLoadData
->mLoaderPrincipal
,
1770 aLoadData
->mSheet
->GetCORSMode());
1772 SheetLoadData
*loadingData
;
1773 NS_ASSERTION(mSheets
->mLoadingDatas
.Get(&key
, &loadingData
) &&
1774 loadingData
== aLoadData
,
1775 "Bad loading table");
1778 mSheets
->mLoadingDatas
.Remove(&key
);
1779 aLoadData
->mIsLoading
= false;
1783 // Go through and deal with the whole linked list.
1784 SheetLoadData
* data
= aLoadData
;
1786 if (!data
->mSheetAlreadyComplete
) {
1787 // If mSheetAlreadyComplete, then the sheet could well be modified between
1788 // when we posted the async call to SheetComplete and now, since the sheet
1789 // was page-accessible during that whole time.
1790 NS_ABORT_IF_FALSE(!data
->mSheet
->IsModified(),
1791 "should not get marked modified during parsing");
1792 data
->mSheet
->SetComplete();
1793 data
->ScheduleLoadEventIfNeeded(aStatus
);
1795 if (data
->mMustNotify
&& (data
->mObserver
|| !mObservers
.IsEmpty())) {
1796 // Don't notify here so we don't trigger script. Remember the
1797 // info we need to notify, then do it later when it's safe.
1798 aDatasToNotify
.AppendElement(data
);
1800 // On append failure, just press on. We'll fail to notify the observer,
1801 // but not much we can do about that....
1804 NS_ASSERTION(!data
->mParentData
||
1805 data
->mParentData
->mPendingChildren
!= 0,
1806 "Broken pending child count on our parent");
1808 // If we have a parent, our parent is no longer being parsed, and
1809 // we are the last pending child, then our load completion
1810 // completes the parent too. Note that the parent _can_ still be
1811 // being parsed (eg if the child (us) failed to open the channel
1813 if (data
->mParentData
&&
1814 --(data
->mParentData
->mPendingChildren
) == 0 &&
1815 !mParsingDatas
.Contains(data
->mParentData
)) {
1816 DoSheetComplete(data
->mParentData
, aStatus
, aDatasToNotify
);
1822 // Now that it's marked complete, put the sheet in our cache.
1823 // If we ever start doing this for failure aStatus, we'll need to
1824 // adjust the PostLoadEvent code that thinks anything already
1825 // complete must have loaded succesfully.
1826 if (NS_SUCCEEDED(aStatus
) && aLoadData
->mURI
) {
1827 // Pick our sheet to cache carefully. Ideally, we want to cache
1828 // one of the sheets that will be kept alive by a document or
1829 // parent sheet anyway, so that if someone then accesses it via
1830 // CSSOM we won't have extra clones of the inner lying around.
1832 CSSStyleSheet
* sheet
= aLoadData
->mSheet
;
1834 if (data
->mSheet
->GetParentSheet() || data
->mSheet
->GetOwnerNode()) {
1835 sheet
= data
->mSheet
;
1841 if (IsChromeURI(aLoadData
->mURI
)) {
1842 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
1843 if (cache
&& cache
->IsEnabled()) {
1844 if (!cache
->GetStyleSheet(aLoadData
->mURI
)) {
1845 LOG((" Putting sheet in XUL prototype cache"));
1846 NS_ASSERTION(sheet
->IsComplete(),
1847 "Should only be caching complete sheets");
1848 cache
->PutStyleSheet(sheet
);
1854 URIPrincipalAndCORSModeHashKey
key(aLoadData
->mURI
,
1855 aLoadData
->mLoaderPrincipal
,
1856 aLoadData
->mSheet
->GetCORSMode());
1857 NS_ASSERTION(sheet
->IsComplete(),
1858 "Should only be caching complete sheets");
1859 mSheets
->mCompleteSheets
.Put(&key
, sheet
);
1865 NS_RELEASE(aLoadData
); // this will release parents and siblings and all that
1869 Loader::LoadInlineStyle(nsIContent
* aElement
,
1870 const nsAString
& aBuffer
,
1871 uint32_t aLineNumber
,
1872 const nsAString
& aTitle
,
1873 const nsAString
& aMedia
,
1874 Element
* aScopeElement
,
1875 nsICSSLoaderObserver
* aObserver
,
1879 LOG(("css::Loader::LoadInlineStyle"));
1880 NS_ASSERTION(mParsingDatas
.Length() == 0, "We're in the middle of a parse?");
1885 LOG_WARN((" Not enabled"));
1886 return NS_ERROR_NOT_AVAILABLE
;
1889 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_INITIALIZED
);
1891 nsCOMPtr
<nsIStyleSheetLinkingElement
> owningElement(do_QueryInterface(aElement
));
1892 NS_ASSERTION(owningElement
, "Element is not a style linking element!");
1894 // Since we're not planning to load a URI, no need to hand a principal to the
1895 // load data or to CreateSheet(). Also, OK to use CORS_NONE for the CORS
1897 StyleSheetState state
;
1898 nsRefPtr
<CSSStyleSheet
> sheet
;
1899 nsresult rv
= CreateSheet(nullptr, aElement
, nullptr, CORS_NONE
, false, false,
1900 aTitle
, state
, aIsAlternate
, getter_AddRefs(sheet
));
1901 NS_ENSURE_SUCCESS(rv
, rv
);
1902 NS_ASSERTION(state
== eSheetNeedsParser
,
1903 "Inline sheets should not be cached");
1905 LOG((" Sheet is alternate: %d", *aIsAlternate
));
1907 PrepareSheet(sheet
, aTitle
, aMedia
, nullptr, aScopeElement
, *aIsAlternate
);
1909 if (aElement
->HasFlag(NODE_IS_IN_SHADOW_TREE
)) {
1910 ShadowRoot
* containingShadow
= aElement
->GetContainingShadow();
1911 MOZ_ASSERT(containingShadow
);
1912 containingShadow
->InsertSheet(sheet
, aElement
);
1914 rv
= InsertSheetInDoc(sheet
, aElement
, mDocument
);
1915 NS_ENSURE_SUCCESS(rv
, rv
);
1918 SheetLoadData
* data
= new SheetLoadData(this, aTitle
, nullptr, sheet
,
1919 owningElement
, *aIsAlternate
,
1920 aObserver
, nullptr);
1922 // We never actually load this, so just set its principal directly
1923 sheet
->SetPrincipal(aElement
->NodePrincipal());
1926 data
->mLineNumber
= aLineNumber
;
1927 // Parse completion releases the load data
1928 rv
= ParseSheet(aBuffer
, data
, *aCompleted
);
1929 NS_ENSURE_SUCCESS(rv
, rv
);
1931 // If aCompleted is true, |data| may well be deleted by now.
1933 data
->mMustNotify
= true;
1939 Loader::LoadStyleLink(nsIContent
* aElement
,
1941 const nsAString
& aTitle
,
1942 const nsAString
& aMedia
,
1943 bool aHasAlternateRel
,
1945 nsICSSLoaderObserver
* aObserver
,
1948 LOG(("css::Loader::LoadStyleLink"));
1949 NS_PRECONDITION(aURL
, "Must have URL to load");
1950 NS_ASSERTION(mParsingDatas
.Length() == 0, "We're in the middle of a parse?");
1952 LOG_URI(" Link uri: '%s'", aURL
);
1953 LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle
).get()));
1954 LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia
).get()));
1955 LOG((" Link alternate rel: %d", aHasAlternateRel
));
1958 LOG_WARN((" Not enabled"));
1959 return NS_ERROR_NOT_AVAILABLE
;
1962 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_INITIALIZED
);
1964 nsIPrincipal
* principal
=
1965 aElement
? aElement
->NodePrincipal() : mDocument
->NodePrincipal();
1967 nsISupports
* context
= aElement
;
1969 context
= mDocument
;
1971 nsresult rv
= CheckLoadAllowed(principal
, aURL
, context
);
1972 if (NS_FAILED(rv
)) return rv
;
1974 LOG((" Passed load check"));
1976 StyleSheetState state
;
1977 nsRefPtr
<CSSStyleSheet
> sheet
;
1978 rv
= CreateSheet(aURL
, aElement
, principal
, aCORSMode
, false,
1979 aHasAlternateRel
, aTitle
, state
, aIsAlternate
,
1980 getter_AddRefs(sheet
));
1981 NS_ENSURE_SUCCESS(rv
, rv
);
1983 LOG((" Sheet is alternate: %d", *aIsAlternate
));
1985 PrepareSheet(sheet
, aTitle
, aMedia
, nullptr, nullptr, *aIsAlternate
);
1987 rv
= InsertSheetInDoc(sheet
, aElement
, mDocument
);
1988 NS_ENSURE_SUCCESS(rv
, rv
);
1990 nsCOMPtr
<nsIStyleSheetLinkingElement
> owningElement(do_QueryInterface(aElement
));
1992 if (state
== eSheetComplete
) {
1993 LOG((" Sheet already complete: 0x%p",
1994 static_cast<void*>(sheet
.get())));
1995 if (aObserver
|| !mObservers
.IsEmpty() || owningElement
) {
1996 rv
= PostLoadEvent(aURL
, sheet
, aObserver
, *aIsAlternate
,
2004 // Now we need to actually load it
2005 SheetLoadData
* data
= new SheetLoadData(this, aTitle
, aURL
, sheet
,
2006 owningElement
, *aIsAlternate
,
2007 aObserver
, principal
);
2010 // If we have to parse and it's an alternate non-inline, defer it
2011 if (aURL
&& state
== eSheetNeedsParser
&& mSheets
->mLoadingDatas
.Count() != 0 &&
2013 LOG((" Deferring alternate sheet load"));
2014 URIPrincipalAndCORSModeHashKey
key(data
->mURI
, data
->mLoaderPrincipal
,
2015 data
->mSheet
->GetCORSMode());
2016 mSheets
->mPendingDatas
.Put(&key
, data
);
2018 data
->mMustNotify
= true;
2022 // Load completion will free the data
2023 rv
= LoadSheet(data
, state
);
2024 NS_ENSURE_SUCCESS(rv
, rv
);
2026 data
->mMustNotify
= true;
2031 HaveAncestorDataWithURI(SheetLoadData
*aData
, nsIURI
*aURI
)
2034 // Inline style; this won't have any ancestors
2035 NS_ABORT_IF_FALSE(!aData
->mParentData
,
2036 "How does inline style have a parent?");
2041 if (NS_FAILED(aData
->mURI
->Equals(aURI
, &equal
)) || equal
) {
2045 // Datas down the mNext chain have the same URI as aData, so we
2046 // don't have to compare to them. But they might have different
2047 // parents, and we have to check all of those.
2049 if (aData
->mParentData
&&
2050 HaveAncestorDataWithURI(aData
->mParentData
, aURI
)) {
2054 aData
= aData
->mNext
;
2061 Loader::LoadChildSheet(CSSStyleSheet
* aParentSheet
,
2063 nsMediaList
* aMedia
,
2064 ImportRule
* aParentRule
)
2066 LOG(("css::Loader::LoadChildSheet"));
2067 NS_PRECONDITION(aURL
, "Must have a URI to load");
2068 NS_PRECONDITION(aParentSheet
, "Must have a parent sheet");
2071 LOG_WARN((" Not enabled"));
2072 return NS_ERROR_NOT_AVAILABLE
;
2075 LOG_URI(" Child uri: '%s'", aURL
);
2077 nsCOMPtr
<nsIDOMNode
> owningNode
;
2079 // check for an owning document: if none, don't bother walking up the parent
2081 if (aParentSheet
->GetOwningDocument()) {
2082 nsCOMPtr
<nsIDOMStyleSheet
> nextParentSheet(aParentSheet
);
2083 NS_ENSURE_TRUE(nextParentSheet
, NS_ERROR_FAILURE
); //Not a stylesheet!?
2085 nsCOMPtr
<nsIDOMStyleSheet
> topSheet
;
2086 //traverse our way to the top-most sheet
2088 topSheet
.swap(nextParentSheet
);
2089 topSheet
->GetParentStyleSheet(getter_AddRefs(nextParentSheet
));
2090 } while (nextParentSheet
);
2092 topSheet
->GetOwnerNode(getter_AddRefs(owningNode
));
2095 nsISupports
* context
= owningNode
;
2097 context
= mDocument
;
2100 nsIPrincipal
* principal
= aParentSheet
->Principal();
2101 nsresult rv
= CheckLoadAllowed(principal
, aURL
, context
);
2102 if (NS_FAILED(rv
)) return rv
;
2104 LOG((" Passed load check"));
2106 SheetLoadData
* parentData
= nullptr;
2107 nsCOMPtr
<nsICSSLoaderObserver
> observer
;
2109 int32_t count
= mParsingDatas
.Length();
2111 LOG((" Have a parent load"));
2112 parentData
= mParsingDatas
.ElementAt(count
- 1);
2114 if (HaveAncestorDataWithURI(parentData
, aURL
)) {
2115 // Houston, we have a loop, blow off this child and pretend this never
2117 LOG_ERROR((" @import cycle detected, dropping load"));
2121 NS_ASSERTION(parentData
->mSheet
== aParentSheet
,
2122 "Unexpected call to LoadChildSheet");
2124 LOG((" No parent load; must be CSSOM"));
2125 // No parent load data, so the sheet will need to be notified when
2126 // we finish, if it can be, if we do the load asynchronously.
2127 observer
= aParentSheet
;
2130 // Now that we know it's safe to load this (passes security check and not a
2132 nsRefPtr
<CSSStyleSheet
> sheet
;
2134 StyleSheetState state
;
2135 const nsSubstring
& empty
= EmptyString();
2136 // For now, use CORS_NONE for child sheets
2137 rv
= CreateSheet(aURL
, nullptr, principal
, CORS_NONE
,
2138 parentData
? parentData
->mSyncLoad
: false,
2139 false, empty
, state
, &isAlternate
, getter_AddRefs(sheet
));
2140 NS_ENSURE_SUCCESS(rv
, rv
);
2142 PrepareSheet(sheet
, empty
, empty
, aMedia
, nullptr, isAlternate
);
2144 rv
= InsertChildSheet(sheet
, aParentSheet
, aParentRule
);
2145 NS_ENSURE_SUCCESS(rv
, rv
);
2147 if (state
== eSheetComplete
) {
2148 LOG((" Sheet already complete"));
2149 // We're completely done. No need to notify, even, since the
2150 // @import rule addition/modification will trigger the right style
2151 // changes automatically.
2155 SheetLoadData
* data
= new SheetLoadData(this, aURL
, sheet
, parentData
,
2156 observer
, principal
);
2159 bool syncLoad
= data
->mSyncLoad
;
2161 // Load completion will release the data
2162 rv
= LoadSheet(data
, state
);
2163 NS_ENSURE_SUCCESS(rv
, rv
);
2165 // If syncLoad is true, |data| will be deleted by now.
2167 data
->mMustNotify
= true;
2173 Loader::LoadSheetSync(nsIURI
* aURL
, bool aAllowUnsafeRules
,
2174 bool aUseSystemPrincipal
,
2175 CSSStyleSheet
** aSheet
)
2177 LOG(("css::Loader::LoadSheetSync"));
2178 return InternalLoadNonDocumentSheet(aURL
, aAllowUnsafeRules
,
2179 aUseSystemPrincipal
, nullptr,
2180 EmptyCString(), aSheet
, nullptr);
2184 Loader::LoadSheet(nsIURI
* aURL
,
2185 nsIPrincipal
* aOriginPrincipal
,
2186 const nsCString
& aCharset
,
2187 nsICSSLoaderObserver
* aObserver
,
2188 CSSStyleSheet
** aSheet
)
2190 LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
2191 NS_PRECONDITION(aSheet
, "aSheet is null");
2192 return InternalLoadNonDocumentSheet(aURL
, false, false,
2193 aOriginPrincipal
, aCharset
,
2198 Loader::LoadSheet(nsIURI
* aURL
,
2199 nsIPrincipal
* aOriginPrincipal
,
2200 const nsCString
& aCharset
,
2201 nsICSSLoaderObserver
* aObserver
,
2204 LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2205 return InternalLoadNonDocumentSheet(aURL
, false, false,
2206 aOriginPrincipal
, aCharset
,
2207 nullptr, aObserver
, aCORSMode
);
2211 Loader::InternalLoadNonDocumentSheet(nsIURI
* aURL
,
2212 bool aAllowUnsafeRules
,
2213 bool aUseSystemPrincipal
,
2214 nsIPrincipal
* aOriginPrincipal
,
2215 const nsCString
& aCharset
,
2216 CSSStyleSheet
** aSheet
,
2217 nsICSSLoaderObserver
* aObserver
,
2220 NS_PRECONDITION(aURL
, "Must have a URI to load");
2221 NS_PRECONDITION(aSheet
|| aObserver
, "Sheet and observer can't both be null");
2222 NS_PRECONDITION(!aUseSystemPrincipal
|| !aObserver
,
2223 "Shouldn't load system-principal sheets async");
2224 NS_ASSERTION(mParsingDatas
.Length() == 0, "We're in the middle of a parse?");
2226 LOG_URI(" Non-document sheet uri: '%s'", aURL
);
2233 LOG_WARN((" Not enabled"));
2234 return NS_ERROR_NOT_AVAILABLE
;
2237 nsresult rv
= CheckLoadAllowed(aOriginPrincipal
, aURL
, mDocument
);
2238 if (NS_FAILED(rv
)) {
2242 StyleSheetState state
;
2244 nsRefPtr
<CSSStyleSheet
> sheet
;
2245 bool syncLoad
= (aObserver
== nullptr);
2246 const nsSubstring
& empty
= EmptyString();
2248 rv
= CreateSheet(aURL
, nullptr, aOriginPrincipal
, aCORSMode
, syncLoad
, false,
2249 empty
, state
, &isAlternate
, getter_AddRefs(sheet
));
2250 NS_ENSURE_SUCCESS(rv
, rv
);
2252 PrepareSheet(sheet
, empty
, empty
, nullptr, nullptr, isAlternate
);
2254 if (state
== eSheetComplete
) {
2255 LOG((" Sheet already complete"));
2256 if (aObserver
|| !mObservers
.IsEmpty()) {
2257 rv
= PostLoadEvent(aURL
, sheet
, aObserver
, false, nullptr);
2260 sheet
.swap(*aSheet
);
2265 SheetLoadData
* data
=
2266 new SheetLoadData(this, aURL
, sheet
, syncLoad
, aAllowUnsafeRules
,
2267 aUseSystemPrincipal
, aCharset
, aObserver
,
2271 rv
= LoadSheet(data
, state
);
2272 NS_ENSURE_SUCCESS(rv
, rv
);
2275 sheet
.swap(*aSheet
);
2278 data
->mMustNotify
= true;
2285 Loader::PostLoadEvent(nsIURI
* aURI
,
2286 CSSStyleSheet
* aSheet
,
2287 nsICSSLoaderObserver
* aObserver
,
2289 nsIStyleSheetLinkingElement
* aElement
)
2291 LOG(("css::Loader::PostLoadEvent"));
2292 NS_PRECONDITION(aSheet
, "Must have sheet");
2293 NS_PRECONDITION(aObserver
|| !mObservers
.IsEmpty() || aElement
,
2294 "Must have observer or element");
2296 nsRefPtr
<SheetLoadData
> evt
=
2297 new SheetLoadData(this, EmptyString(), // title doesn't matter here
2304 NS_ENSURE_TRUE(evt
, NS_ERROR_OUT_OF_MEMORY
);
2306 if (!mPostedEvents
.AppendElement(evt
)) {
2307 return NS_ERROR_OUT_OF_MEMORY
;
2310 nsresult rv
= NS_DispatchToCurrentThread(evt
);
2311 if (NS_FAILED(rv
)) {
2312 NS_WARNING("failed to dispatch stylesheet load event");
2313 mPostedEvents
.RemoveElement(evt
);
2315 // We'll unblock onload when we handle the event.
2317 mDocument
->BlockOnload();
2320 // We want to notify the observer for this data.
2321 evt
->mMustNotify
= true;
2322 evt
->mSheetAlreadyComplete
= true;
2324 // If we get to this code, aSheet loaded correctly at some point, so
2325 // we can just use NS_OK for the status. Note that we do this here
2326 // and not from inside our SheetComplete so that we don't end up
2327 // running the load event async.
2328 evt
->ScheduleLoadEventIfNeeded(NS_OK
);
2335 Loader::HandleLoadEvent(SheetLoadData
* aEvent
)
2337 // XXXbz can't assert this yet.... May not have an observer because
2338 // we're unblocking the parser
2339 // NS_ASSERTION(aEvent->mObserver, "Must have observer");
2340 NS_ASSERTION(aEvent
->mSheet
, "Must have sheet");
2342 // Very important: this needs to come before the SheetComplete call
2343 // below, so that HasPendingLoads() will test true as needed under
2344 // notifications we send from that SheetComplete call.
2345 mPostedEvents
.RemoveElement(aEvent
);
2347 if (!aEvent
->mIsCancelled
) {
2348 // SheetComplete will call Release(), so give it a reference to do
2351 SheetComplete(aEvent
, NS_OK
);
2355 mDocument
->UnblockOnload(true);
2359 static PLDHashOperator
2360 StopLoadingSheetCallback(URIPrincipalAndCORSModeHashKey
* aKey
,
2361 SheetLoadData
*& aData
,
2364 NS_PRECONDITION(aData
, "Must have a data!");
2365 NS_PRECONDITION(aClosure
, "Must have a loader");
2367 aData
->mIsLoading
= false; // we will handle the removal right here
2368 aData
->mIsCancelled
= true;
2370 static_cast<Loader::LoadDataArray
*>(aClosure
)->AppendElement(aData
);
2372 return PL_DHASH_REMOVE
;
2378 uint32_t pendingCount
=
2379 mSheets
? mSheets
->mPendingDatas
.Count() : 0;
2380 uint32_t loadingCount
=
2381 mSheets
? mSheets
->mLoadingDatas
.Count() : 0;
2382 LoadDataArray
arr(pendingCount
+ loadingCount
+ mPostedEvents
.Length());
2385 mSheets
->mPendingDatas
.Enumerate(StopLoadingSheetCallback
, &arr
);
2388 mSheets
->mLoadingDatas
.Enumerate(StopLoadingSheetCallback
, &arr
);
2392 for (i
= 0; i
< mPostedEvents
.Length(); ++i
) {
2393 SheetLoadData
* data
= mPostedEvents
[i
];
2394 data
->mIsCancelled
= true;
2395 if (arr
.AppendElement(data
)) {
2396 // SheetComplete() calls Release(), so give this an extra ref.
2401 NS_NOTREACHED("We preallocated this memory... shouldn't really fail, "
2402 "except we never check that preallocation succeeds.");
2406 mPostedEvents
.Clear();
2408 mDatasToNotifyOn
+= arr
.Length();
2409 for (i
= 0; i
< arr
.Length(); ++i
) {
2411 SheetComplete(arr
[i
], NS_BINDING_ABORTED
);
2417 Loader::HasPendingLoads()
2420 (mSheets
&& mSheets
->mLoadingDatas
.Count() != 0) ||
2421 (mSheets
&& mSheets
->mPendingDatas
.Count() != 0) ||
2422 mPostedEvents
.Length() != 0 ||
2423 mDatasToNotifyOn
!= 0;
2427 Loader::AddObserver(nsICSSLoaderObserver
* aObserver
)
2429 NS_PRECONDITION(aObserver
, "Must have observer");
2430 if (mObservers
.AppendElementUnlessExists(aObserver
)) {
2434 return NS_ERROR_OUT_OF_MEMORY
;
2438 Loader::RemoveObserver(nsICSSLoaderObserver
* aObserver
)
2440 mObservers
.RemoveElement(aObserver
);
2443 static PLDHashOperator
2444 CollectLoadDatas(URIPrincipalAndCORSModeHashKey
*aKey
,
2445 SheetLoadData
* &aData
,
2448 static_cast<Loader::LoadDataArray
*>(aClosure
)->AppendElement(aData
);
2449 return PL_DHASH_REMOVE
;
2453 Loader::StartAlternateLoads()
2455 NS_PRECONDITION(mSheets
, "Don't call me!");
2456 LoadDataArray
arr(mSheets
->mPendingDatas
.Count());
2457 mSheets
->mPendingDatas
.Enumerate(CollectLoadDatas
, &arr
);
2459 mDatasToNotifyOn
+= arr
.Length();
2460 for (uint32_t i
= 0; i
< arr
.Length(); ++i
) {
2462 LoadSheet(arr
[i
], eSheetNeedsParser
);
2466 static PLDHashOperator
2467 TraverseSheet(URIPrincipalAndCORSModeHashKey
*,
2468 CSSStyleSheet
* aSheet
,
2471 nsCycleCollectionTraversalCallback
* cb
=
2472 static_cast<nsCycleCollectionTraversalCallback
*>(aClosure
);
2473 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "Sheet cache nsCSSLoader");
2474 cb
->NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIStyleSheet
*, aSheet
));
2475 return PL_DHASH_NEXT
;
2478 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader
)
2480 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader
)
2482 tmp
->mSheets
->mCompleteSheets
.EnumerateRead(TraverseSheet
, &cb
);
2484 nsTObserverArray
<nsCOMPtr
<nsICSSLoaderObserver
>>::ForwardIterator
2485 it(tmp
->mObservers
);
2486 while (it
.HasMore()) {
2487 ImplCycleCollectionTraverse(cb
, it
.GetNext(),
2488 "mozilla::css::Loader.mObservers");
2490 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2492 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader
)
2494 tmp
->mSheets
->mCompleteSheets
.Clear();
2496 tmp
->mObservers
.Clear();
2497 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2499 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Loader
, AddRef
)
2500 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Loader
, Release
)
2502 struct SheetMemoryCounter
{
2504 mozilla::MallocSizeOf mallocSizeOf
;
2508 CountSheetMemory(URIPrincipalAndCORSModeHashKey
* /* unused */,
2509 const nsRefPtr
<CSSStyleSheet
>& aSheet
,
2510 mozilla::MallocSizeOf aMallocSizeOf
,
2513 // If aSheet has a parent, then its parent will report it so we don't
2514 // have to worry about it here.
2515 // Likewise, if aSheet has an owning node, then the document that
2516 // node is in will report it.
2517 if (aSheet
->GetOwnerNode() || aSheet
->GetParentSheet()) {
2520 return aSheet
->SizeOfIncludingThis(aMallocSizeOf
);
2524 Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const
2526 size_t s
= aMallocSizeOf(this);
2529 s
+= mSheets
->mCompleteSheets
.SizeOfExcludingThis(CountSheetMemory
, aMallocSizeOf
);
2531 s
+= mObservers
.SizeOfExcludingThis(aMallocSizeOf
);
2533 // Measurement of the following members may be added later if DMD finds it is
2535 // - mLoadingDatas: transient, and should be small
2536 // - mPendingDatas: transient, and should be small
2537 // - mParsingDatas: transient, and should be small
2538 // - mPostedEvents: transient, and should be small
2540 // The following members aren't measured:
2541 // - mDocument, because it's a weak backpointer
2542 // - mPreferredSheet, because it can be a shared string
2548 } // namespace mozilla