layout/style/Loader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/style/Loader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2524 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * vim: ft=cpp tw=78 sw=2 et ts=2
     1.6 + *
     1.7 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    1.10 + *
    1.11 + * This Original Code has been modified by IBM Corporation.
    1.12 + * Modifications made by IBM described herein are Copyright (c)
    1.13 + * International Business Machines Corporation, 2000.  Modifications
    1.14 + * to Mozilla code or documentation identified per MPL Section 3.3
    1.15 + *
    1.16 + * Date             Modified by     Description of modification
    1.17 + * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
    1.18 + */
    1.19 +
    1.20 +/* loading of CSS style sheets using the network APIs */
    1.21 +
    1.22 +#include "mozilla/ArrayUtils.h"
    1.23 +#include "mozilla/MemoryReporting.h"
    1.24 +
    1.25 +#include "mozilla/css/Loader.h"
    1.26 +#include "nsIRunnable.h"
    1.27 +#include "nsIUnicharStreamLoader.h"
    1.28 +#include "nsSyncLoadService.h"
    1.29 +#include "nsCOMPtr.h"
    1.30 +#include "nsString.h"
    1.31 +#include "nsIContent.h"
    1.32 +#include "nsIDocument.h"
    1.33 +#include "nsIDOMNode.h"
    1.34 +#include "nsIDOMDocument.h"
    1.35 +#include "nsIURI.h"
    1.36 +#include "nsNetUtil.h"
    1.37 +#include "nsContentUtils.h"
    1.38 +#include "nsIScriptSecurityManager.h"
    1.39 +#include "nsContentPolicyUtils.h"
    1.40 +#include "nsIHttpChannel.h"
    1.41 +#include "nsIHttpChannelInternal.h"
    1.42 +#include "nsIScriptError.h"
    1.43 +#include "nsMimeTypes.h"
    1.44 +#include "nsCSSStyleSheet.h"
    1.45 +#include "nsIStyleSheetLinkingElement.h"
    1.46 +#include "nsICSSLoaderObserver.h"
    1.47 +#include "nsCSSParser.h"
    1.48 +#include "mozilla/css/ImportRule.h"
    1.49 +#include "nsThreadUtils.h"
    1.50 +#include "nsGkAtoms.h"
    1.51 +#include "nsIThreadInternal.h"
    1.52 +#include "nsCrossSiteListenerProxy.h"
    1.53 +#include "nsINetworkSeer.h"
    1.54 +#include "mozilla/dom/ShadowRoot.h"
    1.55 +#include "mozilla/dom/URL.h"
    1.56 +
    1.57 +#ifdef MOZ_XUL
    1.58 +#include "nsXULPrototypeCache.h"
    1.59 +#endif
    1.60 +
    1.61 +#include "nsIMediaList.h"
    1.62 +#include "nsIDOMStyleSheet.h"
    1.63 +#include "nsError.h"
    1.64 +
    1.65 +#include "nsIChannelPolicy.h"
    1.66 +#include "nsIContentSecurityPolicy.h"
    1.67 +#include "nsCycleCollectionParticipant.h"
    1.68 +
    1.69 +#include "mozilla/dom/EncodingUtils.h"
    1.70 +using mozilla::dom::EncodingUtils;
    1.71 +
    1.72 +using namespace mozilla::dom;
    1.73 +
    1.74 +/**
    1.75 + * OVERALL ARCHITECTURE
    1.76 + *
    1.77 + * The CSS Loader gets requests to load various sorts of style sheets:
    1.78 + * inline style from <style> elements, linked style, @import-ed child
    1.79 + * sheets, non-document sheets.  The loader handles the following tasks:
    1.80 + *
    1.81 + * 1) Checking whether the load is allowed: CheckLoadAllowed()
    1.82 + * 2) Creation of the actual style sheet objects: CreateSheet()
    1.83 + * 3) setting of the right media, title, enabled state, etc on the
    1.84 + *    sheet: PrepareSheet()
    1.85 + * 4) Insertion of the sheet in the proper cascade order:
    1.86 + *    InsertSheetInDoc() and InsertChildSheet()
    1.87 + * 5) Load of the sheet: LoadSheet()
    1.88 + * 6) Parsing of the sheet: ParseSheet()
    1.89 + * 7) Cleanup: SheetComplete()
    1.90 + *
    1.91 + * The detailed documentation for these functions is found with the
    1.92 + * function implementations.
    1.93 + *
    1.94 + * The following helper object is used:
    1.95 + *    SheetLoadData -- a small class that is used to store all the
    1.96 + *                     information needed for the loading of a sheet;
    1.97 + *                     this class handles listening for the stream
    1.98 + *                     loader completion and also handles charset
    1.99 + *                     determination.
   1.100 + */
   1.101 +
   1.102 +namespace mozilla {
   1.103 +namespace css {
   1.104 +
   1.105 +/*********************************************
   1.106 + * Data needed to properly load a stylesheet *
   1.107 + *********************************************/
   1.108 +
   1.109 +class SheetLoadData : public nsIRunnable,
   1.110 +                      public nsIUnicharStreamLoaderObserver,
   1.111 +                      public nsIThreadObserver
   1.112 +{
   1.113 +public:
   1.114 +  virtual ~SheetLoadData(void);
   1.115 +  // Data for loading a sheet linked from a document
   1.116 +  SheetLoadData(Loader* aLoader,
   1.117 +                const nsSubstring& aTitle,
   1.118 +                nsIURI* aURI,
   1.119 +                nsCSSStyleSheet* aSheet,
   1.120 +                nsIStyleSheetLinkingElement* aOwningElement,
   1.121 +                bool aIsAlternate,
   1.122 +                nsICSSLoaderObserver* aObserver,
   1.123 +                nsIPrincipal* aLoaderPrincipal);
   1.124 +
   1.125 +  // Data for loading a sheet linked from an @import rule
   1.126 +  SheetLoadData(Loader* aLoader,
   1.127 +                nsIURI* aURI,
   1.128 +                nsCSSStyleSheet* aSheet,
   1.129 +                SheetLoadData* aParentData,
   1.130 +                nsICSSLoaderObserver* aObserver,
   1.131 +                nsIPrincipal* aLoaderPrincipal);
   1.132 +
   1.133 +  // Data for loading a non-document sheet
   1.134 +  SheetLoadData(Loader* aLoader,
   1.135 +                nsIURI* aURI,
   1.136 +                nsCSSStyleSheet* aSheet,
   1.137 +                bool aSyncLoad,
   1.138 +                bool aAllowUnsafeRules,
   1.139 +                bool aUseSystemPrincipal,
   1.140 +                const nsCString& aCharset,
   1.141 +                nsICSSLoaderObserver* aObserver,
   1.142 +                nsIPrincipal* aLoaderPrincipal);
   1.143 +
   1.144 +  already_AddRefed<nsIURI> GetReferrerURI();
   1.145 +
   1.146 +  void ScheduleLoadEventIfNeeded(nsresult aStatus);
   1.147 +
   1.148 +  NS_DECL_ISUPPORTS
   1.149 +  NS_DECL_NSIRUNNABLE
   1.150 +  NS_DECL_NSITHREADOBSERVER
   1.151 +  NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
   1.152 +
   1.153 +  // Hold a ref to the CSSLoader so we can call back to it to let it
   1.154 +  // know the load finished
   1.155 +  Loader*                    mLoader; // strong ref
   1.156 +
   1.157 +  // Title needed to pull datas out of the pending datas table when
   1.158 +  // the preferred title is changed
   1.159 +  nsString                   mTitle;
   1.160 +
   1.161 +  // Charset we decided to use for the sheet
   1.162 +  nsCString                  mCharset;
   1.163 +
   1.164 +  // URI we're loading.  Null for inline sheets
   1.165 +  nsCOMPtr<nsIURI>           mURI;
   1.166 +
   1.167 +  // Should be 1 for non-inline sheets.
   1.168 +  uint32_t                   mLineNumber;
   1.169 +
   1.170 +  // The sheet we're loading data for
   1.171 +  nsRefPtr<nsCSSStyleSheet>  mSheet;
   1.172 +
   1.173 +  // Linked list of datas for the same URI as us
   1.174 +  SheetLoadData*             mNext;  // strong ref
   1.175 +
   1.176 +  // Load data for the sheet that @import-ed us if we were @import-ed
   1.177 +  // during the parse
   1.178 +  SheetLoadData*             mParentData;  // strong ref
   1.179 +
   1.180 +  // Number of sheets we @import-ed that are still loading
   1.181 +  uint32_t                   mPendingChildren;
   1.182 +
   1.183 +  // mSyncLoad is true when the load needs to be synchronous -- right
   1.184 +  // now only for LoadSheetSync and children of sync loads.
   1.185 +  bool                       mSyncLoad : 1;
   1.186 +
   1.187 +  // mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
   1.188 +  // LoadSheet or an @import from such a sheet.  Non-document sheet loads can
   1.189 +  // proceed even if we have no document.
   1.190 +  bool                       mIsNonDocumentSheet : 1;
   1.191 +
   1.192 +  // mIsLoading is true from the moment we are placed in the loader's
   1.193 +  // "loading datas" table (right after the async channel is opened)
   1.194 +  // to the moment we are removed from said table (due to the load
   1.195 +  // completing or being cancelled).
   1.196 +  bool                       mIsLoading : 1;
   1.197 +
   1.198 +  // mIsCancelled is set to true when a sheet load is stopped by
   1.199 +  // Stop() or StopLoadingSheet() (which was removed in Bug 556446).
   1.200 +  // SheetLoadData::OnStreamComplete() checks this to avoid parsing
   1.201 +  // sheets that have been cancelled and such.
   1.202 +  bool                       mIsCancelled : 1;
   1.203 +
   1.204 +  // mMustNotify is true if the load data is being loaded async and
   1.205 +  // the original function call that started the load has returned.
   1.206 +  // This applies only to observer notifications; load/error events
   1.207 +  // are fired for any SheetLoadData that has a non-null
   1.208 +  // mOwningElement.
   1.209 +  bool                       mMustNotify : 1;
   1.210 +
   1.211 +  // mWasAlternate is true if the sheet was an alternate when the load data was
   1.212 +  // created.
   1.213 +  bool                       mWasAlternate : 1;
   1.214 +
   1.215 +  // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed
   1.216 +  // in the loaded sheet.
   1.217 +  bool                       mAllowUnsafeRules : 1;
   1.218 +
   1.219 +  // mUseSystemPrincipal is true if the system principal should be used for
   1.220 +  // this sheet, no matter what the channel principal is.  Only true for sync
   1.221 +  // loads.
   1.222 +  bool                       mUseSystemPrincipal : 1;
   1.223 +
   1.224 +  // If true, this SheetLoadData is being used as a way to handle
   1.225 +  // async observer notification for an already-complete sheet.
   1.226 +  bool                       mSheetAlreadyComplete : 1;
   1.227 +
   1.228 +  // This is the element that imported the sheet.  Needed to get the
   1.229 +  // charset set on it and to fire load/error events.
   1.230 +  nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
   1.231 +
   1.232 +  // The observer that wishes to be notified of load completion
   1.233 +  nsCOMPtr<nsICSSLoaderObserver>        mObserver;
   1.234 +
   1.235 +  // The principal that identifies who started loading us.
   1.236 +  nsCOMPtr<nsIPrincipal>                mLoaderPrincipal;
   1.237 +
   1.238 +  // The charset to use if the transport and sheet don't indicate one.
   1.239 +  // May be empty.  Must be empty if mOwningElement is non-null.
   1.240 +  nsCString                             mCharsetHint;
   1.241 +
   1.242 +  // The status our load ended up with; this determines whether we
   1.243 +  // should fire error events or load events.  This gets initialized
   1.244 +  // by ScheduleLoadEventIfNeeded, and is only used after that has
   1.245 +  // been called.
   1.246 +  nsresult                              mStatus;
   1.247 +
   1.248 +private:
   1.249 +  void FireLoadEvent(nsIThreadInternal* aThread);
   1.250 +};
   1.251 +
   1.252 +#ifdef MOZ_LOGGING
   1.253 +// #define FORCE_PR_LOG /* Allow logging in the release build */
   1.254 +#endif /* MOZ_LOGGING */
   1.255 +#include "prlog.h"
   1.256 +
   1.257 +#ifdef PR_LOGGING
   1.258 +static PRLogModuleInfo *
   1.259 +GetLoaderLog()
   1.260 +{
   1.261 +  static PRLogModuleInfo *sLog;
   1.262 +  if (!sLog)
   1.263 +    sLog = PR_NewLogModule("nsCSSLoader");
   1.264 +  return sLog;
   1.265 +}
   1.266 +#endif /* PR_LOGGING */
   1.267 +
   1.268 +#define LOG_FORCE(args) PR_LOG(GetLoaderLog(), PR_LOG_ALWAYS, args)
   1.269 +#define LOG_ERROR(args) PR_LOG(GetLoaderLog(), PR_LOG_ERROR, args)
   1.270 +#define LOG_WARN(args) PR_LOG(GetLoaderLog(), PR_LOG_WARNING, args)
   1.271 +#define LOG_DEBUG(args) PR_LOG(GetLoaderLog(), PR_LOG_DEBUG, args)
   1.272 +#define LOG(args) LOG_DEBUG(args)
   1.273 +
   1.274 +#define LOG_FORCE_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ALWAYS)
   1.275 +#define LOG_ERROR_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_ERROR)
   1.276 +#define LOG_WARN_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_WARNING)
   1.277 +#define LOG_DEBUG_ENABLED() PR_LOG_TEST(GetLoaderLog(), PR_LOG_DEBUG)
   1.278 +#define LOG_ENABLED() LOG_DEBUG_ENABLED()
   1.279 +
   1.280 +#ifdef PR_LOGGING
   1.281 +#define LOG_URI(format, uri)                        \
   1.282 +  PR_BEGIN_MACRO                                    \
   1.283 +    NS_ASSERTION(uri, "Logging null uri");          \
   1.284 +    if (LOG_ENABLED()) {                            \
   1.285 +      nsAutoCString _logURISpec;                    \
   1.286 +      uri->GetSpec(_logURISpec);                    \
   1.287 +      LOG((format, _logURISpec.get()));             \
   1.288 +    }                                               \
   1.289 +  PR_END_MACRO
   1.290 +#else // PR_LOGGING
   1.291 +#define LOG_URI(format, uri)
   1.292 +#endif // PR_LOGGING
   1.293 +
   1.294 +// And some convenience strings...
   1.295 +#ifdef PR_LOGGING
   1.296 +static const char* const gStateStrings[] = {
   1.297 +  "eSheetStateUnknown",
   1.298 +  "eSheetNeedsParser",
   1.299 +  "eSheetPending",
   1.300 +  "eSheetLoading",
   1.301 +  "eSheetComplete"
   1.302 +};
   1.303 +#endif
   1.304 +
   1.305 +/********************************
   1.306 + * SheetLoadData implementation *
   1.307 + ********************************/
   1.308 +NS_IMPL_ISUPPORTS(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable,
   1.309 +                  nsIThreadObserver)
   1.310 +
   1.311 +SheetLoadData::SheetLoadData(Loader* aLoader,
   1.312 +                             const nsSubstring& aTitle,
   1.313 +                             nsIURI* aURI,
   1.314 +                             nsCSSStyleSheet* aSheet,
   1.315 +                             nsIStyleSheetLinkingElement* aOwningElement,
   1.316 +                             bool aIsAlternate,
   1.317 +                             nsICSSLoaderObserver* aObserver,
   1.318 +                             nsIPrincipal* aLoaderPrincipal)
   1.319 +  : mLoader(aLoader),
   1.320 +    mTitle(aTitle),
   1.321 +    mURI(aURI),
   1.322 +    mLineNumber(1),
   1.323 +    mSheet(aSheet),
   1.324 +    mNext(nullptr),
   1.325 +    mParentData(nullptr),
   1.326 +    mPendingChildren(0),
   1.327 +    mSyncLoad(false),
   1.328 +    mIsNonDocumentSheet(false),
   1.329 +    mIsLoading(false),
   1.330 +    mIsCancelled(false),
   1.331 +    mMustNotify(false),
   1.332 +    mWasAlternate(aIsAlternate),
   1.333 +    mAllowUnsafeRules(false),
   1.334 +    mUseSystemPrincipal(false),
   1.335 +    mSheetAlreadyComplete(false),
   1.336 +    mOwningElement(aOwningElement),
   1.337 +    mObserver(aObserver),
   1.338 +    mLoaderPrincipal(aLoaderPrincipal)
   1.339 +{
   1.340 +  NS_PRECONDITION(mLoader, "Must have a loader!");
   1.341 +  NS_ADDREF(mLoader);
   1.342 +}
   1.343 +
   1.344 +SheetLoadData::SheetLoadData(Loader* aLoader,
   1.345 +                             nsIURI* aURI,
   1.346 +                             nsCSSStyleSheet* aSheet,
   1.347 +                             SheetLoadData* aParentData,
   1.348 +                             nsICSSLoaderObserver* aObserver,
   1.349 +                             nsIPrincipal* aLoaderPrincipal)
   1.350 +  : mLoader(aLoader),
   1.351 +    mURI(aURI),
   1.352 +    mLineNumber(1),
   1.353 +    mSheet(aSheet),
   1.354 +    mNext(nullptr),
   1.355 +    mParentData(aParentData),
   1.356 +    mPendingChildren(0),
   1.357 +    mSyncLoad(false),
   1.358 +    mIsNonDocumentSheet(false),
   1.359 +    mIsLoading(false),
   1.360 +    mIsCancelled(false),
   1.361 +    mMustNotify(false),
   1.362 +    mWasAlternate(false),
   1.363 +    mAllowUnsafeRules(false),
   1.364 +    mUseSystemPrincipal(false),
   1.365 +    mSheetAlreadyComplete(false),
   1.366 +    mOwningElement(nullptr),
   1.367 +    mObserver(aObserver),
   1.368 +    mLoaderPrincipal(aLoaderPrincipal)
   1.369 +{
   1.370 +  NS_PRECONDITION(mLoader, "Must have a loader!");
   1.371 +  NS_ADDREF(mLoader);
   1.372 +  if (mParentData) {
   1.373 +    NS_ADDREF(mParentData);
   1.374 +    mSyncLoad = mParentData->mSyncLoad;
   1.375 +    mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
   1.376 +    mAllowUnsafeRules = mParentData->mAllowUnsafeRules;
   1.377 +    mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
   1.378 +    ++(mParentData->mPendingChildren);
   1.379 +  }
   1.380 +
   1.381 +  NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
   1.382 +                   "Shouldn't use system principal for async loads");
   1.383 +}
   1.384 +
   1.385 +SheetLoadData::SheetLoadData(Loader* aLoader,
   1.386 +                             nsIURI* aURI,
   1.387 +                             nsCSSStyleSheet* aSheet,
   1.388 +                             bool aSyncLoad,
   1.389 +                             bool aAllowUnsafeRules,
   1.390 +                             bool aUseSystemPrincipal,
   1.391 +                             const nsCString& aCharset,
   1.392 +                             nsICSSLoaderObserver* aObserver,
   1.393 +                             nsIPrincipal* aLoaderPrincipal)
   1.394 +  : mLoader(aLoader),
   1.395 +    mURI(aURI),
   1.396 +    mLineNumber(1),
   1.397 +    mSheet(aSheet),
   1.398 +    mNext(nullptr),
   1.399 +    mParentData(nullptr),
   1.400 +    mPendingChildren(0),
   1.401 +    mSyncLoad(aSyncLoad),
   1.402 +    mIsNonDocumentSheet(true),
   1.403 +    mIsLoading(false),
   1.404 +    mIsCancelled(false),
   1.405 +    mMustNotify(false),
   1.406 +    mWasAlternate(false),
   1.407 +    mAllowUnsafeRules(aAllowUnsafeRules),
   1.408 +    mUseSystemPrincipal(aUseSystemPrincipal),
   1.409 +    mSheetAlreadyComplete(false),
   1.410 +    mOwningElement(nullptr),
   1.411 +    mObserver(aObserver),
   1.412 +    mLoaderPrincipal(aLoaderPrincipal),
   1.413 +    mCharsetHint(aCharset)
   1.414 +{
   1.415 +  NS_PRECONDITION(mLoader, "Must have a loader!");
   1.416 +  NS_ADDREF(mLoader);
   1.417 +
   1.418 +  NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
   1.419 +                   "Shouldn't use system principal for async loads");
   1.420 +}
   1.421 +
   1.422 +SheetLoadData::~SheetLoadData()
   1.423 +{
   1.424 +  NS_RELEASE(mLoader);
   1.425 +  NS_IF_RELEASE(mParentData);
   1.426 +  NS_IF_RELEASE(mNext);
   1.427 +}
   1.428 +
   1.429 +NS_IMETHODIMP
   1.430 +SheetLoadData::Run()
   1.431 +{
   1.432 +  mLoader->HandleLoadEvent(this);
   1.433 +  return NS_OK;
   1.434 +}
   1.435 +
   1.436 +NS_IMETHODIMP
   1.437 +SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
   1.438 +{
   1.439 +  return NS_OK;
   1.440 +}
   1.441 +
   1.442 +NS_IMETHODIMP
   1.443 +SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
   1.444 +                                  bool aMayWait,
   1.445 +                                  uint32_t aRecursionDepth)
   1.446 +{
   1.447 +  // We want to fire our load even before or after event processing,
   1.448 +  // whichever comes first.
   1.449 +  FireLoadEvent(aThread);
   1.450 +  return NS_OK;
   1.451 +}
   1.452 +
   1.453 +NS_IMETHODIMP
   1.454 +SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
   1.455 +                                     uint32_t aRecursionDepth,
   1.456 +                                     bool aEventWasProcessed)
   1.457 +{
   1.458 +  // We want to fire our load even before or after event processing,
   1.459 +  // whichever comes first.
   1.460 +  FireLoadEvent(aThread);
   1.461 +  return NS_OK;
   1.462 +}
   1.463 +
   1.464 +void
   1.465 +SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
   1.466 +{
   1.467 +  
   1.468 +  // First remove ourselves as a thread observer.  But we need to keep
   1.469 +  // ourselves alive while doing that!
   1.470 +  nsRefPtr<SheetLoadData> kungFuDeathGrip(this);
   1.471 +  aThread->RemoveObserver(this);
   1.472 +
   1.473 +  // Now fire the event
   1.474 +  nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
   1.475 +  NS_ASSERTION(node, "How did that happen???");
   1.476 +
   1.477 +  nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
   1.478 +                                       node,
   1.479 +                                       NS_SUCCEEDED(mStatus) ?
   1.480 +                                         NS_LITERAL_STRING("load") :
   1.481 +                                         NS_LITERAL_STRING("error"),
   1.482 +                                       false, false);
   1.483 +
   1.484 +  // And unblock onload
   1.485 +  if (mLoader->mDocument) {
   1.486 +    mLoader->mDocument->UnblockOnload(true);
   1.487 +  }  
   1.488 +}
   1.489 +
   1.490 +void
   1.491 +SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
   1.492 +{
   1.493 +  if (!mOwningElement) {
   1.494 +    return;
   1.495 +  }
   1.496 +
   1.497 +  mStatus = aStatus;
   1.498 +
   1.499 +  nsCOMPtr<nsIThread> thread = do_GetMainThread();
   1.500 +  nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
   1.501 +  if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
   1.502 +    // Make sure to block onload here
   1.503 +    if (mLoader->mDocument) {
   1.504 +      mLoader->mDocument->BlockOnload();
   1.505 +    }
   1.506 +  }
   1.507 +}
   1.508 +
   1.509 +/*************************
   1.510 + * Loader Implementation *
   1.511 + *************************/
   1.512 +
   1.513 +Loader::Loader(void)
   1.514 +  : mDocument(nullptr)
   1.515 +  , mDatasToNotifyOn(0)
   1.516 +  , mCompatMode(eCompatibility_FullStandards)
   1.517 +  , mEnabled(true)
   1.518 +#ifdef DEBUG
   1.519 +  , mSyncCallback(false)
   1.520 +#endif
   1.521 +{
   1.522 +}
   1.523 +
   1.524 +Loader::Loader(nsIDocument* aDocument)
   1.525 +  : mDocument(aDocument)
   1.526 +  , mDatasToNotifyOn(0)
   1.527 +  , mCompatMode(eCompatibility_FullStandards)
   1.528 +  , mEnabled(true)
   1.529 +#ifdef DEBUG
   1.530 +  , mSyncCallback(false)
   1.531 +#endif
   1.532 +{
   1.533 +  // We can just use the preferred set, since there are no sheets in the
   1.534 +  // document yet (if there are, how did they get there? _we_ load the sheets!)
   1.535 +  // and hence the selected set makes no sense at this time.
   1.536 +  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
   1.537 +  if (domDoc) {
   1.538 +    domDoc->GetPreferredStyleSheetSet(mPreferredSheet);
   1.539 +  }
   1.540 +}
   1.541 +
   1.542 +Loader::~Loader()
   1.543 +{
   1.544 +  NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0,
   1.545 +               "How did we get destroyed when there are loading data?");
   1.546 +  NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0,
   1.547 +               "How did we get destroyed when there are pending data?");
   1.548 +  // Note: no real need to revoke our stylesheet loaded events -- they
   1.549 +  // hold strong references to us, so if we're going away that means
   1.550 +  // they're all done.
   1.551 +}
   1.552 +
   1.553 +void
   1.554 +Loader::DropDocumentReference(void)
   1.555 +{
   1.556 +  mDocument = nullptr;
   1.557 +  // Flush out pending datas just so we don't leak by accident.  These
   1.558 +  // loads should short-circuit through the mDocument check in
   1.559 +  // LoadSheet and just end up in SheetComplete immediately
   1.560 +  if (mSheets) {
   1.561 +    StartAlternateLoads();
   1.562 +  }
   1.563 +}
   1.564 +
   1.565 +static PLDHashOperator
   1.566 +CollectNonAlternates(URIPrincipalAndCORSModeHashKey *aKey,
   1.567 +                     SheetLoadData* &aData,
   1.568 +                     void* aClosure)
   1.569 +{
   1.570 +  NS_PRECONDITION(aData, "Must have a data");
   1.571 +  NS_PRECONDITION(aClosure, "Must have an array");
   1.572 +
   1.573 +  // Note that we don't want to affect what the selected style set is,
   1.574 +  // so use true for aHasAlternateRel.
   1.575 +  if (aData->mLoader->IsAlternate(aData->mTitle, true)) {
   1.576 +    return PL_DHASH_NEXT;
   1.577 +  }
   1.578 +
   1.579 +  static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
   1.580 +  return PL_DHASH_REMOVE;
   1.581 +}
   1.582 +
   1.583 +nsresult
   1.584 +Loader::SetPreferredSheet(const nsAString& aTitle)
   1.585 +{
   1.586 +#ifdef DEBUG
   1.587 +  nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
   1.588 +  if (doc) {
   1.589 +    nsAutoString currentPreferred;
   1.590 +    doc->GetLastStyleSheetSet(currentPreferred);
   1.591 +    if (DOMStringIsNull(currentPreferred)) {
   1.592 +      doc->GetPreferredStyleSheetSet(currentPreferred);
   1.593 +    }
   1.594 +    NS_ASSERTION(currentPreferred.Equals(aTitle),
   1.595 +                 "Unexpected argument to SetPreferredSheet");
   1.596 +  }
   1.597 +#endif
   1.598 +
   1.599 +  mPreferredSheet = aTitle;
   1.600 +
   1.601 +  // start any pending alternates that aren't alternates anymore
   1.602 +  if (mSheets) {
   1.603 +    LoadDataArray arr(mSheets->mPendingDatas.Count());
   1.604 +    mSheets->mPendingDatas.Enumerate(CollectNonAlternates, &arr);
   1.605 +
   1.606 +    mDatasToNotifyOn += arr.Length();
   1.607 +    for (uint32_t i = 0; i < arr.Length(); ++i) {
   1.608 +      --mDatasToNotifyOn;
   1.609 +      LoadSheet(arr[i], eSheetNeedsParser);
   1.610 +    }
   1.611 +  }
   1.612 +
   1.613 +  return NS_OK;
   1.614 +}
   1.615 +
   1.616 +static const char kCharsetSym[] = "@charset \"";
   1.617 +
   1.618 +static bool GetCharsetFromData(const char* aStyleSheetData,
   1.619 +                               uint32_t aDataLength,
   1.620 +                               nsACString& aCharset)
   1.621 +{
   1.622 +  aCharset.Truncate();
   1.623 +  if (aDataLength <= sizeof(kCharsetSym) - 1)
   1.624 +    return false;
   1.625 +
   1.626 +  if (strncmp(aStyleSheetData,
   1.627 +              kCharsetSym,
   1.628 +              sizeof(kCharsetSym) - 1)) {
   1.629 +    return false;
   1.630 +  }
   1.631 +
   1.632 +  for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
   1.633 +    char c = aStyleSheetData[i];
   1.634 +    if (c == '"') {
   1.635 +      ++i;
   1.636 +      if (i < aDataLength && aStyleSheetData[i] == ';') {
   1.637 +        return true;
   1.638 +      }
   1.639 +      // fail
   1.640 +      break;
   1.641 +    }
   1.642 +    aCharset.Append(c);
   1.643 +  }
   1.644 +
   1.645 +  // Did not see end quote or semicolon
   1.646 +  aCharset.Truncate();
   1.647 +  return false;
   1.648 +}
   1.649 +
   1.650 +NS_IMETHODIMP
   1.651 +SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
   1.652 +                                  nsISupports* aContext,
   1.653 +                                  nsACString const& aSegment,
   1.654 +                                  nsACString& aCharset)
   1.655 +{
   1.656 +  NS_PRECONDITION(!mOwningElement || mCharsetHint.IsEmpty(),
   1.657 +                  "Can't have element _and_ charset hint");
   1.658 +
   1.659 +  LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
   1.660 +
   1.661 +  // The precedence is (per CSS3 Syntax 2012-11-08 ED):
   1.662 +  // BOM
   1.663 +  // Channel
   1.664 +  // @charset rule
   1.665 +  // charset attribute on the referrer
   1.666 +  // encoding of the referrer
   1.667 +  // UTF-8
   1.668 +
   1.669 +  aCharset.Truncate();
   1.670 +
   1.671 +  if (nsContentUtils::CheckForBOM((const unsigned char*)aSegment.BeginReading(),
   1.672 +                                  aSegment.Length(),
   1.673 +                                  aCharset)) {
   1.674 +    // aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8"
   1.675 +    // which will swallow the BOM.
   1.676 +    mCharset.Assign(aCharset);
   1.677 +#ifdef PR_LOGGING
   1.678 +    LOG(("  Setting from BOM to: %s", PromiseFlatCString(aCharset).get()));
   1.679 +#endif
   1.680 +    return NS_OK;
   1.681 +  }
   1.682 +
   1.683 +  nsCOMPtr<nsIChannel> channel;
   1.684 +  nsAutoCString specified;
   1.685 +  aLoader->GetChannel(getter_AddRefs(channel));
   1.686 +  if (channel) {
   1.687 +    channel->GetContentCharset(specified);
   1.688 +    if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
   1.689 +      mCharset.Assign(aCharset);
   1.690 +#ifdef PR_LOGGING
   1.691 +      LOG(("  Setting from HTTP to: %s", PromiseFlatCString(aCharset).get()));
   1.692 +#endif
   1.693 +      return NS_OK;
   1.694 +    }
   1.695 +  }
   1.696 +
   1.697 +  if (GetCharsetFromData(aSegment.BeginReading(),
   1.698 +                         aSegment.Length(),
   1.699 +                         specified)) {
   1.700 +    if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
   1.701 +      // FindEncodingForLabel currently never returns UTF-16LE but will
   1.702 +      // probably change to never return UTF-16 instead, so check both here
   1.703 +      // to avoid relying on the exact behavior.
   1.704 +      if (aCharset.EqualsLiteral("UTF-16") ||
   1.705 +          aCharset.EqualsLiteral("UTF-16BE") ||
   1.706 +          aCharset.EqualsLiteral("UTF-16LE")) {
   1.707 +        // Be consistent with HTML <meta> handling in face of impossibility.
   1.708 +        // When the @charset rule itself evidently was not UTF-16-encoded,
   1.709 +        // it saying UTF-16 has to be a lie.
   1.710 +        aCharset.AssignLiteral("UTF-8");
   1.711 +      }
   1.712 +      mCharset.Assign(aCharset);
   1.713 +#ifdef PR_LOGGING
   1.714 +      LOG(("  Setting from @charset rule to: %s",
   1.715 +          PromiseFlatCString(aCharset).get()));
   1.716 +#endif
   1.717 +      return NS_OK;
   1.718 +    }
   1.719 +  }
   1.720 +
   1.721 +  // Now try the charset on the <link> or processing instruction
   1.722 +  // that loaded us
   1.723 +  if (mOwningElement) {
   1.724 +    nsAutoString specified16;
   1.725 +    mOwningElement->GetCharset(specified16);
   1.726 +    if (EncodingUtils::FindEncodingForLabel(specified16, aCharset)) {
   1.727 +      mCharset.Assign(aCharset);
   1.728 +#ifdef PR_LOGGING
   1.729 +      LOG(("  Setting from charset attribute to: %s",
   1.730 +          PromiseFlatCString(aCharset).get()));
   1.731 +#endif
   1.732 +      return NS_OK;
   1.733 +    }
   1.734 +  }
   1.735 +
   1.736 +  // In the preload case, the value of the charset attribute on <link> comes
   1.737 +  // in via mCharsetHint instead.
   1.738 +  if (EncodingUtils::FindEncodingForLabel(mCharsetHint, aCharset)) {
   1.739 +    mCharset.Assign(aCharset);
   1.740 +#ifdef PR_LOGGING
   1.741 +      LOG(("  Setting from charset attribute (preload case) to: %s",
   1.742 +          PromiseFlatCString(aCharset).get()));
   1.743 +#endif
   1.744 +    return NS_OK;
   1.745 +  }
   1.746 +
   1.747 +  // Try charset from the parent stylesheet.
   1.748 +  if (mParentData) {
   1.749 +    aCharset = mParentData->mCharset;
   1.750 +    if (!aCharset.IsEmpty()) {
   1.751 +      mCharset.Assign(aCharset);
   1.752 +#ifdef PR_LOGGING
   1.753 +      LOG(("  Setting from parent sheet to: %s",
   1.754 +          PromiseFlatCString(aCharset).get()));
   1.755 +#endif
   1.756 +      return NS_OK;
   1.757 +    }
   1.758 +  }
   1.759 +
   1.760 +  if (mLoader->mDocument) {
   1.761 +    // no useful data on charset.  Try the document charset.
   1.762 +    aCharset = mLoader->mDocument->GetDocumentCharacterSet();
   1.763 +    MOZ_ASSERT(!aCharset.IsEmpty());
   1.764 +    mCharset.Assign(aCharset);
   1.765 +#ifdef PR_LOGGING
   1.766 +    LOG(("  Setting from document to: %s", PromiseFlatCString(aCharset).get()));
   1.767 +#endif
   1.768 +    return NS_OK;
   1.769 +  }
   1.770 +
   1.771 +  aCharset.AssignLiteral("UTF-8");
   1.772 +  mCharset = aCharset;
   1.773 +#ifdef PR_LOGGING
   1.774 +  LOG(("  Setting from default to: %s", PromiseFlatCString(aCharset).get()));
   1.775 +#endif
   1.776 +  return NS_OK;
   1.777 +}
   1.778 +
   1.779 +already_AddRefed<nsIURI>
   1.780 +SheetLoadData::GetReferrerURI()
   1.781 +{
   1.782 +  nsCOMPtr<nsIURI> uri;
   1.783 +  if (mParentData)
   1.784 +    uri = mParentData->mSheet->GetSheetURI();
   1.785 +  if (!uri && mLoader->mDocument)
   1.786 +    uri = mLoader->mDocument->GetDocumentURI();
   1.787 +  return uri.forget();
   1.788 +}
   1.789 +
   1.790 +/*
   1.791 + * Here we need to check that the load did not give us an http error
   1.792 + * page and check the mimetype on the channel to make sure we're not
   1.793 + * loading non-text/css data in standards mode.
   1.794 + */
   1.795 +NS_IMETHODIMP
   1.796 +SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
   1.797 +                                nsISupports* aContext,
   1.798 +                                nsresult aStatus,
   1.799 +                                const nsAString& aBuffer)
   1.800 +{
   1.801 +  LOG(("SheetLoadData::OnStreamComplete"));
   1.802 +  NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
   1.803 +
   1.804 +  if (mIsCancelled) {
   1.805 +    // Just return.  Don't call SheetComplete -- it's already been
   1.806 +    // called and calling it again will lead to an extra NS_RELEASE on
   1.807 +    // this data and a likely crash.
   1.808 +    return NS_OK;
   1.809 +  }
   1.810 +
   1.811 +  if (!mLoader->mDocument && !mIsNonDocumentSheet) {
   1.812 +    // Sorry, we don't care about this load anymore
   1.813 +    LOG_WARN(("  No document and not non-document sheet; dropping load"));
   1.814 +    mLoader->SheetComplete(this, NS_BINDING_ABORTED);
   1.815 +    return NS_OK;
   1.816 +  }
   1.817 +
   1.818 +  if (NS_FAILED(aStatus)) {
   1.819 +    LOG_WARN(("  Load failed: status 0x%x", aStatus));
   1.820 +    mLoader->SheetComplete(this, aStatus);
   1.821 +    return NS_OK;
   1.822 +  }
   1.823 +
   1.824 +  nsCOMPtr<nsIChannel> channel;
   1.825 +  nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
   1.826 +  if (NS_FAILED(result)) {
   1.827 +    LOG_WARN(("  No channel from loader"));
   1.828 +    mLoader->SheetComplete(this, result);
   1.829 +    return NS_OK;
   1.830 +  }
   1.831 +
   1.832 +  nsCOMPtr<nsIURI> originalURI;
   1.833 +  channel->GetOriginalURI(getter_AddRefs(originalURI));
   1.834 +
   1.835 +  // If the channel's original URI is "chrome:", we want that, since
   1.836 +  // the observer code in nsXULPrototypeCache depends on chrome stylesheets
   1.837 +  // having a chrome URI.  (Whether or not chrome stylesheets come through
   1.838 +  // this codepath seems nondeterministic.)
   1.839 +  // Otherwise we want the potentially-HTTP-redirected URI.
   1.840 +  nsCOMPtr<nsIURI> channelURI;
   1.841 +  NS_GetFinalChannelURI(channel, getter_AddRefs(channelURI));
   1.842 +
   1.843 +  if (!channelURI || !originalURI) {
   1.844 +    NS_ERROR("Someone just violated the nsIRequest contract");
   1.845 +    LOG_WARN(("  Channel without a URI.  Bad!"));
   1.846 +    mLoader->SheetComplete(this, NS_ERROR_UNEXPECTED);
   1.847 +    return NS_OK;
   1.848 +  }
   1.849 +
   1.850 +  nsCOMPtr<nsIPrincipal> principal;
   1.851 +  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   1.852 +  result = NS_ERROR_NOT_AVAILABLE;
   1.853 +  if (secMan) {  // Could be null if we already shut down
   1.854 +    if (mUseSystemPrincipal) {
   1.855 +      result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
   1.856 +    } else {
   1.857 +      result = secMan->GetChannelPrincipal(channel, getter_AddRefs(principal));
   1.858 +    }
   1.859 +  }
   1.860 +
   1.861 +  if (NS_FAILED(result)) {
   1.862 +    LOG_WARN(("  Couldn't get principal"));
   1.863 +    mLoader->SheetComplete(this, result);
   1.864 +    return NS_OK;
   1.865 +  }
   1.866 +
   1.867 +  mSheet->SetPrincipal(principal);
   1.868 +
   1.869 +  // If it's an HTTP channel, we want to make sure this is not an
   1.870 +  // error document we got.
   1.871 +  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   1.872 +  if (httpChannel) {
   1.873 +    bool requestSucceeded;
   1.874 +    result = httpChannel->GetRequestSucceeded(&requestSucceeded);
   1.875 +    if (NS_SUCCEEDED(result) && !requestSucceeded) {
   1.876 +      LOG(("  Load returned an error page"));
   1.877 +      mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
   1.878 +      return NS_OK;
   1.879 +    }
   1.880 +  }
   1.881 +
   1.882 +  nsAutoCString contentType;
   1.883 +  if (channel) {
   1.884 +    channel->GetContentType(contentType);
   1.885 +  }
   1.886 +
   1.887 +  // In standards mode, a style sheet must have one of these MIME
   1.888 +  // types to be processed at all.  In quirks mode, we accept any
   1.889 +  // MIME type, but only if the style sheet is same-origin with the
   1.890 +  // requesting document or parent sheet.  See bug 524223.
   1.891 +
   1.892 +  bool validType = contentType.EqualsLiteral("text/css") ||
   1.893 +    contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
   1.894 +    contentType.IsEmpty();
   1.895 +
   1.896 +  if (!validType) {
   1.897 +    const char *errorMessage;
   1.898 +    uint32_t errorFlag;
   1.899 +    bool sameOrigin = true;
   1.900 +
   1.901 +    if (mLoaderPrincipal) {
   1.902 +      bool subsumed;
   1.903 +      result = mLoaderPrincipal->Subsumes(principal, &subsumed);
   1.904 +      if (NS_FAILED(result) || !subsumed) {
   1.905 +        sameOrigin = false;
   1.906 +      }
   1.907 +    }
   1.908 +
   1.909 +    if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
   1.910 +      errorMessage = "MimeNotCssWarn";
   1.911 +      errorFlag = nsIScriptError::warningFlag;
   1.912 +    } else {
   1.913 +      errorMessage = "MimeNotCss";
   1.914 +      errorFlag = nsIScriptError::errorFlag;
   1.915 +    }
   1.916 +
   1.917 +    nsAutoCString spec;
   1.918 +    channelURI->GetSpec(spec);
   1.919 +
   1.920 +    const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec);
   1.921 +    const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
   1.922 +    const char16_t *strings[] = { specUTF16.get(), ctypeUTF16.get() };
   1.923 +
   1.924 +    nsCOMPtr<nsIURI> referrer = GetReferrerURI();
   1.925 +    nsContentUtils::ReportToConsole(errorFlag,
   1.926 +                                    NS_LITERAL_CSTRING("CSS Loader"),
   1.927 +                                    mLoader->mDocument,
   1.928 +                                    nsContentUtils::eCSS_PROPERTIES,
   1.929 +                                    errorMessage,
   1.930 +                                    strings, ArrayLength(strings),
   1.931 +                                    referrer);
   1.932 +
   1.933 +    if (errorFlag == nsIScriptError::errorFlag) {
   1.934 +      LOG_WARN(("  Ignoring sheet with improper MIME type %s",
   1.935 +                contentType.get()));
   1.936 +      mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
   1.937 +      return NS_OK;
   1.938 +    }
   1.939 +  }
   1.940 +
   1.941 +  // Enough to set the URIs on mSheet, since any sibling datas we have share
   1.942 +  // the same mInner as mSheet and will thus get the same URI.
   1.943 +  mSheet->SetURIs(channelURI, originalURI, channelURI);
   1.944 +
   1.945 +  bool completed;
   1.946 +  result = mLoader->ParseSheet(aBuffer, this, completed);
   1.947 +  NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
   1.948 +  return result;
   1.949 +}
   1.950 +
   1.951 +bool
   1.952 +Loader::IsAlternate(const nsAString& aTitle, bool aHasAlternateRel)
   1.953 +{
   1.954 +  // A sheet is alternate if it has a nonempty title that doesn't match the
   1.955 +  // currently selected style set.  But if there _is_ no currently selected
   1.956 +  // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
   1.957 +  // is nonempty, we should select the style set corresponding to aTitle, since
   1.958 +  // that's a preferred sheet.
   1.959 +  if (aTitle.IsEmpty()) {
   1.960 +    return false;
   1.961 +  }
   1.962 +
   1.963 +  if (!aHasAlternateRel && mDocument && mPreferredSheet.IsEmpty()) {
   1.964 +    // There's no preferred set yet, and we now have a sheet with a title.
   1.965 +    // Make that be the preferred set.
   1.966 +    mDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, aTitle);
   1.967 +    // We're definitely not an alternate
   1.968 +    return false;
   1.969 +  }
   1.970 +
   1.971 +  return !aTitle.Equals(mPreferredSheet);
   1.972 +}
   1.973 +
   1.974 +/* static */ PLDHashOperator
   1.975 +Loader::RemoveEntriesWithURI(URIPrincipalAndCORSModeHashKey* aKey,
   1.976 +                             nsRefPtr<nsCSSStyleSheet> &aSheet,
   1.977 +                             void* aUserData)
   1.978 +{
   1.979 +  nsIURI* obsoleteURI = static_cast<nsIURI*>(aUserData);
   1.980 +  nsIURI* sheetURI = aKey->GetURI();
   1.981 +  bool areEqual;
   1.982 +  nsresult rv = sheetURI->Equals(obsoleteURI, &areEqual);
   1.983 +  if (NS_SUCCEEDED(rv) && areEqual) {
   1.984 +    return PL_DHASH_REMOVE;
   1.985 +  }
   1.986 +  return PL_DHASH_NEXT;
   1.987 +}
   1.988 +
   1.989 +nsresult
   1.990 +Loader::ObsoleteSheet(nsIURI* aURI)
   1.991 +{
   1.992 +  if (!mSheets) {
   1.993 +    return NS_OK;
   1.994 +  }
   1.995 +  if (!aURI) {
   1.996 +    return NS_ERROR_INVALID_ARG;
   1.997 +  }
   1.998 +  mSheets->mCompleteSheets.Enumerate(RemoveEntriesWithURI, aURI);
   1.999 +  return NS_OK;
  1.1000 +}
  1.1001 +
  1.1002 +/**
  1.1003 + * CheckLoadAllowed will return success if the load is allowed,
  1.1004 + * failure otherwise.
  1.1005 + *
  1.1006 + * @param aSourcePrincipal the principal of the node or document or parent
  1.1007 + *                         sheet loading the sheet
  1.1008 + * @param aTargetURI the uri of the sheet to be loaded
  1.1009 + * @param aContext the node owning the sheet.  This is the element or document
  1.1010 + *                 owning the stylesheet (possibly indirectly, for child sheets)
  1.1011 + */
  1.1012 +nsresult
  1.1013 +Loader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
  1.1014 +                         nsIURI* aTargetURI,
  1.1015 +                         nsISupports* aContext)
  1.1016 +{
  1.1017 +  LOG(("css::Loader::CheckLoadAllowed"));
  1.1018 +
  1.1019 +  nsresult rv;
  1.1020 +
  1.1021 +  if (aSourcePrincipal) {
  1.1022 +    // Check with the security manager
  1.1023 +    nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
  1.1024 +    rv =
  1.1025 +      secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
  1.1026 +                                        nsIScriptSecurityManager::ALLOW_CHROME);
  1.1027 +    if (NS_FAILED(rv)) { // failure is normal here; don't warn
  1.1028 +      return rv;
  1.1029 +    }
  1.1030 +
  1.1031 +    LOG(("  Passed security check"));
  1.1032 +
  1.1033 +    // Check with content policy
  1.1034 +
  1.1035 +    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
  1.1036 +    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
  1.1037 +                                   aTargetURI,
  1.1038 +                                   aSourcePrincipal,
  1.1039 +                                   aContext,
  1.1040 +                                   NS_LITERAL_CSTRING("text/css"),
  1.1041 +                                   nullptr,                     //extra param
  1.1042 +                                   &shouldLoad,
  1.1043 +                                   nsContentUtils::GetContentPolicy(),
  1.1044 +                                   nsContentUtils::GetSecurityManager());
  1.1045 +
  1.1046 +    if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
  1.1047 +      LOG(("  Load blocked by content policy"));
  1.1048 +      return NS_ERROR_CONTENT_BLOCKED;
  1.1049 +    }
  1.1050 +  }
  1.1051 +
  1.1052 +  return NS_OK;
  1.1053 +}
  1.1054 +
  1.1055 +/**
  1.1056 + * CreateSheet() creates an nsCSSStyleSheet object for the given URI,
  1.1057 + * if any.  If there is no URI given, we just create a new style sheet
  1.1058 + * object.  Otherwise, we check for an existing style sheet object for
  1.1059 + * that uri in various caches and clone it if we find it.  Cloned
  1.1060 + * sheets will have the title/media/enabled state of the sheet they
  1.1061 + * are clones off; make sure to call PrepareSheet() on the result of
  1.1062 + * CreateSheet().
  1.1063 + */
  1.1064 +nsresult
  1.1065 +Loader::CreateSheet(nsIURI* aURI,
  1.1066 +                    nsIContent* aLinkingContent,
  1.1067 +                    nsIPrincipal* aLoaderPrincipal,
  1.1068 +                    CORSMode aCORSMode,
  1.1069 +                    bool aSyncLoad,
  1.1070 +                    bool aHasAlternateRel,
  1.1071 +                    const nsAString& aTitle,                       
  1.1072 +                    StyleSheetState& aSheetState,
  1.1073 +                    bool *aIsAlternate,
  1.1074 +                    nsCSSStyleSheet** aSheet)
  1.1075 +{
  1.1076 +  LOG(("css::Loader::CreateSheet"));
  1.1077 +  NS_PRECONDITION(aSheet, "Null out param!");
  1.1078 +
  1.1079 +  if (!mSheets) {
  1.1080 +    mSheets = new Sheets();
  1.1081 +  }
  1.1082 +
  1.1083 +  *aSheet = nullptr;
  1.1084 +  aSheetState = eSheetStateUnknown;
  1.1085 +
  1.1086 +  // Check the alternate state before doing anything else, because it
  1.1087 +  // can mess with our hashtables.
  1.1088 +  *aIsAlternate = IsAlternate(aTitle, aHasAlternateRel);
  1.1089 +
  1.1090 +  if (aURI) {
  1.1091 +    aSheetState = eSheetComplete;
  1.1092 +    nsRefPtr<nsCSSStyleSheet> sheet;
  1.1093 +
  1.1094 +    // First, the XUL cache
  1.1095 +#ifdef MOZ_XUL
  1.1096 +    if (IsChromeURI(aURI)) {
  1.1097 +      nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
  1.1098 +      if (cache) {
  1.1099 +        if (cache->IsEnabled()) {
  1.1100 +          sheet = cache->GetStyleSheet(aURI);
  1.1101 +          LOG(("  From XUL cache: %p", sheet.get()));
  1.1102 +        }
  1.1103 +      }
  1.1104 +    }
  1.1105 +#endif
  1.1106 +
  1.1107 +    bool fromCompleteSheets = false;
  1.1108 +    if (!sheet) {
  1.1109 +      // Then our per-document complete sheets.
  1.1110 +      URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode);
  1.1111 +
  1.1112 +      mSheets->mCompleteSheets.Get(&key, getter_AddRefs(sheet));
  1.1113 +      LOG(("  From completed: %p", sheet.get()));
  1.1114 +
  1.1115 +      fromCompleteSheets = !!sheet;
  1.1116 +    }
  1.1117 +
  1.1118 +    if (sheet) {
  1.1119 +      // This sheet came from the XUL cache or our per-document hashtable; it
  1.1120 +      // better be a complete sheet.
  1.1121 +      NS_ASSERTION(sheet->IsComplete(),
  1.1122 +                   "Sheet thinks it's not complete while we think it is");
  1.1123 +
  1.1124 +      // Make sure it hasn't been modified; if it has, we can't use it
  1.1125 +      if (sheet->IsModified()) {
  1.1126 +        LOG(("  Not cloning completed sheet %p because it's been modified",
  1.1127 +             sheet.get()));
  1.1128 +        sheet = nullptr;
  1.1129 +        fromCompleteSheets = false;
  1.1130 +      }
  1.1131 +    }
  1.1132 +
  1.1133 +    // Then loading sheets
  1.1134 +    if (!sheet && !aSyncLoad) {
  1.1135 +      aSheetState = eSheetLoading;
  1.1136 +      SheetLoadData* loadData = nullptr;
  1.1137 +      URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode);
  1.1138 +      mSheets->mLoadingDatas.Get(&key, &loadData);
  1.1139 +      if (loadData) {
  1.1140 +        sheet = loadData->mSheet;
  1.1141 +        LOG(("  From loading: %p", sheet.get()));
  1.1142 +
  1.1143 +#ifdef DEBUG
  1.1144 +        bool debugEqual;
  1.1145 +        NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
  1.1146 +                     (aLoaderPrincipal && loadData->mLoaderPrincipal &&
  1.1147 +                      NS_SUCCEEDED(aLoaderPrincipal->
  1.1148 +                                   Equals(loadData->mLoaderPrincipal,
  1.1149 +                                          &debugEqual)) && debugEqual),
  1.1150 +                     "Principals should be the same");
  1.1151 +#endif
  1.1152 +      }
  1.1153 +
  1.1154 +      // Then alternate sheets
  1.1155 +      if (!sheet) {
  1.1156 +        aSheetState = eSheetPending;
  1.1157 +        loadData = nullptr;
  1.1158 +        mSheets->mPendingDatas.Get(&key, &loadData);
  1.1159 +        if (loadData) {
  1.1160 +          sheet = loadData->mSheet;
  1.1161 +          LOG(("  From pending: %p", sheet.get()));
  1.1162 +
  1.1163 +#ifdef DEBUG
  1.1164 +          bool debugEqual;
  1.1165 +          NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
  1.1166 +                       (aLoaderPrincipal && loadData->mLoaderPrincipal &&
  1.1167 +                        NS_SUCCEEDED(aLoaderPrincipal->
  1.1168 +                                     Equals(loadData->mLoaderPrincipal,
  1.1169 +                                            &debugEqual)) && debugEqual),
  1.1170 +                       "Principals should be the same");
  1.1171 +#endif
  1.1172 +        }
  1.1173 +      }
  1.1174 +    }
  1.1175 +
  1.1176 +    if (sheet) {
  1.1177 +      // The sheet we have now should be either incomplete or unmodified
  1.1178 +      NS_ASSERTION(!sheet->IsModified() || !sheet->IsComplete(),
  1.1179 +                   "Unexpected modified complete sheet");
  1.1180 +      NS_ASSERTION(sheet->IsComplete() || aSheetState != eSheetComplete,
  1.1181 +                   "Sheet thinks it's not complete while we think it is");
  1.1182 +
  1.1183 +      *aSheet = sheet->Clone(nullptr, nullptr, nullptr, nullptr).take();
  1.1184 +      if (*aSheet && fromCompleteSheets &&
  1.1185 +          !sheet->GetOwnerNode() && !sheet->GetParentSheet()) {
  1.1186 +        // The sheet we're cloning isn't actually referenced by
  1.1187 +        // anyone.  Replace it in the cache, so that if our CSSOM is
  1.1188 +        // later modified we don't end up with two copies of our inner
  1.1189 +        // hanging around.
  1.1190 +        URIPrincipalAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode);
  1.1191 +        NS_ASSERTION((*aSheet)->IsComplete(),
  1.1192 +                     "Should only be caching complete sheets");
  1.1193 +        mSheets->mCompleteSheets.Put(&key, *aSheet);
  1.1194 +      }
  1.1195 +    }
  1.1196 +  }
  1.1197 +
  1.1198 +  if (!*aSheet) {
  1.1199 +    aSheetState = eSheetNeedsParser;
  1.1200 +    nsIURI *sheetURI;
  1.1201 +    nsCOMPtr<nsIURI> baseURI;
  1.1202 +    nsIURI* originalURI;
  1.1203 +    if (!aURI) {
  1.1204 +      // Inline style.  Use the document's base URL so that @import in
  1.1205 +      // the inline sheet picks up the right base.
  1.1206 +      NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?");
  1.1207 +      baseURI = aLinkingContent->GetBaseURI();
  1.1208 +      sheetURI = aLinkingContent->GetDocument()->GetDocumentURI();
  1.1209 +      originalURI = nullptr;
  1.1210 +    } else {
  1.1211 +      baseURI = aURI;
  1.1212 +      sheetURI = aURI;
  1.1213 +      originalURI = aURI;
  1.1214 +    }
  1.1215 +
  1.1216 +    nsRefPtr<nsCSSStyleSheet> sheet = new nsCSSStyleSheet(aCORSMode);
  1.1217 +    sheet->SetURIs(sheetURI, originalURI, baseURI);
  1.1218 +    sheet.forget(aSheet);
  1.1219 +  }
  1.1220 +
  1.1221 +  NS_ASSERTION(*aSheet, "We should have a sheet by now!");
  1.1222 +  NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
  1.1223 +  LOG(("  State: %s", gStateStrings[aSheetState]));
  1.1224 +
  1.1225 +  return NS_OK;
  1.1226 +}
  1.1227 +
  1.1228 +/**
  1.1229 + * PrepareSheet() handles setting the media and title on the sheet, as
  1.1230 + * well as setting the enabled state based on the title and whether
  1.1231 + * the sheet had "alternate" in its rel.
  1.1232 + */
  1.1233 +void
  1.1234 +Loader::PrepareSheet(nsCSSStyleSheet* aSheet,
  1.1235 +                     const nsSubstring& aTitle,
  1.1236 +                     const nsSubstring& aMediaString,
  1.1237 +                     nsMediaList* aMediaList,
  1.1238 +                     Element* aScopeElement,
  1.1239 +                     bool isAlternate)
  1.1240 +{
  1.1241 +  NS_PRECONDITION(aSheet, "Must have a sheet!");
  1.1242 +
  1.1243 +  nsRefPtr<nsMediaList> mediaList(aMediaList);
  1.1244 +
  1.1245 +  if (!aMediaString.IsEmpty()) {
  1.1246 +    NS_ASSERTION(!aMediaList,
  1.1247 +                 "must not provide both aMediaString and aMediaList");
  1.1248 +    mediaList = new nsMediaList();
  1.1249 +
  1.1250 +    nsCSSParser mediumParser(this);
  1.1251 +
  1.1252 +    // We have aMediaString only when linked from link elements, style
  1.1253 +    // elements, or PIs, so pass true.
  1.1254 +    mediumParser.ParseMediaList(aMediaString, nullptr, 0, mediaList, true);
  1.1255 +  }
  1.1256 +
  1.1257 +  aSheet->SetMedia(mediaList);
  1.1258 +
  1.1259 +  aSheet->SetTitle(aTitle);
  1.1260 +  aSheet->SetEnabled(! isAlternate);
  1.1261 +  aSheet->SetScopeElement(aScopeElement);
  1.1262 +}
  1.1263 +
  1.1264 +/**
  1.1265 + * InsertSheetInDoc handles ordering of sheets in the document.  Here
  1.1266 + * we have two types of sheets -- those with linking elements and
  1.1267 + * those without.  The latter are loaded by Link: headers.
  1.1268 + * The following constraints are observed:
  1.1269 + * 1) Any sheet with a linking element comes after all sheets without
  1.1270 + *    linking elements
  1.1271 + * 2) Sheets without linking elements are inserted in the order in
  1.1272 + *    which the inserting requests come in, since all of these are
  1.1273 + *    inserted during header data processing in the content sink
  1.1274 + * 3) Sheets with linking elements are ordered based on document order
  1.1275 + *    as determined by CompareDocumentPosition.
  1.1276 + */
  1.1277 +nsresult
  1.1278 +Loader::InsertSheetInDoc(nsCSSStyleSheet* aSheet,
  1.1279 +                         nsIContent* aLinkingContent,
  1.1280 +                         nsIDocument* aDocument)
  1.1281 +{
  1.1282 +  LOG(("css::Loader::InsertSheetInDoc"));
  1.1283 +  NS_PRECONDITION(aSheet, "Nothing to insert");
  1.1284 +  NS_PRECONDITION(aDocument, "Must have a document to insert into");
  1.1285 +
  1.1286 +  // XXX Need to cancel pending sheet loads for this element, if any
  1.1287 +
  1.1288 +  int32_t sheetCount = aDocument->GetNumberOfStyleSheets();
  1.1289 +
  1.1290 +  /*
  1.1291 +   * Start the walk at the _end_ of the list, since in the typical
  1.1292 +   * case we'll just want to append anyway.  We want to break out of
  1.1293 +   * the loop when insertionPoint points to just before the index we
  1.1294 +   * want to insert at.  In other words, when we leave the loop
  1.1295 +   * insertionPoint is the index of the stylesheet that immediately
  1.1296 +   * precedes the one we're inserting.
  1.1297 +   */
  1.1298 +  int32_t insertionPoint;
  1.1299 +  for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) {
  1.1300 +    nsIStyleSheet *curSheet = aDocument->GetStyleSheetAt(insertionPoint);
  1.1301 +    NS_ASSERTION(curSheet, "There must be a sheet here!");
  1.1302 +    nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(curSheet);
  1.1303 +    NS_ASSERTION(domSheet, "All the \"normal\" sheets implement nsIDOMStyleSheet");
  1.1304 +    nsCOMPtr<nsIDOMNode> sheetOwner;
  1.1305 +    domSheet->GetOwnerNode(getter_AddRefs(sheetOwner));
  1.1306 +    if (sheetOwner && !aLinkingContent) {
  1.1307 +      // Keep moving; all sheets with a sheetOwner come after all
  1.1308 +      // sheets without a linkingNode
  1.1309 +      continue;
  1.1310 +    }
  1.1311 +
  1.1312 +    if (!sheetOwner) {
  1.1313 +      // Aha!  The current sheet has no sheet owner, so we want to
  1.1314 +      // insert after it no matter whether we have a linkingNode
  1.1315 +      break;
  1.1316 +    }
  1.1317 +
  1.1318 +    nsCOMPtr<nsINode> sheetOwnerNode = do_QueryInterface(sheetOwner);
  1.1319 +    NS_ASSERTION(aLinkingContent != sheetOwnerNode,
  1.1320 +                 "Why do we still have our old sheet?");
  1.1321 +
  1.1322 +    // Have to compare
  1.1323 +    if (nsContentUtils::PositionIsBefore(sheetOwnerNode, aLinkingContent)) {
  1.1324 +      // The current sheet comes before us, and it better be the first
  1.1325 +      // such, because now we break
  1.1326 +      break;
  1.1327 +    }
  1.1328 +  }
  1.1329 +
  1.1330 +  ++insertionPoint; // adjust the index to the spot we want to insert in
  1.1331 +
  1.1332 +  // XXX <meta> elements do not implement nsIStyleSheetLinkingElement;
  1.1333 +  // need to fix this for them to be ordered correctly.
  1.1334 +  nsCOMPtr<nsIStyleSheetLinkingElement>
  1.1335 +    linkingElement = do_QueryInterface(aLinkingContent);
  1.1336 +  if (linkingElement) {
  1.1337 +    linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
  1.1338 +  }
  1.1339 +
  1.1340 +  aDocument->BeginUpdate(UPDATE_STYLE);
  1.1341 +  aDocument->InsertStyleSheetAt(aSheet, insertionPoint);
  1.1342 +  aDocument->EndUpdate(UPDATE_STYLE);
  1.1343 +  LOG(("  Inserting into document at position %d", insertionPoint));
  1.1344 +
  1.1345 +  return NS_OK;
  1.1346 +}
  1.1347 +
  1.1348 +/**
  1.1349 + * InsertChildSheet handles ordering of @import-ed sheet in their
  1.1350 + * parent sheets.  Here we want to just insert based on order of the
  1.1351 + * @import rules that imported the sheets.  In theory we can't just
  1.1352 + * append to the end because the CSSOM can insert @import rules.  In
  1.1353 + * practice, we get the call to load the child sheet before the CSSOM
  1.1354 + * has finished inserting the @import rule, so we have no idea where
  1.1355 + * to put it anyway.  So just append for now.
  1.1356 + */
  1.1357 +nsresult
  1.1358 +Loader::InsertChildSheet(nsCSSStyleSheet* aSheet,
  1.1359 +                         nsCSSStyleSheet* aParentSheet,
  1.1360 +                         ImportRule* aParentRule)
  1.1361 +{
  1.1362 +  LOG(("css::Loader::InsertChildSheet"));
  1.1363 +  NS_PRECONDITION(aSheet, "Nothing to insert");
  1.1364 +  NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
  1.1365 +  NS_PRECONDITION(aParentSheet, "How did we get imported?");
  1.1366 +
  1.1367 +  // child sheets should always start out enabled, even if they got
  1.1368 +  // cloned off of top-level sheets which were disabled
  1.1369 +  aSheet->SetEnabled(true);
  1.1370 +
  1.1371 +  aParentSheet->AppendStyleSheet(aSheet);
  1.1372 +  aParentRule->SetSheet(aSheet); // This sets the ownerRule on the sheet
  1.1373 +
  1.1374 +  LOG(("  Inserting into parent sheet"));
  1.1375 +  //  LOG(("  Inserting into parent sheet at position %d", insertionPoint));
  1.1376 +
  1.1377 +  return NS_OK;
  1.1378 +}
  1.1379 +
  1.1380 +/**
  1.1381 + * LoadSheet handles the actual load of a sheet.  If the load is
  1.1382 + * supposed to be synchronous it just opens a channel synchronously
  1.1383 + * using the given uri, wraps the resulting stream in a converter
  1.1384 + * stream and calls ParseSheet.  Otherwise it tries to look for an
  1.1385 + * existing load for this URI and piggyback on it.  Failing all that,
  1.1386 + * a new load is kicked off asynchronously.
  1.1387 + */
  1.1388 +nsresult
  1.1389 +Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
  1.1390 +{
  1.1391 +  LOG(("css::Loader::LoadSheet"));
  1.1392 +  NS_PRECONDITION(aLoadData, "Need a load data");
  1.1393 +  NS_PRECONDITION(aLoadData->mURI, "Need a URI to load");
  1.1394 +  NS_PRECONDITION(aLoadData->mSheet, "Need a sheet to load into");
  1.1395 +  NS_PRECONDITION(aSheetState != eSheetComplete, "Why bother?");
  1.1396 +  NS_PRECONDITION(!aLoadData->mUseSystemPrincipal || aLoadData->mSyncLoad,
  1.1397 +                  "Shouldn't use system principal for async loads");
  1.1398 +  NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
  1.1399 +
  1.1400 +  LOG_URI("  Load from: '%s'", aLoadData->mURI);
  1.1401 +
  1.1402 +  nsresult rv = NS_OK;
  1.1403 +
  1.1404 +  if (!mDocument && !aLoadData->mIsNonDocumentSheet) {
  1.1405 +    // No point starting the load; just release all the data and such.
  1.1406 +    LOG_WARN(("  No document and not non-document sheet; pre-dropping load"));
  1.1407 +    SheetComplete(aLoadData, NS_BINDING_ABORTED);
  1.1408 +    return NS_BINDING_ABORTED;
  1.1409 +  }
  1.1410 +
  1.1411 +  if (aLoadData->mSyncLoad) {
  1.1412 +    LOG(("  Synchronous load"));
  1.1413 +    NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
  1.1414 +    NS_ASSERTION(aSheetState == eSheetNeedsParser,
  1.1415 +                 "Sync loads can't reuse existing async loads");
  1.1416 +
  1.1417 +    // Create a nsIUnicharStreamLoader instance to which we will feed
  1.1418 +    // the data from the sync load.  Do this before creating the
  1.1419 +    // channel to make error recovery simpler.
  1.1420 +    nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
  1.1421 +    rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
  1.1422 +    if (NS_FAILED(rv)) {
  1.1423 +      LOG_ERROR(("  Failed to create stream loader for sync load"));
  1.1424 +      SheetComplete(aLoadData, rv);
  1.1425 +      return rv;
  1.1426 +    }
  1.1427 +
  1.1428 +    if (mDocument) {
  1.1429 +      mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
  1.1430 +                              nsINetworkSeer::LEARN_LOAD_SUBRESOURCE,
  1.1431 +                              mDocument);
  1.1432 +    }
  1.1433 +
  1.1434 +    // Just load it
  1.1435 +    nsCOMPtr<nsIInputStream> stream;
  1.1436 +    nsCOMPtr<nsIChannel> channel;
  1.1437 +    rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI, nullptr,
  1.1438 +                    nullptr, nullptr, nsIRequest::LOAD_NORMAL,
  1.1439 +                    getter_AddRefs(channel));
  1.1440 +    if (NS_FAILED(rv)) {
  1.1441 +      LOG_ERROR(("  Failed to open URI synchronously"));
  1.1442 +      SheetComplete(aLoadData, rv);
  1.1443 +      return rv;
  1.1444 +    }
  1.1445 +
  1.1446 +    NS_ASSERTION(channel, "NS_OpenURI lied?");
  1.1447 +
  1.1448 +    // Force UA sheets to be UTF-8.
  1.1449 +    // XXX this is only necessary because the default in
  1.1450 +    // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
  1.1451 +    channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
  1.1452 +
  1.1453 +    // Manually feed the streamloader the contents of the stream we
  1.1454 +    // got from NS_OpenURI.  This will call back into OnStreamComplete
  1.1455 +    // and thence to ParseSheet.  Regardless of whether this fails,
  1.1456 +    // SheetComplete has been called.
  1.1457 +    return nsSyncLoadService::PushSyncStreamToListener(stream,
  1.1458 +                                                       streamLoader,
  1.1459 +                                                       channel);
  1.1460 +  }
  1.1461 +
  1.1462 +  SheetLoadData* existingData = nullptr;
  1.1463 +
  1.1464 +  URIPrincipalAndCORSModeHashKey key(aLoadData->mURI,
  1.1465 +                                     aLoadData->mLoaderPrincipal,
  1.1466 +                                     aLoadData->mSheet->GetCORSMode());
  1.1467 +  if (aSheetState == eSheetLoading) {
  1.1468 +    mSheets->mLoadingDatas.Get(&key, &existingData);
  1.1469 +    NS_ASSERTION(existingData, "CreateSheet lied about the state");
  1.1470 +  }
  1.1471 +  else if (aSheetState == eSheetPending){
  1.1472 +    mSheets->mPendingDatas.Get(&key, &existingData);
  1.1473 +    NS_ASSERTION(existingData, "CreateSheet lied about the state");
  1.1474 +  }
  1.1475 +
  1.1476 +  if (existingData) {
  1.1477 +    LOG(("  Glomming on to existing load"));
  1.1478 +    SheetLoadData* data = existingData;
  1.1479 +    while (data->mNext) {
  1.1480 +      data = data->mNext;
  1.1481 +    }
  1.1482 +    data->mNext = aLoadData; // transfer ownership
  1.1483 +    if (aSheetState == eSheetPending && !aLoadData->mWasAlternate) {
  1.1484 +      // Kick the load off; someone cares about it right away
  1.1485 +
  1.1486 +#ifdef DEBUG
  1.1487 +      SheetLoadData* removedData;
  1.1488 +      NS_ASSERTION(mSheets->mPendingDatas.Get(&key, &removedData) &&
  1.1489 +                   removedData == existingData,
  1.1490 +                   "Bad pending table.");
  1.1491 +#endif
  1.1492 +
  1.1493 +      mSheets->mPendingDatas.Remove(&key);
  1.1494 +
  1.1495 +      LOG(("  Forcing load of pending data"));
  1.1496 +      return LoadSheet(existingData, eSheetNeedsParser);
  1.1497 +    }
  1.1498 +    // All done here; once the load completes we'll be marked complete
  1.1499 +    // automatically
  1.1500 +    return NS_OK;
  1.1501 +  }
  1.1502 +
  1.1503 +#ifdef DEBUG
  1.1504 +  mSyncCallback = true;
  1.1505 +#endif
  1.1506 +  nsCOMPtr<nsILoadGroup> loadGroup;
  1.1507 +  // Content Security Policy information to pass into channel
  1.1508 +  nsCOMPtr<nsIChannelPolicy> channelPolicy;
  1.1509 +  if (mDocument) {
  1.1510 +    loadGroup = mDocument->GetDocumentLoadGroup();
  1.1511 +    NS_ASSERTION(loadGroup,
  1.1512 +                 "No loadgroup for stylesheet; onload will fire early");
  1.1513 +    nsCOMPtr<nsIContentSecurityPolicy> csp;
  1.1514 +    rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
  1.1515 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1516 +    if (csp) {
  1.1517 +      channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
  1.1518 +      channelPolicy->SetContentSecurityPolicy(csp);
  1.1519 +      channelPolicy->SetLoadType(nsIContentPolicy::TYPE_STYLESHEET);
  1.1520 +    }
  1.1521 +  }
  1.1522 +
  1.1523 +  nsCOMPtr<nsIChannel> channel;
  1.1524 +  rv = NS_NewChannel(getter_AddRefs(channel),
  1.1525 +                     aLoadData->mURI, nullptr, loadGroup, nullptr,
  1.1526 +                     nsIChannel::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI,
  1.1527 +                     channelPolicy);
  1.1528 +
  1.1529 +  if (NS_FAILED(rv)) {
  1.1530 +#ifdef DEBUG
  1.1531 +    mSyncCallback = false;
  1.1532 +#endif
  1.1533 +    LOG_ERROR(("  Failed to create channel"));
  1.1534 +    SheetComplete(aLoadData, rv);
  1.1535 +    return rv;
  1.1536 +  }
  1.1537 +
  1.1538 +  nsCOMPtr<nsIHttpChannelInternal>
  1.1539 +    internalHttpChannel(do_QueryInterface(channel));
  1.1540 +  if (internalHttpChannel)
  1.1541 +      internalHttpChannel->SetLoadAsBlocking(!aLoadData->mWasAlternate);
  1.1542 +
  1.1543 +  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
  1.1544 +  if (httpChannel) {
  1.1545 +    // send a minimal Accept header for text/css
  1.1546 +    httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
  1.1547 +                                  NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
  1.1548 +                                  false);
  1.1549 +    nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
  1.1550 +    if (referrerURI)
  1.1551 +      httpChannel->SetReferrer(referrerURI);
  1.1552 +
  1.1553 +    // Set the initiator type
  1.1554 +    nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
  1.1555 +    if (timedChannel) {
  1.1556 +      if (aLoadData->mParentData) {
  1.1557 +        timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
  1.1558 +      } else {
  1.1559 +        timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
  1.1560 +      }
  1.1561 +    }
  1.1562 +  }
  1.1563 +
  1.1564 +  // Now tell the channel we expect text/css data back....  We do
  1.1565 +  // this before opening it, so it's only treated as a hint.
  1.1566 +  channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
  1.1567 +
  1.1568 +  if (aLoadData->mLoaderPrincipal) {
  1.1569 +    bool inherit;
  1.1570 +    rv = NS_URIChainHasFlags(aLoadData->mURI,
  1.1571 +                             nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
  1.1572 +                             &inherit);
  1.1573 +    if ((NS_SUCCEEDED(rv) && inherit) ||
  1.1574 +        (nsContentUtils::URIIsLocalFile(aLoadData->mURI) &&
  1.1575 +         NS_SUCCEEDED(aLoadData->mLoaderPrincipal->
  1.1576 +                      CheckMayLoad(aLoadData->mURI, false, false)))) {
  1.1577 +      channel->SetOwner(aLoadData->mLoaderPrincipal);
  1.1578 +    }
  1.1579 +  }
  1.1580 +
  1.1581 +  // We don't have to hold on to the stream loader.  The ownership
  1.1582 +  // model is: Necko owns the stream loader, which owns the load data,
  1.1583 +  // which owns us
  1.1584 +  nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
  1.1585 +  rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
  1.1586 +  if (NS_FAILED(rv)) {
  1.1587 +#ifdef DEBUG
  1.1588 +    mSyncCallback = false;
  1.1589 +#endif
  1.1590 +    LOG_ERROR(("  Failed to create stream loader"));
  1.1591 +    SheetComplete(aLoadData, rv);
  1.1592 +    return rv;
  1.1593 +  }
  1.1594 +
  1.1595 +  nsCOMPtr<nsIStreamListener> channelListener;
  1.1596 +  CORSMode ourCORSMode = aLoadData->mSheet->GetCORSMode();
  1.1597 +  if (ourCORSMode != CORS_NONE) {
  1.1598 +    bool withCredentials = (ourCORSMode == CORS_USE_CREDENTIALS);
  1.1599 +    LOG(("  Doing CORS-enabled load; credentials %d", withCredentials));
  1.1600 +    nsRefPtr<nsCORSListenerProxy> corsListener =
  1.1601 +      new nsCORSListenerProxy(streamLoader, aLoadData->mLoaderPrincipal,
  1.1602 +			      withCredentials);
  1.1603 +    rv = corsListener->Init(channel);
  1.1604 +    if (NS_FAILED(rv)) {
  1.1605 +#ifdef DEBUG
  1.1606 +      mSyncCallback = false;
  1.1607 +#endif
  1.1608 +      LOG_ERROR(("  Initial CORS check failed"));
  1.1609 +      SheetComplete(aLoadData, rv);
  1.1610 +      return rv;
  1.1611 +    }
  1.1612 +    channelListener = corsListener;
  1.1613 +  } else {
  1.1614 +    channelListener = streamLoader;
  1.1615 +  }
  1.1616 +
  1.1617 +  if (mDocument) {
  1.1618 +    mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
  1.1619 +                            nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument);
  1.1620 +  }
  1.1621 +
  1.1622 +  rv = channel->AsyncOpen(channelListener, nullptr);
  1.1623 +
  1.1624 +#ifdef DEBUG
  1.1625 +  mSyncCallback = false;
  1.1626 +#endif
  1.1627 +
  1.1628 +  if (NS_FAILED(rv)) {
  1.1629 +    LOG_ERROR(("  Failed to create stream loader"));
  1.1630 +    SheetComplete(aLoadData, rv);
  1.1631 +    return rv;
  1.1632 +  }
  1.1633 +
  1.1634 +  mSheets->mLoadingDatas.Put(&key, aLoadData);
  1.1635 +  aLoadData->mIsLoading = true;
  1.1636 +
  1.1637 +  return NS_OK;
  1.1638 +}
  1.1639 +
  1.1640 +/**
  1.1641 + * ParseSheet handles parsing the data stream.  The main idea here is
  1.1642 + * to push the current load data onto the parse stack before letting
  1.1643 + * the CSS parser at the data stream.  That lets us handle @import
  1.1644 + * correctly.
  1.1645 + */
  1.1646 +nsresult
  1.1647 +Loader::ParseSheet(const nsAString& aInput,
  1.1648 +                   SheetLoadData* aLoadData,
  1.1649 +                   bool& aCompleted)
  1.1650 +{
  1.1651 +  LOG(("css::Loader::ParseSheet"));
  1.1652 +  NS_PRECONDITION(aLoadData, "Must have load data");
  1.1653 +  NS_PRECONDITION(aLoadData->mSheet, "Must have sheet to parse into");
  1.1654 +
  1.1655 +  aCompleted = false;
  1.1656 +
  1.1657 +  nsCSSParser parser(this, aLoadData->mSheet);
  1.1658 +
  1.1659 +  // Push our load data on the stack so any kids can pick it up
  1.1660 +  mParsingDatas.AppendElement(aLoadData);
  1.1661 +  nsIURI* sheetURI = aLoadData->mSheet->GetSheetURI();
  1.1662 +  nsIURI* baseURI = aLoadData->mSheet->GetBaseURI();
  1.1663 +  nsresult rv = parser.ParseSheet(aInput, sheetURI, baseURI,
  1.1664 +                                  aLoadData->mSheet->Principal(),
  1.1665 +                                  aLoadData->mLineNumber,
  1.1666 +                                  aLoadData->mAllowUnsafeRules);
  1.1667 +  mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
  1.1668 +
  1.1669 +  if (NS_FAILED(rv)) {
  1.1670 +    LOG_ERROR(("  Low-level error in parser!"));
  1.1671 +    SheetComplete(aLoadData, rv);
  1.1672 +    return rv;
  1.1673 +  }
  1.1674 +
  1.1675 +  NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad,
  1.1676 +               "Sync load has leftover pending children!");
  1.1677 +
  1.1678 +  if (aLoadData->mPendingChildren == 0) {
  1.1679 +    LOG(("  No pending kids from parse"));
  1.1680 +    aCompleted = true;
  1.1681 +    SheetComplete(aLoadData, NS_OK);
  1.1682 +  }
  1.1683 +  // Otherwise, the children are holding strong refs to the data and
  1.1684 +  // will call SheetComplete() on it when they complete.
  1.1685 +
  1.1686 +  return NS_OK;
  1.1687 +}
  1.1688 +
  1.1689 +/**
  1.1690 + * SheetComplete is the do-it-all cleanup function.  It removes the
  1.1691 + * load data from the "loading" hashtable, adds the sheet to the
  1.1692 + * "completed" hashtable, massages the XUL cache, handles siblings of
  1.1693 + * the load data (other loads for the same URI), handles unblocking
  1.1694 + * blocked parent loads as needed, and most importantly calls
  1.1695 + * NS_RELEASE on the load data to destroy the whole mess.
  1.1696 + */
  1.1697 +void
  1.1698 +Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
  1.1699 +{
  1.1700 +  LOG(("css::Loader::SheetComplete"));
  1.1701 +
  1.1702 +  // 8 is probably big enough for all our common cases.  It's not likely that
  1.1703 +  // imports will nest more than 8 deep, and multiple sheets with the same URI
  1.1704 +  // are rare.
  1.1705 +  nsAutoTArray<nsRefPtr<SheetLoadData>, 8> datasToNotify;
  1.1706 +  DoSheetComplete(aLoadData, aStatus, datasToNotify);
  1.1707 +
  1.1708 +  // Now it's safe to go ahead and notify observers
  1.1709 +  uint32_t count = datasToNotify.Length();
  1.1710 +  mDatasToNotifyOn += count;
  1.1711 +  for (uint32_t i = 0; i < count; ++i) {
  1.1712 +    --mDatasToNotifyOn;
  1.1713 +
  1.1714 +    SheetLoadData* data = datasToNotify[i];
  1.1715 +    NS_ASSERTION(data && data->mMustNotify, "How did this data get here?");
  1.1716 +    if (data->mObserver) {
  1.1717 +      LOG(("  Notifying observer 0x%x for data 0x%x.  wasAlternate: %d",
  1.1718 +           data->mObserver.get(), data, data->mWasAlternate));
  1.1719 +      data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate,
  1.1720 +                                        aStatus);
  1.1721 +    }
  1.1722 +
  1.1723 +    nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers);
  1.1724 +    nsCOMPtr<nsICSSLoaderObserver> obs;
  1.1725 +    while (iter.HasMore()) {
  1.1726 +      obs = iter.GetNext();
  1.1727 +      LOG(("  Notifying global observer 0x%x for data 0x%s.  wasAlternate: %d",
  1.1728 +           obs.get(), data, data->mWasAlternate));
  1.1729 +      obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus);
  1.1730 +    }
  1.1731 +  }
  1.1732 +
  1.1733 +  if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) {
  1.1734 +    LOG(("  No more loading sheets; starting alternates"));
  1.1735 +    StartAlternateLoads();
  1.1736 +  }
  1.1737 +}
  1.1738 +
  1.1739 +void
  1.1740 +Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
  1.1741 +                        LoadDataArray& aDatasToNotify)
  1.1742 +{
  1.1743 +  LOG(("css::Loader::DoSheetComplete"));
  1.1744 +  NS_PRECONDITION(aLoadData, "Must have a load data!");
  1.1745 +  NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
  1.1746 +  NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
  1.1747 +
  1.1748 +  LOG(("Load completed, status: 0x%x", aStatus));
  1.1749 +
  1.1750 +  // Twiddle the hashtables
  1.1751 +  if (aLoadData->mURI) {
  1.1752 +    LOG_URI("  Finished loading: '%s'", aLoadData->mURI);
  1.1753 +    // Remove the data from the list of loading datas
  1.1754 +    if (aLoadData->mIsLoading) {
  1.1755 +      URIPrincipalAndCORSModeHashKey key(aLoadData->mURI,
  1.1756 +                                         aLoadData->mLoaderPrincipal,
  1.1757 +                                         aLoadData->mSheet->GetCORSMode());
  1.1758 +#ifdef DEBUG
  1.1759 +      SheetLoadData *loadingData;
  1.1760 +      NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) &&
  1.1761 +                   loadingData == aLoadData,
  1.1762 +                   "Bad loading table");
  1.1763 +#endif
  1.1764 +
  1.1765 +      mSheets->mLoadingDatas.Remove(&key);
  1.1766 +      aLoadData->mIsLoading = false;
  1.1767 +    }
  1.1768 +  }
  1.1769 +
  1.1770 +  // Go through and deal with the whole linked list.
  1.1771 +  SheetLoadData* data = aLoadData;
  1.1772 +  while (data) {
  1.1773 +    if (!data->mSheetAlreadyComplete) {
  1.1774 +      // If mSheetAlreadyComplete, then the sheet could well be modified between
  1.1775 +      // when we posted the async call to SheetComplete and now, since the sheet
  1.1776 +      // was page-accessible during that whole time.
  1.1777 +      NS_ABORT_IF_FALSE(!data->mSheet->IsModified(),
  1.1778 +                        "should not get marked modified during parsing");
  1.1779 +      data->mSheet->SetComplete();
  1.1780 +      data->ScheduleLoadEventIfNeeded(aStatus);
  1.1781 +    }
  1.1782 +    if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
  1.1783 +      // Don't notify here so we don't trigger script.  Remember the
  1.1784 +      // info we need to notify, then do it later when it's safe.
  1.1785 +      aDatasToNotify.AppendElement(data);
  1.1786 +
  1.1787 +      // On append failure, just press on.  We'll fail to notify the observer,
  1.1788 +      // but not much we can do about that....
  1.1789 +    }
  1.1790 +
  1.1791 +    NS_ASSERTION(!data->mParentData ||
  1.1792 +                 data->mParentData->mPendingChildren != 0,
  1.1793 +                 "Broken pending child count on our parent");
  1.1794 +
  1.1795 +    // If we have a parent, our parent is no longer being parsed, and
  1.1796 +    // we are the last pending child, then our load completion
  1.1797 +    // completes the parent too.  Note that the parent _can_ still be
  1.1798 +    // being parsed (eg if the child (us) failed to open the channel
  1.1799 +    // or some such).
  1.1800 +    if (data->mParentData &&
  1.1801 +        --(data->mParentData->mPendingChildren) == 0 &&
  1.1802 +        !mParsingDatas.Contains(data->mParentData)) {
  1.1803 +      DoSheetComplete(data->mParentData, aStatus, aDatasToNotify);
  1.1804 +    }
  1.1805 +
  1.1806 +    data = data->mNext;
  1.1807 +  }
  1.1808 +
  1.1809 +  // Now that it's marked complete, put the sheet in our cache.
  1.1810 +  // If we ever start doing this for failure aStatus, we'll need to
  1.1811 +  // adjust the PostLoadEvent code that thinks anything already
  1.1812 +  // complete must have loaded succesfully.
  1.1813 +  if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
  1.1814 +    // Pick our sheet to cache carefully.  Ideally, we want to cache
  1.1815 +    // one of the sheets that will be kept alive by a document or
  1.1816 +    // parent sheet anyway, so that if someone then accesses it via
  1.1817 +    // CSSOM we won't have extra clones of the inner lying around.
  1.1818 +    data = aLoadData;
  1.1819 +    nsCSSStyleSheet* sheet = aLoadData->mSheet;
  1.1820 +    while (data) {
  1.1821 +      if (data->mSheet->GetParentSheet() || data->mSheet->GetOwnerNode()) {
  1.1822 +        sheet = data->mSheet;
  1.1823 +        break;
  1.1824 +      }
  1.1825 +      data = data->mNext;
  1.1826 +    }
  1.1827 +#ifdef MOZ_XUL
  1.1828 +    if (IsChromeURI(aLoadData->mURI)) {
  1.1829 +      nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
  1.1830 +      if (cache && cache->IsEnabled()) {
  1.1831 +        if (!cache->GetStyleSheet(aLoadData->mURI)) {
  1.1832 +          LOG(("  Putting sheet in XUL prototype cache"));
  1.1833 +          NS_ASSERTION(sheet->IsComplete(),
  1.1834 +                       "Should only be caching complete sheets");
  1.1835 +          cache->PutStyleSheet(sheet);
  1.1836 +        }
  1.1837 +      }
  1.1838 +    }
  1.1839 +    else {
  1.1840 +#endif
  1.1841 +      URIPrincipalAndCORSModeHashKey key(aLoadData->mURI,
  1.1842 +                                         aLoadData->mLoaderPrincipal,
  1.1843 +                                         aLoadData->mSheet->GetCORSMode());
  1.1844 +      NS_ASSERTION(sheet->IsComplete(),
  1.1845 +                   "Should only be caching complete sheets");
  1.1846 +      mSheets->mCompleteSheets.Put(&key, sheet);
  1.1847 +#ifdef MOZ_XUL
  1.1848 +    }
  1.1849 +#endif
  1.1850 +  }
  1.1851 +
  1.1852 +  NS_RELEASE(aLoadData);  // this will release parents and siblings and all that
  1.1853 +}
  1.1854 +
  1.1855 +nsresult
  1.1856 +Loader::LoadInlineStyle(nsIContent* aElement,
  1.1857 +                        const nsAString& aBuffer,
  1.1858 +                        uint32_t aLineNumber,
  1.1859 +                        const nsAString& aTitle,
  1.1860 +                        const nsAString& aMedia,
  1.1861 +                        Element* aScopeElement,
  1.1862 +                        nsICSSLoaderObserver* aObserver,
  1.1863 +                        bool* aCompleted,
  1.1864 +                        bool* aIsAlternate)
  1.1865 +{
  1.1866 +  LOG(("css::Loader::LoadInlineStyle"));
  1.1867 +  NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
  1.1868 +
  1.1869 +  *aCompleted = true;
  1.1870 +
  1.1871 +  if (!mEnabled) {
  1.1872 +    LOG_WARN(("  Not enabled"));
  1.1873 +    return NS_ERROR_NOT_AVAILABLE;
  1.1874 +  }
  1.1875 +
  1.1876 +  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
  1.1877 +
  1.1878 +  nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
  1.1879 +  NS_ASSERTION(owningElement, "Element is not a style linking element!");
  1.1880 +
  1.1881 +  // Since we're not planning to load a URI, no need to hand a principal to the
  1.1882 +  // load data or to CreateSheet().  Also, OK to use CORS_NONE for the CORS
  1.1883 +  // mode.
  1.1884 +  StyleSheetState state;
  1.1885 +  nsRefPtr<nsCSSStyleSheet> sheet;
  1.1886 +  nsresult rv = CreateSheet(nullptr, aElement, nullptr, CORS_NONE, false, false,
  1.1887 +                            aTitle, state, aIsAlternate, getter_AddRefs(sheet));
  1.1888 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1889 +  NS_ASSERTION(state == eSheetNeedsParser,
  1.1890 +               "Inline sheets should not be cached");
  1.1891 +
  1.1892 +  LOG(("  Sheet is alternate: %d", *aIsAlternate));
  1.1893 +
  1.1894 +  PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate);
  1.1895 +
  1.1896 +  if (aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
  1.1897 +    ShadowRoot* containingShadow = aElement->GetContainingShadow();
  1.1898 +    MOZ_ASSERT(containingShadow);
  1.1899 +    containingShadow->InsertSheet(sheet, aElement);
  1.1900 +  } else {
  1.1901 +    rv = InsertSheetInDoc(sheet, aElement, mDocument);
  1.1902 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1903 +  }
  1.1904 +
  1.1905 +  SheetLoadData* data = new SheetLoadData(this, aTitle, nullptr, sheet,
  1.1906 +                                          owningElement, *aIsAlternate,
  1.1907 +                                          aObserver, nullptr);
  1.1908 +
  1.1909 +  // We never actually load this, so just set its principal directly
  1.1910 +  sheet->SetPrincipal(aElement->NodePrincipal());
  1.1911 +
  1.1912 +  NS_ADDREF(data);
  1.1913 +  data->mLineNumber = aLineNumber;
  1.1914 +  // Parse completion releases the load data
  1.1915 +  rv = ParseSheet(aBuffer, data, *aCompleted);
  1.1916 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1917 +
  1.1918 +  // If aCompleted is true, |data| may well be deleted by now.
  1.1919 +  if (!*aCompleted) {
  1.1920 +    data->mMustNotify = true;
  1.1921 +  }
  1.1922 +  return rv;
  1.1923 +}
  1.1924 +
  1.1925 +nsresult
  1.1926 +Loader::LoadStyleLink(nsIContent* aElement,
  1.1927 +                      nsIURI* aURL,
  1.1928 +                      const nsAString& aTitle,
  1.1929 +                      const nsAString& aMedia,
  1.1930 +                      bool aHasAlternateRel,
  1.1931 +                      CORSMode aCORSMode,
  1.1932 +                      nsICSSLoaderObserver* aObserver,
  1.1933 +                      bool* aIsAlternate)
  1.1934 +{
  1.1935 +  LOG(("css::Loader::LoadStyleLink"));
  1.1936 +  NS_PRECONDITION(aURL, "Must have URL to load");
  1.1937 +  NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
  1.1938 +
  1.1939 +  LOG_URI("  Link uri: '%s'", aURL);
  1.1940 +  LOG(("  Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle).get()));
  1.1941 +  LOG(("  Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia).get()));
  1.1942 +  LOG(("  Link alternate rel: %d", aHasAlternateRel));
  1.1943 +
  1.1944 +  if (!mEnabled) {
  1.1945 +    LOG_WARN(("  Not enabled"));
  1.1946 +    return NS_ERROR_NOT_AVAILABLE;
  1.1947 +  }
  1.1948 +
  1.1949 +  NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
  1.1950 +
  1.1951 +  nsIPrincipal* principal =
  1.1952 +    aElement ? aElement->NodePrincipal() : mDocument->NodePrincipal();
  1.1953 +
  1.1954 +  nsISupports* context = aElement;
  1.1955 +  if (!context) {
  1.1956 +    context = mDocument;
  1.1957 +  }
  1.1958 +  nsresult rv = CheckLoadAllowed(principal, aURL, context);
  1.1959 +  if (NS_FAILED(rv)) return rv;
  1.1960 +
  1.1961 +  LOG(("  Passed load check"));
  1.1962 +
  1.1963 +  StyleSheetState state;
  1.1964 +  nsRefPtr<nsCSSStyleSheet> sheet;
  1.1965 +  rv = CreateSheet(aURL, aElement, principal, aCORSMode, false,
  1.1966 +                   aHasAlternateRel, aTitle, state, aIsAlternate,
  1.1967 +                   getter_AddRefs(sheet));
  1.1968 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1969 +
  1.1970 +  LOG(("  Sheet is alternate: %d", *aIsAlternate));
  1.1971 +
  1.1972 +  PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate);
  1.1973 +
  1.1974 +  rv = InsertSheetInDoc(sheet, aElement, mDocument);
  1.1975 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1976 +
  1.1977 +  nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
  1.1978 +
  1.1979 +  if (state == eSheetComplete) {
  1.1980 +    LOG(("  Sheet already complete: 0x%p",
  1.1981 +         static_cast<void*>(sheet.get())));
  1.1982 +    if (aObserver || !mObservers.IsEmpty() || owningElement) {
  1.1983 +      rv = PostLoadEvent(aURL, sheet, aObserver, *aIsAlternate,
  1.1984 +                         owningElement);
  1.1985 +      return rv;
  1.1986 +    }
  1.1987 +
  1.1988 +    return NS_OK;
  1.1989 +  }
  1.1990 +
  1.1991 +  // Now we need to actually load it
  1.1992 +  SheetLoadData* data = new SheetLoadData(this, aTitle, aURL, sheet,
  1.1993 +                                          owningElement, *aIsAlternate,
  1.1994 +                                          aObserver, principal);
  1.1995 +  NS_ADDREF(data);
  1.1996 +
  1.1997 +  // If we have to parse and it's an alternate non-inline, defer it
  1.1998 +  if (aURL && state == eSheetNeedsParser && mSheets->mLoadingDatas.Count() != 0 &&
  1.1999 +      *aIsAlternate) {
  1.2000 +    LOG(("  Deferring alternate sheet load"));
  1.2001 +    URIPrincipalAndCORSModeHashKey key(data->mURI, data->mLoaderPrincipal,
  1.2002 +                                       data->mSheet->GetCORSMode());
  1.2003 +    mSheets->mPendingDatas.Put(&key, data);
  1.2004 +
  1.2005 +    data->mMustNotify = true;
  1.2006 +    return NS_OK;
  1.2007 +  }
  1.2008 +
  1.2009 +  // Load completion will free the data
  1.2010 +  rv = LoadSheet(data, state);
  1.2011 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2012 +
  1.2013 +  data->mMustNotify = true;
  1.2014 +  return rv;
  1.2015 +}
  1.2016 +
  1.2017 +static bool
  1.2018 +HaveAncestorDataWithURI(SheetLoadData *aData, nsIURI *aURI)
  1.2019 +{
  1.2020 +  if (!aData->mURI) {
  1.2021 +    // Inline style; this won't have any ancestors
  1.2022 +    NS_ABORT_IF_FALSE(!aData->mParentData,
  1.2023 +                      "How does inline style have a parent?");
  1.2024 +    return false;
  1.2025 +  }
  1.2026 +
  1.2027 +  bool equal;
  1.2028 +  if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) {
  1.2029 +    return true;
  1.2030 +  }
  1.2031 +
  1.2032 +  // Datas down the mNext chain have the same URI as aData, so we
  1.2033 +  // don't have to compare to them.  But they might have different
  1.2034 +  // parents, and we have to check all of those.
  1.2035 +  while (aData) {
  1.2036 +    if (aData->mParentData &&
  1.2037 +        HaveAncestorDataWithURI(aData->mParentData, aURI)) {
  1.2038 +      return true;
  1.2039 +    }
  1.2040 +
  1.2041 +    aData = aData->mNext;
  1.2042 +  }
  1.2043 +
  1.2044 +  return false;
  1.2045 +}
  1.2046 +
  1.2047 +nsresult
  1.2048 +Loader::LoadChildSheet(nsCSSStyleSheet* aParentSheet,
  1.2049 +                       nsIURI* aURL,
  1.2050 +                       nsMediaList* aMedia,
  1.2051 +                       ImportRule* aParentRule)
  1.2052 +{
  1.2053 +  LOG(("css::Loader::LoadChildSheet"));
  1.2054 +  NS_PRECONDITION(aURL, "Must have a URI to load");
  1.2055 +  NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
  1.2056 +
  1.2057 +  if (!mEnabled) {
  1.2058 +    LOG_WARN(("  Not enabled"));
  1.2059 +    return NS_ERROR_NOT_AVAILABLE;
  1.2060 +  }
  1.2061 +
  1.2062 +  LOG_URI("  Child uri: '%s'", aURL);
  1.2063 +
  1.2064 +  nsCOMPtr<nsIDOMNode> owningNode;
  1.2065 +
  1.2066 +  // check for an owning document: if none, don't bother walking up the parent
  1.2067 +  // sheets
  1.2068 +  if (aParentSheet->GetOwningDocument()) {
  1.2069 +    nsCOMPtr<nsIDOMStyleSheet> nextParentSheet(aParentSheet);
  1.2070 +    NS_ENSURE_TRUE(nextParentSheet, NS_ERROR_FAILURE); //Not a stylesheet!?
  1.2071 +
  1.2072 +    nsCOMPtr<nsIDOMStyleSheet> topSheet;
  1.2073 +    //traverse our way to the top-most sheet
  1.2074 +    do {
  1.2075 +      topSheet.swap(nextParentSheet);
  1.2076 +      topSheet->GetParentStyleSheet(getter_AddRefs(nextParentSheet));
  1.2077 +    } while (nextParentSheet);
  1.2078 +
  1.2079 +    topSheet->GetOwnerNode(getter_AddRefs(owningNode));
  1.2080 +  }
  1.2081 +
  1.2082 +  nsISupports* context = owningNode;
  1.2083 +  if (!context) {
  1.2084 +    context = mDocument;
  1.2085 +  }
  1.2086 +
  1.2087 +  nsIPrincipal* principal = aParentSheet->Principal();
  1.2088 +  nsresult rv = CheckLoadAllowed(principal, aURL, context);
  1.2089 +  if (NS_FAILED(rv)) return rv;
  1.2090 +
  1.2091 +  LOG(("  Passed load check"));
  1.2092 +
  1.2093 +  SheetLoadData* parentData = nullptr;
  1.2094 +  nsCOMPtr<nsICSSLoaderObserver> observer;
  1.2095 +
  1.2096 +  int32_t count = mParsingDatas.Length();
  1.2097 +  if (count > 0) {
  1.2098 +    LOG(("  Have a parent load"));
  1.2099 +    parentData = mParsingDatas.ElementAt(count - 1);
  1.2100 +    // Check for cycles
  1.2101 +    if (HaveAncestorDataWithURI(parentData, aURL)) {
  1.2102 +      // Houston, we have a loop, blow off this child and pretend this never
  1.2103 +      // happened
  1.2104 +      LOG_ERROR(("  @import cycle detected, dropping load"));
  1.2105 +      return NS_OK;
  1.2106 +    }
  1.2107 +
  1.2108 +    NS_ASSERTION(parentData->mSheet == aParentSheet,
  1.2109 +                 "Unexpected call to LoadChildSheet");
  1.2110 +  } else {
  1.2111 +    LOG(("  No parent load; must be CSSOM"));
  1.2112 +    // No parent load data, so the sheet will need to be notified when
  1.2113 +    // we finish, if it can be, if we do the load asynchronously.
  1.2114 +    observer = aParentSheet;
  1.2115 +  }
  1.2116 +
  1.2117 +  // Now that we know it's safe to load this (passes security check and not a
  1.2118 +  // loop) do so.
  1.2119 +  nsRefPtr<nsCSSStyleSheet> sheet;
  1.2120 +  bool isAlternate;
  1.2121 +  StyleSheetState state;
  1.2122 +  const nsSubstring& empty = EmptyString();
  1.2123 +  // For now, use CORS_NONE for child sheets
  1.2124 +  rv = CreateSheet(aURL, nullptr, principal, CORS_NONE,
  1.2125 +                   parentData ? parentData->mSyncLoad : false,
  1.2126 +                   false, empty, state, &isAlternate, getter_AddRefs(sheet));
  1.2127 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2128 +
  1.2129 +  PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate);
  1.2130 +
  1.2131 +  rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
  1.2132 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2133 +
  1.2134 +  if (state == eSheetComplete) {
  1.2135 +    LOG(("  Sheet already complete"));
  1.2136 +    // We're completely done.  No need to notify, even, since the
  1.2137 +    // @import rule addition/modification will trigger the right style
  1.2138 +    // changes automatically.
  1.2139 +    return NS_OK;
  1.2140 +  }
  1.2141 +
  1.2142 +  SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
  1.2143 +                                          observer, principal);
  1.2144 +
  1.2145 +  NS_ADDREF(data);
  1.2146 +  bool syncLoad = data->mSyncLoad;
  1.2147 +
  1.2148 +  // Load completion will release the data
  1.2149 +  rv = LoadSheet(data, state);
  1.2150 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2151 +
  1.2152 +  // If syncLoad is true, |data| will be deleted by now.
  1.2153 +  if (!syncLoad) {
  1.2154 +    data->mMustNotify = true;
  1.2155 +  }
  1.2156 +  return rv;
  1.2157 +}
  1.2158 +
  1.2159 +nsresult
  1.2160 +Loader::LoadSheetSync(nsIURI* aURL, bool aAllowUnsafeRules,
  1.2161 +                      bool aUseSystemPrincipal,
  1.2162 +                      nsCSSStyleSheet** aSheet)
  1.2163 +{
  1.2164 +  LOG(("css::Loader::LoadSheetSync"));
  1.2165 +  return InternalLoadNonDocumentSheet(aURL, aAllowUnsafeRules,
  1.2166 +                                      aUseSystemPrincipal, nullptr,
  1.2167 +                                      EmptyCString(), aSheet, nullptr);
  1.2168 +}
  1.2169 +
  1.2170 +nsresult
  1.2171 +Loader::LoadSheet(nsIURI* aURL,
  1.2172 +                  nsIPrincipal* aOriginPrincipal,
  1.2173 +                  const nsCString& aCharset,
  1.2174 +                  nsICSSLoaderObserver* aObserver,
  1.2175 +                  nsCSSStyleSheet** aSheet)
  1.2176 +{
  1.2177 +  LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
  1.2178 +  NS_PRECONDITION(aSheet, "aSheet is null");
  1.2179 +  return InternalLoadNonDocumentSheet(aURL, false, false,
  1.2180 +                                      aOriginPrincipal, aCharset,
  1.2181 +                                      aSheet, aObserver);
  1.2182 +}
  1.2183 +
  1.2184 +nsresult
  1.2185 +Loader::LoadSheet(nsIURI* aURL,
  1.2186 +                  nsIPrincipal* aOriginPrincipal,
  1.2187 +                  const nsCString& aCharset,
  1.2188 +                  nsICSSLoaderObserver* aObserver,
  1.2189 +                  CORSMode aCORSMode)
  1.2190 +{
  1.2191 +  LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
  1.2192 +  return InternalLoadNonDocumentSheet(aURL, false, false,
  1.2193 +                                      aOriginPrincipal, aCharset,
  1.2194 +                                      nullptr, aObserver, aCORSMode);
  1.2195 +}
  1.2196 +
  1.2197 +nsresult
  1.2198 +Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
  1.2199 +                                     bool aAllowUnsafeRules,
  1.2200 +                                     bool aUseSystemPrincipal,
  1.2201 +                                     nsIPrincipal* aOriginPrincipal,
  1.2202 +                                     const nsCString& aCharset,
  1.2203 +                                     nsCSSStyleSheet** aSheet,
  1.2204 +                                     nsICSSLoaderObserver* aObserver,
  1.2205 +                                     CORSMode aCORSMode)
  1.2206 +{
  1.2207 +  NS_PRECONDITION(aURL, "Must have a URI to load");
  1.2208 +  NS_PRECONDITION(aSheet || aObserver, "Sheet and observer can't both be null");
  1.2209 +  NS_PRECONDITION(!aUseSystemPrincipal || !aObserver,
  1.2210 +                  "Shouldn't load system-principal sheets async");
  1.2211 +  NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
  1.2212 +
  1.2213 +  LOG_URI("  Non-document sheet uri: '%s'", aURL);
  1.2214 +
  1.2215 +  if (aSheet) {
  1.2216 +    *aSheet = nullptr;
  1.2217 +  }
  1.2218 +
  1.2219 +  if (!mEnabled) {
  1.2220 +    LOG_WARN(("  Not enabled"));
  1.2221 +    return NS_ERROR_NOT_AVAILABLE;
  1.2222 +  }
  1.2223 +
  1.2224 +  nsresult rv = CheckLoadAllowed(aOriginPrincipal, aURL, mDocument);
  1.2225 +  if (NS_FAILED(rv)) {
  1.2226 +    return rv;
  1.2227 +  }
  1.2228 +
  1.2229 +  StyleSheetState state;
  1.2230 +  bool isAlternate;
  1.2231 +  nsRefPtr<nsCSSStyleSheet> sheet;
  1.2232 +  bool syncLoad = (aObserver == nullptr);
  1.2233 +  const nsSubstring& empty = EmptyString();
  1.2234 +
  1.2235 +  rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aCORSMode, syncLoad, false,
  1.2236 +                   empty, state, &isAlternate, getter_AddRefs(sheet));
  1.2237 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2238 +
  1.2239 +  PrepareSheet(sheet, empty, empty, nullptr, nullptr, isAlternate);
  1.2240 +
  1.2241 +  if (state == eSheetComplete) {
  1.2242 +    LOG(("  Sheet already complete"));
  1.2243 +    if (aObserver || !mObservers.IsEmpty()) {
  1.2244 +      rv = PostLoadEvent(aURL, sheet, aObserver, false, nullptr);
  1.2245 +    }
  1.2246 +    if (aSheet) {
  1.2247 +      sheet.swap(*aSheet);
  1.2248 +    }
  1.2249 +    return rv;
  1.2250 +  }
  1.2251 +
  1.2252 +  SheetLoadData* data =
  1.2253 +    new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules,
  1.2254 +                      aUseSystemPrincipal, aCharset, aObserver,
  1.2255 +                      aOriginPrincipal);
  1.2256 +
  1.2257 +  NS_ADDREF(data);
  1.2258 +  rv = LoadSheet(data, state);
  1.2259 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2260 +
  1.2261 +  if (aSheet) {
  1.2262 +    sheet.swap(*aSheet);
  1.2263 +  }
  1.2264 +  if (aObserver) {
  1.2265 +    data->mMustNotify = true;
  1.2266 +  }
  1.2267 +
  1.2268 +  return rv;
  1.2269 +}
  1.2270 +
  1.2271 +nsresult
  1.2272 +Loader::PostLoadEvent(nsIURI* aURI,
  1.2273 +                      nsCSSStyleSheet* aSheet,
  1.2274 +                      nsICSSLoaderObserver* aObserver,
  1.2275 +                      bool aWasAlternate,
  1.2276 +                      nsIStyleSheetLinkingElement* aElement)
  1.2277 +{
  1.2278 +  LOG(("css::Loader::PostLoadEvent"));
  1.2279 +  NS_PRECONDITION(aSheet, "Must have sheet");
  1.2280 +  NS_PRECONDITION(aObserver || !mObservers.IsEmpty() || aElement,
  1.2281 +                  "Must have observer or element");
  1.2282 +
  1.2283 +  nsRefPtr<SheetLoadData> evt =
  1.2284 +    new SheetLoadData(this, EmptyString(), // title doesn't matter here
  1.2285 +                      aURI,
  1.2286 +                      aSheet,
  1.2287 +                      aElement,
  1.2288 +                      aWasAlternate,
  1.2289 +                      aObserver,
  1.2290 +                      nullptr);
  1.2291 +  NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
  1.2292 +
  1.2293 +  if (!mPostedEvents.AppendElement(evt)) {
  1.2294 +    return NS_ERROR_OUT_OF_MEMORY;
  1.2295 +  }
  1.2296 +
  1.2297 +  nsresult rv = NS_DispatchToCurrentThread(evt);
  1.2298 +  if (NS_FAILED(rv)) {
  1.2299 +    NS_WARNING("failed to dispatch stylesheet load event");
  1.2300 +    mPostedEvents.RemoveElement(evt);
  1.2301 +  } else {
  1.2302 +    // We'll unblock onload when we handle the event.
  1.2303 +    if (mDocument) {
  1.2304 +      mDocument->BlockOnload();
  1.2305 +    }
  1.2306 +
  1.2307 +    // We want to notify the observer for this data.
  1.2308 +    evt->mMustNotify = true;
  1.2309 +    evt->mSheetAlreadyComplete = true;
  1.2310 +
  1.2311 +    // If we get to this code, aSheet loaded correctly at some point, so
  1.2312 +    // we can just use NS_OK for the status.  Note that we do this here
  1.2313 +    // and not from inside our SheetComplete so that we don't end up
  1.2314 +    // running the load event async.
  1.2315 +    evt->ScheduleLoadEventIfNeeded(NS_OK);
  1.2316 +  }
  1.2317 +
  1.2318 +  return rv;
  1.2319 +}
  1.2320 +
  1.2321 +void
  1.2322 +Loader::HandleLoadEvent(SheetLoadData* aEvent)
  1.2323 +{
  1.2324 +  // XXXbz can't assert this yet.... May not have an observer because
  1.2325 +  // we're unblocking the parser
  1.2326 +  // NS_ASSERTION(aEvent->mObserver, "Must have observer");
  1.2327 +  NS_ASSERTION(aEvent->mSheet, "Must have sheet");
  1.2328 +
  1.2329 +  // Very important: this needs to come before the SheetComplete call
  1.2330 +  // below, so that HasPendingLoads() will test true as needed under
  1.2331 +  // notifications we send from that SheetComplete call.
  1.2332 +  mPostedEvents.RemoveElement(aEvent);
  1.2333 +
  1.2334 +  if (!aEvent->mIsCancelled) {
  1.2335 +    // SheetComplete will call Release(), so give it a reference to do
  1.2336 +    // that with.
  1.2337 +    NS_ADDREF(aEvent);
  1.2338 +    SheetComplete(aEvent, NS_OK);
  1.2339 +  }
  1.2340 +
  1.2341 +  if (mDocument) {
  1.2342 +    mDocument->UnblockOnload(true);
  1.2343 +  }
  1.2344 +}
  1.2345 +
  1.2346 +static PLDHashOperator
  1.2347 +StopLoadingSheetCallback(URIPrincipalAndCORSModeHashKey* aKey,
  1.2348 +                         SheetLoadData*& aData,
  1.2349 +                         void* aClosure)
  1.2350 +{
  1.2351 +  NS_PRECONDITION(aData, "Must have a data!");
  1.2352 +  NS_PRECONDITION(aClosure, "Must have a loader");
  1.2353 +
  1.2354 +  aData->mIsLoading = false; // we will handle the removal right here
  1.2355 +  aData->mIsCancelled = true;
  1.2356 +
  1.2357 +  static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
  1.2358 +
  1.2359 +  return PL_DHASH_REMOVE;
  1.2360 +}
  1.2361 +
  1.2362 +nsresult
  1.2363 +Loader::Stop()
  1.2364 +{
  1.2365 +  uint32_t pendingCount =
  1.2366 +    mSheets ? mSheets->mPendingDatas.Count() : 0;
  1.2367 +  uint32_t loadingCount =
  1.2368 +    mSheets ? mSheets->mLoadingDatas.Count() : 0;
  1.2369 +  LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length());
  1.2370 +
  1.2371 +  if (pendingCount) {
  1.2372 +    mSheets->mPendingDatas.Enumerate(StopLoadingSheetCallback, &arr);
  1.2373 +  }
  1.2374 +  if (loadingCount) {
  1.2375 +    mSheets->mLoadingDatas.Enumerate(StopLoadingSheetCallback, &arr);
  1.2376 +  }
  1.2377 +
  1.2378 +  uint32_t i;
  1.2379 +  for (i = 0; i < mPostedEvents.Length(); ++i) {
  1.2380 +    SheetLoadData* data = mPostedEvents[i];
  1.2381 +    data->mIsCancelled = true;
  1.2382 +    if (arr.AppendElement(data)) {
  1.2383 +      // SheetComplete() calls Release(), so give this an extra ref.
  1.2384 +      NS_ADDREF(data);
  1.2385 +    }
  1.2386 +#ifdef DEBUG
  1.2387 +    else {
  1.2388 +      NS_NOTREACHED("We preallocated this memory... shouldn't really fail, "
  1.2389 +                    "except we never check that preallocation succeeds.");
  1.2390 +    }
  1.2391 +#endif
  1.2392 +  }
  1.2393 +  mPostedEvents.Clear();
  1.2394 +
  1.2395 +  mDatasToNotifyOn += arr.Length();
  1.2396 +  for (i = 0; i < arr.Length(); ++i) {
  1.2397 +    --mDatasToNotifyOn;
  1.2398 +    SheetComplete(arr[i], NS_BINDING_ABORTED);
  1.2399 +  }
  1.2400 +  return NS_OK;
  1.2401 +}
  1.2402 +
  1.2403 +bool
  1.2404 +Loader::HasPendingLoads()
  1.2405 +{
  1.2406 +  return
  1.2407 +    (mSheets && mSheets->mLoadingDatas.Count() != 0) ||
  1.2408 +    (mSheets && mSheets->mPendingDatas.Count() != 0) ||
  1.2409 +    mPostedEvents.Length() != 0 ||
  1.2410 +    mDatasToNotifyOn != 0;
  1.2411 +}
  1.2412 +
  1.2413 +nsresult
  1.2414 +Loader::AddObserver(nsICSSLoaderObserver* aObserver)
  1.2415 +{
  1.2416 +  NS_PRECONDITION(aObserver, "Must have observer");
  1.2417 +  if (mObservers.AppendElementUnlessExists(aObserver)) {
  1.2418 +    return NS_OK;
  1.2419 +  }
  1.2420 +
  1.2421 +  return NS_ERROR_OUT_OF_MEMORY;
  1.2422 +}
  1.2423 +
  1.2424 +void
  1.2425 +Loader::RemoveObserver(nsICSSLoaderObserver* aObserver)
  1.2426 +{
  1.2427 +  mObservers.RemoveElement(aObserver);
  1.2428 +}
  1.2429 +
  1.2430 +static PLDHashOperator
  1.2431 +CollectLoadDatas(URIPrincipalAndCORSModeHashKey *aKey,
  1.2432 +                 SheetLoadData* &aData,
  1.2433 +                 void* aClosure)
  1.2434 +{
  1.2435 +  static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
  1.2436 +  return PL_DHASH_REMOVE;
  1.2437 +}
  1.2438 +
  1.2439 +void
  1.2440 +Loader::StartAlternateLoads()
  1.2441 +{
  1.2442 +  NS_PRECONDITION(mSheets, "Don't call me!");
  1.2443 +  LoadDataArray arr(mSheets->mPendingDatas.Count());
  1.2444 +  mSheets->mPendingDatas.Enumerate(CollectLoadDatas, &arr);
  1.2445 +
  1.2446 +  mDatasToNotifyOn += arr.Length();
  1.2447 +  for (uint32_t i = 0; i < arr.Length(); ++i) {
  1.2448 +    --mDatasToNotifyOn;
  1.2449 +    LoadSheet(arr[i], eSheetNeedsParser);
  1.2450 +  }
  1.2451 +}
  1.2452 +
  1.2453 +static PLDHashOperator
  1.2454 +TraverseSheet(URIPrincipalAndCORSModeHashKey*,
  1.2455 +              nsCSSStyleSheet* aSheet,
  1.2456 +              void* aClosure)
  1.2457 +{
  1.2458 +  nsCycleCollectionTraversalCallback* cb =
  1.2459 +    static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
  1.2460 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "Sheet cache nsCSSLoader");
  1.2461 +  cb->NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIStyleSheet*, aSheet));
  1.2462 +  return PL_DHASH_NEXT;
  1.2463 +}
  1.2464 +
  1.2465 +void
  1.2466 +Loader::TraverseCachedSheets(nsCycleCollectionTraversalCallback& cb)
  1.2467 +{
  1.2468 +  if (mSheets) {
  1.2469 +    mSheets->mCompleteSheets.EnumerateRead(TraverseSheet, &cb);
  1.2470 +  }
  1.2471 +}
  1.2472 +
  1.2473 +void
  1.2474 +Loader::UnlinkCachedSheets()
  1.2475 +{
  1.2476 +  if (mSheets) {
  1.2477 +    mSheets->mCompleteSheets.Clear();
  1.2478 +  }
  1.2479 +}
  1.2480 +
  1.2481 +struct SheetMemoryCounter {
  1.2482 +  size_t size;
  1.2483 +  mozilla::MallocSizeOf mallocSizeOf;
  1.2484 +};
  1.2485 +
  1.2486 +static size_t
  1.2487 +CountSheetMemory(URIPrincipalAndCORSModeHashKey* /* unused */,
  1.2488 +                 const nsRefPtr<nsCSSStyleSheet>& aSheet,
  1.2489 +                 mozilla::MallocSizeOf aMallocSizeOf,
  1.2490 +                 void* /* unused */)
  1.2491 +{
  1.2492 +  // If aSheet has a parent, then its parent will report it so we don't
  1.2493 +  // have to worry about it here.
  1.2494 +  // Likewise, if aSheet has an owning node, then the document that
  1.2495 +  // node is in will report it.
  1.2496 +  if (aSheet->GetOwnerNode() || aSheet->GetParentSheet()) {
  1.2497 +    return 0;
  1.2498 +  }
  1.2499 +  return aSheet->SizeOfIncludingThis(aMallocSizeOf);
  1.2500 +}
  1.2501 +
  1.2502 +size_t
  1.2503 +Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  1.2504 +{
  1.2505 +  size_t s = aMallocSizeOf(this);
  1.2506 +
  1.2507 +  if (mSheets) {
  1.2508 +    s += mSheets->mCompleteSheets.SizeOfExcludingThis(CountSheetMemory, aMallocSizeOf);
  1.2509 +  }
  1.2510 +  s += mObservers.SizeOfExcludingThis(aMallocSizeOf);
  1.2511 +
  1.2512 +  // Measurement of the following members may be added later if DMD finds it is
  1.2513 +  // worthwhile:
  1.2514 +  // - mLoadingDatas: transient, and should be small
  1.2515 +  // - mPendingDatas: transient, and should be small
  1.2516 +  // - mParsingDatas: transient, and should be small
  1.2517 +  // - mPostedEvents: transient, and should be small
  1.2518 +  //
  1.2519 +  // The following members aren't measured:
  1.2520 +  // - mDocument, because it's a weak backpointer
  1.2521 +  // - mPreferredSheet, because it can be a shared string
  1.2522 +
  1.2523 +  return s;
  1.2524 +}
  1.2525 +
  1.2526 +} // namespace css
  1.2527 +} // namespace mozilla

mercurial