Bug 1635702 [wpt PR 23413] - Move some internal scroll anchoring tests to wpt, a...
[gecko.git] / layout / style / Loader.h
blob1ab12d644ebee3278ccd25dc664272a76c001f88
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
12 #include <tuple>
13 #include <utility>
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"
29 #include "nsTArray.h"
30 #include "nsTObserverArray.h"
31 #include "nsURIHashKey.h"
33 class nsICSSLoaderObserver;
34 class nsIConsoleReportCollector;
35 class nsIContent;
37 namespace mozilla {
38 namespace dom {
39 class DocGroup;
40 class Element;
41 } // namespace dom
42 } // namespace mozilla
44 namespace mozilla {
46 namespace css {
48 class SheetLoadData;
49 class ImportRule;
51 /*********************
52 * Style sheet reuse *
53 *********************/
55 class MOZ_RAII LoaderReusableStyleSheets {
56 public:
57 LoaderReusableStyleSheets() = default;
59 /**
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);
70 /**
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);
81 private:
82 LoaderReusableStyleSheets(const LoaderReusableStyleSheets&) = delete;
83 LoaderReusableStyleSheets& operator=(const LoaderReusableStyleSheets&) =
84 delete;
86 // The sheets that can be reused.
87 nsTArray<RefPtr<StyleSheet>> mReusableSheets;
90 class Loader final {
91 using ReferrerPolicy = dom::ReferrerPolicy;
93 public:
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;
103 Loader();
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*);
110 private:
111 // Private destructor, to discourage deletion outside of Release():
112 ~Loader();
114 public:
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.
139 * May be null.
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.
155 * May be null.
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
173 * CSSOM.
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
177 * for this load
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.
214 FromParser,
215 // This is a speculative load as well, but initiated by
216 // <link rel="preload" as="style">
217 FromLink,
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.
233 * Must not be null.
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,
251 UseSystemPrincipal,
252 nsICSSLoaderObserver*);
255 * Stop loading all sheets. All nsICSSLoaderObservers involved will be
256 * notified with NS_BINDING_ABORTED as the status, possibly synchronously.
258 void Stop();
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;
318 // Measure our size.
319 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
321 private:
322 friend class SheetLoadData;
323 friend class StreamLoader;
325 // Helpers to conditionally block onload if mDocument is non-null.
326 void BlockOnload();
327 void UnblockOnload(bool aFireSync);
329 // Helper to select the correct dispatch target for asynchronous events for
330 // this loader.
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 {
339 Unknown = 0,
340 NeedsParser,
341 Pending,
342 Loading,
343 Complete
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
363 // pass both.
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
401 // failure.
402 nsresult LoadSheet(SheetLoadData&, SheetState);
404 enum class AllowAsyncParse {
405 Yes,
409 // Parse the stylesheet in the load data.
411 // Returns whether the parse finished. It may not finish e.g. if the sheet had
412 // an @import.
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.
419 // Do final cleanup.
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&);
432 struct Sheets;
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;
461 #ifdef DEBUG
462 // Whether we're in a necko callback atm.
463 bool mSyncCallback = false;
464 #endif
467 } // namespace css
468 } // namespace mozilla
470 #endif /* mozilla_css_Loader_h */