StyleAdjuster.cpp   [plain text]


/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
 * Copyright (C) 2005-2019 Apple Inc. All rights reserved.
 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
 * Copyright (C) 2012, 2013 Google Inc. All rights reserved.
 * Copyright (C) 2014 Igalia S.L.
 *
 * 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.
 */

#include "config.h"
#include "StyleAdjuster.h"

#include "AnimationBase.h"
#include "CSSFontSelector.h"
#include "Element.h"
#include "FrameView.h"
#include "HTMLInputElement.h"
#include "HTMLMarqueeElement.h"
#include "HTMLNames.h"
#include "HTMLSlotElement.h"
#include "HTMLTableElement.h"
#include "HTMLTextAreaElement.h"
#include "MathMLElement.h"
#include "Page.h"
#include "Quirks.h"
#include "RenderBox.h"
#include "RenderStyle.h"
#include "RenderTheme.h"
#include "RuntimeEnabledFeatures.h"
#include "SVGDocument.h"
#include "SVGElement.h"
#include "SVGNames.h"
#include "SVGURIReference.h"
#include "Settings.h"
#include "Text.h"

namespace WebCore {
namespace Style {

using namespace HTMLNames;

Adjuster::Adjuster(const Document& document, const RenderStyle& parentStyle, const RenderStyle* parentBoxStyle, const Element* element)
    : m_document(document)
    , m_parentStyle(parentStyle)
    , m_parentBoxStyle(parentBoxStyle ? *parentBoxStyle : m_parentStyle)
    , m_element(element)
{
}

static void addIntrinsicMargins(RenderStyle& style)
{
    // Intrinsic margin value.
    const int intrinsicMargin = clampToInteger(2 * style.effectiveZoom());

    // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
    // FIXME: Using "hasQuirk" to decide the margin wasn't set is kind of lame.
    if (style.width().isIntrinsicOrAuto()) {
        if (style.marginLeft().hasQuirk())
            style.setMarginLeft(Length(intrinsicMargin, Fixed));
        if (style.marginRight().hasQuirk())
            style.setMarginRight(Length(intrinsicMargin, Fixed));
    }

    if (style.height().isAuto()) {
        if (style.marginTop().hasQuirk())
            style.setMarginTop(Length(intrinsicMargin, Fixed));
        if (style.marginBottom().hasQuirk())
            style.setMarginBottom(Length(intrinsicMargin, Fixed));
    }
}

static DisplayType equivalentBlockDisplay(const RenderStyle& style, const Document& document)
{
    switch (auto display = style.display()) {
    case DisplayType::Block:
    case DisplayType::Table:
    case DisplayType::Box:
    case DisplayType::Flex:
    case DisplayType::WebKitFlex:
    case DisplayType::Grid:
    case DisplayType::FlowRoot:
        return display;

    case DisplayType::ListItem:
        // It is a WinIE bug that floated list items lose their bullets, so we'll emulate the quirk, but only in quirks mode.
        if (document.inQuirksMode() && style.isFloating())
            return DisplayType::Block;
        return display;
    case DisplayType::InlineTable:
        return DisplayType::Table;
    case DisplayType::InlineBox:
        return DisplayType::Box;
    case DisplayType::InlineFlex:
    case DisplayType::WebKitInlineFlex:
        return DisplayType::Flex;
    case DisplayType::InlineGrid:
        return DisplayType::Grid;

    case DisplayType::Inline:
    case DisplayType::InlineBlock:
    case DisplayType::TableRowGroup:
    case DisplayType::TableHeaderGroup:
    case DisplayType::TableFooterGroup:
    case DisplayType::TableRow:
    case DisplayType::TableColumnGroup:
    case DisplayType::TableColumn:
    case DisplayType::TableCell:
    case DisplayType::TableCaption:
        return DisplayType::Block;
    case DisplayType::Contents:
        ASSERT_NOT_REACHED();
        return DisplayType::Contents;
    case DisplayType::None:
        ASSERT_NOT_REACHED();
        return DisplayType::None;
    }
    ASSERT_NOT_REACHED();
    return DisplayType::Block;
}

static inline bool isAtShadowBoundary(const Element& element)
{
    auto* parentNode = element.parentNode();
    return parentNode && parentNode->isShadowRoot();
}

// CSS requires text-decoration to be reset at each DOM element for tables,
// inline blocks, inline tables, shadow DOM crossings, floating elements,
// and absolute or relatively positioned elements.
static bool doesNotInheritTextDecoration(const RenderStyle& style, const Element* element)
{
    return style.display() == DisplayType::Table || style.display() == DisplayType::InlineTable
        || style.display() == DisplayType::InlineBlock || style.display() == DisplayType::InlineBox || (element && isAtShadowBoundary(*element))
        || style.isFloating() || style.hasOutOfFlowPosition();
}

#if ENABLE(OVERFLOW_SCROLLING_TOUCH) || ENABLE(POINTER_EVENTS)
static bool isScrollableOverflow(Overflow overflow)
{
    return overflow == Overflow::Scroll || overflow == Overflow::Auto;
}
#endif

#if ENABLE(POINTER_EVENTS)
static OptionSet<TouchAction> computeEffectiveTouchActions(const RenderStyle& style, OptionSet<TouchAction> effectiveTouchActions)
{
    // https://w3c.github.io/pointerevents/#determining-supported-touch-behavior
    // "A touch behavior is supported if it conforms to the touch-action property of each element between
    // the hit tested element and its nearest ancestor with the default touch behavior (including both the
    // hit tested element and the element with the default touch behavior)."

    bool hasDefaultTouchBehavior = isScrollableOverflow(style.overflowX()) || isScrollableOverflow(style.overflowY());
    if (hasDefaultTouchBehavior)
        effectiveTouchActions = RenderStyle::initialTouchActions();

    auto touchActions = style.touchActions();
    if (touchActions == RenderStyle::initialTouchActions())
        return effectiveTouchActions;

    if (effectiveTouchActions.contains(TouchAction::None))
        return { TouchAction::None };

    if (effectiveTouchActions.containsAny({ TouchAction::Auto, TouchAction::Manipulation }))
        return touchActions;

    if (touchActions.containsAny({ TouchAction::Auto, TouchAction::Manipulation }))
        return effectiveTouchActions;

    auto sharedTouchActions = effectiveTouchActions & touchActions;
    if (sharedTouchActions.isEmpty())
        return { TouchAction::None };

    return sharedTouchActions;
}
#endif

void Adjuster::adjust(RenderStyle& style, const RenderStyle* userAgentAppearanceStyle) const
{
    // Cache our original display.
    style.setOriginalDisplay(style.display());

    if (style.display() == DisplayType::Contents)
        adjustDisplayContentsStyle(style);

    if (style.display() != DisplayType::None && style.display() != DisplayType::Contents) {
        if (m_element) {
            // If we have a <td> that specifies a float property, in quirks mode we just drop the float
            // property.
            // Sites also commonly use display:inline/block on <td>s and <table>s. In quirks mode we force
            // these tags to retain their display types.
            if (m_document.inQuirksMode()) {
                if (m_element->hasTagName(tdTag)) {
                    style.setDisplay(DisplayType::TableCell);
                    style.setFloating(Float::No);
                } else if (is<HTMLTableElement>(*m_element))
                    style.setDisplay(style.isDisplayInlineType() ? DisplayType::InlineTable : DisplayType::Table);
            }

            if (m_element->hasTagName(tdTag) || m_element->hasTagName(thTag)) {
                if (style.whiteSpace() == WhiteSpace::KHTMLNoWrap) {
                    // Figure out if we are really nowrapping or if we should just
                    // use normal instead. If the width of the cell is fixed, then
                    // we don't actually use WhiteSpace::NoWrap.
                    if (style.width().isFixed())
                        style.setWhiteSpace(WhiteSpace::Normal);
                    else
                        style.setWhiteSpace(WhiteSpace::NoWrap);
                }
            }

            // Tables never support the -webkit-* values for text-align and will reset back to the default.
            if (is<HTMLTableElement>(*m_element) && (style.textAlign() == TextAlignMode::WebKitLeft || style.textAlign() == TextAlignMode::WebKitCenter || style.textAlign() == TextAlignMode::WebKitRight))
                style.setTextAlign(TextAlignMode::Start);

            // Frames and framesets never honor position:relative or position:absolute. This is necessary to
            // fix a crash where a site tries to position these objects. They also never honor display.
            if (m_element->hasTagName(frameTag) || m_element->hasTagName(framesetTag)) {
                style.setPosition(PositionType::Static);
                style.setDisplay(DisplayType::Block);
            }

            // Ruby text does not support float or position. This might change with evolution of the specification.
            if (m_element->hasTagName(rtTag)) {
                style.setPosition(PositionType::Static);
                style.setFloating(Float::No);
            }

            // User agents are expected to have a rule in their user agent stylesheet that matches th elements that have a parent
            // node whose computed value for the 'text-align' property is its initial value, whose declaration block consists of
            // just a single declaration that sets the 'text-align' property to the value 'center'.
            // https://html.spec.whatwg.org/multipage/rendering.html#rendering
            if (m_element->hasTagName(thTag) && !style.hasExplicitlySetTextAlign() && m_parentStyle.textAlign() == RenderStyle::initialTextAlign())
                style.setTextAlign(TextAlignMode::Center);

            if (m_element->hasTagName(legendTag))
                style.setDisplay(DisplayType::Block);
        }

        // Absolute/fixed positioned elements, floating elements and the document element need block-like outside display.
        if (style.hasOutOfFlowPosition() || style.isFloating() || (m_element && m_document.documentElement() == m_element))
            style.setDisplay(equivalentBlockDisplay(style, m_document));

        // FIXME: Don't support this mutation for pseudo styles like first-letter or first-line, since it's not completely
        // clear how that should work.
        if (style.display() == DisplayType::Inline && style.styleType() == PseudoId::None && style.writingMode() != m_parentStyle.writingMode())
            style.setDisplay(DisplayType::InlineBlock);

        // After performing the display mutation, check table rows. We do not honor position:relative or position:sticky on
        // table rows or cells. This has been established for position:relative in CSS2.1 (and caused a crash in containingBlock()
        // on some sites).
        if ((style.display() == DisplayType::TableHeaderGroup || style.display() == DisplayType::TableRowGroup
            || style.display() == DisplayType::TableFooterGroup || style.display() == DisplayType::TableRow)
            && style.position() == PositionType::Relative)
            style.setPosition(PositionType::Static);

        // writing-mode does not apply to table row groups, table column groups, table rows, and table columns.
        // FIXME: Table cells should be allowed to be perpendicular or flipped with respect to the table, though.
        if (style.display() == DisplayType::TableColumn || style.display() == DisplayType::TableColumnGroup || style.display() == DisplayType::TableFooterGroup
            || style.display() == DisplayType::TableHeaderGroup || style.display() == DisplayType::TableRow || style.display() == DisplayType::TableRowGroup
            || style.display() == DisplayType::TableCell)
            style.setWritingMode(m_parentStyle.writingMode());

        // FIXME: Since we don't support block-flow on flexible boxes yet, disallow setting
        // of block-flow to anything other than TopToBottomWritingMode.
        // https://bugs.webkit.org/show_bug.cgi?id=46418 - Flexible box support.
        if (style.writingMode() != TopToBottomWritingMode && (style.display() == DisplayType::Box || style.display() == DisplayType::InlineBox))
            style.setWritingMode(TopToBottomWritingMode);

        // https://www.w3.org/TR/css-display/#transformations
        // "A parent with a grid or flex display value blockifies the box’s display type."
        if (m_parentBoxStyle.isDisplayFlexibleOrGridBox()) {
            style.setFloating(Float::No);
            style.setDisplay(equivalentBlockDisplay(style, m_document));
        }
    }

    // Make sure our z-index value is only applied if the object is positioned.
    if (style.hasAutoSpecifiedZIndex() || (style.position() == PositionType::Static && !m_parentBoxStyle.isDisplayFlexibleOrGridBox()))
        style.setHasAutoUsedZIndex();
    else
        style.setUsedZIndex(style.specifiedZIndex());

    // Auto z-index becomes 0 for the root element and transparent objects. This prevents
    // cases where objects that should be blended as a single unit end up with a non-transparent
    // object wedged in between them. Auto z-index also becomes 0 for objects that specify transforms/masks/reflections.
    if (style.hasAutoUsedZIndex()) {
        if ((m_element && m_document.documentElement() == m_element)
            || style.opacity() < 1.0f
            || style.hasTransformRelatedProperty()
            || style.hasMask()
            || style.clipPath()
            || style.boxReflect()
            || style.hasFilter()
#if ENABLE(FILTERS_LEVEL_2)
            || style.hasBackdropFilter()
#endif
            || style.hasBlendMode()
            || style.hasIsolation()
            || style.position() == PositionType::Sticky
            || style.position() == PositionType::Fixed
            || style.willChangeCreatesStackingContext())
            style.setUsedZIndex(0);
    }

    if (m_element) {
        // Textarea considers overflow visible as auto.
        if (is<HTMLTextAreaElement>(*m_element)) {
            style.setOverflowX(style.overflowX() == Overflow::Visible ? Overflow::Auto : style.overflowX());
            style.setOverflowY(style.overflowY() == Overflow::Visible ? Overflow::Auto : style.overflowY());
        }

        // Disallow -webkit-user-modify on :pseudo and ::pseudo elements.
        if (!m_element->shadowPseudoId().isNull())
            style.setUserModify(UserModify::ReadOnly);

        if (is<HTMLMarqueeElement>(*m_element)) {
            // For now, <marquee> requires an overflow clip to work properly.
            style.setOverflowX(Overflow::Hidden);
            style.setOverflowY(Overflow::Hidden);

            bool isVertical = style.marqueeDirection() == MarqueeDirection::Up || style.marqueeDirection() == MarqueeDirection::Down;
            // Make horizontal marquees not wrap.
            if (!isVertical) {
                style.setWhiteSpace(WhiteSpace::NoWrap);
                style.setTextAlign(TextAlignMode::Start);
            }
            // Apparently this is the expected legacy behavior.
            if (isVertical && style.height().isAuto())
                style.setHeight(Length(200, Fixed));
        }
    }

    if (doesNotInheritTextDecoration(style, m_element))
        style.setTextDecorationsInEffect(style.textDecoration());
    else
        style.addToTextDecorationsInEffect(style.textDecoration());

    // If either overflow value is not visible, change to auto.
    if (style.overflowX() == Overflow::Visible && style.overflowY() != Overflow::Visible) {
        // FIXME: Once we implement pagination controls, overflow-x should default to hidden
        // if overflow-y is set to -webkit-paged-x or -webkit-page-y. For now, we'll let it
        // default to auto so we can at least scroll through the pages.
        style.setOverflowX(Overflow::Auto);
    } else if (style.overflowY() == Overflow::Visible && style.overflowX() != Overflow::Visible)
        style.setOverflowY(Overflow::Auto);

    // Call setStylesForPaginationMode() if a pagination mode is set for any non-root elements. If these
    // styles are specified on a root element, then they will be incorporated in
    // Style::createForm_document.
    if ((style.overflowY() == Overflow::PagedX || style.overflowY() == Overflow::PagedY) && !(m_element && (m_element->hasTagName(htmlTag) || m_element->hasTagName(bodyTag))))
        style.setColumnStylesFromPaginationMode(WebCore::paginationModeForRenderStyle(style));

    // Table rows, sections and the table itself will support overflow:hidden and will ignore scroll/auto.
    // FIXME: Eventually table sections will support auto and scroll.
    if (style.display() == DisplayType::Table || style.display() == DisplayType::InlineTable
        || style.display() == DisplayType::TableRowGroup || style.display() == DisplayType::TableRow) {
        if (style.overflowX() != Overflow::Visible && style.overflowX() != Overflow::Hidden)
            style.setOverflowX(Overflow::Visible);
        if (style.overflowY() != Overflow::Visible && style.overflowY() != Overflow::Hidden)
            style.setOverflowY(Overflow::Visible);
    }

    // Menulists should have visible overflow
    if (style.appearance() == MenulistPart) {
        style.setOverflowX(Overflow::Visible);
        style.setOverflowY(Overflow::Visible);
    }

#if ENABLE(OVERFLOW_SCROLLING_TOUCH)
    // Touch overflow scrolling creates a stacking context.
    if (style.hasAutoUsedZIndex() && style.useTouchOverflowScrolling() && (isScrollableOverflow(style.overflowX()) || isScrollableOverflow(style.overflowY())))
        style.setUsedZIndex(0);
#endif

    // Cull out any useless layers and also repeat patterns into additional layers.
    style.adjustBackgroundLayers();
    style.adjustMaskLayers();

    // Do the same for animations and transitions.
    style.adjustAnimations();
    style.adjustTransitions();

    // Important: Intrinsic margins get added to controls before the theme has adjusted the style, since the theme will
    // alter fonts and heights/widths.
    if (is<HTMLFormControlElement>(m_element) && style.computedFontPixelSize() >= 11) {
        // Don't apply intrinsic margins to image buttons. The designer knows how big the images are,
        // so we have to treat all image buttons as though they were explicitly sized.
        if (!is<HTMLInputElement>(*m_element) || !downcast<HTMLInputElement>(*m_element).isImageButton())
            addIntrinsicMargins(style);
    }

    // Let the theme also have a crack at adjusting the style.
    if (style.hasAppearance())
        RenderTheme::singleton().adjustStyle(style, m_element, userAgentAppearanceStyle);

    // If we have first-letter pseudo style, do not share this style.
    if (style.hasPseudoStyle(PseudoId::FirstLetter))
        style.setUnique();

    // FIXME: when dropping the -webkit prefix on transform-style, we should also have opacity < 1 cause flattening.
    if (style.preserves3D() && (style.overflowX() != Overflow::Visible
        || style.overflowY() != Overflow::Visible
        || style.hasClip()
        || style.clipPath()
        || style.hasFilter()
#if ENABLE(FILTERS_LEVEL_2)
        || style.hasBackdropFilter()
#endif
        || style.hasBlendMode()))
        style.setTransformStyle3D(TransformStyle3D::Flat);

    if (is<SVGElement>(m_element))
        adjustSVGElementStyle(style, downcast<SVGElement>(*m_element));

    // If the inherited value of justify-items includes the 'legacy' keyword (plus 'left', 'right' or
    // 'center'), 'legacy' computes to the the inherited value. Otherwise, 'auto' computes to 'normal'.
    if (m_parentBoxStyle.justifyItems().positionType() == ItemPositionType::Legacy && style.justifyItems().position() == ItemPosition::Legacy)
        style.setJustifyItems(m_parentBoxStyle.justifyItems());

#if ENABLE(POINTER_EVENTS)
    style.setEffectiveTouchActions(computeEffectiveTouchActions(style, m_parentStyle.effectiveTouchActions()));
#endif

#if ENABLE(TEXT_AUTOSIZING)
    if (m_element)
        adjustForTextAutosizing(style, *m_element);
#endif

    adjustForSiteSpecificQuirks(style);
}

static bool hasEffectiveDisplayNoneForDisplayContents(const Element& element)
{
    // https://drafts.csswg.org/css-display-3/#unbox-html
    static NeverDestroyed<HashSet<AtomString>> tagNames = [] {
        static const HTMLQualifiedName* const tagList[] = {
            &brTag.get(),
            &wbrTag.get(),
            &meterTag.get(),
            &appletTag.get(),
            &progressTag.get(),
            &canvasTag.get(),
            &embedTag.get(),
            &objectTag.get(),
            &audioTag.get(),
            &iframeTag.get(),
            &imgTag.get(),
            &videoTag.get(),
            &frameTag.get(),
            &framesetTag.get(),
            &inputTag.get(),
            &textareaTag.get(),
            &selectTag.get(),
        };
        HashSet<AtomString> set;
        for (auto& name : tagList)
            set.add(name->localName());
        return set;
    }();

    // https://drafts.csswg.org/css-display-3/#unbox-svg
    // FIXME: <g>, <use> and <tspan> have special (?) behavior for display:contents in the current draft spec.
    if (is<SVGElement>(element))
        return true;
#if ENABLE(MATHML)
    // Not sure MathML code can handle it.
    if (is<MathMLElement>(element))
        return true;
#endif // ENABLE(MATHML)
    if (!is<HTMLElement>(element))
        return false;
    return tagNames.get().contains(element.localName());
}

void Adjuster::adjustDisplayContentsStyle(RenderStyle& style) const
{
    if (!m_element) {
        if (style.styleType() != PseudoId::Before && style.styleType() != PseudoId::After)
            style.setDisplay(DisplayType::None);
        return;
    }

    if (m_document.documentElement() == m_element) {
        style.setDisplay(DisplayType::Block);
        return;
    }

    if (hasEffectiveDisplayNoneForDisplayContents(*m_element))
        style.setDisplay(DisplayType::None);
}

void Adjuster::adjustSVGElementStyle(RenderStyle& style, const SVGElement& svgElement)
{
    // Only the root <svg> element in an SVG document fragment tree honors css position
    auto isPositioningAllowed = svgElement.hasTagName(SVGNames::svgTag) && svgElement.parentNode() && !svgElement.parentNode()->isSVGElement() && !svgElement.correspondingElement();
    if (!isPositioningAllowed)
        style.setPosition(RenderStyle::initialPosition());

    // RenderSVGRoot handles zooming for the whole SVG subtree, so foreignObject content should
    // not be scaled again.
    if (svgElement.hasTagName(SVGNames::foreignObjectTag))
        style.setEffectiveZoom(RenderStyle::initialZoom());

    // SVG text layout code expects us to be a block-level style element.
    if ((svgElement.hasTagName(SVGNames::foreignObjectTag) || svgElement.hasTagName(SVGNames::textTag)) && style.isDisplayInlineType())
        style.setDisplay(DisplayType::Block);
}

void Adjuster::adjustAnimatedStyle(RenderStyle& style, const RenderStyle* parentBoxStyle, OptionSet<AnimationImpact> impact)
{
    // Set an explicit used z-index in two cases:
    // 1. When the element respects z-index, and the style has an explicit z-index set (for example, the animation
    //    itself may animate z-index).
    // 2. When we want the stacking context side-effets of explicit z-index, via forceStackingContext.
    // It's important to not clobber an existing used z-index, since an earlier animation may have set it, but we
    // may still need to update the used z-index value from the specified value.
    bool elementRespectsZIndex = style.position() != PositionType::Static || (parentBoxStyle && parentBoxStyle->isDisplayFlexibleOrGridBox());

    if (elementRespectsZIndex && !style.hasAutoSpecifiedZIndex())
        style.setUsedZIndex(style.specifiedZIndex());
    else if (impact.contains(AnimationImpact::ForcesStackingContext))
        style.setUsedZIndex(0);
}

void Adjuster::adjustForSiteSpecificQuirks(RenderStyle& style) const
{
    if (m_document.quirks().needsGMailOverflowScrollQuirk() && m_element) {
        // This turns sidebar scrollable without mouse move event.
        static NeverDestroyed<AtomString> roleValue("navigation", AtomString::ConstructFromLiteral);
        if (style.overflowY() == Overflow::Hidden && m_element->attributeWithoutSynchronization(roleAttr) == roleValue)
            style.setOverflowY(Overflow::Auto);
    }
    if (m_document.quirks().needsYouTubeOverflowScrollQuirk() && m_element) {
        // This turns sidebar scrollable without hover.
        static NeverDestroyed<AtomString> idValue("guide-inner-content", AtomString::ConstructFromLiteral);
        if (style.overflowY() == Overflow::Hidden && m_element->idForStyleResolution() == idValue)
            style.setOverflowY(Overflow::Auto);
    }
}

#if ENABLE(TEXT_AUTOSIZING)
static bool hasTextChild(const Element& element)
{
    for (auto* child = element.firstChild(); child; child = child->nextSibling()) {
        if (is<Text>(child))
            return true;
    }
    return false;
}

bool Adjuster::adjustForTextAutosizing(RenderStyle& style, const Element& element)
{
    auto& document = element.document();
    if (!document.settings().textAutosizingEnabled() || !document.settings().textAutosizingUsesIdempotentMode())
        return false;

    AutosizeStatus::updateStatus(style);
    if (style.textSizeAdjust().isNone())
        return false;

    float initialScale = document.page() ? document.page()->initialScale() : 1;
    auto adjustLineHeightIfNeeded = [&](auto computedFontSize) {
        auto lineHeight = style.specifiedLineHeight();
        constexpr static unsigned eligibleFontSize = 12;
        if (computedFontSize * initialScale >= eligibleFontSize)
            return;

        constexpr static float boostFactor = 1.25;
        auto minimumLineHeight = boostFactor * computedFontSize;
        if (!lineHeight.isFixed() || lineHeight.value() >= minimumLineHeight)
            return;

        if (AutosizeStatus::probablyContainsASmallFixedNumberOfLines(style))
            return;

        style.setLineHeight({ minimumLineHeight, Fixed });
    };

    auto fontDescription = style.fontDescription();
    auto initialComputedFontSize = fontDescription.computedSize();
    auto specifiedFontSize = fontDescription.specifiedSize();
    bool isCandidate = style.isIdempotentTextAutosizingCandidate();
    if (!isCandidate && WTF::areEssentiallyEqual(initialComputedFontSize, specifiedFontSize))
        return false;

    auto adjustedFontSize = AutosizeStatus::idempotentTextSize(fontDescription.specifiedSize(), initialScale);
    if (isCandidate && WTF::areEssentiallyEqual(initialComputedFontSize, adjustedFontSize))
        return false;

    if (!hasTextChild(element))
        return false;

    fontDescription.setComputedSize(isCandidate ? adjustedFontSize : specifiedFontSize);
    style.setFontDescription(WTFMove(fontDescription));
    style.fontCascade().update(&document.fontSelector());

    // FIXME: We should restore computed line height to its original value in the case where the element is not
    // an idempotent text autosizing candidate; otherwise, if an element that is a text autosizing candidate contains
    // children which are not autosized, the non-autosized content will end up with a boosted line height.
    if (isCandidate)
        adjustLineHeightIfNeeded(adjustedFontSize);

    return true;
}
#endif

}
}