layout/style/Loader.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial