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/. */
7 /* loading of CSS style sheets using the network APIs */
9 #ifndef mozilla_css_Loader_h
10 #define mozilla_css_Loader_h
15 #include "mozilla/Attributes.h"
16 #include "mozilla/CORSMode.h"
17 #include "mozilla/Maybe.h"
18 #include "mozilla/MemoryReporting.h"
19 #include "mozilla/StyleSheet.h"
20 #include "mozilla/StyleSheetInlines.h"
21 #include "mozilla/UniquePtr.h"
22 #include "nsCompatibility.h"
23 #include "nsCycleCollectionParticipant.h"
24 #include "nsDataHashtable.h"
25 #include "nsIPrincipal.h"
26 #include "nsIStyleSheetLinkingElement.h"
27 #include "nsRefPtrHashtable.h"
28 #include "nsStringFwd.h"
30 #include "nsTObserverArray.h"
31 #include "nsURIHashKey.h"
33 class nsICSSLoaderObserver
;
34 class nsIConsoleReportCollector
;
42 } // namespace mozilla
51 /*********************
53 *********************/
55 class MOZ_RAII LoaderReusableStyleSheets
{
57 LoaderReusableStyleSheets() = default;
60 * Look for a reusable sheet (see AddReusableSheet) matching the
61 * given URL. If found, set aResult, remove the reused sheet from
62 * the internal list, and return true. If not found, return false;
63 * in this case, aResult is not modified.
65 * @param aURL the url to match
66 * @param aResult [out] the style sheet which can be reused
68 bool FindReusableStyleSheet(nsIURI
* aURL
, RefPtr
<StyleSheet
>& aResult
);
71 * Indicate that a certain style sheet is available for reuse if its
72 * URI matches the URI of an @import. Sheets should be added in the
73 * opposite order in which they are intended to be reused.
75 * @param aSheet the sheet which can be reused
77 void AddReusableSheet(StyleSheet
* aSheet
) {
78 mReusableSheets
.AppendElement(aSheet
);
82 LoaderReusableStyleSheets(const LoaderReusableStyleSheets
&) = delete;
83 LoaderReusableStyleSheets
& operator=(const LoaderReusableStyleSheets
&) =
86 // The sheets that can be reused.
87 nsTArray
<RefPtr
<StyleSheet
>> mReusableSheets
;
91 using ReferrerPolicy
= dom::ReferrerPolicy
;
94 typedef nsIStyleSheetLinkingElement::Completed Completed
;
95 typedef nsIStyleSheetLinkingElement::HasAlternateRel HasAlternateRel
;
96 typedef nsIStyleSheetLinkingElement::IsAlternate IsAlternate
;
97 typedef nsIStyleSheetLinkingElement::IsInline IsInline
;
98 typedef nsIStyleSheetLinkingElement::IsExplicitlyEnabled IsExplicitlyEnabled
;
99 typedef nsIStyleSheetLinkingElement::MediaMatched MediaMatched
;
100 typedef nsIStyleSheetLinkingElement::Update LoadSheetResult
;
101 typedef nsIStyleSheetLinkingElement::SheetInfo SheetInfo
;
104 // aDocGroup is used for dispatching SheetLoadData in PostLoadEvent(). It
105 // can be null if you want to use this constructor, and there's no
106 // document when the Loader is constructed.
107 explicit Loader(dom::DocGroup
*);
108 explicit Loader(dom::Document
*);
111 // Private destructor, to discourage deletion outside of Release():
115 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Loader
)
116 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Loader
)
118 void DropDocumentReference(); // notification that doc is going away
120 void SetCompatibilityMode(nsCompatibility aCompatMode
) {
121 mCompatMode
= aCompatMode
;
123 nsCompatibility
GetCompatibilityMode() { return mCompatMode
; }
125 // TODO(emilio): Is the complexity of this method and carrying the titles
126 // around worth it? The alternate sheets will load anyhow eventually...
127 void DocumentStyleSheetSetChanged();
129 // XXXbz sort out what the deal is with events! When should they fire?
132 * Load an inline style sheet. If a successful result is returned and
133 * result.WillNotify() is true, then aObserver is guaranteed to be notified
134 * asynchronously once the sheet is marked complete. If an error is
135 * returned, or if result.WillNotify() is false, aObserver will not be
136 * notified. In addition to parsing the sheet, this method will insert it
137 * into the stylesheet list of this CSSLoader's document.
138 * @param aObserver the observer to notify when the load completes.
140 * @param aBuffer the stylesheet data
141 * @param aLineNumber the line number at which the stylesheet data started.
143 Result
<LoadSheetResult
, nsresult
> LoadInlineStyle(
144 const SheetInfo
&, const nsAString
& aBuffer
, uint32_t aLineNumber
,
145 nsICSSLoaderObserver
* aObserver
);
148 * Load a linked (document) stylesheet. If a successful result is returned,
149 * aObserver is guaranteed to be notified asynchronously once the sheet is
150 * loaded and marked complete, i.e., result.WillNotify() will always return
151 * true. If an error is returned, aObserver will not be notified. In
152 * addition to loading the sheet, this method will insert it into the
153 * stylesheet list of this CSSLoader's document.
154 * @param aObserver the observer to notify when the load completes.
157 Result
<LoadSheetResult
, nsresult
> LoadStyleLink(
158 const SheetInfo
&, nsICSSLoaderObserver
* aObserver
);
161 * Load a child (@import-ed) style sheet. In addition to loading the sheet,
162 * this method will insert it into the child sheet list of aParentSheet. If
163 * there is no sheet currently being parsed and the child sheet is not
164 * complete when this method returns, then when the child sheet becomes
165 * complete aParentSheet will be QIed to nsICSSLoaderObserver and
166 * asynchronously notified, just like for LoadStyleLink. Note that if the
167 * child sheet is already complete when this method returns, no
168 * nsICSSLoaderObserver notification will be sent.
170 * @param aParentSheet the parent of this child sheet
171 * @param aParentData the SheetLoadData corresponding to the load of the
172 * parent sheet. May be null for @import rules inserted via
174 * @param aURL the URL of the child sheet
175 * @param aMedia the already-parsed media list for the child sheet
176 * @param aSavedSheets any saved style sheets which could be reused
179 nsresult
LoadChildSheet(StyleSheet
& aParentSheet
, SheetLoadData
* aParentData
,
180 nsIURI
* aURL
, dom::MediaList
* aMedia
,
181 LoaderReusableStyleSheets
* aSavedSheets
);
183 enum class UseSystemPrincipal
{ No
, Yes
};
186 * Synchronously load and return the stylesheet at aURL. Any child sheets
187 * will also be loaded synchronously. Note that synchronous loads over some
188 * protocols may involve spinning up a new event loop, so use of this method
189 * does NOT guarantee not receiving any events before the sheet loads. This
190 * method can be used to load sheets not associated with a document.
192 * @param aURL the URL of the sheet to load
193 * @param aParsingMode the mode in which to parse the sheet
194 * (see comments at enum SheetParsingMode, above).
195 * @param aUseSystemPrincipal if true, give the resulting sheet the system
196 * principal no matter where it's being loaded from.
198 * NOTE: At the moment, this method assumes the sheet will be UTF-8, but
199 * ideally it would allow arbitrary encodings. Callers should NOT depend on
200 * non-UTF8 sheets being treated as UTF-8 by this method.
202 * NOTE: A successful return from this method doesn't indicate anything about
203 * whether the data could be parsed as CSS and doesn't indicate anything
204 * about the status of child sheets of the returned sheet.
206 Result
<RefPtr
<StyleSheet
>, nsresult
> LoadSheetSync(
207 nsIURI
*, SheetParsingMode
= eAuthorSheetFeatures
,
208 UseSystemPrincipal
= UseSystemPrincipal::No
);
210 enum class IsPreload
: uint8_t {
212 // This is a speculative load initiated by a <link rel=stylesheet> tag
213 // scanned by the parser, or @import rules found in a <style> tag.
215 // This is a speculative load as well, but initiated by
216 // <link rel="preload" as="style">
221 * Asynchronously load the stylesheet at aURL. If a successful result is
222 * returned, aObserver is guaranteed to be notified asynchronously once the
223 * sheet is loaded and marked complete. This method can be used to load
224 * sheets not associated with a document.
226 * @param aURL the URL of the sheet to load
227 * @param aParsingMode the mode in which to parse the sheet
228 * (see comments at enum SheetParsingMode, above).
229 * @param aUseSystemPrincipal if true, give the resulting sheet the system
230 * principal no matter where it's being loaded from.
231 * @param aReferrerInfo referrer information of the sheet.
232 * @param aObserver the observer to notify when the load completes.
234 * @return the sheet to load. Note that the sheet may well not be loaded by
235 * the time this method returns.
237 * NOTE: At the moment, this method assumes the sheet will be UTF-8, but
238 * ideally it would allow arbitrary encodings. Callers should NOT depend on
239 * non-UTF8 sheets being treated as UTF-8 by this method.
241 Result
<RefPtr
<StyleSheet
>, nsresult
> LoadSheet(
242 nsIURI
* aURI
, IsPreload
, nsIPrincipal
* aOriginPrincipal
,
243 const Encoding
* aPreloadEncoding
, nsIReferrerInfo
* aReferrerInfo
,
244 nsICSSLoaderObserver
* aObserver
, CORSMode aCORSMode
= CORS_NONE
,
245 const nsAString
& aIntegrity
= EmptyString());
248 * As above, but without caring for a couple things.
250 Result
<RefPtr
<StyleSheet
>, nsresult
> LoadSheet(nsIURI
*, SheetParsingMode
,
252 nsICSSLoaderObserver
*);
255 * Stop loading all sheets. All nsICSSLoaderObservers involved will be
256 * notified with NS_BINDING_ABORTED as the status, possibly synchronously.
261 * nsresult Loader::StopLoadingSheet(nsIURI* aURL), which notifies the
262 * nsICSSLoaderObserver with NS_BINDING_ABORTED, was removed in Bug 556446.
263 * It can be found in revision 2c44a32052ad.
267 * Whether the loader is enabled or not.
268 * When disabled, processing of new styles is disabled and an attempt
269 * to do so will fail with a return code of
270 * NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable
271 * currently loading styles or already processed styles.
273 bool GetEnabled() { return mEnabled
; }
274 void SetEnabled(bool aEnabled
) { mEnabled
= aEnabled
; }
277 * Get the document we live for. May return null.
279 dom::Document
* GetDocument() const { return mDocument
; }
282 * Return true if this loader has pending loads (ones that would send
283 * notifications to an nsICSSLoaderObserver attached to this loader).
284 * If called from inside nsICSSLoaderObserver::StyleSheetLoaded, this will
285 * return false if and only if that is the last StyleSheetLoaded
286 * notification the CSSLoader knows it's going to send. In other words, if
287 * two sheets load at once (via load coalescing, e.g.), HasPendingLoads()
288 * will return true during notification for the first one, and false
289 * during notification for the second one.
291 bool HasPendingLoads();
294 * Add an observer to this loader. The observer will be notified
295 * for all loads that would have notified their own observers (even
296 * if those loads don't have observers attached to them).
297 * Load-specific observers will be notified before generic
298 * observers. The loader holds a reference to the observer.
300 * aObserver must not be null.
302 void AddObserver(nsICSSLoaderObserver
* aObserver
);
305 * Remove an observer added via AddObserver.
307 void RemoveObserver(nsICSSLoaderObserver
* aObserver
);
309 // These interfaces are public only for the benefit of static functions
310 // within nsCSSLoader.cpp.
312 // IsAlternateSheet can change our currently selected style set if none is
313 // selected and aHasAlternateRel is false.
314 IsAlternate
IsAlternateSheet(const nsAString
& aTitle
, bool aHasAlternateRel
);
316 typedef nsTArray
<RefPtr
<SheetLoadData
>> LoadDataArray
;
319 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const;
322 friend class SheetLoadData
;
323 friend class StreamLoader
;
325 // Helpers to conditionally block onload if mDocument is non-null.
327 void UnblockOnload(bool aFireSync
);
329 // Helper to select the correct dispatch target for asynchronous events for
331 already_AddRefed
<nsISerialEventTarget
> DispatchTarget();
333 nsresult
CheckContentPolicy(nsIPrincipal
* aLoadingPrincipal
,
334 nsIPrincipal
* aTriggeringPrincipal
,
335 nsIURI
* aTargetURI
, nsINode
* aRequestingNode
,
336 const nsAString
& aNonce
, IsPreload
);
338 enum class SheetState
: uint8_t {
346 std::tuple
<RefPtr
<StyleSheet
>, SheetState
> CreateSheet(
347 const SheetInfo
& aInfo
, nsIPrincipal
* aLoaderPrincipal
,
348 css::SheetParsingMode aParsingMode
, bool aSyncLoad
) {
349 return CreateSheet(aInfo
.mURI
, aInfo
.mContent
, aLoaderPrincipal
,
350 aParsingMode
, aInfo
.mCORSMode
, aInfo
.mReferrerInfo
,
351 aInfo
.mIntegrity
, aSyncLoad
);
354 // For inline style, the aURI param is null, but the aLinkingContent
355 // must be non-null then. The loader principal must never be null
356 // if aURI is not null.
357 std::tuple
<RefPtr
<StyleSheet
>, SheetState
> CreateSheet(
358 nsIURI
* aURI
, nsIContent
* aLinkingContent
, nsIPrincipal
* aLoaderPrincipal
,
359 css::SheetParsingMode
, CORSMode
, nsIReferrerInfo
* aLoadingReferrerInfo
,
360 const nsAString
& aIntegrity
, bool aSyncLoad
);
362 // Pass in either a media string or the MediaList from the CSSParser. Don't
365 // This method will set the sheet's enabled state based on IsAlternate and co.
366 MediaMatched
PrepareSheet(StyleSheet
&, const nsAString
& aTitle
,
367 const nsAString
& aMediaString
, dom::MediaList
*,
368 IsAlternate
, IsExplicitlyEnabled
);
370 // Inserts a style sheet in a document or a ShadowRoot.
371 void InsertSheetInTree(StyleSheet
& aSheet
, nsIContent
* aLinkingContent
);
372 // Inserts a style sheet into a parent style sheet.
373 void InsertChildSheet(StyleSheet
& aSheet
, StyleSheet
& aParentSheet
);
375 Result
<RefPtr
<StyleSheet
>, nsresult
> InternalLoadNonDocumentSheet(
376 nsIURI
* aURL
, IsPreload
, SheetParsingMode aParsingMode
,
377 UseSystemPrincipal
, nsIPrincipal
* aOriginPrincipal
,
378 const Encoding
* aPreloadEncoding
, nsIReferrerInfo
* aReferrerInfo
,
379 nsICSSLoaderObserver
* aObserver
, CORSMode aCORSMode
,
380 const nsAString
& aIntegrity
);
382 // Post a load event for aObserver to be notified about aSheet. The
383 // notification will be sent with status NS_OK unless the load event is
384 // canceled at some point (in which case it will be sent with
385 // NS_BINDING_ABORTED). aWasAlternate indicates the state when the load was
386 // initiated, not the state at some later time. aURI should be the URI the
387 // sheet was loaded from (may be null for inline sheets). aElement is the
388 // owning element for this sheet.
389 nsresult
PostLoadEvent(nsIURI
* aURI
, StyleSheet
* aSheet
,
390 nsICSSLoaderObserver
* aObserver
,
391 IsAlternate aWasAlternate
, MediaMatched aMediaMatched
,
392 nsIReferrerInfo
* aReferrerInfo
,
393 nsIStyleSheetLinkingElement
* aElement
);
395 // Start the loads of all the sheets in mPendingDatas
396 void StartDeferredLoads();
398 void HandleLoadEvent(SheetLoadData
&);
400 // Note: LoadSheet is responsible for setting the sheet to complete on
402 nsresult
LoadSheet(SheetLoadData
&, SheetState
);
404 enum class AllowAsyncParse
{
409 // Parse the stylesheet in the load data.
411 // Returns whether the parse finished. It may not finish e.g. if the sheet had
414 // If this function returns Completed::Yes, then ParseSheet also called
415 // SheetComplete on aLoadData.
416 Completed
ParseSheet(const nsACString
&, SheetLoadData
&, AllowAsyncParse
);
418 // The load of the sheet in the load data is done, one way or another.
420 void SheetComplete(SheetLoadData
&, nsresult aStatus
);
422 // The guts of SheetComplete. This may be called recursively on parent datas
423 // or datas that had glommed on to a single load. The array is there so load
424 // datas whose observers need to be notified can be added to it.
425 void DoSheetComplete(SheetLoadData
&, LoadDataArray
& aDatasToNotify
);
427 // Mark the given SheetLoadData, as well as any of its siblings, parents, etc
428 // transitively, as failed. The idea is to mark as failed any load that was
429 // directly or indirectly @importing the sheet this SheetLoadData represents.
430 void MarkLoadTreeFailed(SheetLoadData
&);
433 UniquePtr
<Sheets
> mSheets
;
435 // The array of posted stylesheet loaded events (SheetLoadDatas) we have.
436 // Note that these are rare.
437 LoadDataArray mPostedEvents
;
439 // Our array of "global" observers
440 nsTObserverArray
<nsCOMPtr
<nsICSSLoaderObserver
>> mObservers
;
442 // This reference is nulled by the Document in it's destructor through
443 // DropDocumentReference().
444 dom::Document
* MOZ_NON_OWNING_REF mDocument
; // the document we live for
446 // For dispatching events via DocGroup::Dispatch() when mDocument is nullptr.
447 RefPtr
<dom::DocGroup
> mDocGroup
;
449 // Number of datas still waiting to be notified on if we're notifying on a
450 // whole bunch at once (e.g. in one of the stop methods). This is used to
451 // make sure that HasPendingLoads() won't return false until we're notifying
452 // on the last data we're working with.
453 uint32_t mDatasToNotifyOn
;
455 nsCompatibility mCompatMode
;
457 bool mEnabled
; // is enabled to load new styles
459 nsCOMPtr
<nsIConsoleReportCollector
> mReporter
;
462 // Whether we're in a necko callback atm.
463 bool mSyncCallback
= false;
468 } // namespace mozilla
470 #endif /* mozilla_css_Loader_h */