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