| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * (C) 2001 Dirk Mueller (mueller@kde.org) |
| * (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
| * Copyright (C) 2004-2025 Apple Inc. All rights reserved. |
| * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
| * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #pragma once |
| |
| #include <WebCore/AnchorPositionEvaluator.h> |
| #include <WebCore/Document.h> |
| #include <WebCore/LayoutRect.h> |
| #include <WebCore/LayoutSize.h> |
| #include <WebCore/StyleScopeIdentifier.h> |
| #include <WebCore/StyleScopeOrdinal.h> |
| #include <WebCore/Styleable.h> |
| #include <WebCore/Timer.h> |
| #include <memory> |
| #include <wtf/CheckedPtr.h> |
| #include <wtf/HashMap.h> |
| #include <wtf/HashSet.h> |
| #include <wtf/Identified.h> |
| #include <wtf/RefPtr.h> |
| #include <wtf/TZoneMalloc.h> |
| #include <wtf/UniqueRef.h> |
| #include <wtf/Vector.h> |
| #include <wtf/WeakHashMap.h> |
| #include <wtf/WeakHashSet.h> |
| #include <wtf/WeakListHashSet.h> |
| #include <wtf/text/AtomStringHash.h> |
| #include <wtf/text/WTFString.h> |
| |
| namespace WebCore { |
| |
| class CSSCounterStyleRegistry; |
| class CSSStyleSheet; |
| class Element; |
| class HTMLSlotElement; |
| class Node; |
| class ProcessingInstruction; |
| class RenderBoxModelObject; |
| class StyleSheet; |
| class StyleSheetContents; |
| class StyleSheetList; |
| class ShadowRoot; |
| class TreeScope; |
| class WeakPtrImplWithEventTargetData; |
| |
| namespace Style { |
| |
| class CustomPropertyRegistry; |
| class DocumentScope; |
| class MatchResultCache; |
| class Resolver; |
| class RuleSet; |
| |
| // Whether a dependency on an attribute reaches past a shadow boundary, via a shadow-piercing |
| // rule like ::part() or a document author rule matching a UA shadow pseudo-element. |
| enum class AttributeAffectsShadowTree : bool { No, Yes }; |
| |
| class Scope : public CanMakeWeakPtr<Scope>, public CanMakeCheckedPtr<Scope>, public Identified<ScopeIdentifier> { |
| WTF_MAKE_TZONE_ALLOCATED(Scope); |
| WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(Scope); |
| public: |
| explicit Scope(ShadowRoot&); |
| |
| ~Scope(); |
| |
| const Vector<Ref<CSSStyleSheet>>& activeStyleSheets() const LIFETIME_BOUND { return m_activeStyleSheets; } |
| |
| const Vector<Ref<StyleSheet>>& styleSheetsForStyleSheetList() LIFETIME_BOUND; |
| const Vector<Ref<CSSStyleSheet>> activeStyleSheetsForInspector(); |
| |
| void addStyleSheetCandidateNode(Node&, bool createdByParser); |
| void removeStyleSheetCandidateNode(Node&); |
| |
| void establishPreferredStylesheetSetName(const Element&, const CSSStyleSheet&); |
| |
| void addPendingSheet(const Element&); |
| void removePendingSheet(const Element&); |
| void addPendingSheet(const ProcessingInstruction&); |
| void removePendingSheet(const ProcessingInstruction&); |
| bool NODELETE hasPendingSheets() const; |
| bool NODELETE hasPendingSheetsBeforeBody() const; |
| bool NODELETE hasPendingSheetsInBody() const; |
| bool NODELETE hasPendingSheet(const Element&) const; |
| bool NODELETE hasPendingSheetInBody(const Element&) const; |
| bool NODELETE hasPendingSheet(const ProcessingInstruction&) const; |
| |
| bool usesStyleBasedEditability() const { return m_usesStyleBasedEditability; } |
| bool usesHasPseudoClass() const { return m_usesHasPseudoClass; } |
| |
| bool activeStyleSheetsContains(const CSSStyleSheet&) const; |
| |
| // This is called when some stylesheet becomes newly enabled or disabled. |
| void didChangeActiveStyleSheetCandidates(); |
| // This is called when contents of a stylesheet is mutated. |
| void didChangeStyleSheetContents(); |
| |
| void didChangeViewportSize(); |
| |
| void invalidateMatchedDeclarationsCache(); |
| |
| bool hasPendingUpdate() const { return m_pendingUpdate || m_hasDescendantWithPendingUpdate; } |
| void flushPendingUpdate(); |
| |
| #if ENABLE(XSLT) |
| Vector<Ref<ProcessingInstruction>> collectXSLTransforms(); |
| #endif |
| |
| WEBCORE_EXPORT Resolver& resolver(); |
| Resolver* resolverIfExists() { return m_resolver.get(); } |
| const Resolver* resolverIfExists() const { return m_resolver.get(); } |
| void clearResolver(); |
| void releaseMemory(); |
| |
| const Document& document() const { return m_document; } |
| Document& document() { return m_document; } |
| const ShadowRoot* shadowRoot() const { return m_shadowRoot; } |
| ShadowRoot* shadowRoot() { return m_shadowRoot; } |
| |
| // The document scope for this scope's tree (itself if this is the document scope). |
| DocumentScope& NODELETE documentScope(); |
| |
| CheckedPtr<const Scope> NODELETE hostScope() const; |
| |
| static Scope& NODELETE forNode(Node&); |
| static const Scope& forNode(const Node&); |
| static Scope* forOrdinal(Element&, ScopeOrdinal); |
| static const Scope* forOrdinal(const Element&, ScopeOrdinal); |
| |
| const CustomPropertyRegistry& customPropertyRegistry() const LIFETIME_BOUND { return m_customPropertyRegistry.get(); } |
| CustomPropertyRegistry& customPropertyRegistry() LIFETIME_BOUND { return m_customPropertyRegistry.get(); } |
| const CSSCounterStyleRegistry& counterStyleRegistry() const LIFETIME_BOUND { return m_counterStyleRegistry.get(); } |
| CSSCounterStyleRegistry& counterStyleRegistry() LIFETIME_BOUND { return m_counterStyleRegistry.get(); } |
| |
| // Names of the attributes that attr() has been seen reading from elements in this scope. These |
| // are discovered by building style, so they are not derivable from the style sheets and have to |
| // survive both style sheet changes and the resolver being dropped. |
| void registerSubstitutionAttribute(const AtomString&, AttributeAffectsShadowTree) const; |
| std::optional<AttributeAffectsShadowTree> substitutionAttribute(const AtomString& lowercaseLocalName) const { return m_substitutionAttributes.getOptional(lowercaseLocalName); } |
| |
| protected: |
| explicit Scope(Document&); |
| |
| private: |
| friend class DocumentScope; |
| |
| bool NODELETE isForUserAgentShadowTree() const; |
| |
| void didRemovePendingStylesheet(); |
| |
| enum class UpdateType : uint8_t { ActiveSet, FullForExtensionStyleSheets, ContentsOrInterpretation }; |
| void updateActiveStyleSheets(UpdateType); |
| void scheduleUpdate(UpdateType); |
| |
| WEBCORE_EXPORT void flushPendingSelfUpdate(); |
| WEBCORE_EXPORT void flushPendingDescendantUpdates(); |
| |
| struct ActiveStyleSheetCollection { |
| Vector<Ref<StyleSheet>> activeStyleSheets; |
| Vector<Ref<StyleSheet>> styleSheetsForStyleSheetList; |
| }; |
| |
| ActiveStyleSheetCollection collectActiveStyleSheets(); |
| |
| enum class ResolverUpdateType { |
| Reconstruct, |
| Reset, |
| Additive |
| }; |
| struct StyleSheetChange { |
| ResolverUpdateType resolverUpdateType; |
| Vector<Ref<StyleSheetContents>> addedSheets { }; |
| }; |
| StyleSheetChange analyzeStyleSheetChange(const Vector<Ref<CSSStyleSheet>>& newStylesheets); |
| void invalidateStyleAfterStyleSheetChange(const StyleSheetChange&); |
| |
| void updateResolver(std::span<const Ref<CSSStyleSheet>>, ResolverUpdateType); |
| void createOrFindSharedShadowTreeResolver(); |
| void unshareShadowTreeResolverBeforeMutation(); |
| |
| using ResolverSharingKey = std::tuple<Vector<RefPtr<StyleSheetContents>>, bool, bool>; |
| ResolverSharingKey makeResolverSharingKey(); |
| |
| void pendingUpdateTimerFired(); |
| void clearPendingUpdate(); |
| |
| TreeScope& NODELETE treeScope(); |
| |
| const CheckedRef<Document> m_document; |
| ShadowRoot* m_shadowRoot { nullptr }; |
| |
| RefPtr<Resolver> m_resolver; |
| |
| Vector<Ref<StyleSheet>> m_styleSheetsForStyleSheetList; |
| Vector<Ref<CSSStyleSheet>> m_activeStyleSheets; |
| |
| Timer m_pendingUpdateTimer; |
| |
| mutable HashSet<SingleThreadWeakRef<const CSSStyleSheet>> m_weakCopyOfActiveStyleSheetListForFastLookup; |
| |
| // Track the currently loading top-level stylesheets needed for rendering. |
| // Sheets loaded using the @import directive are not included in this count. |
| // We use this count of pending sheets to detect when we can begin attaching |
| // elements and when it is safe to execute scripts. |
| WeakHashSet<const ProcessingInstruction, WeakPtrImplWithEventTargetData> m_processingInstructionsWithPendingSheets; |
| WeakHashSet<const Element, WeakPtrImplWithEventTargetData> m_elementsInHeadWithPendingSheets; |
| WeakHashSet<const Element, WeakPtrImplWithEventTargetData> m_elementsInBodyWithPendingSheets; |
| |
| WeakListHashSet<Node, WeakPtrImplWithEventTargetData> m_styleSheetCandidateNodes; |
| |
| std::optional<UpdateType> m_pendingUpdate; |
| |
| bool m_hasDescendantWithPendingUpdate { false }; |
| bool m_usesStyleBasedEditability { false }; |
| bool m_usesHasPseudoClass { false }; |
| bool m_isUpdatingStyleResolver { false }; |
| |
| const UniqueRef<CustomPropertyRegistry> m_customPropertyRegistry; |
| const UniqueRef<CSSCounterStyleRegistry> m_counterStyleRegistry; |
| |
| mutable HashMap<AtomString, AttributeAffectsShadowTree> m_substitutionAttributes; |
| }; |
| |
| RefPtr<HTMLSlotElement> assignedSlotForScopeOrdinal(const Element&, ScopeOrdinal); |
| RefPtr<Element> hostForScopeOrdinal(const Element&, ScopeOrdinal); |
| |
| inline void Scope::flushPendingUpdate() |
| { |
| if (m_hasDescendantWithPendingUpdate) |
| flushPendingDescendantUpdates(); |
| if (m_pendingUpdate) |
| flushPendingSelfUpdate(); |
| } |
| |
| // Resolves a tree-scoped reference per https://drafts.csswg.org/css-scoping-1/#shadow-names. |
| // The provided function is called, for each relevant scope, with the scope and the reference's name |
| // paired with that scope's ordinal (as a ScopedName), until it returns a truthy value. |
| template<std::invocable<const Scope&, ScopedName> F> |
| auto resolveTreeScopedReference(const Element& element, const ScopedName& reference, const F&& function) |
| { |
| using ReturnType = std::invoke_result_t<F, Scope, ScopedName>; |
| |
| // "Whenever a tree-scoped reference is dereferenced to find the CSS construct it is referencing, |
| // first search only the tree-scoped names associated with the same root as the tree-scoped reference must be searched." |
| CheckedPtr firstScope = Scope::forOrdinal(element, reference.scopeOrdinal); |
| if (!firstScope) |
| return ReturnType { }; |
| |
| auto scopeOrdinal = reference.scopeOrdinal; |
| if (auto result = function(*firstScope, ScopedName { reference.name, scopeOrdinal })) |
| return result; |
| |
| // "If no relevant tree-scoped name is found, and the root is a shadow root, then repeat this search in the root’s host’s node tree." |
| for (CheckedPtr hostScope = firstScope->hostScope(); hostScope; hostScope = hostScope->hostScope()) { |
| --scopeOrdinal; |
| if (auto result = function(*hostScope, ScopedName { reference.name, scopeOrdinal })) |
| return result; |
| } |
| return ReturnType { }; |
| } |
| |
| } |
| } |