Thu, 15 Jan 2015 21:03:48 +0100
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 |