Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / style / StyleSheet.h
blob5e4fb070a9d9fea76021326ad7ead2768f9f4935
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 #ifndef mozilla_StyleSheet_h
8 #define mozilla_StyleSheet_h
10 #include "mozilla/css/SheetParsingMode.h"
11 #include "mozilla/dom/CSSStyleSheetBinding.h"
12 #include "mozilla/dom/SRIMetadata.h"
13 #include "mozilla/CORSMode.h"
14 #include "mozilla/MozPromise.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/ServoBindingTypes.h"
17 #include "mozilla/ServoTypes.h"
18 #include "mozilla/StyleSheetInfo.h"
19 #include "nsICSSLoaderObserver.h"
20 #include "nsIPrincipal.h"
21 #include "nsWrapperCache.h"
22 #include "nsStringFwd.h"
24 class nsIGlobalObject;
25 class nsINode;
26 class nsIPrincipal;
27 struct StyleLockedCssRules;
28 class nsIReferrerInfo;
30 namespace mozilla {
32 class ServoCSSRuleList;
33 class ServoStyleSet;
35 using StyleSheetParsePromise = MozPromise</* Dummy */ bool,
36 /* Dummy */ bool,
37 /* IsExclusive = */ true>;
39 enum class StyleRuleChangeKind : uint32_t;
41 namespace css {
42 class GroupRule;
43 class Loader;
44 class LoaderReusableStyleSheets;
45 class Rule;
46 class SheetLoadData;
47 } // namespace css
49 namespace dom {
50 class CSSImportRule;
51 class CSSRuleList;
52 class DocumentOrShadowRoot;
53 class MediaList;
54 class ShadowRoot;
55 struct CSSStyleSheetInit;
56 } // namespace dom
58 enum class StyleSheetState : uint8_t {
59 // Whether the sheet is disabled. Sheets can be made disabled via CSSOM, or
60 // via alternate links and such.
61 Disabled = 1 << 0,
62 // Whether the sheet is complete. The sheet is complete if it's finished
63 // loading. See StyleSheet::SetComplete.
64 Complete = 1 << 1,
65 // Whether we've forced a unique inner. StyleSheet objects share an 'inner'
66 // StyleSheetInfo object if they share URL, CORS mode, etc.
68 // See the Loader's `mCompleteSheets` and `mLoadingSheets`.
69 ForcedUniqueInner = 1 << 2,
70 // Whether this stylesheet has suffered any modification to the rules via
71 // CSSOM.
72 ModifiedRules = 1 << 3,
73 // Same flag, but devtools clears it in some specific situations.
75 // Used to control whether devtools shows the rule in its authored form or
76 // not.
77 ModifiedRulesForDevtools = 1 << 4,
78 // Whether modifications to the sheet are currently disallowed.
79 // This flag is set during the async Replace() function to ensure
80 // that the sheet is not modified until the promise is resolved.
81 ModificationDisallowed = 1 << 5,
84 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StyleSheetState)
86 class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
87 StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
88 dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
89 dom::Document* aConstructorDocToUse);
91 virtual ~StyleSheet();
93 using State = StyleSheetState;
95 public:
96 StyleSheet(css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
97 const dom::SRIMetadata& aIntegrity);
99 static already_AddRefed<StyleSheet> Constructor(const dom::GlobalObject&,
100 const dom::CSSStyleSheetInit&,
101 ErrorResult&);
103 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
104 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(StyleSheet)
106 already_AddRefed<StyleSheet> CreateEmptyChildSheet(
107 already_AddRefed<dom::MediaList> aMediaList) const;
109 bool HasRules() const;
111 // Parses a stylesheet. The load data argument corresponds to the
112 // SheetLoadData for this stylesheet.
113 // NOTE: ParseSheet can run synchronously or asynchronously
114 // based on the result of `AllowParallelParse`
115 RefPtr<StyleSheetParsePromise> ParseSheet(css::Loader&,
116 const nsACString& aBytes,
117 css::SheetLoadData&);
119 // Common code that needs to be called after servo finishes parsing. This is
120 // shared between the parallel and sequential paths.
121 void FinishAsyncParse(already_AddRefed<StyleStylesheetContents>,
122 UniquePtr<StyleUseCounters>);
124 // Similar to `ParseSheet`, but guarantees that
125 // parsing will be performed synchronously.
126 // NOTE: ParseSheet can still run synchronously.
127 // This is not a strict alternative.
129 // The load data may be null sometimes.
130 void ParseSheetSync(
131 css::Loader* aLoader, const nsACString& aBytes,
132 css::SheetLoadData* aLoadData,
133 css::LoaderReusableStyleSheets* aReusableSheets = nullptr);
135 void ReparseSheet(const nsACString& aInput, ErrorResult& aRv);
137 const StyleStylesheetContents* RawContents() const {
138 return Inner().mContents;
141 const StyleUseCounters* GetStyleUseCounters() const {
142 return Inner().mUseCounters.get();
145 URLExtraData* URLData() const { return Inner().mURLData; }
147 // nsICSSLoaderObserver interface
148 NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
149 nsresult aStatus) final;
151 // Internal GetCssRules methods which do not have security check and
152 // completeness check.
153 ServoCSSRuleList* GetCssRulesInternal();
155 // Returns the stylesheet's Servo origin as a StyleOrigin value.
156 StyleOrigin GetOrigin() const;
158 void SetOwningNode(nsINode* aOwningNode) { mOwningNode = aOwningNode; }
160 css::SheetParsingMode ParsingMode() const { return mParsingMode; }
161 dom::CSSStyleSheetParsingMode ParsingModeDOM();
164 * Whether the sheet is complete.
166 bool IsComplete() const { return bool(mState & State::Complete); }
168 void SetComplete();
170 void SetEnabled(bool aEnabled) { SetDisabled(!aEnabled); }
172 // Whether the sheet is for an inline <style> element.
173 bool IsInline() const { return !GetOriginalURI(); }
175 nsIURI* GetSheetURI() const { return Inner().mSheetURI; }
178 * Get the URI this sheet was originally loaded from, if any. Can return null.
180 nsIURI* GetOriginalURI() const { return Inner().mOriginalSheetURI; }
182 nsIURI* GetBaseURI() const { return Inner().mBaseURI; }
185 * SetURIs must be called on all sheets before parsing into them.
186 * SetURIs may only be called while the sheet is 1) incomplete and 2)
187 * has no rules in it.
189 * FIXME(emilio): Can we pass this down when constructing the sheet instead?
191 inline void SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
192 nsIURI* aBaseURI);
195 * Whether the sheet is applicable. A sheet that is not applicable
196 * should never be inserted into a style set. A sheet may not be
197 * applicable for a variety of reasons including being disabled and
198 * being incomplete.
200 bool IsApplicable() const { return !Disabled() && IsComplete(); }
202 already_AddRefed<StyleSheet> Clone(
203 StyleSheet* aCloneParent,
204 dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot) const;
207 * Creates a clone of the adopted style sheet as though it were constructed
208 * by aConstructorDocument. This should only be used for printing.
210 already_AddRefed<StyleSheet> CloneAdoptedSheet(
211 dom::Document& aConstructorDocument) const;
213 bool HasForcedUniqueInner() const {
214 return bool(mState & State::ForcedUniqueInner);
217 bool HasModifiedRules() const { return bool(mState & State::ModifiedRules); }
219 bool HasModifiedRulesForDevtools() const {
220 return bool(mState & State::ModifiedRulesForDevtools);
223 bool HasUniqueInner() const { return Inner().mSheets.Length() == 1; }
225 void AssertHasUniqueInner() const { MOZ_ASSERT(HasUniqueInner()); }
227 void EnsureUniqueInner();
229 // Returns the DocumentOrShadowRoot* that owns us, if any.
231 // TODO(emilio): Maybe rename to GetOwner*() or such? Might be
232 // confusing with nsINode::OwnerDoc and such.
233 dom::DocumentOrShadowRoot* GetAssociatedDocumentOrShadowRoot() const;
235 // Whether this stylesheet is kept alive by the associated or constructor
236 // document somehow, and thus at least has the same lifetime as
237 // GetAssociatedDocument().
238 dom::Document* GetKeptAliveByDocument() const;
240 // If this is a constructed style sheet, return mConstructorDocument.
241 // Otherwise return the document we're associated to,
242 // via mDocumentOrShadowRoot.
244 // Non-null iff GetAssociatedDocumentOrShadowRoot is non-null.
245 dom::Document* GetAssociatedDocument() const;
247 void SetAssociatedDocumentOrShadowRoot(dom::DocumentOrShadowRoot*);
248 void ClearAssociatedDocumentOrShadowRoot() {
249 SetAssociatedDocumentOrShadowRoot(nullptr);
252 nsINode* GetOwnerNode() const { return mOwningNode; }
254 nsINode* GetOwnerNodeOfOutermostSheet() const {
255 return OutermostSheet().GetOwnerNode();
258 StyleSheet* GetParentSheet() const { return mParentSheet; }
260 void AddReferencingRule(dom::CSSImportRule& aRule) {
261 MOZ_ASSERT(!mReferencingRules.Contains(&aRule));
262 mReferencingRules.AppendElement(&aRule);
265 void RemoveReferencingRule(dom::CSSImportRule& aRule) {
266 MOZ_ASSERT(mReferencingRules.Contains(&aRule));
267 mReferencingRules.RemoveElement(&aRule);
270 // Note that when exposed to script, this should always have a <= 1 length.
271 // CSSImportRule::GetStyleSheetForBindings takes care of that.
272 dom::CSSImportRule* GetOwnerRule() const {
273 return mReferencingRules.SafeElementAt(0);
276 void AppendStyleSheet(StyleSheet&);
278 // Append a stylesheet to the child list without calling WillDirty.
279 void AppendStyleSheetSilently(StyleSheet&);
281 const nsTArray<RefPtr<StyleSheet>>& ChildSheets() const {
282 #ifdef DEBUG
283 for (StyleSheet* child : Inner().mChildren) {
284 MOZ_ASSERT(child->GetParentSheet());
285 MOZ_ASSERT(child->GetParentSheet()->mInner == mInner);
287 #endif
288 return Inner().mChildren;
291 // Principal() never returns a null pointer.
292 nsIPrincipal* Principal() const { return Inner().mPrincipal; }
295 * SetPrincipal should be called on all sheets before parsing into them.
296 * This can only be called once with a non-null principal.
298 * Calling this with a null pointer is allowed and is treated as a no-op.
300 * FIXME(emilio): Can we get this at construction time instead?
302 void SetPrincipal(nsIPrincipal* aPrincipal) {
303 StyleSheetInfo& info = Inner();
304 MOZ_ASSERT(!info.mPrincipalSet, "Should only set principal once");
305 if (aPrincipal) {
306 info.mPrincipal = aPrincipal;
307 #ifdef DEBUG
308 info.mPrincipalSet = true;
309 #endif
313 void SetTitle(const nsAString& aTitle) { mTitle = aTitle; }
314 void SetMedia(already_AddRefed<dom::MediaList> aMedia);
316 // Get this style sheet's CORS mode
317 CORSMode GetCORSMode() const { return Inner().mCORSMode; }
319 // Get this style sheet's ReferrerInfo
320 nsIReferrerInfo* GetReferrerInfo() const { return Inner().mReferrerInfo; }
322 // Set this style sheet's ReferrerInfo
323 void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
324 Inner().mReferrerInfo = aReferrerInfo;
327 // Get this style sheet's integrity metadata
328 void GetIntegrity(dom::SRIMetadata& aResult) const {
329 aResult = Inner().mIntegrity;
332 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
333 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
334 void List(FILE* aOut = stdout, int32_t aIndex = 0);
335 #endif
337 // WebIDL StyleSheet API
338 void GetType(nsAString& aType);
339 void GetHref(nsAString& aHref, ErrorResult& aRv);
340 // GetOwnerNode is defined above.
341 StyleSheet* GetParentStyleSheet() const { return GetParentSheet(); }
342 void GetTitle(nsAString& aTitle);
343 dom::MediaList* Media();
344 bool Disabled() const { return bool(mState & State::Disabled); }
345 void SetDisabled(bool aDisabled);
347 void GetSourceMapURL(nsACString&);
348 void SetSourceMapURL(nsCString&&);
349 void GetSourceURL(nsACString& aSourceURL);
351 // WebIDL CSSStyleSheet API
352 // Can't be inline because we can't include ImportRule here. And can't be
353 // called GetOwnerRule because that would be ambiguous with the ImportRule
354 // version.
355 css::Rule* GetDOMOwnerRule() const;
356 dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal, ErrorResult&);
357 uint32_t InsertRule(const nsACString& aRule, uint32_t aIndex,
358 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
359 void DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
360 ErrorResult& aRv);
361 int32_t AddRule(const nsACString& aSelector, const nsACString& aBlock,
362 const dom::Optional<uint32_t>& aIndex,
363 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
364 already_AddRefed<dom::Promise> Replace(const nsACString& aText, ErrorResult&);
365 void ReplaceSync(const nsACString& aText, ErrorResult&);
366 bool ModificationDisallowed() const {
367 return bool(mState & State::ModificationDisallowed);
370 // Called before and after the asynchronous Replace() function
371 // to disable/re-enable modification while there is a pending promise.
372 void SetModificationDisallowed(bool aDisallowed) {
373 MOZ_ASSERT(IsConstructed());
374 MOZ_ASSERT(!IsReadOnly());
375 if (aDisallowed) {
376 mState |= State::ModificationDisallowed;
377 // Sheet will be re-set to complete when its rules are replaced
378 mState &= ~State::Complete;
379 if (!Disabled()) {
380 ApplicableStateChanged(false);
382 } else {
383 mState &= ~State::ModificationDisallowed;
387 // True if the sheet was created through the Constructable StyleSheets API
388 bool IsConstructed() const { return !!mConstructorDocument; }
390 // True if any of this sheet's ancestors were created through the
391 // Constructable StyleSheets API
392 bool SelfOrAncestorIsConstructed() const {
393 return OutermostSheet().IsConstructed();
396 // Ture if the sheet's constructor document matches the given document
397 bool ConstructorDocumentMatches(const dom::Document& aDocument) const {
398 return mConstructorDocument == &aDocument;
401 // Add a document or shadow root to the list of adopters.
402 // Adopters will be notified when styles are changed.
403 void AddAdopter(dom::DocumentOrShadowRoot& aAdopter) {
404 MOZ_ASSERT(IsConstructed());
405 MOZ_ASSERT(!mAdopters.Contains(&aAdopter));
406 mAdopters.AppendElement(&aAdopter);
409 // Remove a document or shadow root from the list of adopters.
410 void RemoveAdopter(dom::DocumentOrShadowRoot& aAdopter) {
411 // Cannot assert IsConstructed() because this can run after unlink.
412 mAdopters.RemoveElement(&aAdopter);
415 const nsTArray<dom::DocumentOrShadowRoot*>& SelfOrAncestorAdopters() const {
416 return OutermostSheet().mAdopters;
419 // WebIDL miscellaneous bits
420 inline dom::ParentObject GetParentObject() const;
421 JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
423 // Changes to sheets should be after a WillDirty call.
424 void WillDirty();
426 // Called when a rule changes from CSSOM.
428 // FIXME(emilio): This shouldn't allow null, but MediaList doesn't know about
429 // its owning media rule, plus it's used for the stylesheet media itself.
430 void RuleChanged(css::Rule*, StyleRuleChangeKind);
432 void AddStyleSet(ServoStyleSet* aStyleSet);
433 void DropStyleSet(ServoStyleSet* aStyleSet);
435 nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex);
436 nsresult InsertRuleIntoGroup(const nsACString& aRule, css::GroupRule* aGroup,
437 uint32_t aIndex);
439 // Find the ID of the owner inner window.
440 uint64_t FindOwningWindowInnerID() const;
442 // Copy the contents of this style sheet into the shared memory buffer managed
443 // by aBuilder. Returns the pointer into the buffer that the sheet contents
444 // were stored at. (The returned pointer is to an Arc<Locked<Rules>> value,
445 // or null, with a filled in aErrorMessage, on failure.)
446 const StyleLockedCssRules* ToShared(StyleSharedMemoryBuilder* aBuilder,
447 nsCString& aErrorMessage);
449 // Sets the contents of this style sheet to the specified aSharedRules
450 // pointer, which must be a pointer somewhere in the aSharedMemory buffer
451 // as previously returned by a ToShared() call.
452 void SetSharedContents(const StyleLockedCssRules* aSharedRules);
454 // Whether this style sheet should not allow any modifications.
456 // This is true for any User Agent sheets once they are complete.
457 bool IsReadOnly() const;
459 // Removes a stylesheet from its parent sheet child list, if any.
460 void RemoveFromParent();
462 // Resolves mReplacePromise with this sheet.
463 void MaybeResolveReplacePromise();
465 // Rejects mReplacePromise with a NetworkError.
466 void MaybeRejectReplacePromise();
468 // Gets the relevant global if exists.
469 nsISupports* GetRelevantGlobal() const;
471 private:
472 void SetModifiedRules() {
473 mState |= State::ModifiedRules | State::ModifiedRulesForDevtools;
476 const StyleSheet& OutermostSheet() const {
477 const auto* current = this;
478 while (current->mParentSheet) {
479 MOZ_ASSERT(!current->mDocumentOrShadowRoot,
480 "Shouldn't be set on child sheets");
481 MOZ_ASSERT(!current->mConstructorDocument,
482 "Shouldn't be set on child sheets");
483 current = current->mParentSheet;
485 return *current;
488 StyleSheetInfo& Inner() {
489 MOZ_ASSERT(mInner);
490 return *mInner;
493 const StyleSheetInfo& Inner() const {
494 MOZ_ASSERT(mInner);
495 return *mInner;
498 // Check if the rules are available for read and write.
499 // It does the security check as well as whether the rules have been
500 // completely loaded. aRv will have an exception set if this function
501 // returns false.
502 bool AreRulesAvailable(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
504 void SetURLExtraData();
506 protected:
507 // Internal methods which do not have security check and completeness check.
508 uint32_t InsertRuleInternal(const nsACString& aRule, uint32_t aIndex,
509 ErrorResult&);
510 void DeleteRuleInternal(uint32_t aIndex, ErrorResult&);
511 nsresult InsertRuleIntoGroupInternal(const nsACString& aRule,
512 css::GroupRule* aGroup, uint32_t aIndex);
514 // Take the recently cloned sheets from the `@import` rules, and reparent them
515 // correctly to `aPrimarySheet`.
516 void FixUpAfterInnerClone();
518 // aFromClone says whether this comes from a clone of the stylesheet (and thus
519 // we should also fix up the wrappers for the individual rules in the rule
520 // lists).
521 void FixUpRuleListAfterContentsChangeIfNeeded(bool aFromClone = false);
523 void DropRuleList();
525 // Called when a rule is removed from the sheet from CSSOM.
526 void RuleAdded(css::Rule&);
528 // Called when a rule is added to the sheet from CSSOM.
529 void RuleRemoved(css::Rule&);
531 // Called when a stylesheet is cloned.
532 void StyleSheetCloned(StyleSheet&);
534 // Notifies that the applicable state changed.
535 // aApplicable is the value that we expect to get from IsApplicable().
536 // assertion will fail if the expectation does not match reality.
537 void ApplicableStateChanged(bool aApplicable);
539 void LastRelease();
541 // Return success if the subject principal subsumes the principal of our
542 // inner, error otherwise. This will also succeed if access is allowed by
543 // CORS. In that case, it will set the principal of the inner to the
544 // subject principal.
545 void SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
546 ErrorResult& aRv);
548 // Drop our reference to mMedia
549 void DropMedia();
551 // Unlink our inner, if needed, for cycle collection.
552 void UnlinkInner();
553 // Traverse our inner, if needed, for cycle collection
554 void TraverseInner(nsCycleCollectionTraversalCallback&);
556 // Return whether the given @import rule has pending child sheet.
557 static bool RuleHasPendingChildSheet(css::Rule* aRule);
559 StyleSheet* mParentSheet; // weak ref
561 // A pointer to the sheets relevant global object.
562 // This is populated when the sheet gets an associated document.
563 // This is required for the sheet to be able to create a promise.
564 // https://html.spec.whatwg.org/#concept-relevant-everything
565 nsCOMPtr<nsIGlobalObject> mRelevantGlobal;
567 RefPtr<dom::Document> mConstructorDocument;
569 // Will be set in the Replace() function and resolved/rejected by the
570 // sheet once its rules have been replaced and the sheet is complete again.
571 RefPtr<dom::Promise> mReplacePromise;
573 nsString mTitle;
575 // weak ref; parents maintain this for their children
576 dom::DocumentOrShadowRoot* mDocumentOrShadowRoot;
577 nsINode* mOwningNode = nullptr; // weak ref
578 nsTArray<dom::CSSImportRule*> mReferencingRules; // weak ref
580 RefPtr<dom::MediaList> mMedia;
582 // mParsingMode controls access to nonstandard style constructs that
583 // are not safe for use on the public Web but necessary in UA sheets
584 // and/or useful in user sheets.
586 // FIXME(emilio): Given we store the parsed contents in the Inner, this should
587 // probably also move there.
588 css::SheetParsingMode mParsingMode;
590 State mState;
592 // Core information we get from parsed sheets, which are shared amongst
593 // StyleSheet clones.
595 // Always nonnull until LastRelease().
596 StyleSheetInfo* mInner;
598 nsTArray<ServoStyleSet*> mStyleSets;
600 RefPtr<ServoCSSRuleList> mRuleList;
602 MozPromiseHolder<StyleSheetParsePromise> mParsePromise;
604 nsTArray<dom::DocumentOrShadowRoot*> mAdopters;
606 // Make StyleSheetInfo and subclasses into friends so they can use
607 // ChildSheetListBuilder.
608 friend struct StyleSheetInfo;
611 } // namespace mozilla
613 #endif // mozilla_StyleSheet_h