layout/style/Loader.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: ft=cpp tw=78 sw=2 et ts=2
     3  *
     4  * This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     7  *
     8  * This Original Code has been modified by IBM Corporation.
     9  * Modifications made by IBM described herein are Copyright (c)
    10  * International Business Machines Corporation, 2000.  Modifications
    11  * to Mozilla code or documentation identified per MPL Section 3.3
    12  *
    13  * Date             Modified by     Description of modification
    14  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
    15  */
    17 /* loading of CSS style sheets using the network APIs */
    19 #include "mozilla/ArrayUtils.h"
    20 #include "mozilla/MemoryReporting.h"
    22 #include "mozilla/css/Loader.h"
    23 #include "nsIRunnable.h"
    24 #include "nsIUnicharStreamLoader.h"
    25 #include "nsSyncLoadService.h"
    26 #include "nsCOMPtr.h"
    27 #include "nsString.h"
    28 #include "nsIContent.h"
    29 #include "nsIDocument.h"
    30 #include "nsIDOMNode.h"
    31 #include "nsIDOMDocument.h"
    32 #include "nsIURI.h"
    33 #include "nsNetUtil.h"
    34 #include "nsContentUtils.h"
    35 #include "nsIScriptSecurityManager.h"
    36 #include "nsContentPolicyUtils.h"
    37 #include "nsIHttpChannel.h"
    38 #include "nsIHttpChannelInternal.h"
    39 #include "nsIScriptError.h"
    40 #include "nsMimeTypes.h"
    41 #include "nsCSSStyleSheet.h"
    42 #include "nsIStyleSheetLinkingElement.h"
    43 #include "nsICSSLoaderObserver.h"
    44 #include "nsCSSParser.h"
    45 #include "mozilla/css/ImportRule.h"
    46 #include "nsThreadUtils.h"
    47 #include "nsGkAtoms.h"
    48 #include "nsIThreadInternal.h"
    49 #include "nsCrossSiteListenerProxy.h"
    50 #include "nsINetworkSeer.h"
    51 #include "mozilla/dom/ShadowRoot.h"
    52 #include "mozilla/dom/URL.h"
    54 #ifdef MOZ_XUL
    55 #include "nsXULPrototypeCache.h"
    56 #endif
    58 #include "nsIMediaList.h"
    59 #include "nsIDOMStyleSheet.h"
    60 #include "nsError.h"
    62 #include "nsIChannelPolicy.h"
    63 #include "nsIContentSecurityPolicy.h"
    64 #include "nsCycleCollectionParticipant.h"
    66 #include "mozilla/dom/EncodingUtils.h"
    67 using mozilla::dom::EncodingUtils;
    69 using namespace mozilla::dom;
    71 /**
    72  * OVERALL ARCHITECTURE
    73  *
    74  * The CSS Loader gets requests to load various sorts of style sheets:
    75  * inline style from <style> elements, linked style, @import-ed child
    76  * sheets, non-document sheets.  The loader handles the following tasks:
    77  *
    78  * 1) Checking whether the load is allowed: CheckLoadAllowed()
    79  * 2) Creation of the actual style sheet objects: CreateSheet()
    80  * 3) setting of the right media, title, enabled state, etc on the
    81  *    sheet: PrepareSheet()
    82  * 4) Insertion of the sheet in the proper cascade order:
    83  *    InsertSheetInDoc() and InsertChildSheet()
    84  * 5) Load of the sheet: LoadSheet()
    85  * 6) Parsing of the sheet: ParseSheet()
    86  * 7) Cleanup: SheetComplete()
    87  *
    88  * The detailed documentation for these functions is found with the
    89  * function implementations.
    90  *
    91  * The following helper object is used:
    92  *    SheetLoadData -- a small class that is used to store all the
    93  *                     information needed for the loading of a sheet;
    94  *                     this class handles listening for the stream
    95  *                     loader completion and also handles charset
    96  *                     determination.
    97  */
    99 namespace mozilla {
   100 namespace css {
   102 /*********************************************
   103  * Data needed to properly load a stylesheet *
   104  *********************************************/
   106 class SheetLoadData : public nsIRunnable,
   107                       public nsIUnicharStreamLoaderObserver,
   108                       public nsIThreadObserver
   109 {
   110 public:
   111   virtual ~SheetLoadData(void);
   112   // Data for loading a sheet linked from a document
   113   SheetLoadData(Loader* aLoader,
   114                 const nsSubstring& aTitle,
   115                 nsIURI* aURI,
   116                 nsCSSStyleSheet* aSheet,
   117                 nsIStyleSheetLinkingElement* aOwningElement,
   118                 bool aIsAlternate,
   119                 nsICSSLoaderObserver* aObserver,
   120                 nsIPrincipal* aLoaderPrincipal);
   122   // Data for loading a sheet linked from an @import rule
   123   SheetLoadData(Loader* aLoader,
   124                 nsIURI* aURI,
   125                 nsCSSStyleSheet* aSheet,
   126                 SheetLoadData* aParentData,
   127                 nsICSSLoaderObserver* aObserver,
   128                 nsIPrincipal* aLoaderPrincipal);
   130   // Data for loading a non-document sheet
   131   SheetLoadData(Loader* aLoader,
   132                 nsIURI* aURI,
   133                 nsCSSStyleSheet* aSheet,
   134                 bool aSyncLoad,
   135                 bool aAllowUnsafeRules,
   136                 bool aUseSystemPrincipal,
   137                 const nsCString& aCharset,
   138                 nsICSSLoaderObserver* aObserver,
   139                 nsIPrincipal* aLoaderPrincipal);
   141   already_AddRefed<nsIURI> GetReferrerURI();
   143   void ScheduleLoadEventIfNeeded(nsresult aStatus);
   145   NS_DECL_ISUPPORTS
   146   NS_DECL_NSIRUNNABLE
   147   NS_DECL_NSITHREADOBSERVER
   148   NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
   150   // Hold a ref to the CSSLoader so we can call back to it to let it
   151   // know the load finished
   152   Loader*                    mLoader; // strong ref
   154   // Title needed to pull datas out of the pending datas table when
   155   // the preferred title is changed
   156   nsString                   mTitle;
   158   // Charset we decided to use for the sheet
   159   nsCString                  mCharset;
   161   // URI we're loading.  Null for inline sheets
   162   nsCOMPtr<nsIURI>           mURI;
   164   // Should be 1 for non-inline sheets.
   165   uint32_t                   mLineNumber;
   167   // The sheet we're loading data for
   168   nsRefPtr<nsCSSStyleSheet>  mSheet;
   170   // Linked list of datas for the same URI as us
   171   SheetLoadData*             mNext;  // strong ref
   173   // Load data for the sheet that @import-ed us if we were @import-ed
   174   // during the parse
   175   SheetLoadData*             mParentData;  // strong ref
   177   // Number of sheets we @import-ed that are still loading
   178   uint32_t                   mPendingChildren;
   180   // mSyncLoad is true when the load needs to be synchronous -- right
   181   // now only for LoadSheetSync and children of sync loads.
   182   bool                       mSyncLoad : 1;
   184   // mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
   185   // LoadSheet or an @import from such a sheet.  Non-document sheet loads can
   186   // proceed even if we have no document.
   187   bool                       mIsNonDocumentSheet : 1;
   189   // mIsLoading is true from the moment we are placed in the loader's
   190   // "loading datas" table (right after the async channel is opened)
   191   // to the moment we are removed from said table (due to the load
   192   // completing or being cancelled).
   193   bool                       mIsLoading : 1;
   195   // mIsCancelled is set to true when a sheet load is stopped by
   196   // Stop() or StopLoadingSheet() (which was removed in Bug 556446).
   197   // SheetLoadData::OnStreamComplete() checks this to avoid parsing
   198   // sheets that have been cancelled and such.
   199   bool                       mIsCancelled : 1;
   201   // mMustNotify is true if the load data is being loaded async and
   202   // the original function call that started the load has returned.
   203   // This applies only to observer notifications; load/error events
   204   // are fired for any SheetLoadData that has a non-null
   205   // mOwningElement.
   206   bool                       mMustNotify : 1;
   208   // mWasAlternate is true if the sheet was an alternate when the load data was
   209   // created.
   210   bool                       mWasAlternate : 1;
   212   // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed
   213   // in the loaded sheet.
   214   bool                       mAllowUnsafeRules : 1;
   216   // mUseSystemPrincipal is true if the system principal should be used for
   217   // this sheet, no matter what the channel principal is.  Only true for sync
   218   // loads.
   219   bool                       mUseSystemPrincipal : 1;
   221   // If true, this SheetLoadData is being used as a way to handle
   222   // async observer notification for an already-complete sheet.
   223   bool                       mSheetAlreadyComplete : 1;
   225   // This is the element that imported the sheet.  Needed to get the
   226   // charset set on it and to fire load/error events.
   227   nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
   229   // The observer that wishes to be notified of load completion
   230   nsCOMPtr<nsICSSLoaderObserver>        mObserver;
   232   // The principal that identifies who started loading us.
   233   nsCOMPtr<nsIPrincipal>                mLoaderPrincipal;
   235   // The charset to use if the transport and sheet don't indicate one.
   236   // May be empty.  Must be empty if mOwningElement is non-null.
   237   nsCString                             mCharsetHint;
   239   // The status our load ended up with; this determines whether we
   240   // should fire error events or load events.  This gets initialized
   241   // by ScheduleLoadEventIfNeeded, and is only used after that has
   242   // been called.
   243   nsresult                              mStatus;
   245 private:
   246   void FireLoadEvent(nsIThreadInternal* aThread);
   247 };
   249 #ifdef MOZ_LOGGING
   250 // #define FORCE_PR_LOG /* Allow logging in the release build */
   251 #endif /* MOZ_LOGGING */
   252 #include "prlog.h"
   254 #ifdef PR_LOGGING
   255 static PRLogModuleInfo *
   256 GetLoaderLog()
   257 {
   258   static PRLogModuleInfo *sLog;
   259   if (!sLog)
   260     sLog = PR_NewLogModule("nsCSSLoader");
   261   return sLog;
   262 }
   263 #endif /* PR_LOGGING */
   265 #define LOG_FORCE(args) PR_LOG(GetLoaderLog(), PR_LOG_ALWAYS, args)
   266 #define LOG_ERROR(args) PR_LOG(GetLoaderLog(), PR_LOG_ERROR, args)
   267 #define LOG_WARN(args) PR_LOG(GetLoaderLog(), PR_LOG_WARNING, args)
   268 #define LOG_DEBUG(args) PR_LOG(GetLoaderLog(), PR_LOG_DEBUG, args)
   269 #define LOG(args) LOG_DEBUG(args)
   271 #define LOG_FORCE_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ALWAYS)
   272 #define LOG_ERROR_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ERROR)
   273 #define LOG_WARN_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_WARNING)
   274 #define LOG_DEBUG_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_DEBUG)
   275 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
   277 #ifdef PR_LOGGING
   278 #define LOG_URI(format, uri)                        \
   279   PR_BEGIN_MACRO                                    \
   280     NS_ASSERTION(uri, "Logging null uri");          \
   281     if (LOG_ENABLED()) {                            \
   282       nsAutoCString _logURISpec;                    \
   283       uri->GetSpec(_logURISpec);                    \
   284       LOG((format, _logURISpec.get()));             \
   285     }                                               \
   286   PR_END_MACRO
   287 #else // PR_LOGGING
   288 #define LOG_URI(format, uri)
   289 #endif // PR_LOGGING
   291 // And some convenience strings...
   292 #ifdef PR_LOGGING
   293 static const char* const gStateStrings[] = {
   294   "eSheetStateUnknown",
   295   "eSheetNeedsParser",
   296   "eSheetPending",
   297   "eSheetLoading",
   298   "eSheetComplete"
   299 };
   300 #endif
   302 /********************************
   303  * SheetLoadData implementation *
   304  ********************************/
   305 NS_IMPL_ISUPPORTS(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable,
   306                   nsIThreadObserver)
   308 SheetLoadData::SheetLoadData(Loader* aLoader,
   309                              const nsSubstring& aTitle,
   310                              nsIURI* aURI,
   311                              nsCSSStyleSheet* aSheet,
   312                              nsIStyleSheetLinkingElement* aOwningElement,
   313                              bool aIsAlternate,
   314                              nsICSSLoaderObserver* aObserver,
   315                              nsIPrincipal* aLoaderPrincipal)
   316   : mLoader(aLoader),
   317     mTitle(aTitle),
   318     mURI(aURI),
   319     mLineNumber(1),
   320     mSheet(aSheet),
   321     mNext(nullptr),
   322     mParentData(nullptr),
   323     mPendingChildren(0),
   324     mSyncLoad(false),
   325     mIsNonDocumentSheet(false),
   326     mIsLoading(false),
   327     mIsCancelled(false),
   328     mMustNotify(false),
   329     mWasAlternate(aIsAlternate),
   330     mAllowUnsafeRules(false),
   331     mUseSystemPrincipal(false),
   332     mSheetAlreadyComplete(false),
   333     mOwningElement(aOwningElement),
   334     mObserver(aObserver),
   335     mLoaderPrincipal(aLoaderPrincipal)
   336 {
   337   NS_PRECONDITION(mLoader, "Must have a loader!");
   338   NS_ADDREF(mLoader);
   339 }
   341 SheetLoadData::SheetLoadData(Loader* aLoader,
   342                              nsIURI* aURI,
   343                              nsCSSStyleSheet* aSheet,
   344                              SheetLoadData* aParentData,
   345                              nsICSSLoaderObserver* aObserver,
   346                              nsIPrincipal* aLoaderPrincipal)
   347   : mLoader(aLoader),
   348     mURI(aURI),
   349     mLineNumber(1),
   350     mSheet(aSheet),
   351     mNext(nullptr),
   352     mParentData(aParentData),
   353     mPendingChildren(0),
   354     mSyncLoad(false),
   355     mIsNonDocumentSheet(false),
   356     mIsLoading(false),
   357     mIsCancelled(false),
   358     mMustNotify(false),
   359     mWasAlternate(false),
   360     mAllowUnsafeRules(false),
   361     mUseSystemPrincipal(false),
   362     mSheetAlreadyComplete(false),
   363     mOwningElement(nullptr),
   364     mObserver(aObserver),
   365     mLoaderPrincipal(aLoaderPrincipal)
   366 {
   367   NS_PRECONDITION(mLoader, "Must have a loader!");
   368   NS_ADDREF(mLoader);
   369   if (mParentData) {
   370     NS_ADDREF(mParentData);
   371     mSyncLoad = mParentData->mSyncLoad;
   372     mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
   373     mAllowUnsafeRules = mParentData->mAllowUnsafeRules;
   374     mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
   375     ++(mParentData->mPendingChildren);
   376   }
   378   NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
   379                    "Shouldn't use system principal for async loads");
   380 }
   382 SheetLoadData::SheetLoadData(Loader* aLoader,
   383                              nsIURI* aURI,
   384                              nsCSSStyleSheet* aSheet,
   385                              bool aSyncLoad,
   386                              bool aAllowUnsafeRules,
   387                              bool aUseSystemPrincipal,
   388                              const nsCString& aCharset,
   389                              nsICSSLoaderObserver* aObserver,
   390                              nsIPrincipal* aLoaderPrincipal)
   391   : mLoader(aLoader),
   392     mURI(aURI),
   393     mLineNumber(1),
   394     mSheet(aSheet),
   395     mNext(nullptr),
   396     mParentData(nullptr),
   397     mPendingChildren(0),
   398     mSyncLoad(aSyncLoad),
   399     mIsNonDocumentSheet(true),
   400     mIsLoading(false),
   401     mIsCancelled(false),
   402     mMustNotify(false),
   403     mWasAlternate(false),
   404     mAllowUnsafeRules(aAllowUnsafeRules),
   405     mUseSystemPrincipal(aUseSystemPrincipal),
   406     mSheetAlreadyComplete(false),
   407     mOwningElement(nullptr),
   408     mObserver(aObserver),
   409     mLoaderPrincipal(aLoaderPrincipal),
   410     mCharsetHint(aCharset)
   411 {
   412   NS_PRECONDITION(mLoader, "Must have a loader!");
   413   NS_ADDREF(mLoader);
   415   NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
   416                    "Shouldn't use system principal for async loads");
   417 }
   419 SheetLoadData::~SheetLoadData()
   420 {
   421   NS_RELEASE(mLoader);
   422   NS_IF_RELEASE(mParentData);
   423   NS_IF_RELEASE(mNext);
   424 }
   426 NS_IMETHODIMP
   427 SheetLoadData::Run()
   428 {
   429   mLoader->HandleLoadEvent(this);
   430   return NS_OK;
   431 }
   433 NS_IMETHODIMP
   434 SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
   435 {
   436   return NS_OK;
   437 }
   439 NS_IMETHODIMP
   440 SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
   441                                   bool aMayWait,
   442                                   uint32_t aRecursionDepth)
   443 {
   444   // We want to fire our load even before or after event processing,
   445   // whichever comes first.
   446   FireLoadEvent(aThread);
   447   return NS_OK;
   448 }
   450 NS_IMETHODIMP
   451 SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
   452                                      uint32_t aRecursionDepth,
   453                                      bool aEventWasProcessed)
   454 {
   455   // We want to fire our load even before or after event processing,
   456   // whichever comes first.
   457   FireLoadEvent(aThread);
   458   return NS_OK;
   459 }
   461 void
   462 SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
   463 {
   465   // First remove ourselves as a thread observer.  But we need to keep
   466   // ourselves alive while doing that!
   467   nsRefPtr<SheetLoadData> kungFuDeathGrip(this);
   468   aThread->RemoveObserver(this);
   470   // Now fire the event
   471   nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
   472   NS_ASSERTION(node, "How did that happen???");
   474   nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
   475                                        node,
   476                                        NS_SUCCEEDED(mStatus) ?
   477                                          NS_LITERAL_STRING("load") :
   478                                          NS_LITERAL_STRING("error"),
   479                                        false, false);
   481   // And unblock onload
   482   if (mLoader->mDocument) {
   483     mLoader->mDocument->UnblockOnload(true);
   484   }  
   485 }
   487 void
   488 SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
   489 {
   490   if (!mOwningElement) {
   491     return;
   492   }
   494   mStatus = aStatus;
   496   nsCOMPtr<nsIThread> thread = do_GetMainThread();
   497   nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
   498   if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
   499     // Make sure to block onload here
   500     if (mLoader->mDocument) {
   501       mLoader->mDocument->BlockOnload();
   502     }
   503   }
   504 }
   506 /*************************
   507  * Loader Implementation *
   508  *************************/
   510 Loader::Loader(void)
   511   : mDocument(nullptr)
   512   , mDatasToNotifyOn(0)
   513   , mCompatMode(eCompatibility_FullStandards)
   514   , mEnabled(true)
   515 #ifdef DEBUG
   516   , mSyncCallback(false)
   517 #endif
   518 {
   519 }
   521 Loader::Loader(nsIDocument* aDocument)
   522   : mDocument(aDocument)
   523   , mDatasToNotifyOn(0)
   524   , mCompatMode(eCompatibility_FullStandards)
   525   , mEnabled(true)
   526 #ifdef DEBUG
   527   , mSyncCallback(false)
   528 #endif
   529 {
   530   // We can just use the preferred set, since there are no sheets in the
   531   // document yet (if there are, how did they get there? _we_ load the sheets!)
   532   // and hence the selected set makes no sense at this time.
   533   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
   534   if (domDoc) {
   535     domDoc->GetPreferredStyleSheetSet(mPreferredSheet);
   536   }
   537 }
   539 Loader::~Loader()
   540 {
   541   NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0,
   542                "How did we get destroyed when there are loading data?");
   543   NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0,
   544                "How did we get destroyed when there are pending data?");
   545   // Note: no real need to revoke our stylesheet loaded events -- they
   546   // hold strong references to us, so if we're going away that means
   547   // they're all done.
   548 }
   550 void
   551 Loader::DropDocumentReference(void)
   552 {
   553   mDocument = nullptr;
   554   // Flush out pending datas just so we don't leak by accident.  These
   555   // loads should short-circuit through the mDocument check in
   556   // LoadSheet and just end up in SheetComplete immediately
   557   if (mSheets) {
   558     StartAlternateLoads();
   559   }
   560 }
   562 static PLDHashOperator
   563 CollectNonAlternates(URIPrincipalAndCORSModeHashKey *aKey,
   564                      SheetLoadData* &aData,
   565                      void* aClosure)
   566 {
   567   NS_PRECONDITION(aData, "Must have a data");
   568   NS_PRECONDITION(aClosure, "Must have an array");
   570   // Note that we don't want to affect what the selected style set is,
   571   // so use true for aHasAlternateRel.
   572   if (aData->mLoader->IsAlternate(aData->mTitle, true)) {
   573     return PL_DHASH_NEXT;
   574   }
   576   static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
   577   return PL_DHASH_REMOVE;
   578 }
   580 nsresult
   581 Loader::SetPreferredSheet(const nsAString& aTitle)
   582 {
   583 #ifdef DEBUG
   584   nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
   585   if (doc) {
   586     nsAutoString currentPreferred;
   587     doc->GetLastStyleSheetSet(currentPreferred);
   588     if (DOMStringIsNull(currentPreferred)) {
   589       doc->GetPreferredStyleSheetSet(currentPreferred);
   590     }
   591     NS_ASSERTION(currentPreferred.Equals(aTitle),
   592                  "Unexpected argument to SetPreferredSheet");
   593   }
   594 #endif
   596   mPreferredSheet = aTitle;
   598   // start any pending alternates that aren't alternates anymore
   599   if (mSheets) {
   600     LoadDataArray arr(mSheets->mPendingDatas.Count());
   601     mSheets->mPendingDatas.Enumerate(CollectNonAlternates, &arr);
   603     mDatasToNotifyOn += arr.Length();
   604     for (uint32_t i = 0; i < arr.Length(); ++i) {
   605       --mDatasToNotifyOn;
   606       LoadSheet(arr[i], eSheetNeedsParser);
   607     }
   608   }
   610   return NS_OK;
   611 }
   613 static const char kCharsetSym[] = "@charset \"";
   615 static bool GetCharsetFromData(const char* aStyleSheetData,
   616                                uint32_t aDataLength,
   617                                nsACString& aCharset)
   618 {
   619   aCharset.Truncate();
   620   if (aDataLength <= sizeof(kCharsetSym) - 1)
   621     return false;
   623   if (strncmp(aStyleSheetData,
   624               kCharsetSym,
   625               sizeof(kCharsetSym) - 1)) {
   626     return false;
   627   }
   629   for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
   630     char c = aStyleSheetData[i];
   631     if (c == '"') {
   632       ++i;
   633       if (i < aDataLength && aStyleSheetData[i] == ';') {
   634         return true;
   635       }
   636       // fail
   637       break;
   638     }
   639     aCharset.Append(c);
   640   }
   642   // Did not see end quote or semicolon
   643   aCharset.Truncate();
   644   return false;
   645 }
   647 NS_IMETHODIMP
   648 SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
   649                                   nsISupports* aContext,
   650                                   nsACString const& aSegment,
   651                                   nsACString& aCharset)
   652 {
   653   NS_PRECONDITION(!mOwningElement || mCharsetHint.IsEmpty(),
   654                   "Can't have element _and_ charset hint");
   656   LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
   658   // The precedence is (per CSS3 Syntax 2012-11-08 ED):
   659   // BOM
   660   // Channel
   661   // @charset rule
   662   // charset attribute on the referrer
   663   // encoding of the referrer
   664   // UTF-8
   666   aCharset.Truncate();
   668   if (nsContentUtils::CheckForBOM((const unsigned char*)aSegment.BeginReading(),
   669                                   aSegment.Length(),
   670                                   aCharset)) {
   671     // aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8"
   672     // which will swallow the BOM.
   673     mCharset.Assign(aCharset);
   674 #ifdef PR_LOGGING
   675     LOG(("  Setting from BOM to: %s", PromiseFlatCString(aCharset).get()));
   676 #endif
   677     return NS_OK;
   678   }
   680   nsCOMPtr<nsIChannel> channel;
   681   nsAutoCString specified;
   682   aLoader->GetChannel(getter_AddRefs(channel));
   683   if (channel) {
   684     channel->GetContentCharset(specified);
   685     if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
   686       mCharset.Assign(aCharset);
   687 #ifdef PR_LOGGING
   688       LOG(("  Setting from HTTP to: %s", PromiseFlatCString(aCharset).get()));
   689 #endif
   690       return NS_OK;
   691     }
   692   }
   694   if (GetCharsetFromData(aSegment.BeginReading(),
   695                          aSegment.Length(),
   696                          specified)) {
   697     if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
   698       // FindEncodingForLabel currently never returns UTF-16LE but will
   699       // probably change to never return UTF-16 instead, so check both here
   700       // to avoid relying on the exact behavior.
   701       if (aCharset.EqualsLiteral("UTF-16") ||
   702           aCharset.EqualsLiteral("UTF-16BE") ||
   703           aCharset.EqualsLiteral("UTF-16LE")) {
   704         // Be consistent with HTML <meta> handling in face of impossibility.
   705         // When the @charset rule itself evidently was not UTF-16-encoded,
   706         // it saying UTF-16 has to be a lie.
   707         aCharset.AssignLiteral("UTF-8");
   708       }
   709       mCharset.Assign(aCharset);
   710 #ifdef PR_LOGGING
   711       LOG(("  Setting from @charset rule to: %s",
   712           PromiseFlatCString(aCharset).get()));
   713 #endif
   714       return NS_OK;
   715     }
   716   }
   718   // Now try the charset on the <link> or processing instruction
   719   // that loaded us
   720   if (mOwningElement) {
   721     nsAutoString specified16;
   722     mOwningElement->GetCharset(specified16);
   723     if (EncodingUtils::FindEncodingForLabel(specified16, aCharset)) {
   724       mCharset.Assign(aCharset);
   725 #ifdef PR_LOGGING
   726       LOG(("  Setting from charset attribute to: %s",
   727           PromiseFlatCString(aCharset).get()));
   728 #endif
   729       return NS_OK;
   730     }
   731   }
   733   // In the preload case, the value of the charset attribute on <link> comes
   734   // in via mCharsetHint instead.
   735   if (EncodingUtils::FindEncodingForLabel(mCharsetHint, aCharset)) {
   736     mCharset.Assign(aCharset);
   737 #ifdef PR_LOGGING
   738       LOG(("  Setting from charset attribute (preload case) to: %s",
   739           PromiseFlatCString(aCharset).get()));
   740 #endif
   741     return NS_OK;
   742   }
   744   // Try charset from the parent stylesheet.
   745   if (mParentData) {
   746     aCharset = mParentData->mCharset;
   747     if (!aCharset.IsEmpty()) {
   748       mCharset.Assign(aCharset);
   749 #ifdef PR_LOGGING
   750       LOG(("  Setting from parent sheet to: %s",
   751           PromiseFlatCString(aCharset).get()));
   752 #endif
   753       return NS_OK;
   754     }
   755   }
   757   if (mLoader->mDocument) {
   758     // no useful data on charset.  Try the document charset.
   759     aCharset = mLoader->mDocument->GetDocumentCharacterSet();
   760     MOZ_ASSERT(!aCharset.IsEmpty());
   761     mCharset.Assign(aCharset);
   762 #ifdef PR_LOGGING
   763     LOG(("  Setting from document to: %s", PromiseFlatCString(aCharset).get()));
   764 #endif
   765     return NS_OK;
   766   }
   768   aCharset.AssignLiteral("UTF-8");
   769   mCharset = aCharset;
   770 #ifdef PR_LOGGING
   771   LOG(("  Setting from default to: %s", PromiseFlatCString(aCharset).get()));
   772 #endif
   773   return NS_OK;
   774 }
   776 already_AddRefed<nsIURI>
   777 SheetLoadData::GetReferrerURI()
   778 {
   779   nsCOMPtr<nsIURI> uri;
   780   if (mParentData)
   781     uri = mParentData->mSheet->GetSheetURI();
   782   if (!uri && mLoader->mDocument)
   783     uri = mLoader->mDocument->GetDocumentURI();
   784   return uri.forget();
   785 }
   787 /*
   788  * Here we need to check that the load did not give us an http error
   789  * page and check the mimetype on the channel to make sure we're not
   790  * loading non-text/css data in standards mode.
   791  */
   792 NS_IMETHODIMP
   793 SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
   794                                 nsISupports* aContext,
   795                                 nsresult aStatus,
   796                                 const nsAString& aBuffer)
   797 {
   798   LOG(("SheetLoadData::OnStreamComplete"));
   799   NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
   801   if (mIsCancelled) {
   802     // Just return.  Don't call SheetComplete -- it's already been
   803     // called and calling it again will lead to an extra NS_RELEASE on
   804     // this data and a likely crash.
   805     return NS_OK;
   806   }
   808   if (!mLoader->mDocument && !mIsNonDocumentSheet) {
   809     // Sorry, we don't care about this load anymore
   810     LOG_WARN(("  No document and not non-document sheet; dropping load"));
   811     mLoader->SheetComplete(this, NS_BINDING_ABORTED);
   812     return NS_OK;
   813   }
   815   if (NS_FAILED(aStatus)) {
   816     LOG_WARN(("  Load failed: status 0x%x", aStatus));
   817     mLoader->SheetComplete(this, aStatus);
   818     return NS_OK;
   819   }
   821   nsCOMPtr<nsIChannel> channel;
   822   nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
   823   if (NS_FAILED(result)) {
   824     LOG_WARN(("  No channel from loader"));
   825     mLoader->SheetComplete(this, result);
   826     return NS_OK;
   827   }
   829   nsCOMPtr<nsIURI> originalURI;
   830   channel->GetOriginalURI(getter_AddRefs(originalURI));
   832   // If the channel's original URI is "chrome:", we want that, since
   833   // the observer code in nsXULPrototypeCache depends on chrome stylesheets
   834   // having a chrome URI.  (Whether or not chrome stylesheets come through
   835   // this codepath seems nondeterministic.)
   836   // Otherwise we want the potentially-HTTP-redirected URI.
   837   nsCOMPtr<nsIURI> channelURI;
   838   NS_GetFinalChannelURI(channel, getter_AddRefs(channelURI));
   840   if (!channelURI || !originalURI) {
   841     NS_ERROR("Someone just violated the nsIRequest contract");
   842     LOG_WARN(("  Channel without a URI.  Bad!"));
   843     mLoader->SheetComplete(this, NS_ERROR_UNEXPECTED);
   844     return NS_OK;
   845   }
   847   nsCOMPtr<nsIPrincipal> principal;
   848   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   849   result = NS_ERROR_NOT_AVAILABLE;
   850   if (secMan) {  // Could be null if we already shut down
   851     if (mUseSystemPrincipal) {
   852       result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
   853     } else {
   854       result = secMan->GetChannelPrincipal(channel, getter_AddRefs(principal));
   855     }
   856   }
   858   if (NS_FAILED(result)) {
   859     LOG_WARN(("  Couldn't get principal"));
   860     mLoader->SheetComplete(this, result);
   861     return NS_OK;
   862   }
   864   mSheet->SetPrincipal(principal);
   866   // If it's an HTTP channel, we want to make sure this is not an
   867   // error document we got.
   868   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   869   if (httpChannel) {
   870     bool requestSucceeded;
   871     result = httpChannel->GetRequestSucceeded(&requestSucceeded);
   872     if (NS_SUCCEEDED(result) && !requestSucceeded) {
   873       LOG(("  Load returned an error page"));
   874       mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
   875       return NS_OK;
   876     }
   877   }
   879   nsAutoCString contentType;
   880   if (channel) {
   881     channel->GetContentType(contentType);
   882   }
   884   // In standards mode, a style sheet must have one of these MIME
   885   // types to be processed at all.  In quirks mode, we accept any
   886   // MIME type, but only if the style sheet is same-origin with the
   887   // requesting document or parent sheet.  See bug 524223.
   889   bool validType = contentType.EqualsLiteral("text/css") ||
   890     contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
   891     contentType.IsEmpty();
   893   if (!validType) {
   894     const char *errorMessage;
   895     uint32_t errorFlag;
   896     bool sameOrigin = true;
   898     if (mLoaderPrincipal) {
   899       bool subsumed;
   900       result = mLoaderPrincipal->Subsumes(principal, &subsumed);
   901       if (NS_FAILED(result) || !subsumed) {
   902         sameOrigin = false;
   903       }
   904     }
   906     if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
   907       errorMessage = "MimeNotCssWarn";
   908       errorFlag = nsIScriptError::warningFlag;
   909     } else {
   910       errorMessage = "MimeNotCss";
   911       errorFlag = nsIScriptError::errorFlag;
   912     }
   914     nsAutoCString spec;
   915     channelURI->GetSpec(spec);
   917     const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec);
   918     const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
   919     const char16_t *strings[] = { specUTF16.get(), ctypeUTF16.get() };
   921     nsCOMPtr<nsIURI> referrer = GetReferrerURI();
   922     nsContentUtils::ReportToConsole(errorFlag,
   923                                     NS_LITERAL_CSTRING("CSS Loader"),
   924                                     mLoader->mDocument,
   925                                     nsContentUtils::eCSS_PROPERTIES,
   926                                     errorMessage,
   927                                     strings, ArrayLength(strings),
   928                                     referrer);
   930     if (errorFlag == nsIScriptError::errorFlag) {
   931       LOG_WARN(("  Ignoring sheet with improper MIME type %s",
   932                 contentType.get()));
   933       mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
   934       return NS_OK;
   935     }
   936   }
   938   // Enough to set the URIs on mSheet, since any sibling datas we have share
   939   // the same mInner as mSheet and will thus get the same URI.
   940   mSheet->SetURIs(channelURI, originalURI, channelURI);
   942   bool completed;
   943   result = mLoader->ParseSheet(aBuffer, this, completed);
   944   NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
   945   return result;
   946 }
   948 bool
   949 Loader::IsAlternate(const nsAString& aTitle, bool aHasAlternateRel)
   950 {
   951   // A sheet is alternate if it has a nonempty title that doesn't match the
   952   // currently selected style set.  But if there _is_ no currently selected
   953   // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
   954   // is nonempty, we should select the style set corresponding to aTitle, since
   955   // that's a preferred sheet.
   956   if (aTitle.IsEmpty()) {
   957     return false;
   958   }
   960   if (!aHasAlternateRel && mDocument && mPreferredSheet.IsEmpty()) {
   961     // There's no preferred set yet, and we now have a sheet with a title.
   962     // Make that be the preferred set.
   963     mDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, aTitle);
   964     // We're definitely not an alternate
   965     return false;
   966   }
   968   return !aTitle.Equals(mPreferredSheet);
   969 }
   971 /* static */ PLDHashOperator
   972 Loader::RemoveEntriesWithURI(URIPrincipalAndCORSModeHashKey* aKey,
   973                              nsRefPtr<nsCSSStyleSheet> &aSheet,
   974                              void* aUserData)
   975 {
   976   nsIURI* obsoleteURI = static_cast<nsIURI*>(aUserData);
   977   nsIURI* sheetURI = aKey->GetURI();
   978   bool areEqual;
   979   nsresult rv = sheetURI->Equals(obsoleteURI, &areEqual);
   980   if (NS_SUCCEEDED(rv) && areEqual) {
   981     return PL_DHASH_REMOVE;
   982   }
   983   return PL_DHASH_NEXT;
   984 }
   986 nsresult
   987 Loader::ObsoleteSheet(nsIURI* aURI)
   988 {
   989   if (!mSheets) {
   990     return NS_OK;
   991   }
   992   if (!aURI) {
   993     return NS_ERROR_INVALID_ARG;
   994   }
   995   mSheets->mCompleteSheets.Enumerate(RemoveEntriesWithURI, aURI);
   996   return NS_OK;
   997 }
   999 /**
  1000  * CheckLoadAllowed will return success if the load is allowed,
  1001  * failure otherwise.
  1003  * @param aSourcePrincipal the principal of the node or document or parent
  1004  *                         sheet loading the sheet
  1005  * @param aTargetURI the uri of the sheet to be loaded
  1006  * @param aContext the node owning the sheet.  This is the element or document
  1007  *                 owning the stylesheet (possibly indirectly, for child sheets)
  1008  */
  1009 nsresult
  1010 Loader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
  1011                          nsIURI* aTargetURI,
  1012                          nsISupports* aContext)
  1014   LOG(("css::Loader::CheckLoadAllowed"));
  1016   nsresult rv;
  1018   if (aSourcePrincipal) {
  1019     // Check with the security manager
  1020     nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
  1021     rv =
  1022       secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
  1023                                         nsIScriptSecurityManager::ALLOW_CHROME);
  1024     if (NS_FAILED(rv)) { // failure is normal here; don't warn
  1025       return rv;
  1028     LOG(("  Passed security check"));
  1030     // Check with content policy
  1032     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
  1033     rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
  1034                                    aTargetURI,
  1035                                    aSourcePrincipal,
  1036                                    aContext,
  1037                                    NS_LITERAL_CSTRING("text/css"),
  1038                                    nullptr,                     //extra param
  1039                                    &shouldLoad,
  1040                                    nsContentUtils::GetContentPolicy(),
  1041                                    nsContentUtils::GetSecurityManager());
  1043     if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
  1044       LOG(("  Load blocked by content policy"));
  1045       return NS_ERROR_CONTENT_BLOCKED;
  1049   return NS_OK;
  1052 /**
  1053  * CreateSheet() creates an nsCSSStyleSheet object for the given URI,
  1054  * if any.  If there is no URI given, we just create a new style sheet
  1055  * object.  Otherwise, we check for an existing style sheet object for
  1056  * that uri in various caches and clone it if we find it.  Cloned
  1057  * sheets will have the title/media/enabled state of the sheet they
  1058  * are clones off; make sure to call PrepareSheet() on the result of
  1059  * CreateSheet().
  1060  */
  1061 nsresult
  1062 Loader::CreateSheet(nsIURI* aURI,
  1063                     nsIContent* aLinkingContent,
  1064                     nsIPrincipal* aLoaderPrincipal,
  1065                     CORSMode aCORSMode,
  1066                     bool aSyncLoad,
  1067                     bool aHasAlternateRel,
  1068                     const nsAString& aTitle,                       
  1069                     StyleSheetState& aSheetState,
  1070                     bool *aIsAlternate,
  1071                     nsCSSStyleSheet** aSheet)
  1073   LOG(("css::Loader::CreateSheet"));
  1074   NS_PRECONDITION(aSheet, "Null out param!");
  1076   if (!mSheets) {
  1077     mSheets = new Sheets();
  1080   *aSheet = nullptr;
  1081   aSheetState = eSheetStateUnknown;
  1083   // Check the alternate state before doing anything else, because it
  1084   // can mess with our hashtables.
  1085   *aIsAlternate = IsAlternate(aTitle, aHasAlternateRel);
  1087   if (aURI) {
  1088     aSheetState = eSheetComplete;
  1089     nsRefPtr<nsCSSStyleSheet> sheet;
  1091     // First, the XUL cache
  1092 #ifdef MOZ_XUL
  1093     if (IsChromeURI(aURI)) {
  1094       nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
  1095       if (cache) {
  1096         if (cache->IsEnabled()) {
  1097           sheet = cache->GetStyleSheet(aURI);
  1098           LOG(("  From XUL cache: %p", sheet.get()));
  1102 #endif
  1104     bool fromCompleteSheets = false;
  1105     if (!sheet) {
  1106       // Then our per-document complete sheets.
  1107       URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode);
  1109       mSheets->mCompleteSheets.Get(&key, getter_AddRefs(sheet));
  1110       LOG(("  From completed: %p", sheet.get()));
  1112       fromCompleteSheets = !!sheet;
  1115     if (sheet) {
  1116       // This sheet came from the XUL cache or our per-document hashtable; it
  1117       // better be a complete sheet.
  1118       NS_ASSERTION(sheet->IsComplete(),
  1119                    "Sheet thinks it's not complete while we think it is");
  1121       // Make sure it hasn't been modified; if it has, we can't use it
  1122       if (sheet->IsModified()) {
  1123         LOG(("  Not cloning completed sheet %p because it's been modified",
  1124              sheet.get()));
  1125         sheet = nullptr;
  1126         fromCompleteSheets = false;
  1130     // Then loading sheets
  1131     if (!sheet && !aSyncLoad) {
  1132       aSheetState = eSheetLoading;
  1133       SheetLoadData* loadData = nullptr;
  1134       URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode);
  1135       mSheets->mLoadingDatas.Get(&key, &loadData);
  1136       if (loadData) {
  1137         sheet = loadData->mSheet;
  1138         LOG(("  From loading: %p", sheet.get()));
  1140 #ifdef DEBUG
  1141         bool debugEqual;
  1142         NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
  1143                      (aLoaderPrincipal && loadData->mLoaderPrincipal &&
  1144                       NS_SUCCEEDED(aLoaderPrincipal->
  1145                                    Equals(loadData->mLoaderPrincipal,
  1146                                           &debugEqual)) && debugEqual),
  1147                      "Principals should be the same");
  1148 #endif
  1151       // Then alternate sheets
  1152       if (!sheet) {
  1153         aSheetState = eSheetPending;
  1154         loadData = nullptr;
  1155         mSheets->mPendingDatas.Get(&key, &loadData);
  1156         if (loadData) {
  1157           sheet = loadData->mSheet;
  1158           LOG(("  From pending: %p", sheet.get()));
  1160 #ifdef DEBUG
  1161           bool debugEqual;
  1162           NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
  1163                        (aLoaderPrincipal && loadData->mLoaderPrincipal &&
  1164                         NS_SUCCEEDED(aLoaderPrincipal->
  1165                                      Equals(loadData->mLoaderPrincipal,
  1166                                             &debugEqual)) && debugEqual),
  1167                        "Principals should be the same");
  1168 #endif
  1173     if (sheet) {
  1174       // The sheet we have now should be either incomplete or unmodified
  1175       NS_ASSERTION(!sheet->IsModified() || !sheet->IsComplete(),
  1176                    "Unexpected modified complete sheet");
  1177       NS_ASSERTION(sheet->IsComplete() || aSheetState != eSheetComplete,
  1178                    "Sheet thinks it's not complete while we think it is");
  1180       *aSheet = sheet->Clone(nullptr, nullptr, nullptr, nullptr).take();
  1181       if (*aSheet && fromCompleteSheets &&
  1182           !sheet->GetOwnerNode() && !sheet->GetParentSheet()) {
  1183         // The sheet we're cloning isn't actually referenced by
  1184         // anyone.  Replace it in the cache, so that if our CSSOM is
  1185         // later modified we don't end up with two copies of our inner
  1186         // hanging around.
  1187         URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode);
  1188         NS_ASSERTION((*aSheet)->IsComplete(),
  1189                      "Should only be caching complete sheets");
  1190         mSheets->mCompleteSheets.Put(&key, *aSheet);
  1195   if (!*aSheet) {
  1196     aSheetState = eSheetNeedsParser;
  1197     nsIURI *sheetURI;
  1198     nsCOMPtr<nsIURI> baseURI;
  1199     nsIURI* originalURI;
  1200     if (!aURI) {
  1201       // Inline style.  Use the document's base URL so that @import in
  1202       // the inline sheet picks up the right base.
  1203       NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?");
  1204       baseURI = aLinkingContent->GetBaseURI();
  1205       sheetURI = aLinkingContent->GetDocument()->GetDocumentURI();
  1206       originalURI = nullptr;
  1207     } else {
  1208       baseURI = aURI;
  1209       sheetURI = aURI;
  1210       originalURI = aURI;
  1213     nsRefPtr<nsCSSStyleSheet> sheet = new nsCSSStyleSheet(aCORSMode);
  1214     sheet->SetURIs(sheetURI, originalURI, baseURI);
  1215     sheet.forget(aSheet);
  1218   NS_ASSERTION(*aSheet, "We should have a sheet by now!");
  1219   NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
  1220   LOG(("  State: %s", gStateStrings[aSheetState]));
  1222   return NS_OK;
  1225 /**
  1226  * PrepareSheet() handles setting the media and title on the sheet, as
  1227  * well as setting the enabled state based on the title and whether
  1228  * the sheet had "alternate" in its rel.
  1229  */
  1230 void
  1231 Loader::PrepareSheet(nsCSSStyleSheet* aSheet,
  1232                      const nsSubstring& aTitle,
  1233                      const nsSubstring& aMediaString,
  1234                      nsMediaList* aMediaList,
  1235                      Element* aScopeElement,
  1236                      bool isAlternate)
  1238   NS_PRECONDITION(aSheet, "Must have a sheet!");
  1240   nsRefPtr<nsMediaList> mediaList(aMediaList);
  1242   if (!aMediaString.IsEmpty()) {
  1243     NS_ASSERTION(!aMediaList,
  1244                  "must not provide both aMediaString and aMediaList");
  1245     mediaList = new nsMediaList();
  1247     nsCSSParser mediumParser(this);
  1249     // We have aMediaString only when linked from link elements, style
  1250     // elements, or PIs, so pass true.
  1251     mediumParser.ParseMediaList(aMediaString, nullptr, 0, mediaList, true);
  1254   aSheet->SetMedia(mediaList);
  1256   aSheet->SetTitle(aTitle);
  1257   aSheet->SetEnabled(! isAlternate);
  1258   aSheet->SetScopeElement(aScopeElement);
  1261 /**
  1262  * InsertSheetInDoc handles ordering of sheets in the document.  Here
  1263  * we have two types of sheets -- those with linking elements and
  1264  * those without.  The latter are loaded by Link: headers.
  1265  * The following constraints are observed:
  1266  * 1) Any sheet with a linking element comes after all sheets without
  1267  *    linking elements
  1268  * 2) Sheets without linking elements are inserted in the order in
  1269  *    which the inserting requests come in, since all of these are
  1270  *    inserted during header data processing in the content sink
  1271  * 3) Sheets with linking elements are ordered based on document order
  1272  *    as determined by CompareDocumentPosition.
  1273  */
  1274 nsresult
  1275 Loader::InsertSheetInDoc(nsCSSStyleSheet* aSheet,
  1276                          nsIContent* aLinkingContent,
  1277                          nsIDocument* aDocument)
  1279   LOG(("css::Loader::InsertSheetInDoc"));
  1280   NS_PRECONDITION(aSheet, "Nothing to insert");
  1281   NS_PRECONDITION(aDocument, "Must have a document to insert into");
  1283   // XXX Need to cancel pending sheet loads for this element, if any
  1285   int32_t sheetCount = aDocument->GetNumberOfStyleSheets();
  1287   /*
  1288    * Start the walk at the _end_ of the list, since in the typical
  1289    * case we'll just want to append anyway.  We want to break out of
  1290    * the loop when insertionPoint points to just before the index we
  1291    * want to insert at.  In other words, when we leave the loop
  1292    * insertionPoint is the index of the stylesheet that immediately
  1293    * precedes the one we're inserting.
  1294    */
  1295   int32_t insertionPoint;
  1296   for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) {
  1297     nsIStyleSheet *curSheet = aDocument->GetStyleSheetAt(insertionPoint);
  1298     NS_ASSERTION(curSheet, "There must be a sheet here!");
  1299     nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(curSheet);
  1300     NS_ASSERTION(domSheet, "All the \"normal\" sheets implement nsIDOMStyleSheet");
  1301     nsCOMPtr<nsIDOMNode> sheetOwner;
  1302     domSheet->GetOwnerNode(getter_AddRefs(sheetOwner));
  1303     if (sheetOwner && !aLinkingContent) {
  1304       // Keep moving; all sheets with a sheetOwner come after all
  1305       // sheets without a linkingNode
  1306       continue;
  1309     if (!sheetOwner) {
  1310       // Aha!  The current sheet has no sheet owner, so we want to
  1311       // insert after it no matter whether we have a linkingNode
  1312       break;
  1315     nsCOMPtr<nsINode> sheetOwnerNode = do_QueryInterface(sheetOwner);
  1316     NS_ASSERTION(aLinkingContent != sheetOwnerNode,
  1317                  "Why do we still have our old sheet?");
  1319     // Have to compare
  1320     if (nsContentUtils::PositionIsBefore(sheetOwnerNode, aLinkingContent)) {
  1321       // The current sheet comes before us, and it better be the first
  1322       // such, because now we break
  1323       break;
  1327   ++insertionPoint; // adjust the index to the spot we want to insert in
  1329   // XXX <meta> elements do not implement nsIStyleSheetLinkingElement;
  1330   // need to fix this for them to be ordered correctly.
  1331   nsCOMPtr<nsIStyleSheetLinkingElement>
  1332     linkingElement = do_QueryInterface(aLinkingContent);
  1333   if (linkingElement) {
  1334     linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
  1337   aDocument->BeginUpdate(UPDATE_STYLE);
  1338   aDocument->InsertStyleSheetAt(aSheet, insertionPoint);
  1339   aDocument->EndUpdate(UPDATE_STYLE);
  1340   LOG(("  Inserting into document at position %d", insertionPoint));
  1342   return NS_OK;
  1345 /**
  1346  * InsertChildSheet handles ordering of @import-ed sheet in their
  1347  * parent sheets.  Here we want to just insert based on order of the
  1348  * @import rules that imported the sheets.  In theory we can't just
  1349  * append to the end because the CSSOM can insert @import rules.  In
  1350  * practice, we get the call to load the child sheet before the CSSOM
  1351  * has finished inserting the @import rule, so we have no idea where
  1352  * to put it anyway.  So just append for now.
  1353  */
  1354 nsresult
  1355 Loader::InsertChildSheet(nsCSSStyleSheet* aSheet,
  1356                          nsCSSStyleSheet* aParentSheet,
  1357                          ImportRule* aParentRule)
  1359   LOG(("css::Loader::InsertChildSheet"));
  1360   NS_PRECONDITION(aSheet, "Nothing to insert");
  1361   NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
  1362   NS_PRECONDITION(aParentSheet, "How did we get imported?");
  1364   // child sheets should always start out enabled, even if they got
  1365   // cloned off of top-level sheets which were disabled
  1366   aSheet->SetEnabled(true);
  1368   aParentSheet->AppendStyleSheet(aSheet);
  1369   aParentRule->SetSheet(aSheet); // This sets the ownerRule on the sheet
  1371   LOG(("  Inserting into parent sheet"));
  1372   //  LOG(("  Inserting into parent sheet at position %d", insertionPoint));
  1374   return NS_OK;
  1377 /**
  1378  * LoadSheet handles the actual load of a sheet.  If the load is
  1379  * supposed to be synchronous it just opens a channel synchronously
  1380  * using the given uri, wraps the resulting stream in a converter
  1381  * stream and calls ParseSheet.  Otherwise it tries to look for an
  1382  * existing load for this URI and piggyback on it.  Failing all that,
  1383  * a new load is kicked off asynchronously.
  1384  */
  1385 nsresult
  1386 Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
  1388   LOG(("css::Loader::LoadSheet"));
  1389   NS_PRECONDITION(aLoadData, "Need a load data");
  1390   NS_PRECONDITION(aLoadData->mURI, "Need a URI to load");
  1391   NS_PRECONDITION(aLoadData->mSheet, "Need a sheet to load into");
  1392   NS_PRECONDITION(aSheetState != eSheetComplete, "Why bother?");
  1393   NS_PRECONDITION(!aLoadData->mUseSystemPrincipal || aLoadData->mSyncLoad,
  1394                   "Shouldn't use system principal for async loads");
  1395   NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
  1397   LOG_URI("  Load from: '%s'", aLoadData->mURI);
  1399   nsresult rv = NS_OK;
  1401   if (!mDocument && !aLoadData->mIsNonDocumentSheet) {
  1402     // No point starting the load; just release all the data and such.
  1403     LOG_WARN(("  No document and not non-document sheet; pre-dropping load"));
  1404     SheetComplete(aLoadData, NS_BINDING_ABORTED);
  1405     return NS_BINDING_ABORTED;
  1408   if (aLoadData->mSyncLoad) {
  1409     LOG(("  Synchronous load"));
  1410     NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
  1411     NS_ASSERTION(aSheetState == eSheetNeedsParser,
  1412                  "Sync loads can't reuse existing async loads");
  1414     // Create a nsIUnicharStreamLoader instance to which we will feed
  1415     // the data from the sync load.  Do this before creating the
  1416     // channel to make error recovery simpler.
  1417     nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
  1418     rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
  1419     if (NS_FAILED(rv)) {
  1420       LOG_ERROR(("  Failed to create stream loader for sync load"));
  1421       SheetComplete(aLoadData, rv);
  1422       return rv;
  1425     if (mDocument) {
  1426       mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
  1427                               nsINetworkSeer::LEARN_LOAD_SUBRESOURCE,
  1428                               mDocument);
  1431     // Just load it
  1432     nsCOMPtr<nsIInputStream> stream;
  1433     nsCOMPtr<nsIChannel> channel;
  1434     rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI, nullptr,
  1435                     nullptr, nullptr, nsIRequest::LOAD_NORMAL,
  1436                     getter_AddRefs(channel));
  1437     if (NS_FAILED(rv)) {
  1438       LOG_ERROR(("  Failed to open URI synchronously"));
  1439       SheetComplete(aLoadData, rv);
  1440       return rv;
  1443     NS_ASSERTION(channel, "NS_OpenURI lied?");
  1445     // Force UA sheets to be UTF-8.
  1446     // XXX this is only necessary because the default in
  1447     // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
  1448     channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
  1450     // Manually feed the streamloader the contents of the stream we
  1451     // got from NS_OpenURI.  This will call back into OnStreamComplete
  1452     // and thence to ParseSheet.  Regardless of whether this fails,
  1453     // SheetComplete has been called.
  1454     return nsSyncLoadService::PushSyncStreamToListener(stream,
  1455                                                        streamLoader,
  1456                                                        channel);
  1459   SheetLoadData* existingData = nullptr;
  1461   URIPrincipalAndCORSModeHashKey key(aLoadData->mURI,
  1462                                      aLoadData->mLoaderPrincipal,
  1463                                      aLoadData->mSheet->GetCORSMode());
  1464   if (aSheetState == eSheetLoading) {
  1465     mSheets->mLoadingDatas.Get(&key, &existingData);
  1466     NS_ASSERTION(existingData, "CreateSheet lied about the state");
  1468   else if (aSheetState == eSheetPending){
  1469     mSheets->mPendingDatas.Get(&key, &existingData);
  1470     NS_ASSERTION(existingData, "CreateSheet lied about the state");
  1473   if (existingData) {
  1474     LOG(("  Glomming on to existing load"));
  1475     SheetLoadData* data = existingData;
  1476     while (data->mNext) {
  1477       data = data->mNext;
  1479     data->mNext = aLoadData; // transfer ownership
  1480     if (aSheetState == eSheetPending && !aLoadData->mWasAlternate) {
  1481       // Kick the load off; someone cares about it right away
  1483 #ifdef DEBUG
  1484       SheetLoadData* removedData;
  1485       NS_ASSERTION(mSheets->mPendingDatas.Get(&key, &removedData) &&
  1486                    removedData == existingData,
  1487                    "Bad pending table.");
  1488 #endif
  1490       mSheets->mPendingDatas.Remove(&key);
  1492       LOG(("  Forcing load of pending data"));
  1493       return LoadSheet(existingData, eSheetNeedsParser);
  1495     // All done here; once the load completes we'll be marked complete
  1496     // automatically
  1497     return NS_OK;
  1500 #ifdef DEBUG
  1501   mSyncCallback = true;
  1502 #endif
  1503   nsCOMPtr<nsILoadGroup> loadGroup;
  1504   // Content Security Policy information to pass into channel
  1505   nsCOMPtr<nsIChannelPolicy> channelPolicy;
  1506   if (mDocument) {
  1507     loadGroup = mDocument->GetDocumentLoadGroup();
  1508     NS_ASSERTION(loadGroup,
  1509                  "No loadgroup for stylesheet; onload will fire early");
  1510     nsCOMPtr<nsIContentSecurityPolicy> csp;
  1511     rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
  1512     NS_ENSURE_SUCCESS(rv, rv);
  1513     if (csp) {
  1514       channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
  1515       channelPolicy->SetContentSecurityPolicy(csp);
  1516       channelPolicy->SetLoadType(nsIContentPolicy::TYPE_STYLESHEET);
  1520   nsCOMPtr<nsIChannel> channel;
  1521   rv = NS_NewChannel(getter_AddRefs(channel),
  1522                      aLoadData->mURI, nullptr, loadGroup, nullptr,
  1523                      nsIChannel::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI,
  1524                      channelPolicy);
  1526   if (NS_FAILED(rv)) {
  1527 #ifdef DEBUG
  1528     mSyncCallback = false;
  1529 #endif
  1530     LOG_ERROR(("  Failed to create channel"));
  1531     SheetComplete(aLoadData, rv);
  1532     return rv;
  1535   nsCOMPtr<nsIHttpChannelInternal>
  1536     internalHttpChannel(do_QueryInterface(channel));
  1537   if (internalHttpChannel)
  1538       internalHttpChannel->SetLoadAsBlocking(!aLoadData->mWasAlternate);
  1540   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
  1541   if (httpChannel) {
  1542     // send a minimal Accept header for text/css
  1543     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
  1544                                   NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
  1545                                   false);
  1546     nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
  1547     if (referrerURI)
  1548       httpChannel->SetReferrer(referrerURI);
  1550     // Set the initiator type
  1551     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
  1552     if (timedChannel) {
  1553       if (aLoadData->mParentData) {
  1554         timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
  1555       } else {
  1556         timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
  1561   // Now tell the channel we expect text/css data back....  We do
  1562   // this before opening it, so it's only treated as a hint.
  1563   channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
  1565   if (aLoadData->mLoaderPrincipal) {
  1566     bool inherit;
  1567     rv = NS_URIChainHasFlags(aLoadData->mURI,
  1568                              nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
  1569                              &inherit);
  1570     if ((NS_SUCCEEDED(rv) && inherit) ||
  1571         (nsContentUtils::URIIsLocalFile(aLoadData->mURI) &&
  1572          NS_SUCCEEDED(aLoadData->mLoaderPrincipal->
  1573                       CheckMayLoad(aLoadData->mURI, false, false)))) {
  1574       channel->SetOwner(aLoadData->mLoaderPrincipal);
  1578   // We don't have to hold on to the stream loader.  The ownership
  1579   // model is: Necko owns the stream loader, which owns the load data,
  1580   // which owns us
  1581   nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
  1582   rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
  1583   if (NS_FAILED(rv)) {
  1584 #ifdef DEBUG
  1585     mSyncCallback = false;
  1586 #endif
  1587     LOG_ERROR(("  Failed to create stream loader"));
  1588     SheetComplete(aLoadData, rv);
  1589     return rv;
  1592   nsCOMPtr<nsIStreamListener> channelListener;
  1593   CORSMode ourCORSMode = aLoadData->mSheet->GetCORSMode();
  1594   if (ourCORSMode != CORS_NONE) {
  1595     bool withCredentials = (ourCORSMode == CORS_USE_CREDENTIALS);
  1596     LOG(("  Doing CORS-enabled load; credentials %d", withCredentials));
  1597     nsRefPtr<nsCORSListenerProxy> corsListener =
  1598       new nsCORSListenerProxy(streamLoader, aLoadData->mLoaderPrincipal,
  1599 			      withCredentials);
  1600     rv = corsListener->Init(channel);
  1601     if (NS_FAILED(rv)) {
  1602 #ifdef DEBUG
  1603       mSyncCallback = false;
  1604 #endif
  1605       LOG_ERROR(("  Initial CORS check failed"));
  1606       SheetComplete(aLoadData, rv);
  1607       return rv;
  1609     channelListener = corsListener;
  1610   } else {
  1611     channelListener = streamLoader;
  1614   if (mDocument) {
  1615     mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
  1616                             nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument);
  1619   rv = channel->AsyncOpen(channelListener, nullptr);
  1621 #ifdef DEBUG
  1622   mSyncCallback = false;
  1623 #endif
  1625   if (NS_FAILED(rv)) {
  1626     LOG_ERROR(("  Failed to create stream loader"));
  1627     SheetComplete(aLoadData, rv);
  1628     return rv;
  1631   mSheets->mLoadingDatas.Put(&key, aLoadData);
  1632   aLoadData->mIsLoading = true;
  1634   return NS_OK;
  1637 /**
  1638  * ParseSheet handles parsing the data stream.  The main idea here is
  1639  * to push the current load data onto the parse stack before letting
  1640  * the CSS parser at the data stream.  That lets us handle @import
  1641  * correctly.
  1642  */
  1643 nsresult
  1644 Loader::ParseSheet(const nsAString& aInput,
  1645                    SheetLoadData* aLoadData,
  1646                    bool& aCompleted)
  1648   LOG(("css::Loader::ParseSheet"));
  1649   NS_PRECONDITION(aLoadData, "Must have load data");
  1650   NS_PRECONDITION(aLoadData->mSheet, "Must have sheet to parse into");
  1652   aCompleted = false;
  1654   nsCSSParser parser(this, aLoadData->mSheet);
  1656   // Push our load data on the stack so any kids can pick it up
  1657   mParsingDatas.AppendElement(aLoadData);
  1658   nsIURI* sheetURI = aLoadData->mSheet->GetSheetURI();
  1659   nsIURI* baseURI = aLoadData->mSheet->GetBaseURI();
  1660   nsresult rv = parser.ParseSheet(aInput, sheetURI, baseURI,
  1661                                   aLoadData->mSheet->Principal(),
  1662                                   aLoadData->mLineNumber,
  1663                                   aLoadData->mAllowUnsafeRules);
  1664   mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
  1666   if (NS_FAILED(rv)) {
  1667     LOG_ERROR(("  Low-level error in parser!"));
  1668     SheetComplete(aLoadData, rv);
  1669     return rv;
  1672   NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad,
  1673                "Sync load has leftover pending children!");
  1675   if (aLoadData->mPendingChildren == 0) {
  1676     LOG(("  No pending kids from parse"));
  1677     aCompleted = true;
  1678     SheetComplete(aLoadData, NS_OK);
  1680   // Otherwise, the children are holding strong refs to the data and
  1681   // will call SheetComplete() on it when they complete.
  1683   return NS_OK;
  1686 /**
  1687  * SheetComplete is the do-it-all cleanup function.  It removes the
  1688  * load data from the "loading" hashtable, adds the sheet to the
  1689  * "completed" hashtable, massages the XUL cache, handles siblings of
  1690  * the load data (other loads for the same URI), handles unblocking
  1691  * blocked parent loads as needed, and most importantly calls
  1692  * NS_RELEASE on the load data to destroy the whole mess.
  1693  */
  1694 void
  1695 Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
  1697   LOG(("css::Loader::SheetComplete"));
  1699   // 8 is probably big enough for all our common cases.  It's not likely that
  1700   // imports will nest more than 8 deep, and multiple sheets with the same URI
  1701   // are rare.
  1702   nsAutoTArray<nsRefPtr<SheetLoadData>, 8> datasToNotify;
  1703   DoSheetComplete(aLoadData, aStatus, datasToNotify);
  1705   // Now it's safe to go ahead and notify observers
  1706   uint32_t count = datasToNotify.Length();
  1707   mDatasToNotifyOn += count;
  1708   for (uint32_t i = 0; i < count; ++i) {
  1709     --mDatasToNotifyOn;
  1711     SheetLoadData* data = datasToNotify[i];
  1712     NS_ASSERTION(data && data->mMustNotify, "How did this data get here?");
  1713     if (data->mObserver) {
  1714       LOG(("  Notifying observer 0x%x for data 0x%x.  wasAlternate: %d",
  1715            data->mObserver.get(), data, data->mWasAlternate));
  1716       data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate,
  1717                                         aStatus);
  1720     nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers);
  1721     nsCOMPtr<nsICSSLoaderObserver> obs;
  1722     while (iter.HasMore()) {
  1723       obs = iter.GetNext();
  1724       LOG(("  Notifying global observer 0x%x for data 0x%s.  wasAlternate: %d",
  1725            obs.get(), data, data->mWasAlternate));
  1726       obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus);
  1730   if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) {
  1731     LOG(("  No more loading sheets; starting alternates"));
  1732     StartAlternateLoads();
  1736 void
  1737 Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
  1738                         LoadDataArray& aDatasToNotify)
  1740   LOG(("css::Loader::DoSheetComplete"));
  1741   NS_PRECONDITION(aLoadData, "Must have a load data!");
  1742   NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
  1743   NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
  1745   LOG(("Load completed, status: 0x%x", aStatus));
  1747   // Twiddle the hashtables
  1748   if (aLoadData->mURI) {
  1749     LOG_URI("  Finished loading: '%s'", aLoadData->mURI);
  1750     // Remove the data from the list of loading datas
  1751     if (aLoadData->mIsLoading) {
  1752       URIPrincipalAndCORSModeHashKey key(aLoadData->mURI,
  1753                                          aLoadData->mLoaderPrincipal,
  1754                                          aLoadData->mSheet->GetCORSMode());
  1755 #ifdef DEBUG
  1756       SheetLoadData *loadingData;
  1757       NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) &&
  1758                    loadingData == aLoadData,
  1759                    "Bad loading table");
  1760 #endif
  1762       mSheets->mLoadingDatas.Remove(&key);
  1763       aLoadData->mIsLoading = false;
  1767   // Go through and deal with the whole linked list.
  1768   SheetLoadData* data = aLoadData;
  1769   while (data) {
  1770     if (!data->mSheetAlreadyComplete) {
  1771       // If mSheetAlreadyComplete, then the sheet could well be modified between
  1772       // when we posted the async call to SheetComplete and now, since the sheet
  1773       // was page-accessible during that whole time.
  1774       NS_ABORT_IF_FALSE(!data->mSheet->IsModified(),
  1775                         "should not get marked modified during parsing");
  1776       data->mSheet->SetComplete();
  1777       data->ScheduleLoadEventIfNeeded(aStatus);
  1779     if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
  1780       // Don't notify here so we don't trigger script.  Remember the
  1781       // info we need to notify, then do it later when it's safe.
  1782       aDatasToNotify.AppendElement(data);
  1784       // On append failure, just press on.  We'll fail to notify the observer,
  1785       // but not much we can do about that....
  1788     NS_ASSERTION(!data->mParentData ||
  1789                  data->mParentData->mPendingChildren != 0,
  1790                  "Broken pending child count on our parent");
  1792     // If we have a parent, our parent is no longer being parsed, and
  1793     // we are the last pending child, then our load completion
  1794     // completes the parent too.  Note that the parent _can_ still be
  1795     // being parsed (eg if the child (us) failed to open the channel
  1796     // or some such).
  1797     if (data->mParentData &&
  1798         --(data->mParentData->mPendingChildren) == 0 &&
  1799         !mParsingDatas.Contains(data->mParentData)) {
  1800       DoSheetComplete(data->mParentData, aStatus, aDatasToNotify);
  1803     data = data->mNext;
  1806   // Now that it's marked complete, put the sheet in our cache.
  1807   // If we ever start doing this for failure aStatus, we'll need to
  1808   // adjust the PostLoadEvent code that thinks anything already
  1809   // complete must have loaded succesfully.
  1810   if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
  1811     // Pick our sheet to cache carefully.  Ideally, we want to cache
  1812     // one of the sheets that will be kept alive by a document or
  1813     // parent sheet anyway, so that if someone then accesses it via
  1814     // CSSOM we won't have extra clones of the inner lying around.
  1815     data = aLoadData;
  1816     nsCSSStyleSheet* sheet = aLoadData->mSheet;
  1817     while (data) {
  1818       if (data->mSheet->GetParentSheet() || data->mSheet->GetOwnerNode()) {
  1819         sheet = data->mSheet;
  1820         break;
  1822       data = data->mNext;
  1824 #ifdef MOZ_XUL
  1825     if (IsChromeURI(aLoadData->mURI)) {
  1826       nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
  1827       if (cache && cache->IsEnabled()) {
  1828         if (!cache->GetStyleSheet(aLoadData->mURI)) {
  1829           LOG(("  Putting sheet in XUL prototype cache"));
  1830           NS_ASSERTION(sheet->IsComplete(),
  1831                        "Should only be caching complete sheets");
  1832           cache->PutStyleSheet(sheet);
  1836     else {
  1837 #endif
  1838       URIPrincipalAndCORSModeHashKey key(aLoadData->mURI,
  1839                                          aLoadData->mLoaderPrincipal,
  1840                                          aLoadData->mSheet->GetCORSMode());
  1841       NS_ASSERTION(sheet->IsComplete(),
  1842                    "Should only be caching complete sheets");
  1843       mSheets->mCompleteSheets.Put(&key, sheet);
  1844 #ifdef MOZ_XUL
  1846 #endif
  1849   NS_RELEASE(aLoadData);  // this will release parents and siblings and all that
  1852 nsresult
  1853 Loader::LoadInlineStyle(nsIContent* aElement,
  1854                         const nsAString& aBuffer,
  1855                         uint32_t aLineNumber,
  1856                         const nsAString& aTitle,
  1857                         const nsAString& aMedia,
  1858                         Element* aScopeElement,
  1859                         nsICSSLoaderObserver* aObserver,
  1860                         bool* aCompleted,
  1861                         bool* aIsAlternate)
  1863   LOG(("css::Loader::LoadInlineStyle"));
  1864   NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
  1866   *aCompleted = true;
  1868   if (!mEnabled) {
  1869     LOG_WARN(("  Not enabled"));
  1870     return NS_ERROR_NOT_AVAILABLE;
  1873   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
  1875   nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
  1876   NS_ASSERTION(owningElement, "Element is not a style linking element!");
  1878   // Since we're not planning to load a URI, no need to hand a principal to the
  1879   // load data or to CreateSheet().  Also, OK to use CORS_NONE for the CORS
  1880   // mode.
  1881   StyleSheetState state;
  1882   nsRefPtr<nsCSSStyleSheet> sheet;
  1883   nsresult rv = CreateSheet(nullptr, aElement, nullptr, CORS_NONE, false, false,
  1884                             aTitle, state, aIsAlternate, getter_AddRefs(sheet));
  1885   NS_ENSURE_SUCCESS(rv, rv);
  1886   NS_ASSERTION(state == eSheetNeedsParser,
  1887                "Inline sheets should not be cached");
  1889   LOG(("  Sheet is alternate: %d", *aIsAlternate));
  1891   PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate);
  1893   if (aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
  1894     ShadowRoot* containingShadow = aElement->GetContainingShadow();
  1895     MOZ_ASSERT(containingShadow);
  1896     containingShadow->InsertSheet(sheet, aElement);
  1897   } else {
  1898     rv = InsertSheetInDoc(sheet, aElement, mDocument);
  1899     NS_ENSURE_SUCCESS(rv, rv);
  1902   SheetLoadData* data = new SheetLoadData(this, aTitle, nullptr, sheet,
  1903                                           owningElement, *aIsAlternate,
  1904                                           aObserver, nullptr);
  1906   // We never actually load this, so just set its principal directly
  1907   sheet->SetPrincipal(aElement->NodePrincipal());
  1909   NS_ADDREF(data);
  1910   data->mLineNumber = aLineNumber;
  1911   // Parse completion releases the load data
  1912   rv = ParseSheet(aBuffer, data, *aCompleted);
  1913   NS_ENSURE_SUCCESS(rv, rv);
  1915   // If aCompleted is true, |data| may well be deleted by now.
  1916   if (!*aCompleted) {
  1917     data->mMustNotify = true;
  1919   return rv;
  1922 nsresult
  1923 Loader::LoadStyleLink(nsIContent* aElement,
  1924                       nsIURI* aURL,
  1925                       const nsAString& aTitle,
  1926                       const nsAString& aMedia,
  1927                       bool aHasAlternateRel,
  1928                       CORSMode aCORSMode,
  1929                       nsICSSLoaderObserver* aObserver,
  1930                       bool* aIsAlternate)
  1932   LOG(("css::Loader::LoadStyleLink"));
  1933   NS_PRECONDITION(aURL, "Must have URL to load");
  1934   NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
  1936   LOG_URI("  Link uri: '%s'", aURL);
  1937   LOG(("  Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle).get()));
  1938   LOG(("  Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia).get()));
  1939   LOG(("  Link alternate rel: %d", aHasAlternateRel));
  1941   if (!mEnabled) {
  1942     LOG_WARN(("  Not enabled"));
  1943     return NS_ERROR_NOT_AVAILABLE;
  1946   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
  1948   nsIPrincipal* principal =
  1949     aElement ? aElement->NodePrincipal() : mDocument->NodePrincipal();
  1951   nsISupports* context = aElement;
  1952   if (!context) {
  1953     context = mDocument;
  1955   nsresult rv = CheckLoadAllowed(principal, aURL, context);
  1956   if (NS_FAILED(rv)) return rv;
  1958   LOG(("  Passed load check"));
  1960   StyleSheetState state;
  1961   nsRefPtr<nsCSSStyleSheet> sheet;
  1962   rv = CreateSheet(aURL, aElement, principal, aCORSMode, false,
  1963                    aHasAlternateRel, aTitle, state, aIsAlternate,
  1964                    getter_AddRefs(sheet));
  1965   NS_ENSURE_SUCCESS(rv, rv);
  1967   LOG(("  Sheet is alternate: %d", *aIsAlternate));
  1969   PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate);
  1971   rv = InsertSheetInDoc(sheet, aElement, mDocument);
  1972   NS_ENSURE_SUCCESS(rv, rv);
  1974   nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
  1976   if (state == eSheetComplete) {
  1977     LOG(("  Sheet already complete: 0x%p",
  1978          static_cast<void*>(sheet.get())));
  1979     if (aObserver || !mObservers.IsEmpty() || owningElement) {
  1980       rv = PostLoadEvent(aURL, sheet, aObserver, *aIsAlternate,
  1981                          owningElement);
  1982       return rv;
  1985     return NS_OK;
  1988   // Now we need to actually load it
  1989   SheetLoadData* data = new SheetLoadData(this, aTitle, aURL, sheet,
  1990                                           owningElement, *aIsAlternate,
  1991                                           aObserver, principal);
  1992   NS_ADDREF(data);
  1994   // If we have to parse and it's an alternate non-inline, defer it
  1995   if (aURL && state == eSheetNeedsParser && mSheets->mLoadingDatas.Count() != 0 &&
  1996       *aIsAlternate) {
  1997     LOG(("  Deferring alternate sheet load"));
  1998     URIPrincipalAndCORSModeHashKey key(data->mURI, data->mLoaderPrincipal,
  1999                                        data->mSheet->GetCORSMode());
  2000     mSheets->mPendingDatas.Put(&key, data);
  2002     data->mMustNotify = true;
  2003     return NS_OK;
  2006   // Load completion will free the data
  2007   rv = LoadSheet(data, state);
  2008   NS_ENSURE_SUCCESS(rv, rv);
  2010   data->mMustNotify = true;
  2011   return rv;
  2014 static bool
  2015 HaveAncestorDataWithURI(SheetLoadData *aData, nsIURI *aURI)
  2017   if (!aData->mURI) {
  2018     // Inline style; this won't have any ancestors
  2019     NS_ABORT_IF_FALSE(!aData->mParentData,
  2020                       "How does inline style have a parent?");
  2021     return false;
  2024   bool equal;
  2025   if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) {
  2026     return true;
  2029   // Datas down the mNext chain have the same URI as aData, so we
  2030   // don't have to compare to them.  But they might have different
  2031   // parents, and we have to check all of those.
  2032   while (aData) {
  2033     if (aData->mParentData &&
  2034         HaveAncestorDataWithURI(aData->mParentData, aURI)) {
  2035       return true;
  2038     aData = aData->mNext;
  2041   return false;
  2044 nsresult
  2045 Loader::LoadChildSheet(nsCSSStyleSheet* aParentSheet,
  2046                        nsIURI* aURL,
  2047                        nsMediaList* aMedia,
  2048                        ImportRule* aParentRule)
  2050   LOG(("css::Loader::LoadChildSheet"));
  2051   NS_PRECONDITION(aURL, "Must have a URI to load");
  2052   NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
  2054   if (!mEnabled) {
  2055     LOG_WARN(("  Not enabled"));
  2056     return NS_ERROR_NOT_AVAILABLE;
  2059   LOG_URI("  Child uri: '%s'", aURL);
  2061   nsCOMPtr<nsIDOMNode> owningNode;
  2063   // check for an owning document: if none, don't bother walking up the parent
  2064   // sheets
  2065   if (aParentSheet->GetOwningDocument()) {
  2066     nsCOMPtr<nsIDOMStyleSheet> nextParentSheet(aParentSheet);
  2067     NS_ENSURE_TRUE(nextParentSheet, NS_ERROR_FAILURE); //Not a stylesheet!?
  2069     nsCOMPtr<nsIDOMStyleSheet> topSheet;
  2070     //traverse our way to the top-most sheet
  2071     do {
  2072       topSheet.swap(nextParentSheet);
  2073       topSheet->GetParentStyleSheet(getter_AddRefs(nextParentSheet));
  2074     } while (nextParentSheet);
  2076     topSheet->GetOwnerNode(getter_AddRefs(owningNode));
  2079   nsISupports* context = owningNode;
  2080   if (!context) {
  2081     context = mDocument;
  2084   nsIPrincipal* principal = aParentSheet->Principal();
  2085   nsresult rv = CheckLoadAllowed(principal, aURL, context);
  2086   if (NS_FAILED(rv)) return rv;
  2088   LOG(("  Passed load check"));
  2090   SheetLoadData* parentData = nullptr;
  2091   nsCOMPtr<nsICSSLoaderObserver> observer;
  2093   int32_t count = mParsingDatas.Length();
  2094   if (count > 0) {
  2095     LOG(("  Have a parent load"));
  2096     parentData = mParsingDatas.ElementAt(count - 1);
  2097     // Check for cycles
  2098     if (HaveAncestorDataWithURI(parentData, aURL)) {
  2099       // Houston, we have a loop, blow off this child and pretend this never
  2100       // happened
  2101       LOG_ERROR(("  @import cycle detected, dropping load"));
  2102       return NS_OK;
  2105     NS_ASSERTION(parentData->mSheet == aParentSheet,
  2106                  "Unexpected call to LoadChildSheet");
  2107   } else {
  2108     LOG(("  No parent load; must be CSSOM"));
  2109     // No parent load data, so the sheet will need to be notified when
  2110     // we finish, if it can be, if we do the load asynchronously.
  2111     observer = aParentSheet;
  2114   // Now that we know it's safe to load this (passes security check and not a
  2115   // loop) do so.
  2116   nsRefPtr<nsCSSStyleSheet> sheet;
  2117   bool isAlternate;
  2118   StyleSheetState state;
  2119   const nsSubstring& empty = EmptyString();
  2120   // For now, use CORS_NONE for child sheets
  2121   rv = CreateSheet(aURL, nullptr, principal, CORS_NONE,
  2122                    parentData ? parentData->mSyncLoad : false,
  2123                    false, empty, state, &isAlternate, getter_AddRefs(sheet));
  2124   NS_ENSURE_SUCCESS(rv, rv);
  2126   PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate);
  2128   rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
  2129   NS_ENSURE_SUCCESS(rv, rv);
  2131   if (state == eSheetComplete) {
  2132     LOG(("  Sheet already complete"));
  2133     // We're completely done.  No need to notify, even, since the
  2134     // @import rule addition/modification will trigger the right style
  2135     // changes automatically.
  2136     return NS_OK;
  2139   SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
  2140                                           observer, principal);
  2142   NS_ADDREF(data);
  2143   bool syncLoad = data->mSyncLoad;
  2145   // Load completion will release the data
  2146   rv = LoadSheet(data, state);
  2147   NS_ENSURE_SUCCESS(rv, rv);
  2149   // If syncLoad is true, |data| will be deleted by now.
  2150   if (!syncLoad) {
  2151     data->mMustNotify = true;
  2153   return rv;
  2156 nsresult
  2157 Loader::LoadSheetSync(nsIURI* aURL, bool aAllowUnsafeRules,
  2158                       bool aUseSystemPrincipal,
  2159                       nsCSSStyleSheet** aSheet)
  2161   LOG(("css::Loader::LoadSheetSync"));
  2162   return InternalLoadNonDocumentSheet(aURL, aAllowUnsafeRules,
  2163                                       aUseSystemPrincipal, nullptr,
  2164                                       EmptyCString(), aSheet, nullptr);
  2167 nsresult
  2168 Loader::LoadSheet(nsIURI* aURL,
  2169                   nsIPrincipal* aOriginPrincipal,
  2170                   const nsCString& aCharset,
  2171                   nsICSSLoaderObserver* aObserver,
  2172                   nsCSSStyleSheet** aSheet)
  2174   LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
  2175   NS_PRECONDITION(aSheet, "aSheet is null");
  2176   return InternalLoadNonDocumentSheet(aURL, false, false,
  2177                                       aOriginPrincipal, aCharset,
  2178                                       aSheet, aObserver);
  2181 nsresult
  2182 Loader::LoadSheet(nsIURI* aURL,
  2183                   nsIPrincipal* aOriginPrincipal,
  2184                   const nsCString& aCharset,
  2185                   nsICSSLoaderObserver* aObserver,
  2186                   CORSMode aCORSMode)
  2188   LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
  2189   return InternalLoadNonDocumentSheet(aURL, false, false,
  2190                                       aOriginPrincipal, aCharset,
  2191                                       nullptr, aObserver, aCORSMode);
  2194 nsresult
  2195 Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
  2196                                      bool aAllowUnsafeRules,
  2197                                      bool aUseSystemPrincipal,
  2198                                      nsIPrincipal* aOriginPrincipal,
  2199                                      const nsCString& aCharset,
  2200                                      nsCSSStyleSheet** aSheet,
  2201                                      nsICSSLoaderObserver* aObserver,
  2202                                      CORSMode aCORSMode)
  2204   NS_PRECONDITION(aURL, "Must have a URI to load");
  2205   NS_PRECONDITION(aSheet || aObserver, "Sheet and observer can't both be null");
  2206   NS_PRECONDITION(!aUseSystemPrincipal || !aObserver,
  2207                   "Shouldn't load system-principal sheets async");
  2208   NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
  2210   LOG_URI("  Non-document sheet uri: '%s'", aURL);
  2212   if (aSheet) {
  2213     *aSheet = nullptr;
  2216   if (!mEnabled) {
  2217     LOG_WARN(("  Not enabled"));
  2218     return NS_ERROR_NOT_AVAILABLE;
  2221   nsresult rv = CheckLoadAllowed(aOriginPrincipal, aURL, mDocument);
  2222   if (NS_FAILED(rv)) {
  2223     return rv;
  2226   StyleSheetState state;
  2227   bool isAlternate;
  2228   nsRefPtr<nsCSSStyleSheet> sheet;
  2229   bool syncLoad = (aObserver == nullptr);
  2230   const nsSubstring& empty = EmptyString();
  2232   rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aCORSMode, syncLoad, false,
  2233                    empty, state, &isAlternate, getter_AddRefs(sheet));
  2234   NS_ENSURE_SUCCESS(rv, rv);
  2236   PrepareSheet(sheet, empty, empty, nullptr, nullptr, isAlternate);
  2238   if (state == eSheetComplete) {
  2239     LOG(("  Sheet already complete"));
  2240     if (aObserver || !mObservers.IsEmpty()) {
  2241       rv = PostLoadEvent(aURL, sheet, aObserver, false, nullptr);
  2243     if (aSheet) {
  2244       sheet.swap(*aSheet);
  2246     return rv;
  2249   SheetLoadData* data =
  2250     new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules,
  2251                       aUseSystemPrincipal, aCharset, aObserver,
  2252                       aOriginPrincipal);
  2254   NS_ADDREF(data);
  2255   rv = LoadSheet(data, state);
  2256   NS_ENSURE_SUCCESS(rv, rv);
  2258   if (aSheet) {
  2259     sheet.swap(*aSheet);
  2261   if (aObserver) {
  2262     data->mMustNotify = true;
  2265   return rv;
  2268 nsresult
  2269 Loader::PostLoadEvent(nsIURI* aURI,
  2270                       nsCSSStyleSheet* aSheet,
  2271                       nsICSSLoaderObserver* aObserver,
  2272                       bool aWasAlternate,
  2273                       nsIStyleSheetLinkingElement* aElement)
  2275   LOG(("css::Loader::PostLoadEvent"));
  2276   NS_PRECONDITION(aSheet, "Must have sheet");
  2277   NS_PRECONDITION(aObserver || !mObservers.IsEmpty() || aElement,
  2278                   "Must have observer or element");
  2280   nsRefPtr<SheetLoadData> evt =
  2281     new SheetLoadData(this, EmptyString(), // title doesn't matter here
  2282                       aURI,
  2283                       aSheet,
  2284                       aElement,
  2285                       aWasAlternate,
  2286                       aObserver,
  2287                       nullptr);
  2288   NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
  2290   if (!mPostedEvents.AppendElement(evt)) {
  2291     return NS_ERROR_OUT_OF_MEMORY;
  2294   nsresult rv = NS_DispatchToCurrentThread(evt);
  2295   if (NS_FAILED(rv)) {
  2296     NS_WARNING("failed to dispatch stylesheet load event");
  2297     mPostedEvents.RemoveElement(evt);
  2298   } else {
  2299     // We'll unblock onload when we handle the event.
  2300     if (mDocument) {
  2301       mDocument->BlockOnload();
  2304     // We want to notify the observer for this data.
  2305     evt->mMustNotify = true;
  2306     evt->mSheetAlreadyComplete = true;
  2308     // If we get to this code, aSheet loaded correctly at some point, so
  2309     // we can just use NS_OK for the status.  Note that we do this here
  2310     // and not from inside our SheetComplete so that we don't end up
  2311     // running the load event async.
  2312     evt->ScheduleLoadEventIfNeeded(NS_OK);
  2315   return rv;
  2318 void
  2319 Loader::HandleLoadEvent(SheetLoadData* aEvent)
  2321   // XXXbz can't assert this yet.... May not have an observer because
  2322   // we're unblocking the parser
  2323   // NS_ASSERTION(aEvent->mObserver, "Must have observer");
  2324   NS_ASSERTION(aEvent->mSheet, "Must have sheet");
  2326   // Very important: this needs to come before the SheetComplete call
  2327   // below, so that HasPendingLoads() will test true as needed under
  2328   // notifications we send from that SheetComplete call.
  2329   mPostedEvents.RemoveElement(aEvent);
  2331   if (!aEvent->mIsCancelled) {
  2332     // SheetComplete will call Release(), so give it a reference to do
  2333     // that with.
  2334     NS_ADDREF(aEvent);
  2335     SheetComplete(aEvent, NS_OK);
  2338   if (mDocument) {
  2339     mDocument->UnblockOnload(true);
  2343 static PLDHashOperator
  2344 StopLoadingSheetCallback(URIPrincipalAndCORSModeHashKey* aKey,
  2345                          SheetLoadData*& aData,
  2346                          void* aClosure)
  2348   NS_PRECONDITION(aData, "Must have a data!");
  2349   NS_PRECONDITION(aClosure, "Must have a loader");
  2351   aData->mIsLoading = false; // we will handle the removal right here
  2352   aData->mIsCancelled = true;
  2354   static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
  2356   return PL_DHASH_REMOVE;
  2359 nsresult
  2360 Loader::Stop()
  2362   uint32_t pendingCount =
  2363     mSheets ? mSheets->mPendingDatas.Count() : 0;
  2364   uint32_t loadingCount =
  2365     mSheets ? mSheets->mLoadingDatas.Count() : 0;
  2366   LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length());
  2368   if (pendingCount) {
  2369     mSheets->mPendingDatas.Enumerate(StopLoadingSheetCallback, &arr);
  2371   if (loadingCount) {
  2372     mSheets->mLoadingDatas.Enumerate(StopLoadingSheetCallback, &arr);
  2375   uint32_t i;
  2376   for (i = 0; i < mPostedEvents.Length(); ++i) {
  2377     SheetLoadData* data = mPostedEvents[i];
  2378     data->mIsCancelled = true;
  2379     if (arr.AppendElement(data)) {
  2380       // SheetComplete() calls Release(), so give this an extra ref.
  2381       NS_ADDREF(data);
  2383 #ifdef DEBUG
  2384     else {
  2385       NS_NOTREACHED("We preallocated this memory... shouldn't really fail, "
  2386                     "except we never check that preallocation succeeds.");
  2388 #endif
  2390   mPostedEvents.Clear();
  2392   mDatasToNotifyOn += arr.Length();
  2393   for (i = 0; i < arr.Length(); ++i) {
  2394     --mDatasToNotifyOn;
  2395     SheetComplete(arr[i], NS_BINDING_ABORTED);
  2397   return NS_OK;
  2400 bool
  2401 Loader::HasPendingLoads()
  2403   return
  2404     (mSheets && mSheets->mLoadingDatas.Count() != 0) ||
  2405     (mSheets && mSheets->mPendingDatas.Count() != 0) ||
  2406     mPostedEvents.Length() != 0 ||
  2407     mDatasToNotifyOn != 0;
  2410 nsresult
  2411 Loader::AddObserver(nsICSSLoaderObserver* aObserver)
  2413   NS_PRECONDITION(aObserver, "Must have observer");
  2414   if (mObservers.AppendElementUnlessExists(aObserver)) {
  2415     return NS_OK;
  2418   return NS_ERROR_OUT_OF_MEMORY;
  2421 void
  2422 Loader::RemoveObserver(nsICSSLoaderObserver* aObserver)
  2424   mObservers.RemoveElement(aObserver);
  2427 static PLDHashOperator
  2428 CollectLoadDatas(URIPrincipalAndCORSModeHashKey *aKey,
  2429                  SheetLoadData* &aData,
  2430                  void* aClosure)
  2432   static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
  2433   return PL_DHASH_REMOVE;
  2436 void
  2437 Loader::StartAlternateLoads()
  2439   NS_PRECONDITION(mSheets, "Don't call me!");
  2440   LoadDataArray arr(mSheets->mPendingDatas.Count());
  2441   mSheets->mPendingDatas.Enumerate(CollectLoadDatas, &arr);
  2443   mDatasToNotifyOn += arr.Length();
  2444   for (uint32_t i = 0; i < arr.Length(); ++i) {
  2445     --mDatasToNotifyOn;
  2446     LoadSheet(arr[i], eSheetNeedsParser);
  2450 static PLDHashOperator
  2451 TraverseSheet(URIPrincipalAndCORSModeHashKey*,
  2452               nsCSSStyleSheet* aSheet,
  2453               void* aClosure)
  2455   nsCycleCollectionTraversalCallback* cb =
  2456     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
  2457   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "Sheet cache nsCSSLoader");
  2458   cb->NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIStyleSheet*, aSheet));
  2459   return PL_DHASH_NEXT;
  2462 void
  2463 Loader::TraverseCachedSheets(nsCycleCollectionTraversalCallback& cb)
  2465   if (mSheets) {
  2466     mSheets->mCompleteSheets.EnumerateRead(TraverseSheet, &cb);
  2470 void
  2471 Loader::UnlinkCachedSheets()
  2473   if (mSheets) {
  2474     mSheets->mCompleteSheets.Clear();
  2478 struct SheetMemoryCounter {
  2479   size_t size;
  2480   mozilla::MallocSizeOf mallocSizeOf;
  2481 };
  2483 static size_t
  2484 CountSheetMemory(URIPrincipalAndCORSModeHashKey* /* unused */,
  2485                  const nsRefPtr<nsCSSStyleSheet>& aSheet,
  2486                  mozilla::MallocSizeOf aMallocSizeOf,
  2487                  void* /* unused */)
  2489   // If aSheet has a parent, then its parent will report it so we don't
  2490   // have to worry about it here.
  2491   // Likewise, if aSheet has an owning node, then the document that
  2492   // node is in will report it.
  2493   if (aSheet->GetOwnerNode() || aSheet->GetParentSheet()) {
  2494     return 0;
  2496   return aSheet->SizeOfIncludingThis(aMallocSizeOf);
  2499 size_t
  2500 Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  2502   size_t s = aMallocSizeOf(this);
  2504   if (mSheets) {
  2505     s += mSheets->mCompleteSheets.SizeOfExcludingThis(CountSheetMemory, aMallocSizeOf);
  2507   s += mObservers.SizeOfExcludingThis(aMallocSizeOf);
  2509   // Measurement of the following members may be added later if DMD finds it is
  2510   // worthwhile:
  2511   // - mLoadingDatas: transient, and should be small
  2512   // - mPendingDatas: transient, and should be small
  2513   // - mParsingDatas: transient, and should be small
  2514   // - mPostedEvents: transient, and should be small
  2515   //
  2516   // The following members aren't measured:
  2517   // - mDocument, because it's a weak backpointer
  2518   // - mPreferredSheet, because it can be a shared string
  2520   return s;
  2523 } // namespace css
  2524 } // namespace mozilla

mercurial