blob: 6592e76243c03e9dba21d16299031e16caa73bec [file] [log] [blame]
/*
* Copyright (C) 2013-2025 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "AnchorPositionEvaluator.h"
#include "PropertyCascade.h"
#include "RenderStyle.h"
#include "ResolvedStyle.h"
#include "SelectorChecker.h"
#include "SelectorMatchingState.h"
#include "StyleChange.h"
#include "StylePositionTryFallback.h"
#include "StyleUpdate.h"
#include "Styleable.h"
#include "TreeResolutionState.h"
#include <wtf/Function.h>
#include <wtf/Ref.h>
namespace WebCore {
class Document;
class Element;
class Node;
class RenderStyle;
class ShadowRoot;
namespace Style {
class Resolver;
struct BuilderPositionTryFallback;
struct MatchResult;
struct PositionTryFallback;
struct PseudoElementIdentifier;
struct ResolutionContext;
struct ResolvedStyle;
enum class IsInDisplayNoneTree : bool { No, Yes };
DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(TreeResolverScope);
class TreeResolver {
public:
TreeResolver(Document&, std::unique_ptr<Update> = { });
~TreeResolver();
std::unique_ptr<Update> resolve();
bool needsInterleavedLayout() const { return m_needsInterleavedLayout; }
private:
enum class ResolutionType : uint8_t { RebuildUsingExisting, AnimationOnly, FastPathInherit, FullWithMatchResultCache, Full };
ResolvedStyle styleForStyleable(const Styleable&, ResolutionType, const ResolutionContext&, const RenderStyle* existingStyle);
void resolveComposedTree();
const RenderStyle* existingStyle(const Element&);
enum class LayoutInterleavingAction : uint8_t { None, SkipDescendants };
enum class DescendantsToResolve : uint8_t { None, RebuildAllUsingExisting, ChildrenWithExplicitInherit, Children, All };
LayoutInterleavingAction updateStateForQueryContainer(Element&, const RenderStyle*, DescendantsToResolve&);
// For elements requiring style/layout interleaving (anchor-positioned and query
// containers), descendant resolution is deferred on the first style resolution
// pass and resumed in subsequent passes.
// When deferral is needed, deferDescendantResolution saves the internal state
// relevant to which descendants should be resolved.
void deferDescendantResolution(Element&, OptionSet<Change>, DescendantsToResolve);
// When descendant resolution can be resumed, resumeDescendantResolutionIfNeeded
// restores the previously saved state (if there is)
void resumeDescendantResolutionIfNeeded(Element&, OptionSet<Change>&, DescendantsToResolve&);
std::pair<ElementUpdate, DescendantsToResolve> resolveElement(Element&, const RenderStyle* existingStyle, ResolutionType);
ElementUpdate createAnimatedElementUpdate(ResolvedStyle&&, const Styleable&, OptionSet<Change>, const ResolutionContext&, IsInDisplayNoneTree = IsInDisplayNoneTree::No);
std::unique_ptr<RenderStyle> resolveStartingStyle(const ResolvedStyle&, const Styleable&, const ResolutionContext&);
std::unique_ptr<RenderStyle> resolveAfterChangeStyleForNonAnimated(const ResolvedStyle&, const Styleable&, const ResolutionContext&);
std::unique_ptr<RenderStyle> resolveAgainInDifferentContext(const ResolvedStyle&, const Styleable&, const RenderStyle& parentStyle, OptionSet<PropertyCascade::PropertyType>, std::optional<BuilderPositionTryFallback>&&, const ResolutionContext&);
const RenderStyle& parentAfterChangeStyle(const Styleable&, const ResolutionContext&) const;
HashSet<AnimatableCSSProperty> applyCascadeAfterAnimation(RenderStyle&, const HashSet<AnimatableCSSProperty>&, bool isTransition, const MatchResult&, const Element&, const ResolutionContext&);
std::optional<ElementUpdate> resolvePseudoElement(Element&, const PseudoElementIdentifier&, const ElementUpdate&, IsInDisplayNoneTree, const RenderStyle*);
std::optional<ElementUpdate> resolveAncestorPseudoElement(Element&, const PseudoElementIdentifier&, const ElementUpdate&);
std::optional<ResolvedStyle> resolveAncestorFirstLinePseudoElement(Element&, const ElementUpdate&);
std::optional<ResolvedStyle> resolveAncestorFirstLetterPseudoElement(Element&, const ElementUpdate&, ResolutionContext&);
void resetStyleForNonRenderedDescendants(Element&);
struct Scope : RefCounted<Scope> {
WTF_DEPRECATED_MAKE_STRUCT_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(Scope, TreeResolverScope);
Ref<Resolver> resolver;
SelectorMatchingState selectorMatchingState;
RefPtr<ShadowRoot> shadowRoot;
RefPtr<Scope> enclosingScope;
Scope(Document&, Update&);
Scope(ShadowRoot&, Scope& enclosingScope);
~Scope();
};
struct Parent {
Element* element;
const RenderStyle& style;
OptionSet<Change> changes;
DescendantsToResolve descendantsToResolve { DescendantsToResolve::None };
bool didPushScope { false };
bool resolvedFirstLineAndLetterChild { false };
bool needsUpdateQueryContainerDependentStyle { false };
IsInDisplayNoneTree isInDisplayNoneTree { IsInDisplayNoneTree::No };
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
// Used to determine whether the AXObjectCache has already propagated down font and color updates for the current subtree.
bool didAXUpdateFontSubtree { false };
bool didAXUpdateTextColorSubtree { false };
#endif
Parent(Document&);
Parent(Element&, const RenderStyle&, OptionSet<Change>, DescendantsToResolve, IsInDisplayNoneTree);
};
Scope& scope() { return m_scopeStack.last(); }
const Scope& scope() const { return m_scopeStack.last(); }
Parent& parent() { return m_parentStack.last(); }
const Parent& parent() const { return m_parentStack.last(); }
void pushScope(ShadowRoot&);
void pushEnclosingScope();
void popScope();
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void pushParent(Element&, const RenderStyle&, OptionSet<Change>, DescendantsToResolve, IsInDisplayNoneTree, bool, bool);
#else
void pushParent(Element&, const RenderStyle&, OptionSet<Change>, DescendantsToResolve, IsInDisplayNoneTree);
#endif
void popParent();
void popParentsToDepth(unsigned depth);
DescendantsToResolve computeDescendantsToResolve(const ElementUpdate&, const RenderStyle* existingStyle, Validity) const;
static std::optional<ResolutionType> determineResolutionType(const Element&, const RenderStyle*, DescendantsToResolve, OptionSet<Change> parentChange);
static void resetDescendantStyleRelations(Element&, DescendantsToResolve);
ResolutionContext makeResolutionContext();
ResolutionContext makeResolutionContextForPseudoElement(const ElementUpdate&, const PseudoElementIdentifier&);
std::optional<ResolutionContext> makeResolutionContextForInheritedFirstLine(const ElementUpdate&, const RenderStyle& inheritStyle);
const Parent* boxGeneratingParent() const;
const RenderStyle* parentBoxStyle() const;
const RenderStyle* parentBoxStyleForPseudoElement(const ElementUpdate&) const;
const RenderStyle* documentElementStyle() const;
LayoutInterleavingAction updateAnchorPositioningState(Element&, const RenderStyle*);
void generatePositionOptionsIfNeeded(const ResolvedStyle&, const Styleable&, const ResolutionContext&);
std::unique_ptr<RenderStyle> generatePositionOption(const PositionTryFallback&, const ResolvedStyle&, const Styleable&, const ResolutionContext&);
struct PositionOptions;
void sortPositionOptionsIfNeeded(PositionOptions&, const Styleable&);
std::optional<ResolvedStyle> tryChoosePositionOption(const Styleable&, const ResolutionContext&);
void updateForPositionVisibility(RenderStyle&, const Styleable&);
// This returns the style that was in effect (applied to the render tree) before we started the style resolution.
// Layout interleaving may cause different styles to be applied during the style resolution.
const RenderStyle* beforeResolutionStyle(const Element&, std::optional<PseudoElementIdentifier>);
void saveBeforeResolutionStyleForInterleaving(const Element&, const RenderStyle*);
bool hasUnresolvedAnchorPosition(const Styleable&) const;
bool hasResolvedAnchorPosition(const Styleable&) const;
// Returns true if (1) the styleable specifies position fallbacks and
// (2) we're in the middle of trying position options.
bool isTryingPositionOption(const Styleable&) const;
void collectChangedAnchorNames(const RenderStyle&, const RenderStyle* currentStyle);
const CheckedRef<Document> m_document;
std::unique_ptr<RenderStyle> m_computedDocumentElementStyle;
Vector<Ref<Scope>, 4> m_scopeStack;
Vector<Parent, 32> m_parentStack;
bool m_didSeePendingStylesheet { false };
// States relevant to deferring and resuming descendant resolution.
// Also see deferDescendantResolution and resumeDescendantResolutionIfNeeded.
struct DeferredDescendantResolutionState {
OptionSet<Change> changes;
DescendantsToResolve descendantsToResolve { DescendantsToResolve::None };
};
HashMap<Ref<Element>, DeferredDescendantResolutionState> m_deferredDescendantResolutionStates;
bool m_needsInterleavedLayout { false };
bool m_didFirstInterleavedLayout { false };
struct QueryContainerState {
bool invalidated { false };
};
HashMap<Ref<Element>, QueryContainerState> m_queryContainerStates;
// This state gets passes to the style builder and holds state for a single tree resolution, including over any interleaving.
TreeResolutionState m_treeResolutionState;
HashMap<Ref<const Element>, std::unique_ptr<RenderStyle>> m_savedBeforeResolutionStylesForInterleaving;
struct PositionOption {
// New style after applying the option.
std::unique_ptr<RenderStyle> style;
// The position option used to generate the style. If option is nullopt, no position option is used.
std::optional<PositionTryFallback> option;
// The size of the content box of the element when the style is generated.
// Non-overlay scrollbars appearing or disappearing may affect the content box size that anchor functions are resolved against.
std::optional<LayoutSize> scrollContainerSizeOnGeneration;
};
struct PositionOptions {
// Array of option styles. By convention, the original style is at index 0.
Vector<PositionOption> optionStyles { };
ResolvedStyle originalResolvedStyle;
size_t index { 0 };
bool sorted { false };
bool chosen { false };
bool isFirstTry { true };
const RenderStyle& originalStyle() const;
std::unique_ptr<RenderStyle> currentOption() const;
};
HashMap<AnchorPositionedKey, PositionOptions> m_positionOptions;
HashSet<AtomString> m_changedAnchorNames;
bool m_allAnchorNamesInvalid { false };
std::unique_ptr<Update> m_update;
};
// Integrate with the HTML5 event loop instead, see EventLoop.cpp and consumers.
void deprecatedQueuePostResolutionCallback(Function<void()>&&);
bool postResolutionCallbacksAreSuspended();
inline bool supportsFirstLineAndLetterPseudoElement(const RenderStyle& style)
{
auto display = style.display();
return display == DisplayType::Block
|| display == DisplayType::ListItem
|| display == DisplayType::InlineBlock
|| display == DisplayType::TableCell
|| display == DisplayType::TableCaption
|| display == DisplayType::FlowRoot;
}
class PostResolutionCallbackDisabler {
public:
enum class DrainCallbacks : bool { No, Yes };
explicit PostResolutionCallbackDisabler(Document&, DrainCallbacks = DrainCallbacks::Yes);
~PostResolutionCallbackDisabler();
private:
DrainCallbacks m_drainCallbacks;
};
}
}