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