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
;
27 struct StyleLockedCssRules
;
28 class nsIReferrerInfo
;
32 class ServoCSSRuleList
;
35 using StyleSheetParsePromise
= MozPromise
</* Dummy */ bool,
37 /* IsExclusive = */ true>;
39 enum class StyleRuleChangeKind
: uint32_t;
44 class LoaderReusableStyleSheets
;
52 class DocumentOrShadowRoot
;
55 struct CSSStyleSheetInit
;
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.
62 // Whether the sheet is complete. The sheet is complete if it's finished
63 // loading. See StyleSheet::SetComplete.
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
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
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
;
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
&,
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.
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
); }
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
,
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
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 {
283 for (StyleSheet
* child
: Inner().mChildren
) {
284 MOZ_ASSERT(child
->GetParentSheet());
285 MOZ_ASSERT(child
->GetParentSheet()->mInner
== mInner
);
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");
306 info
.mPrincipal
= aPrincipal
;
308 info
.mPrincipalSet
= true;
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);
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
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
,
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());
376 mState
|= State::ModificationDisallowed
;
377 // Sheet will be re-set to complete when its rules are replaced
378 mState
&= ~State::Complete
;
380 ApplicableStateChanged(false);
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.
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
,
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;
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
;
488 StyleSheetInfo
& Inner() {
493 const StyleSheetInfo
& Inner() const {
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
502 bool AreRulesAvailable(nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
);
504 void SetURLExtraData();
507 // Internal methods which do not have security check and completeness check.
508 uint32_t InsertRuleInternal(const nsACString
& aRule
, uint32_t aIndex
,
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
521 void FixUpRuleListAfterContentsChangeIfNeeded(bool aFromClone
= false);
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
);
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
,
548 // Drop our reference to mMedia
551 // Unlink our inner, if needed, for cycle collection.
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
;
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
;
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