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