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