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