summaryrefslogtreecommitdiffstats
path: root/layout/style/StyleSheet.h
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/StyleSheet.h')
-rw-r--r--layout/style/StyleSheet.h593
1 files changed, 593 insertions, 0 deletions
diff --git a/layout/style/StyleSheet.h b/layout/style/StyleSheet.h
new file mode 100644
index 0000000000..fcd7460af1
--- /dev/null
+++ b/layout/style/StyleSheet.h
@@ -0,0 +1,593 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_StyleSheet_h
+#define mozilla_StyleSheet_h
+
+#include "mozilla/css/SheetParsingMode.h"
+#include "mozilla/dom/CSSStyleSheetBinding.h"
+#include "mozilla/dom/SRIMetadata.h"
+#include "mozilla/CORSMode.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/ServoBindingTypes.h"
+#include "mozilla/ServoTypes.h"
+#include "mozilla/StyleSheetInfo.h"
+#include "nsICSSLoaderObserver.h"
+#include "nsIPrincipal.h"
+#include "nsWrapperCache.h"
+#include "nsStringFwd.h"
+
+class nsIGlobalObject;
+class nsINode;
+class nsIPrincipal;
+struct ServoCssRules;
+class nsIReferrerInfo;
+
+namespace mozilla {
+
+class ServoCSSRuleList;
+class ServoStyleSet;
+
+typedef MozPromise</* Dummy */ bool,
+ /* Dummy */ bool,
+ /* IsExclusive = */ true>
+ StyleSheetParsePromise;
+
+enum class StyleRuleChangeKind : uint32_t;
+
+namespace css {
+class GroupRule;
+class Loader;
+class LoaderReusableStyleSheets;
+class Rule;
+class SheetLoadData;
+} // namespace css
+
+namespace dom {
+class CSSImportRule;
+class CSSRuleList;
+class DocumentOrShadowRoot;
+class MediaList;
+class ShadowRoot;
+struct CSSStyleSheetInit;
+} // namespace dom
+
+enum class StyleSheetState : uint8_t {
+ // Whether the sheet is disabled. Sheets can be made disabled via CSSOM, or
+ // via alternate links and such.
+ Disabled = 1 << 0,
+ // Whether the sheet is complete. The sheet is complete if it's finished
+ // loading. See StyleSheet::SetComplete.
+ Complete = 1 << 1,
+ // Whether we've forced a unique inner. StyleSheet objects share an 'inner'
+ // StyleSheetInfo object if they share URL, CORS mode, etc.
+ //
+ // See the Loader's `mCompleteSheets` and `mLoadingSheets`.
+ ForcedUniqueInner = 1 << 2,
+ // Whether this stylesheet has suffered any modification to the rules via
+ // CSSOM.
+ ModifiedRules = 1 << 3,
+ // Same flag, but devtools clears it in some specific situations.
+ //
+ // Used to control whether devtools shows the rule in its authored form or
+ // not.
+ ModifiedRulesForDevtools = 1 << 4,
+ // Whether modifications to the sheet are currently disallowed.
+ // This flag is set during the async Replace() function to ensure
+ // that the sheet is not modified until the promise is resolved.
+ ModificationDisallowed = 1 << 5,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StyleSheetState)
+
+class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
+ StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
+ dom::CSSImportRule* aOwnerRuleToUse,
+ dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
+ dom::Document* aConstructorDocToUse, nsINode* aOwningNodeToUse);
+
+ virtual ~StyleSheet();
+
+ using State = StyleSheetState;
+
+ public:
+ StyleSheet(css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
+ const dom::SRIMetadata& aIntegrity);
+
+ static already_AddRefed<StyleSheet> Constructor(const dom::GlobalObject&,
+ const dom::CSSStyleSheetInit&,
+ ErrorResult&);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StyleSheet)
+
+ already_AddRefed<StyleSheet> CreateEmptyChildSheet(
+ already_AddRefed<dom::MediaList> aMediaList) const;
+
+ bool HasRules() const;
+
+ // Parses a stylesheet. The load data argument corresponds to the
+ // SheetLoadData for this stylesheet.
+ // NOTE: ParseSheet can run synchronously or asynchronously
+ // based on the result of `AllowParallelParse`
+ RefPtr<StyleSheetParsePromise> ParseSheet(css::Loader&,
+ const nsACString& aBytes,
+ css::SheetLoadData&);
+
+ // Common code that needs to be called after servo finishes parsing. This is
+ // shared between the parallel and sequential paths.
+ void FinishAsyncParse(already_AddRefed<RawServoStyleSheetContents>);
+
+ // Similar to `ParseSheet`, but guarantees that
+ // parsing will be performed synchronously.
+ // NOTE: ParseSheet can still run synchronously.
+ // This is not a strict alternative.
+ //
+ // The load data may be null sometimes.
+ void ParseSheetSync(
+ css::Loader* aLoader, const nsACString& aBytes,
+ css::SheetLoadData* aLoadData, uint32_t aLineNumber,
+ css::LoaderReusableStyleSheets* aReusableSheets = nullptr);
+
+ void ReparseSheet(const nsACString& aInput, ErrorResult& aRv);
+
+ const RawServoStyleSheetContents* RawContents() const {
+ return Inner().mContents;
+ }
+
+ void SetContentsForImport(const RawServoStyleSheetContents* aContents) {
+ MOZ_ASSERT(!Inner().mContents);
+ Inner().mContents = aContents;
+ }
+
+ URLExtraData* URLData() const { return Inner().mURLData; }
+
+ // nsICSSLoaderObserver interface
+ NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
+ nsresult aStatus) final;
+
+ // Internal GetCssRules methods which do not have security check and
+ // completeness check.
+ ServoCSSRuleList* GetCssRulesInternal();
+
+ // Returns the stylesheet's Servo origin as a StyleOrigin value.
+ StyleOrigin GetOrigin() const;
+
+ void SetOwningNode(nsINode* aOwningNode) { mOwningNode = aOwningNode; }
+
+ css::SheetParsingMode ParsingMode() const { return mParsingMode; }
+ dom::CSSStyleSheetParsingMode ParsingModeDOM();
+
+ /**
+ * Whether the sheet is complete.
+ */
+ bool IsComplete() const { return bool(mState & State::Complete); }
+
+ void SetComplete();
+
+ void SetEnabled(bool aEnabled) { SetDisabled(!aEnabled); }
+
+ // Whether the sheet is for an inline <style> element.
+ bool IsInline() const { return !GetOriginalURI(); }
+
+ nsIURI* GetSheetURI() const { return Inner().mSheetURI; }
+
+ /**
+ * Get the URI this sheet was originally loaded from, if any. Can return null.
+ */
+ nsIURI* GetOriginalURI() const { return Inner().mOriginalSheetURI; }
+
+ nsIURI* GetBaseURI() const { return Inner().mBaseURI; }
+
+ /**
+ * SetURIs must be called on all sheets before parsing into them.
+ * SetURIs may only be called while the sheet is 1) incomplete and 2)
+ * has no rules in it.
+ *
+ * FIXME(emilio): Can we pass this down when constructing the sheet instead?
+ */
+ inline void SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
+ nsIURI* aBaseURI);
+
+ /**
+ * Whether the sheet is applicable. A sheet that is not applicable
+ * should never be inserted into a style set. A sheet may not be
+ * applicable for a variety of reasons including being disabled and
+ * being incomplete.
+ */
+ bool IsApplicable() const { return !Disabled() && IsComplete(); }
+
+ already_AddRefed<StyleSheet> Clone(
+ StyleSheet* aCloneParent, dom::CSSImportRule* aCloneOwnerRule,
+ dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
+ nsINode* aCloneOwningNode) const;
+
+ /**
+ * Creates a clone of the adopted style sheet as though it were constructed
+ * by aConstructorDocument. This should only be used for printing.
+ */
+ already_AddRefed<StyleSheet> CloneAdoptedSheet(
+ dom::Document& aConstructorDocument) const;
+
+ bool HasForcedUniqueInner() const {
+ return bool(mState & State::ForcedUniqueInner);
+ }
+
+ bool HasModifiedRules() const { return bool(mState & State::ModifiedRules); }
+
+ bool HasModifiedRulesForDevtools() const {
+ return bool(mState & State::ModifiedRulesForDevtools);
+ }
+
+ bool HasUniqueInner() const { return Inner().mSheets.Length() == 1; }
+
+ void AssertHasUniqueInner() const { MOZ_ASSERT(HasUniqueInner()); }
+
+ void EnsureUniqueInner();
+
+ // Returns the DocumentOrShadowRoot* that owns us, if any.
+ //
+ // TODO(emilio): Maybe rename to GetOwner*() or such? Might be
+ // confusing with nsINode::OwnerDoc and such.
+ dom::DocumentOrShadowRoot* GetAssociatedDocumentOrShadowRoot() const;
+
+ // Whether this stylesheet is kept alive by the associated or constructor
+ // document somehow, and thus at least has the same lifetime as
+ // GetAssociatedDocument().
+ dom::Document* GetKeptAliveByDocument() const;
+
+ // If this is a constructed style sheet, return mConstructorDocument.
+ // Otherwise return the document we're associated to,
+ // via mDocumentOrShadowRoot.
+ //
+ // Non-null iff GetAssociatedDocumentOrShadowRoot is non-null.
+ dom::Document* GetAssociatedDocument() const;
+
+ void SetAssociatedDocumentOrShadowRoot(dom::DocumentOrShadowRoot*);
+ void ClearAssociatedDocumentOrShadowRoot() {
+ SetAssociatedDocumentOrShadowRoot(nullptr);
+ }
+
+ nsINode* GetOwnerNode() const { return mOwningNode; }
+
+ StyleSheet* GetParentSheet() const { return mParentSheet; }
+
+ void SetOwnerRule(dom::CSSImportRule* aOwnerRule) {
+ mOwnerRule = aOwnerRule; /* Not ref counted */
+ }
+ dom::CSSImportRule* GetOwnerRule() const { return mOwnerRule; }
+
+ void AppendStyleSheet(StyleSheet&);
+
+ // Append a stylesheet to the child list without calling WillDirty.
+ void AppendStyleSheetSilently(StyleSheet&);
+
+ const nsTArray<RefPtr<StyleSheet>>& ChildSheets() const {
+#ifdef DEBUG
+ for (StyleSheet* child : Inner().mChildren) {
+ MOZ_ASSERT(child->GetParentSheet());
+ MOZ_ASSERT(child->GetParentSheet()->mInner == mInner);
+ }
+#endif
+ return Inner().mChildren;
+ }
+
+ // Principal() never returns a null pointer.
+ nsIPrincipal* Principal() const { return Inner().mPrincipal; }
+
+ /**
+ * SetPrincipal should be called on all sheets before parsing into them.
+ * This can only be called once with a non-null principal.
+ *
+ * Calling this with a null pointer is allowed and is treated as a no-op.
+ *
+ * FIXME(emilio): Can we get this at construction time instead?
+ */
+ void SetPrincipal(nsIPrincipal* aPrincipal) {
+ StyleSheetInfo& info = Inner();
+ MOZ_ASSERT(!info.mPrincipalSet, "Should only set principal once");
+ if (aPrincipal) {
+ info.mPrincipal = aPrincipal;
+#ifdef DEBUG
+ info.mPrincipalSet = true;
+#endif
+ }
+ }
+
+ void SetTitle(const nsAString& aTitle) { mTitle = aTitle; }
+ void SetMedia(already_AddRefed<dom::MediaList> aMedia);
+
+ // Get this style sheet's CORS mode
+ CORSMode GetCORSMode() const { return Inner().mCORSMode; }
+
+ // Get this style sheet's ReferrerInfo
+ nsIReferrerInfo* GetReferrerInfo() const { return Inner().mReferrerInfo; }
+
+ // Set this style sheet's ReferrerInfo
+ void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+ Inner().mReferrerInfo = aReferrerInfo;
+ }
+
+ // Get this style sheet's integrity metadata
+ void GetIntegrity(dom::SRIMetadata& aResult) const {
+ aResult = Inner().mIntegrity;
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+#if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
+ void List(FILE* aOut = stdout, int32_t aIndex = 0);
+#endif
+
+ // WebIDL StyleSheet API
+ void GetType(nsAString& aType);
+ void GetHref(nsAString& aHref, ErrorResult& aRv);
+ // GetOwnerNode is defined above.
+ StyleSheet* GetParentStyleSheet() const { return GetParentSheet(); }
+ void GetTitle(nsAString& aTitle);
+ dom::MediaList* Media();
+ bool Disabled() const { return bool(mState & State::Disabled); }
+ void SetDisabled(bool aDisabled);
+ void GetSourceMapURL(nsAString& aTitle);
+ void SetSourceMapURL(const nsAString& aSourceMapURL);
+ void SetSourceMapURLFromComment(const nsAString& aSourceMapURLFromComment);
+ void GetSourceURL(nsAString& aSourceURL);
+ void SetSourceURL(const nsAString& aSourceURL);
+
+ // WebIDL CSSStyleSheet API
+ // Can't be inline because we can't include ImportRule here. And can't be
+ // called GetOwnerRule because that would be ambiguous with the ImportRule
+ // version.
+ css::Rule* GetDOMOwnerRule() const;
+ dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal, ErrorResult&);
+ uint32_t InsertRule(const nsACString& aRule, uint32_t aIndex,
+ nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
+ void DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv);
+ int32_t AddRule(const nsACString& aSelector, const nsACString& aBlock,
+ const dom::Optional<uint32_t>& aIndex,
+ nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
+ already_AddRefed<dom::Promise> Replace(const nsACString& aText, ErrorResult&);
+ void ReplaceSync(const nsACString& aText, ErrorResult&);
+ bool ModificationDisallowed() const {
+ return bool(mState & State::ModificationDisallowed);
+ }
+
+ // Called before and after the asynchronous Replace() function
+ // to disable/re-enable modification while there is a pending promise.
+ void SetModificationDisallowed(bool aDisallowed) {
+ MOZ_ASSERT(IsConstructed());
+ MOZ_ASSERT(!IsReadOnly());
+ if (aDisallowed) {
+ mState |= State::ModificationDisallowed;
+ // Sheet will be re-set to complete when its rules are replaced
+ mState &= ~State::Complete;
+ if (!Disabled()) {
+ ApplicableStateChanged(false);
+ }
+ } else {
+ mState &= ~State::ModificationDisallowed;
+ }
+ }
+
+ // True if the sheet was created through the Constructable StyleSheets API
+ bool IsConstructed() const { return !!mConstructorDocument; }
+
+ // True if any of this sheet's ancestors were created through the
+ // Constructable StyleSheets API
+ bool SelfOrAncestorIsConstructed() const {
+ return OutermostSheet().IsConstructed();
+ }
+
+ // Ture if the sheet's constructor document matches the given document
+ bool ConstructorDocumentMatches(const dom::Document& aDocument) const {
+ return mConstructorDocument == &aDocument;
+ }
+
+ // Add a document or shadow root to the list of adopters.
+ // Adopters will be notified when styles are changed.
+ void AddAdopter(dom::DocumentOrShadowRoot& aAdopter) {
+ MOZ_ASSERT(IsConstructed());
+ MOZ_ASSERT(!mAdopters.Contains(&aAdopter));
+ mAdopters.AppendElement(&aAdopter);
+ }
+
+ // Remove a document or shadow root from the list of adopters.
+ void RemoveAdopter(dom::DocumentOrShadowRoot& aAdopter) {
+ // Cannot assert IsConstructed() because this can run after unlink.
+ mAdopters.RemoveElement(&aAdopter);
+ }
+
+ // WebIDL miscellaneous bits
+ inline dom::ParentObject GetParentObject() const;
+ JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
+
+ // Changes to sheets should be after a WillDirty call.
+ void WillDirty();
+
+ // Called when a rule changes from CSSOM.
+ //
+ // FIXME(emilio): This shouldn't allow null, but MediaList doesn't know about
+ // its owning media rule, plus it's used for the stylesheet media itself.
+ void RuleChanged(css::Rule*, StyleRuleChangeKind);
+
+ void AddStyleSet(ServoStyleSet* aStyleSet);
+ void DropStyleSet(ServoStyleSet* aStyleSet);
+
+ nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex);
+ nsresult InsertRuleIntoGroup(const nsACString& aRule, css::GroupRule* aGroup,
+ uint32_t aIndex);
+
+ // Find the ID of the owner inner window.
+ uint64_t FindOwningWindowInnerID() const;
+
+ // Copy the contents of this style sheet into the shared memory buffer managed
+ // by aBuilder. Returns the pointer into the buffer that the sheet contents
+ // were stored at. (The returned pointer is to an Arc<Locked<Rules>> value,
+ // or null, with a filled in aErrorMessage, on failure.)
+ const ServoCssRules* ToShared(RawServoSharedMemoryBuilder* aBuilder,
+ nsCString& aErrorMessage);
+
+ // Sets the contents of this style sheet to the specified aSharedRules
+ // pointer, which must be a pointer somewhere in the aSharedMemory buffer
+ // as previously returned by a ToShared() call.
+ void SetSharedContents(const ServoCssRules* aSharedRules);
+
+ // Whether this style sheet should not allow any modifications.
+ //
+ // This is true for any User Agent sheets once they are complete.
+ bool IsReadOnly() const;
+
+ // Removes a stylesheet from its parent sheet child list, if any.
+ void RemoveFromParent();
+
+ // Resolves mReplacePromise with this sheet.
+ void MaybeResolveReplacePromise();
+
+ // Rejects mReplacePromise with a NetworkError.
+ void MaybeRejectReplacePromise();
+
+ private:
+ void SetModifiedRules() {
+ mState |= State::ModifiedRules | State::ModifiedRulesForDevtools;
+ }
+
+ const StyleSheet& OutermostSheet() const {
+ auto* current = this;
+ while (current->mParentSheet) {
+ MOZ_ASSERT(!current->mDocumentOrShadowRoot,
+ "Shouldn't be set on child sheets");
+ MOZ_ASSERT(!current->mConstructorDocument,
+ "Shouldn't be set on child sheets");
+ current = current->mParentSheet;
+ }
+ return *current;
+ }
+
+ StyleSheetInfo& Inner() {
+ MOZ_ASSERT(mInner);
+ return *mInner;
+ }
+
+ const StyleSheetInfo& Inner() const {
+ MOZ_ASSERT(mInner);
+ return *mInner;
+ }
+
+ // Check if the rules are available for read and write.
+ // It does the security check as well as whether the rules have been
+ // completely loaded. aRv will have an exception set if this function
+ // returns false.
+ bool AreRulesAvailable(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
+
+ void SetURLExtraData();
+
+ protected:
+ // Internal methods which do not have security check and completeness check.
+ uint32_t InsertRuleInternal(const nsACString& aRule, uint32_t aIndex,
+ ErrorResult&);
+ void DeleteRuleInternal(uint32_t aIndex, ErrorResult&);
+ nsresult InsertRuleIntoGroupInternal(const nsACString& aRule,
+ css::GroupRule* aGroup, uint32_t aIndex);
+
+ // Common tail routine for the synchronous and asynchronous parsing paths.
+ void FinishParse();
+
+ // Take the recently cloned sheets from the `@import` rules, and reparent them
+ // correctly to `aPrimarySheet`.
+ void BuildChildListAfterInnerClone();
+
+ void DropRuleList();
+
+ // Called when a rule is removed from the sheet from CSSOM.
+ void RuleAdded(css::Rule&);
+
+ // Called when a rule is added to the sheet from CSSOM.
+ void RuleRemoved(css::Rule&);
+
+ // Called when a stylesheet is cloned.
+ void StyleSheetCloned(StyleSheet&);
+
+ // Notifies that the applicable state changed.
+ // aApplicable is the value that we expect to get from IsApplicable().
+ // assertion will fail if the expectation does not match reality.
+ void ApplicableStateChanged(bool aApplicable);
+
+ void LastRelease();
+
+ // Return success if the subject principal subsumes the principal of our
+ // inner, error otherwise. This will also succeed if access is allowed by
+ // CORS. In that case, it will set the principal of the inner to the
+ // subject principal.
+ void SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv);
+
+ // Drop our reference to mMedia
+ void DropMedia();
+
+ // Unlink our inner, if needed, for cycle collection.
+ void UnlinkInner();
+ // Traverse our inner, if needed, for cycle collection
+ void TraverseInner(nsCycleCollectionTraversalCallback&);
+
+ // Return whether the given @import rule has pending child sheet.
+ static bool RuleHasPendingChildSheet(css::Rule* aRule);
+
+ StyleSheet* mParentSheet; // weak ref
+
+ // A pointer to the sheets relevant global object.
+ // This is populated when the sheet gets an associated document.
+ // This is required for the sheet to be able to create a promise.
+ // https://html.spec.whatwg.org/#concept-relevant-everything
+ nsCOMPtr<nsIGlobalObject> mRelevantGlobal;
+
+ RefPtr<dom::Document> mConstructorDocument;
+
+ // Will be set in the Replace() function and resolved/rejected by the
+ // sheet once its rules have been replaced and the sheet is complete again.
+ RefPtr<dom::Promise> mReplacePromise;
+
+ nsString mTitle;
+
+ // weak ref; parents maintain this for their children
+ dom::DocumentOrShadowRoot* mDocumentOrShadowRoot;
+ nsINode* mOwningNode; // weak ref
+ dom::CSSImportRule* mOwnerRule; // weak ref
+
+ RefPtr<dom::MediaList> mMedia;
+
+ // mParsingMode controls access to nonstandard style constructs that
+ // are not safe for use on the public Web but necessary in UA sheets
+ // and/or useful in user sheets.
+ //
+ // FIXME(emilio): Given we store the parsed contents in the Inner, this should
+ // probably also move there.
+ css::SheetParsingMode mParsingMode;
+
+ State mState;
+
+ // Core information we get from parsed sheets, which are shared amongst
+ // StyleSheet clones.
+ //
+ // Always nonnull until LastRelease().
+ StyleSheetInfo* mInner;
+
+ nsTArray<ServoStyleSet*> mStyleSets;
+
+ RefPtr<ServoCSSRuleList> mRuleList;
+
+ MozPromiseHolder<StyleSheetParsePromise> mParsePromise;
+
+ nsTArray<dom::DocumentOrShadowRoot*> mAdopters;
+
+ // Make StyleSheetInfo and subclasses into friends so they can use
+ // ChildSheetListBuilder.
+ friend struct StyleSheetInfo;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_StyleSheet_h