| /* |
| * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * Copyright (C) 2004-2017 Apple Inc. All rights reserved. |
| * Copyright (C) 2011 Adobe Systems Incorporated. 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. |
| * |
| */ |
| |
| #include "config.h" |
| #include "RenderStyle.h" |
| |
| #include "CSSComputedStyleDeclaration.h" |
| #include "CSSCustomPropertyValue.h" |
| #include "CSSParser.h" |
| #include "CSSPropertyNames.h" |
| #include "CSSPropertyParser.h" |
| #include "ContentData.h" |
| #include "CursorList.h" |
| #include "FloatRoundedRect.h" |
| #include "FontCascade.h" |
| #include "FontSelector.h" |
| #include "InlineTextBoxStyle.h" |
| #include "Pagination.h" |
| #include "QuotesData.h" |
| #include "RenderObject.h" |
| #include "RenderTheme.h" |
| #include "ScaleTransformOperation.h" |
| #include "ShadowData.h" |
| #include "StyleBuilderConverter.h" |
| #include "StyleImage.h" |
| #include "StyleInheritedData.h" |
| #include "StyleResolver.h" |
| #include "StyleScrollSnapPoints.h" |
| #include "StyleSelfAlignmentData.h" |
| #include "StyleTreeResolver.h" |
| #include "WillChangeData.h" |
| #include <wtf/MathExtras.h> |
| #include <wtf/PointerComparison.h> |
| #include <wtf/StdLibExtras.h> |
| #include <algorithm> |
| |
| #if ENABLE(TEXT_AUTOSIZING) |
| #include <wtf/text/StringHash.h> |
| #endif |
| |
| namespace WebCore { |
| |
| struct SameSizeAsBorderValue { |
| Color m_color; |
| float m_width; |
| int m_restBits; |
| }; |
| |
| COMPILE_ASSERT(sizeof(BorderValue) == sizeof(SameSizeAsBorderValue), BorderValue_should_not_grow); |
| |
| struct SameSizeAsRenderStyle { |
| void* dataRefs[7]; |
| void* ownPtrs[1]; |
| void* dataRefSvgStyle; |
| struct InheritedFlags { |
| unsigned m_bitfields[2]; |
| } m_inheritedFlags; |
| |
| struct NonInheritedFlags { |
| unsigned m_bitfields[2]; |
| } m_nonInheritedFlags; |
| #if ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS) |
| bool deletionCheck; |
| #endif |
| }; |
| |
| static_assert(sizeof(RenderStyle) == sizeof(SameSizeAsRenderStyle), "RenderStyle should stay small"); |
| |
| DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(RenderStyle); |
| |
| RenderStyle& RenderStyle::defaultStyle() |
| { |
| static NeverDestroyed<RenderStyle> style { CreateDefaultStyle }; |
| return style; |
| } |
| |
| RenderStyle RenderStyle::create() |
| { |
| return clone(defaultStyle()); |
| } |
| |
| std::unique_ptr<RenderStyle> RenderStyle::createPtr() |
| { |
| return clonePtr(defaultStyle()); |
| } |
| |
| RenderStyle RenderStyle::clone(const RenderStyle& style) |
| { |
| return RenderStyle(style, Clone); |
| } |
| |
| std::unique_ptr<RenderStyle> RenderStyle::clonePtr(const RenderStyle& style) |
| { |
| return makeUnique<RenderStyle>(style, Clone); |
| } |
| |
| RenderStyle RenderStyle::createAnonymousStyleWithDisplay(const RenderStyle& parentStyle, DisplayType display) |
| { |
| auto newStyle = create(); |
| newStyle.inheritFrom(parentStyle); |
| newStyle.inheritUnicodeBidiFrom(&parentStyle); |
| newStyle.setDisplay(display); |
| return newStyle; |
| } |
| |
| RenderStyle RenderStyle::createStyleInheritingFromPseudoStyle(const RenderStyle& pseudoStyle) |
| { |
| ASSERT(pseudoStyle.styleType() == PseudoId::Before || pseudoStyle.styleType() == PseudoId::After); |
| |
| auto style = create(); |
| style.inheritFrom(pseudoStyle); |
| return style; |
| } |
| |
| RenderStyle::RenderStyle(RenderStyle&&) = default; |
| RenderStyle& RenderStyle::operator=(RenderStyle&&) = default; |
| |
| RenderStyle::RenderStyle(CreateDefaultStyleTag) |
| : m_boxData(StyleBoxData::create()) |
| , m_visualData(StyleVisualData::create()) |
| , m_backgroundData(StyleBackgroundData::create()) |
| , m_surroundData(StyleSurroundData::create()) |
| , m_rareNonInheritedData(StyleRareNonInheritedData::create()) |
| , m_rareInheritedData(StyleRareInheritedData::create()) |
| , m_inheritedData(StyleInheritedData::create()) |
| , m_svgStyle(SVGRenderStyle::create()) |
| { |
| m_inheritedFlags.emptyCells = static_cast<unsigned>(initialEmptyCells()); |
| m_inheritedFlags.captionSide = static_cast<unsigned>(initialCaptionSide()); |
| m_inheritedFlags.listStyleType = static_cast<unsigned>(initialListStyleType()); |
| m_inheritedFlags.listStylePosition = static_cast<unsigned>(initialListStylePosition()); |
| m_inheritedFlags.visibility = static_cast<unsigned>(initialVisibility()); |
| m_inheritedFlags.textAlign = static_cast<unsigned>(initialTextAlign()); |
| m_inheritedFlags.textTransform = static_cast<unsigned>(initialTextTransform()); |
| m_inheritedFlags.textDecorations = initialTextDecoration().toRaw(); |
| m_inheritedFlags.cursor = static_cast<unsigned>(initialCursor()); |
| #if ENABLE(CURSOR_VISIBILITY) |
| m_inheritedFlags.cursorVisibility = static_cast<unsigned>(initialCursorVisibility()); |
| #endif |
| m_inheritedFlags.direction = static_cast<unsigned>(initialDirection()); |
| m_inheritedFlags.whiteSpace = static_cast<unsigned>(initialWhiteSpace()); |
| m_inheritedFlags.borderCollapse = static_cast<unsigned>(initialBorderCollapse()); |
| m_inheritedFlags.rtlOrdering = static_cast<unsigned>(initialRTLOrdering()); |
| m_inheritedFlags.boxDirection = static_cast<unsigned>(initialBoxDirection()); |
| m_inheritedFlags.printColorAdjust = static_cast<unsigned>(initialPrintColorAdjust()); |
| m_inheritedFlags.pointerEvents = static_cast<unsigned>(initialPointerEvents()); |
| m_inheritedFlags.insideLink = static_cast<unsigned>(InsideLink::NotInside); |
| m_inheritedFlags.insideDefaultButton = false; |
| m_inheritedFlags.writingMode = initialWritingMode(); |
| #if ENABLE(TEXT_AUTOSIZING) |
| m_inheritedFlags.autosizeStatus = 0; |
| #endif |
| |
| m_nonInheritedFlags.effectiveDisplay = static_cast<unsigned>(initialDisplay()); |
| m_nonInheritedFlags.originalDisplay = static_cast<unsigned>(initialDisplay()); |
| m_nonInheritedFlags.overflowX = static_cast<unsigned>(initialOverflowX()); |
| m_nonInheritedFlags.overflowY = static_cast<unsigned>(initialOverflowY()); |
| m_nonInheritedFlags.verticalAlign = static_cast<unsigned>(initialVerticalAlign()); |
| m_nonInheritedFlags.clear = static_cast<unsigned>(initialClear()); |
| m_nonInheritedFlags.position = static_cast<unsigned>(initialPosition()); |
| m_nonInheritedFlags.unicodeBidi = initialUnicodeBidi(); |
| m_nonInheritedFlags.floating = static_cast<unsigned>(initialFloating()); |
| m_nonInheritedFlags.tableLayout = static_cast<unsigned>(initialTableLayout()); |
| m_nonInheritedFlags.hasExplicitlySetDirection = false; |
| m_nonInheritedFlags.hasExplicitlySetWritingMode = false; |
| m_nonInheritedFlags.hasExplicitlySetTextAlign = false; |
| m_nonInheritedFlags.hasViewportUnits = false; |
| m_nonInheritedFlags.hasExplicitlyInheritedProperties = false; |
| m_nonInheritedFlags.isUnique = false; |
| m_nonInheritedFlags.emptyState = false; |
| m_nonInheritedFlags.firstChildState = false; |
| m_nonInheritedFlags.lastChildState = false; |
| m_nonInheritedFlags.affectedByHover = false; |
| m_nonInheritedFlags.affectedByActive = false; |
| m_nonInheritedFlags.affectedByDrag = false; |
| m_nonInheritedFlags.isLink = false; |
| m_nonInheritedFlags.styleType = static_cast<unsigned>(PseudoId::None); |
| m_nonInheritedFlags.pseudoBits = static_cast<unsigned>(PseudoId::None); |
| |
| static_assert((sizeof(InheritedFlags) <= 8), "InheritedFlags does not grow"); |
| static_assert((sizeof(NonInheritedFlags) <= 8), "NonInheritedFlags does not grow"); |
| } |
| |
| inline RenderStyle::RenderStyle(const RenderStyle& other, CloneTag) |
| : m_boxData(other.m_boxData) |
| , m_visualData(other.m_visualData) |
| , m_backgroundData(other.m_backgroundData) |
| , m_surroundData(other.m_surroundData) |
| , m_rareNonInheritedData(other.m_rareNonInheritedData) |
| , m_nonInheritedFlags(other.m_nonInheritedFlags) |
| , m_rareInheritedData(other.m_rareInheritedData) |
| , m_inheritedData(other.m_inheritedData) |
| , m_inheritedFlags(other.m_inheritedFlags) |
| , m_svgStyle(other.m_svgStyle) |
| { |
| } |
| |
| inline RenderStyle::RenderStyle(RenderStyle& a, RenderStyle&& b) |
| : m_boxData(a.m_boxData.replace(WTFMove(b.m_boxData))) |
| , m_visualData(a.m_visualData.replace(WTFMove(b.m_visualData))) |
| , m_backgroundData(a.m_backgroundData.replace(WTFMove(b.m_backgroundData))) |
| , m_surroundData(a.m_surroundData.replace(WTFMove(b.m_surroundData))) |
| , m_rareNonInheritedData(a.m_rareNonInheritedData.replace(WTFMove(b.m_rareNonInheritedData))) |
| , m_nonInheritedFlags(std::exchange(a.m_nonInheritedFlags, b.m_nonInheritedFlags)) |
| , m_rareInheritedData(a.m_rareInheritedData.replace(WTFMove(b.m_rareInheritedData))) |
| , m_inheritedData(a.m_inheritedData.replace(WTFMove(b.m_inheritedData))) |
| , m_inheritedFlags(std::exchange(a.m_inheritedFlags, b.m_inheritedFlags)) |
| , m_cachedPseudoStyles(std::exchange(a.m_cachedPseudoStyles, WTFMove(b.m_cachedPseudoStyles))) |
| , m_svgStyle(a.m_svgStyle.replace(WTFMove(b.m_svgStyle))) |
| { |
| } |
| |
| RenderStyle::~RenderStyle() |
| { |
| #if ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS) |
| ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun); |
| m_deletionHasBegun = true; |
| #endif |
| } |
| |
| RenderStyle RenderStyle::replace(RenderStyle&& newStyle) |
| { |
| return RenderStyle { *this, WTFMove(newStyle) }; |
| } |
| |
| static StyleSelfAlignmentData resolvedSelfAlignment(const StyleSelfAlignmentData& value, ItemPosition normalValueBehavior) |
| { |
| if (value.position() == ItemPosition::Legacy || value.position() == ItemPosition::Normal || value.position() == ItemPosition::Auto) |
| return { normalValueBehavior, OverflowAlignment::Default }; |
| return value; |
| } |
| |
| StyleSelfAlignmentData RenderStyle::resolvedAlignItems(ItemPosition normalValueBehaviour) const |
| { |
| return resolvedSelfAlignment(alignItems(), normalValueBehaviour); |
| } |
| |
| StyleSelfAlignmentData RenderStyle::resolvedAlignSelf(const RenderStyle* parentStyle, ItemPosition normalValueBehaviour) const |
| { |
| // The auto keyword computes to the parent's align-items computed value. |
| // We will return the behaviour of 'normal' value if needed, which is specific of each layout model. |
| if (!parentStyle || alignSelf().position() != ItemPosition::Auto) |
| return resolvedSelfAlignment(alignSelf(), normalValueBehaviour); |
| return parentStyle->resolvedAlignItems(normalValueBehaviour); |
| } |
| |
| StyleSelfAlignmentData RenderStyle::resolvedJustifyItems(ItemPosition normalValueBehaviour) const |
| { |
| return resolvedSelfAlignment(justifyItems(), normalValueBehaviour); |
| } |
| |
| StyleSelfAlignmentData RenderStyle::resolvedJustifySelf(const RenderStyle* parentStyle, ItemPosition normalValueBehaviour) const |
| { |
| // The auto keyword computes to the parent's justify-items computed value. |
| // We will return the behaviour of 'normal' value if needed, which is specific of each layout model. |
| if (!parentStyle || justifySelf().position() != ItemPosition::Auto) |
| return resolvedSelfAlignment(justifySelf(), normalValueBehaviour); |
| return parentStyle->resolvedJustifyItems(normalValueBehaviour); |
| } |
| |
| static inline StyleContentAlignmentData resolvedContentAlignment(const StyleContentAlignmentData& value, const StyleContentAlignmentData& normalValueBehavior) |
| { |
| return (value.position() == ContentPosition::Normal && value.distribution() == ContentDistribution::Default) ? normalValueBehavior : value; |
| } |
| |
| StyleContentAlignmentData RenderStyle::resolvedAlignContent(const StyleContentAlignmentData& normalValueBehavior) const |
| { |
| // We will return the behaviour of 'normal' value if needed, which is specific of each layout model. |
| return resolvedContentAlignment(alignContent(), normalValueBehavior); |
| } |
| |
| StyleContentAlignmentData RenderStyle::resolvedJustifyContent(const StyleContentAlignmentData& normalValueBehavior) const |
| { |
| // We will return the behaviour of 'normal' value if needed, which is specific of each layout model. |
| return resolvedContentAlignment(justifyContent(), normalValueBehavior); |
| } |
| |
| static inline ContentPosition resolvedContentAlignmentPosition(const StyleContentAlignmentData& value, const StyleContentAlignmentData& normalValueBehavior) |
| { |
| return (value.position() == ContentPosition::Normal && value.distribution() == ContentDistribution::Default) ? normalValueBehavior.position() : value.position(); |
| } |
| |
| static inline ContentDistribution resolvedContentAlignmentDistribution(const StyleContentAlignmentData& value, const StyleContentAlignmentData& normalValueBehavior) |
| { |
| return (value.position() == ContentPosition::Normal && value.distribution() == ContentDistribution::Default) ? normalValueBehavior.distribution() : value.distribution(); |
| } |
| |
| ContentPosition RenderStyle::resolvedJustifyContentPosition(const StyleContentAlignmentData& normalValueBehavior) const |
| { |
| return resolvedContentAlignmentPosition(justifyContent(), normalValueBehavior); |
| } |
| |
| ContentDistribution RenderStyle::resolvedJustifyContentDistribution(const StyleContentAlignmentData& normalValueBehavior) const |
| { |
| return resolvedContentAlignmentDistribution(justifyContent(), normalValueBehavior); |
| } |
| |
| ContentPosition RenderStyle::resolvedAlignContentPosition(const StyleContentAlignmentData& normalValueBehavior) const |
| { |
| return resolvedContentAlignmentPosition(alignContent(), normalValueBehavior); |
| } |
| |
| ContentDistribution RenderStyle::resolvedAlignContentDistribution(const StyleContentAlignmentData& normalValueBehavior) const |
| { |
| return resolvedContentAlignmentDistribution(alignContent(), normalValueBehavior); |
| } |
| |
| void RenderStyle::inheritFrom(const RenderStyle& inheritParent) |
| { |
| m_rareInheritedData = inheritParent.m_rareInheritedData; |
| m_inheritedData = inheritParent.m_inheritedData; |
| m_inheritedFlags = inheritParent.m_inheritedFlags; |
| |
| if (m_svgStyle != inheritParent.m_svgStyle) |
| m_svgStyle.access().inheritFrom(inheritParent.m_svgStyle.get()); |
| } |
| |
| void RenderStyle::copyNonInheritedFrom(const RenderStyle& other) |
| { |
| m_boxData = other.m_boxData; |
| m_visualData = other.m_visualData; |
| m_backgroundData = other.m_backgroundData; |
| m_surroundData = other.m_surroundData; |
| m_rareNonInheritedData = other.m_rareNonInheritedData; |
| m_nonInheritedFlags.copyNonInheritedFrom(other.m_nonInheritedFlags); |
| |
| if (m_svgStyle != other.m_svgStyle) |
| m_svgStyle.access().copyNonInheritedFrom(other.m_svgStyle.get()); |
| |
| ASSERT(zoom() == initialZoom()); |
| } |
| |
| void RenderStyle::copyContentFrom(const RenderStyle& other) |
| { |
| if (!other.m_rareNonInheritedData->content) |
| return; |
| m_rareNonInheritedData.access().content = other.m_rareNonInheritedData->content->clone(); |
| } |
| |
| bool RenderStyle::operator==(const RenderStyle& other) const |
| { |
| // compare everything except the pseudoStyle pointer |
| return m_inheritedFlags == other.m_inheritedFlags |
| && m_nonInheritedFlags == other.m_nonInheritedFlags |
| && m_boxData == other.m_boxData |
| && m_visualData == other.m_visualData |
| && m_backgroundData == other.m_backgroundData |
| && m_surroundData == other.m_surroundData |
| && m_rareNonInheritedData == other.m_rareNonInheritedData |
| && m_rareInheritedData == other.m_rareInheritedData |
| && m_inheritedData == other.m_inheritedData |
| && m_svgStyle == other.m_svgStyle; |
| } |
| |
| bool RenderStyle::hasUniquePseudoStyle() const |
| { |
| if (!m_cachedPseudoStyles || styleType() != PseudoId::None) |
| return false; |
| |
| for (auto& pseudoStyle : *m_cachedPseudoStyles) { |
| if (pseudoStyle->unique()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| RenderStyle* RenderStyle::getCachedPseudoStyle(PseudoId pid) const |
| { |
| if (!m_cachedPseudoStyles || !m_cachedPseudoStyles->size()) |
| return nullptr; |
| |
| if (styleType() != PseudoId::None) |
| return nullptr; |
| |
| for (auto& pseudoStyle : *m_cachedPseudoStyles) { |
| if (pseudoStyle->styleType() == pid) |
| return pseudoStyle.get(); |
| } |
| |
| return nullptr; |
| } |
| |
| RenderStyle* RenderStyle::addCachedPseudoStyle(std::unique_ptr<RenderStyle> pseudo) |
| { |
| if (!pseudo) |
| return nullptr; |
| |
| ASSERT(pseudo->styleType() > PseudoId::None); |
| |
| RenderStyle* result = pseudo.get(); |
| |
| if (!m_cachedPseudoStyles) |
| m_cachedPseudoStyles = makeUnique<PseudoStyleCache>(); |
| |
| m_cachedPseudoStyles->append(WTFMove(pseudo)); |
| |
| return result; |
| } |
| |
| void RenderStyle::removeCachedPseudoStyle(PseudoId pid) |
| { |
| if (!m_cachedPseudoStyles) |
| return; |
| for (size_t i = 0; i < m_cachedPseudoStyles->size(); ++i) { |
| RenderStyle* pseudoStyle = m_cachedPseudoStyles->at(i).get(); |
| if (pseudoStyle->styleType() == pid) { |
| m_cachedPseudoStyles->remove(i); |
| return; |
| } |
| } |
| } |
| |
| bool RenderStyle::inheritedEqual(const RenderStyle& other) const |
| { |
| return m_inheritedFlags == other.m_inheritedFlags |
| && m_inheritedData == other.m_inheritedData |
| && (m_svgStyle.ptr() == other.m_svgStyle.ptr() || m_svgStyle->inheritedEqual(other.m_svgStyle)) |
| && m_rareInheritedData == other.m_rareInheritedData; |
| } |
| |
| #if ENABLE(TEXT_AUTOSIZING) |
| |
| static inline unsigned computeFontHash(const FontCascade& font) |
| { |
| IntegerHasher hasher; |
| hasher.add(ASCIICaseInsensitiveHash::hash(font.fontDescription().firstFamily())); |
| hasher.add(font.fontDescription().specifiedSize()); |
| return hasher.hash(); |
| } |
| |
| unsigned RenderStyle::hashForTextAutosizing() const |
| { |
| // FIXME: Not a very smart hash. Could be improved upon. See <https://bugs.webkit.org/show_bug.cgi?id=121131>. |
| unsigned hash = m_rareNonInheritedData->appearance; |
| hash ^= m_rareNonInheritedData->marginBeforeCollapse; |
| hash ^= m_rareNonInheritedData->marginAfterCollapse; |
| hash ^= m_rareNonInheritedData->lineClamp.value(); |
| hash ^= m_rareInheritedData->overflowWrap; |
| hash ^= m_rareInheritedData->nbspMode; |
| hash ^= m_rareInheritedData->lineBreak; |
| hash ^= WTF::FloatHash<float>::hash(m_inheritedData->specifiedLineHeight.value()); |
| hash ^= computeFontHash(m_inheritedData->fontCascade); |
| hash ^= WTF::FloatHash<float>::hash(m_inheritedData->horizontalBorderSpacing); |
| hash ^= WTF::FloatHash<float>::hash(m_inheritedData->verticalBorderSpacing); |
| hash ^= m_inheritedFlags.boxDirection; |
| hash ^= m_inheritedFlags.rtlOrdering; |
| hash ^= m_nonInheritedFlags.position; |
| hash ^= m_nonInheritedFlags.floating; |
| hash ^= m_rareNonInheritedData->textOverflow; |
| hash ^= m_rareInheritedData->textSecurity; |
| return hash; |
| } |
| |
| bool RenderStyle::equalForTextAutosizing(const RenderStyle& other) const |
| { |
| return m_rareNonInheritedData->appearance == other.m_rareNonInheritedData->appearance |
| && m_rareNonInheritedData->marginBeforeCollapse == other.m_rareNonInheritedData->marginBeforeCollapse |
| && m_rareNonInheritedData->marginAfterCollapse == other.m_rareNonInheritedData->marginAfterCollapse |
| && m_rareNonInheritedData->lineClamp == other.m_rareNonInheritedData->lineClamp |
| && m_rareInheritedData->textSizeAdjust == other.m_rareInheritedData->textSizeAdjust |
| && m_rareInheritedData->overflowWrap == other.m_rareInheritedData->overflowWrap |
| && m_rareInheritedData->nbspMode == other.m_rareInheritedData->nbspMode |
| && m_rareInheritedData->lineBreak == other.m_rareInheritedData->lineBreak |
| && m_rareInheritedData->textSecurity == other.m_rareInheritedData->textSecurity |
| && m_inheritedData->specifiedLineHeight == other.m_inheritedData->specifiedLineHeight |
| && m_inheritedData->fontCascade.equalForTextAutoSizing(other.m_inheritedData->fontCascade) |
| && m_inheritedData->horizontalBorderSpacing == other.m_inheritedData->horizontalBorderSpacing |
| && m_inheritedData->verticalBorderSpacing == other.m_inheritedData->verticalBorderSpacing |
| && m_inheritedFlags.boxDirection == other.m_inheritedFlags.boxDirection |
| && m_inheritedFlags.rtlOrdering == other.m_inheritedFlags.rtlOrdering |
| && m_nonInheritedFlags.position == other.m_nonInheritedFlags.position |
| && m_nonInheritedFlags.floating == other.m_nonInheritedFlags.floating |
| && m_rareNonInheritedData->textOverflow == other.m_rareNonInheritedData->textOverflow; |
| } |
| |
| bool RenderStyle::isIdempotentTextAutosizingCandidate() const |
| { |
| // Refer to <rdar://problem/51826266> for more information regarding how this function was generated. |
| auto fields = OptionSet<AutosizeStatus::Fields>::fromRaw(m_inheritedFlags.autosizeStatus); |
| if (fields.contains(AutosizeStatus::Fields::AvoidSubtree)) |
| return false; |
| |
| const float smallMinimumDifferenceThresholdBetweenLineHeightAndSpecifiedFontSizeForBoostingText = 5; |
| const float largeMinimumDifferenceThresholdBetweenLineHeightAndSpecifiedFontSizeForBoostingText = 25; |
| |
| if (fields.contains(AutosizeStatus::Fields::FixedHeight)) { |
| if (fields.contains(AutosizeStatus::Fields::FixedWidth)) { |
| if (whiteSpace() == WhiteSpace::NoWrap) { |
| if (width().isFixed()) |
| return false; |
| |
| if (height().isFixed() && specifiedLineHeight().isFixed()) { |
| float specifiedSize = specifiedFontSize(); |
| if (height().value() == specifiedSize && specifiedLineHeight().value() == specifiedSize) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| if (fields.contains(AutosizeStatus::Fields::Floating)) { |
| if (specifiedLineHeight().isFixed() && height().isFixed()) { |
| float specifiedSize = specifiedFontSize(); |
| if (specifiedLineHeight().value() - specifiedSize > smallMinimumDifferenceThresholdBetweenLineHeightAndSpecifiedFontSizeForBoostingText |
| && height().value() - specifiedSize > smallMinimumDifferenceThresholdBetweenLineHeightAndSpecifiedFontSizeForBoostingText) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| if (fields.contains(AutosizeStatus::Fields::OverflowXHidden)) |
| return false; |
| |
| return true; |
| } |
| |
| if (fields.contains(AutosizeStatus::Fields::OverflowXHidden)) { |
| if (fields.contains(AutosizeStatus::Fields::Floating)) |
| return false; |
| |
| return true; |
| } |
| |
| return true; |
| } |
| |
| if (width().isFixed()) { |
| if (breakWords()) |
| return true; |
| |
| return false; |
| } |
| |
| if (textSizeAdjust().isPercentage() && textSizeAdjust().percentage() == 100) { |
| if (fields.contains(AutosizeStatus::Fields::Floating)) |
| return true; |
| |
| if (fields.contains(AutosizeStatus::Fields::FixedWidth)) |
| return true; |
| |
| if (specifiedLineHeight().isFixed() && specifiedLineHeight().value() - specifiedFontSize() > largeMinimumDifferenceThresholdBetweenLineHeightAndSpecifiedFontSizeForBoostingText) |
| return true; |
| |
| return false; |
| } |
| |
| if (hasBackgroundImage() && backgroundRepeatX() == FillRepeat::NoRepeat && backgroundRepeatY() == FillRepeat::NoRepeat) |
| return false; |
| |
| return true; |
| } |
| |
| AutosizeStatus RenderStyle::autosizeStatus() const |
| { |
| return OptionSet<AutosizeStatus::Fields>::fromRaw(m_inheritedFlags.autosizeStatus); |
| } |
| |
| void RenderStyle::setAutosizeStatus(AutosizeStatus autosizeStatus) |
| { |
| m_inheritedFlags.autosizeStatus = autosizeStatus.fields().toRaw(); |
| } |
| |
| #endif // ENABLE(TEXT_AUTOSIZING) |
| |
| static bool positionChangeIsMovementOnly(const LengthBox& a, const LengthBox& b, const Length& width) |
| { |
| // If any unit types are different, then we can't guarantee |
| // that this was just a movement. |
| if (a.left().type() != b.left().type() |
| || a.right().type() != b.right().type() |
| || a.top().type() != b.top().type() |
| || a.bottom().type() != b.bottom().type()) |
| return false; |
| |
| // Only one unit can be non-auto in the horizontal direction and |
| // in the vertical direction. Otherwise the adjustment of values |
| // is changing the size of the box. |
| if (!a.left().isIntrinsicOrAuto() && !a.right().isIntrinsicOrAuto()) |
| return false; |
| if (!a.top().isIntrinsicOrAuto() && !a.bottom().isIntrinsicOrAuto()) |
| return false; |
| // If our width is auto and left or right is specified then this |
| // is not just a movement - we need to resize to our container. |
| if ((!a.left().isIntrinsicOrAuto() || !a.right().isIntrinsicOrAuto()) && width.isIntrinsicOrAuto()) |
| return false; |
| |
| // One of the units is fixed or percent in both directions and stayed |
| // that way in the new style. Therefore all we are doing is moving. |
| return true; |
| } |
| |
| inline bool RenderStyle::changeAffectsVisualOverflow(const RenderStyle& other) const |
| { |
| if (m_rareNonInheritedData.ptr() != other.m_rareNonInheritedData.ptr() |
| && !arePointingToEqualData(m_rareNonInheritedData->boxShadow, other.m_rareNonInheritedData->boxShadow)) |
| return true; |
| |
| if (m_rareInheritedData.ptr() != other.m_rareInheritedData.ptr() |
| && !arePointingToEqualData(m_rareInheritedData->textShadow, other.m_rareInheritedData->textShadow)) |
| return true; |
| |
| if (m_inheritedFlags.textDecorations != other.m_inheritedFlags.textDecorations |
| || m_rareNonInheritedData->textDecorationStyle != other.m_rareNonInheritedData->textDecorationStyle |
| || m_rareInheritedData->textDecorationThickness != other.m_rareInheritedData->textDecorationThickness |
| || m_rareInheritedData->textUnderlineOffset != other.m_rareInheritedData->textUnderlineOffset |
| || m_rareInheritedData->textUnderlinePosition != other.m_rareInheritedData->textUnderlinePosition) { |
| // Underlines are always drawn outside of their textbox bounds when text-underline-position: under; |
| // is specified. We can take an early out here. |
| if (textUnderlinePosition() == TextUnderlinePosition::Under || other.textUnderlinePosition() == TextUnderlinePosition::Under) |
| return true; |
| return visualOverflowForDecorations(*this, nullptr) != visualOverflowForDecorations(other, nullptr); |
| } |
| |
| if (hasOutlineInVisualOverflow() != other.hasOutlineInVisualOverflow()) |
| return true; |
| return false; |
| } |
| |
| static bool rareNonInheritedDataChangeRequiresLayout(const StyleRareNonInheritedData& first, const StyleRareNonInheritedData& second, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) |
| { |
| ASSERT(&first != &second); |
| |
| if (first.appearance != second.appearance |
| || first.marginBeforeCollapse != second.marginBeforeCollapse |
| || first.marginAfterCollapse != second.marginAfterCollapse |
| || first.lineClamp != second.lineClamp |
| || first.initialLetter != second.initialLetter |
| || first.textOverflow != second.textOverflow) |
| return true; |
| |
| if (first.shapeMargin != second.shapeMargin) |
| return true; |
| |
| if (first.deprecatedFlexibleBox != second.deprecatedFlexibleBox) |
| return true; |
| |
| if (first.flexibleBox != second.flexibleBox) |
| return true; |
| |
| if (first.order != second.order |
| || first.alignContent != second.alignContent |
| || first.alignItems != second.alignItems |
| || first.alignSelf != second.alignSelf |
| || first.justifyContent != second.justifyContent |
| || first.justifyItems != second.justifyItems |
| || first.justifySelf != second.justifySelf) |
| return true; |
| |
| if (!arePointingToEqualData(first.boxReflect, second.boxReflect)) |
| return true; |
| |
| if (first.multiCol != second.multiCol) |
| return true; |
| |
| if (first.transform.ptr() != second.transform.ptr()) { |
| if (first.transform->hasTransform() != second.transform->hasTransform()) |
| return true; |
| if (*first.transform != *second.transform) { |
| changedContextSensitiveProperties.add(StyleDifferenceContextSensitiveProperty::Transform); |
| // Don't return; keep looking for another change |
| } |
| } |
| |
| if (first.grid != second.grid |
| || first.gridItem != second.gridItem) |
| return true; |
| |
| if (!arePointingToEqualData(first.willChange, second.willChange)) { |
| changedContextSensitiveProperties.add(StyleDifferenceContextSensitiveProperty::WillChange); |
| // Don't return; keep looking for another change |
| } |
| |
| if (first.textCombine != second.textCombine) |
| return true; |
| |
| if (first.breakBefore != second.breakBefore |
| || first.breakAfter != second.breakAfter |
| || first.breakInside != second.breakInside) |
| return true; |
| |
| if (first.hasOpacity() != second.hasOpacity()) { |
| // FIXME: We would like to use SimplifiedLayout here, but we can't quite do that yet. |
| // We need to make sure SimplifiedLayout can operate correctly on RenderInlines (we will need |
| // to add a selfNeedsSimplifiedLayout bit in order to not get confused and taint every line). |
| // In addition we need to solve the floating object issue when layers come and go. Right now |
| // a full layout is necessary to keep floating object lists sane. |
| return true; |
| } |
| |
| #if ENABLE(CSS_COMPOSITING) |
| if (first.isolation != second.isolation) { |
| // Ideally this would trigger a cheaper layout that just updates layer z-order trees (webit.org/b/190088). |
| return true; |
| } |
| #endif |
| |
| if (first.hasFilters() != second.hasFilters()) |
| return true; |
| |
| #if ENABLE(FILTERS_LEVEL_2) |
| if (first.hasBackdropFilters() != second.hasBackdropFilters()) |
| return true; |
| #endif |
| |
| return false; |
| } |
| |
| static bool rareInheritedDataChangeRequiresLayout(const StyleRareInheritedData& first, const StyleRareInheritedData& second) |
| { |
| ASSERT(&first != &second); |
| |
| if (first.indent != second.indent |
| #if ENABLE(CSS3_TEXT) |
| || first.textAlignLast != second.textAlignLast |
| || first.textJustify != second.textJustify |
| || first.textIndentLine != second.textIndentLine |
| #endif |
| || first.effectiveZoom != second.effectiveZoom |
| || first.textZoom != second.textZoom |
| #if ENABLE(TEXT_AUTOSIZING) |
| || first.textSizeAdjust != second.textSizeAdjust |
| #endif |
| || first.wordBreak != second.wordBreak |
| || first.overflowWrap != second.overflowWrap |
| || first.nbspMode != second.nbspMode |
| || first.lineBreak != second.lineBreak |
| || first.textSecurity != second.textSecurity |
| || first.hyphens != second.hyphens |
| || first.hyphenationLimitBefore != second.hyphenationLimitBefore |
| || first.hyphenationLimitAfter != second.hyphenationLimitAfter |
| || first.hyphenationString != second.hyphenationString |
| || first.rubyPosition != second.rubyPosition |
| || first.textEmphasisMark != second.textEmphasisMark |
| || first.textEmphasisPosition != second.textEmphasisPosition |
| || first.textEmphasisCustomMark != second.textEmphasisCustomMark |
| || first.textOrientation != second.textOrientation |
| || first.tabSize != second.tabSize |
| || first.lineBoxContain != second.lineBoxContain |
| || first.lineGrid != second.lineGrid |
| || first.imageOrientation != second.imageOrientation |
| #if ENABLE(CSS_IMAGE_RESOLUTION) |
| || first.imageResolutionSource != second.imageResolutionSource |
| || first.imageResolutionSnap != second.imageResolutionSnap |
| || first.imageResolution != second.imageResolution |
| #endif |
| || first.lineSnap != second.lineSnap |
| || first.lineAlign != second.lineAlign |
| || first.hangingPunctuation != second.hangingPunctuation |
| #if ENABLE(OVERFLOW_SCROLLING_TOUCH) |
| || first.useTouchOverflowScrolling != second.useTouchOverflowScrolling |
| #endif |
| || first.listStyleImage != second.listStyleImage) // FIXME: needs arePointingToEqualData()? |
| return true; |
| |
| if (first.textStrokeWidth != second.textStrokeWidth) |
| return true; |
| |
| // These properties affect the cached stroke bounding box rects. |
| if (first.capStyle != second.capStyle |
| || first.joinStyle != second.joinStyle |
| || first.strokeWidth != second.strokeWidth |
| || first.miterLimit != second.miterLimit) |
| return true; |
| |
| if (!arePointingToEqualData(first.quotes, second.quotes)) |
| return true; |
| |
| return false; |
| } |
| |
| bool RenderStyle::changeRequiresLayout(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) const |
| { |
| if (m_boxData.ptr() != other.m_boxData.ptr()) { |
| if (m_boxData->width() != other.m_boxData->width() |
| || m_boxData->minWidth() != other.m_boxData->minWidth() |
| || m_boxData->maxWidth() != other.m_boxData->maxWidth() |
| || m_boxData->height() != other.m_boxData->height() |
| || m_boxData->minHeight() != other.m_boxData->minHeight() |
| || m_boxData->maxHeight() != other.m_boxData->maxHeight()) |
| return true; |
| |
| if (m_boxData->verticalAlign() != other.m_boxData->verticalAlign()) |
| return true; |
| |
| if (m_boxData->boxSizing() != other.m_boxData->boxSizing()) |
| return true; |
| } |
| |
| if (m_surroundData->margin != other.m_surroundData->margin) |
| return true; |
| |
| if (m_surroundData->padding != other.m_surroundData->padding) |
| return true; |
| |
| // FIXME: We should add an optimized form of layout that just recomputes visual overflow. |
| if (changeAffectsVisualOverflow(other)) |
| return true; |
| |
| if (m_rareNonInheritedData.ptr() != other.m_rareNonInheritedData.ptr() |
| && rareNonInheritedDataChangeRequiresLayout(*m_rareNonInheritedData, *other.m_rareNonInheritedData, changedContextSensitiveProperties)) |
| return true; |
| |
| if (m_rareInheritedData.ptr() != other.m_rareInheritedData.ptr() |
| && rareInheritedDataChangeRequiresLayout(*m_rareInheritedData, *other.m_rareInheritedData)) |
| return true; |
| |
| if (m_inheritedData.ptr() != other.m_inheritedData.ptr()) { |
| if (m_inheritedData->lineHeight != other.m_inheritedData->lineHeight |
| #if ENABLE(TEXT_AUTOSIZING) |
| || m_inheritedData->specifiedLineHeight != other.m_inheritedData->specifiedLineHeight |
| #endif |
| || m_inheritedData->fontCascade != other.m_inheritedData->fontCascade |
| || m_inheritedData->horizontalBorderSpacing != other.m_inheritedData->horizontalBorderSpacing |
| || m_inheritedData->verticalBorderSpacing != other.m_inheritedData->verticalBorderSpacing) |
| return true; |
| } |
| |
| if (m_inheritedFlags.boxDirection != other.m_inheritedFlags.boxDirection |
| || m_inheritedFlags.rtlOrdering != other.m_inheritedFlags.rtlOrdering |
| || m_nonInheritedFlags.position != other.m_nonInheritedFlags.position |
| || m_nonInheritedFlags.floating != other.m_nonInheritedFlags.floating |
| || m_nonInheritedFlags.originalDisplay != other.m_nonInheritedFlags.originalDisplay |
| || m_nonInheritedFlags.verticalAlign != other.m_nonInheritedFlags.verticalAlign) |
| return true; |
| |
| if (static_cast<DisplayType>(m_nonInheritedFlags.effectiveDisplay) >= DisplayType::Table) { |
| if (m_inheritedFlags.borderCollapse != other.m_inheritedFlags.borderCollapse |
| || m_inheritedFlags.emptyCells != other.m_inheritedFlags.emptyCells |
| || m_inheritedFlags.captionSide != other.m_inheritedFlags.captionSide |
| || m_nonInheritedFlags.tableLayout != other.m_nonInheritedFlags.tableLayout) |
| return true; |
| |
| // In the collapsing border model, 'hidden' suppresses other borders, while 'none' |
| // does not, so these style differences can be width differences. |
| if (m_inheritedFlags.borderCollapse |
| && ((borderTopStyle() == BorderStyle::Hidden && other.borderTopStyle() == BorderStyle::None) |
| || (borderTopStyle() == BorderStyle::None && other.borderTopStyle() == BorderStyle::Hidden) |
| || (borderBottomStyle() == BorderStyle::Hidden && other.borderBottomStyle() == BorderStyle::None) |
| || (borderBottomStyle() == BorderStyle::None && other.borderBottomStyle() == BorderStyle::Hidden) |
| || (borderLeftStyle() == BorderStyle::Hidden && other.borderLeftStyle() == BorderStyle::None) |
| || (borderLeftStyle() == BorderStyle::None && other.borderLeftStyle() == BorderStyle::Hidden) |
| || (borderRightStyle() == BorderStyle::Hidden && other.borderRightStyle() == BorderStyle::None) |
| || (borderRightStyle() == BorderStyle::None && other.borderRightStyle() == BorderStyle::Hidden))) |
| return true; |
| } |
| |
| if (static_cast<DisplayType>(m_nonInheritedFlags.effectiveDisplay) == DisplayType::ListItem) { |
| if (m_inheritedFlags.listStyleType != other.m_inheritedFlags.listStyleType |
| || m_inheritedFlags.listStylePosition != other.m_inheritedFlags.listStylePosition) |
| return true; |
| } |
| |
| if (m_inheritedFlags.textAlign != other.m_inheritedFlags.textAlign |
| || m_inheritedFlags.textTransform != other.m_inheritedFlags.textTransform |
| || m_inheritedFlags.direction != other.m_inheritedFlags.direction |
| || m_inheritedFlags.whiteSpace != other.m_inheritedFlags.whiteSpace |
| || m_nonInheritedFlags.clear != other.m_nonInheritedFlags.clear |
| || m_nonInheritedFlags.unicodeBidi != other.m_nonInheritedFlags.unicodeBidi) |
| return true; |
| |
| // Check block flow direction. |
| if (m_inheritedFlags.writingMode != other.m_inheritedFlags.writingMode) |
| return true; |
| |
| // Overflow returns a layout hint. |
| if (m_nonInheritedFlags.overflowX != other.m_nonInheritedFlags.overflowX |
| || m_nonInheritedFlags.overflowY != other.m_nonInheritedFlags.overflowY) |
| return true; |
| |
| // If our border widths change, then we need to layout. Other changes to borders |
| // only necessitate a repaint. |
| if (borderLeftWidth() != other.borderLeftWidth() |
| || borderTopWidth() != other.borderTopWidth() |
| || borderBottomWidth() != other.borderBottomWidth() |
| || borderRightWidth() != other.borderRightWidth()) |
| return true; |
| |
| // If the counter directives change, trigger a relayout to re-calculate counter values and rebuild the counter node tree. |
| if (!arePointingToEqualData(m_rareNonInheritedData->counterDirectives, other.m_rareNonInheritedData->counterDirectives)) |
| return true; |
| |
| if ((visibility() == Visibility::Collapse) != (other.visibility() == Visibility::Collapse)) |
| return true; |
| |
| if (position() != PositionType::Static) { |
| if (m_surroundData->offset != other.m_surroundData->offset) { |
| // FIXME: We would like to use SimplifiedLayout for relative positioning, but we can't quite do that yet. |
| // We need to make sure SimplifiedLayout can operate correctly on RenderInlines (we will need |
| // to add a selfNeedsSimplifiedLayout bit in order to not get confused and taint every line). |
| if (position() != PositionType::Absolute) |
| return true; |
| |
| // Optimize for the case where a positioned layer is moving but not changing size. |
| if (!positionChangeIsMovementOnly(m_surroundData->offset, other.m_surroundData->offset, m_boxData->width())) |
| return true; |
| } |
| } |
| |
| bool hasFirstLineStyle = hasPseudoStyle(PseudoId::FirstLine); |
| if (hasFirstLineStyle != other.hasPseudoStyle(PseudoId::FirstLine)) |
| return true; |
| if (hasFirstLineStyle) { |
| auto* firstLineStyle = getCachedPseudoStyle(PseudoId::FirstLine); |
| if (!firstLineStyle) |
| return true; |
| auto* otherFirstLineStyle = other.getCachedPseudoStyle(PseudoId::FirstLine); |
| if (!otherFirstLineStyle) |
| return true; |
| // FIXME: Not all first line style changes actually need layout. |
| if (*firstLineStyle != *otherFirstLineStyle) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RenderStyle::changeRequiresPositionedLayoutOnly(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>&) const |
| { |
| if (position() == PositionType::Static) |
| return false; |
| |
| if (m_surroundData->offset != other.m_surroundData->offset) { |
| // Optimize for the case where a positioned layer is moving but not changing size. |
| if (position() == PositionType::Absolute && positionChangeIsMovementOnly(m_surroundData->offset, other.m_surroundData->offset, m_boxData->width())) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool rareNonInheritedDataChangeRequiresLayerRepaint(const StyleRareNonInheritedData& first, const StyleRareNonInheritedData& second, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) |
| { |
| #if ENABLE(CSS_COMPOSITING) |
| if (first.effectiveBlendMode != second.effectiveBlendMode) |
| return true; |
| #endif |
| |
| if (first.opacity != second.opacity) { |
| changedContextSensitiveProperties.add(StyleDifferenceContextSensitiveProperty::Opacity); |
| // Don't return true; keep looking for another change. |
| } |
| |
| if (first.filter != second.filter) { |
| changedContextSensitiveProperties.add(StyleDifferenceContextSensitiveProperty::Filter); |
| // Don't return true; keep looking for another change. |
| } |
| |
| #if ENABLE(FILTERS_LEVEL_2) |
| if (first.backdropFilter != second.backdropFilter) { |
| changedContextSensitiveProperties.add(StyleDifferenceContextSensitiveProperty::Filter); |
| // Don't return true; keep looking for another change. |
| } |
| #endif |
| |
| if (first.mask != second.mask || first.maskBoxImage != second.maskBoxImage) |
| return true; |
| |
| return false; |
| } |
| |
| bool RenderStyle::changeRequiresLayerRepaint(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) const |
| { |
| // Style::Resolver has ensured that zIndex is non-auto only if it's applicable. |
| if (m_boxData->usedZIndex() != other.m_boxData->usedZIndex() || m_boxData->hasAutoUsedZIndex() != other.m_boxData->hasAutoUsedZIndex()) |
| return true; |
| |
| if (position() != PositionType::Static) { |
| if (m_visualData->clip != other.m_visualData->clip || m_visualData->hasClip != other.m_visualData->hasClip) { |
| changedContextSensitiveProperties.add(StyleDifferenceContextSensitiveProperty::ClipRect); |
| return true; |
| } |
| } |
| |
| if (m_rareNonInheritedData.ptr() != other.m_rareNonInheritedData.ptr() |
| && rareNonInheritedDataChangeRequiresLayerRepaint(*m_rareNonInheritedData, *other.m_rareNonInheritedData, changedContextSensitiveProperties)) |
| return true; |
| |
| return false; |
| } |
| |
| static bool requiresPainting(const RenderStyle& style) |
| { |
| if (style.visibility() == Visibility::Hidden) |
| return false; |
| if (!style.opacity()) |
| return false; |
| return true; |
| } |
| |
| static bool rareNonInheritedDataChangeRequiresRepaint(const StyleRareNonInheritedData& first, const StyleRareNonInheritedData& second, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) |
| { |
| if (first.userDrag != second.userDrag |
| || first.borderFit != second.borderFit |
| || first.objectFit != second.objectFit |
| || first.objectPosition != second.objectPosition) |
| return true; |
| |
| if (first.isNotFinal != second.isNotFinal) |
| return true; |
| |
| if (first.shapeOutside != second.shapeOutside) |
| return true; |
| |
| // FIXME: this should probably be moved to changeRequiresLayerRepaint(). |
| if (first.clipPath != second.clipPath) { |
| changedContextSensitiveProperties.add(StyleDifferenceContextSensitiveProperty::ClipPath); |
| // Don't return true; keep looking for another change. |
| } |
| |
| return false; |
| } |
| |
| static bool rareInheritedDataChangeRequiresRepaint(const StyleRareInheritedData& first, const StyleRareInheritedData& second) |
| { |
| return first.userModify != second.userModify |
| || first.userSelect != second.userSelect |
| || first.appleColorFilter != second.appleColorFilter |
| || first.imageRendering != second.imageRendering |
| #if ENABLE(DARK_MODE_CSS) |
| || first.colorScheme != second.colorScheme |
| #endif |
| ; |
| } |
| |
| #if ENABLE(CSS_PAINTING_API) |
| void RenderStyle::addCustomPaintWatchProperty(const String& name) |
| { |
| auto& data = m_rareNonInheritedData.access(); |
| if (!data.customPaintWatchedProperties) |
| data.customPaintWatchedProperties = makeUnique<HashSet<String>>(); |
| data.customPaintWatchedProperties->add(name); |
| } |
| |
| inline static bool changedCustomPaintWatchedProperty(const RenderStyle& a, const StyleRareNonInheritedData& aData, const RenderStyle& b, const StyleRareNonInheritedData& bData) |
| { |
| auto* propertiesA = aData.customPaintWatchedProperties.get(); |
| auto* propertiesB = bData.customPaintWatchedProperties.get(); |
| |
| if (UNLIKELY(propertiesA || propertiesB)) { |
| // FIXME: We should not need to use ComputedStyleExtractor here. |
| ComputedStyleExtractor extractor((Element*) nullptr); |
| |
| for (auto* watchPropertiesMap : { propertiesA, propertiesB }) { |
| if (!watchPropertiesMap) |
| continue; |
| |
| for (auto& name : *watchPropertiesMap) { |
| RefPtr<CSSValue> valueA; |
| RefPtr<CSSValue> valueB; |
| if (isCustomPropertyName(name)) { |
| if (a.getCustomProperty(name)) |
| valueA = CSSCustomPropertyValue::create(*a.getCustomProperty(name)); |
| if (b.getCustomProperty(name)) |
| valueB = CSSCustomPropertyValue::create(*b.getCustomProperty(name)); |
| } else { |
| CSSPropertyID propertyID = cssPropertyID(name); |
| if (!propertyID) |
| continue; |
| valueA = extractor.valueForPropertyInStyle(a, propertyID); |
| valueB = extractor.valueForPropertyInStyle(b, propertyID); |
| } |
| |
| if ((valueA && !valueB) || (!valueA && valueB)) |
| return true; |
| |
| if (!valueA) |
| continue; |
| |
| if (!(*valueA == *valueB)) |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| #endif |
| |
| bool RenderStyle::changeRequiresRepaint(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) const |
| { |
| if (!requiresPainting(*this) && !requiresPainting(other)) |
| return false; |
| |
| if (m_inheritedFlags.visibility != other.m_inheritedFlags.visibility |
| || m_inheritedFlags.printColorAdjust != other.m_inheritedFlags.printColorAdjust |
| || m_inheritedFlags.insideLink != other.m_inheritedFlags.insideLink |
| || m_inheritedFlags.insideDefaultButton != other.m_inheritedFlags.insideDefaultButton |
| || m_surroundData->border != other.m_surroundData->border |
| || !m_backgroundData->isEquivalentForPainting(*other.m_backgroundData)) |
| return true; |
| |
| if (m_rareNonInheritedData.ptr() != other.m_rareNonInheritedData.ptr() |
| && rareNonInheritedDataChangeRequiresRepaint(*m_rareNonInheritedData, *other.m_rareNonInheritedData, changedContextSensitiveProperties)) |
| return true; |
| |
| if (m_rareInheritedData.ptr() != other.m_rareInheritedData.ptr() |
| && rareInheritedDataChangeRequiresRepaint(*m_rareInheritedData, *other.m_rareInheritedData)) |
| return true; |
| |
| #if ENABLE(CSS_PAINTING_API) |
| if (changedCustomPaintWatchedProperty(*this, *m_rareNonInheritedData, other, *other.m_rareNonInheritedData)) |
| return true; |
| #endif |
| |
| return false; |
| } |
| |
| bool RenderStyle::changeRequiresRepaintIfTextOrBorderOrOutline(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>&) const |
| { |
| if (m_inheritedData->color != other.m_inheritedData->color |
| || m_inheritedFlags.textDecorations != other.m_inheritedFlags.textDecorations |
| || m_visualData->textDecoration != other.m_visualData->textDecoration |
| || m_rareNonInheritedData->textDecorationStyle != other.m_rareNonInheritedData->textDecorationStyle |
| || m_rareNonInheritedData->textDecorationColor != other.m_rareNonInheritedData->textDecorationColor |
| || m_rareInheritedData->textDecorationSkip != other.m_rareInheritedData->textDecorationSkip |
| || m_rareInheritedData->textFillColor != other.m_rareInheritedData->textFillColor |
| || m_rareInheritedData->textStrokeColor != other.m_rareInheritedData->textStrokeColor |
| || m_rareInheritedData->textEmphasisColor != other.m_rareInheritedData->textEmphasisColor |
| || m_rareInheritedData->textEmphasisFill != other.m_rareInheritedData->textEmphasisFill |
| || m_rareInheritedData->strokeColor != other.m_rareInheritedData->strokeColor |
| || m_rareInheritedData->caretColor != other.m_rareInheritedData->caretColor) |
| return true; |
| |
| return false; |
| } |
| |
| bool RenderStyle::changeRequiresRecompositeLayer(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>&) const |
| { |
| if (m_rareNonInheritedData.ptr() != other.m_rareNonInheritedData.ptr()) { |
| if (m_rareNonInheritedData->transformStyle3D != other.m_rareNonInheritedData->transformStyle3D |
| || m_rareNonInheritedData->backfaceVisibility != other.m_rareNonInheritedData->backfaceVisibility |
| || m_rareNonInheritedData->perspective != other.m_rareNonInheritedData->perspective |
| || m_rareNonInheritedData->perspectiveOriginX != other.m_rareNonInheritedData->perspectiveOriginX |
| || m_rareNonInheritedData->perspectiveOriginY != other.m_rareNonInheritedData->perspectiveOriginY) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| StyleDifference RenderStyle::diff(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) const |
| { |
| changedContextSensitiveProperties = OptionSet<StyleDifferenceContextSensitiveProperty>(); |
| |
| StyleDifference svgChange = StyleDifference::Equal; |
| if (m_svgStyle != other.m_svgStyle) { |
| svgChange = m_svgStyle->diff(other.m_svgStyle.get()); |
| if (svgChange == StyleDifference::Layout) |
| return svgChange; |
| } |
| |
| if (changeRequiresLayout(other, changedContextSensitiveProperties)) |
| return StyleDifference::Layout; |
| |
| // SVGRenderStyle::diff() might have returned StyleDifference::Repaint, eg. if fill changes. |
| // If eg. the font-size changed at the same time, we're not allowed to return StyleDifference::Repaint, |
| // but have to return StyleDifference::Layout, that's why this if branch comes after all branches |
| // that are relevant for SVG and might return StyleDifference::Layout. |
| if (svgChange != StyleDifference::Equal) |
| return svgChange; |
| |
| if (changeRequiresPositionedLayoutOnly(other, changedContextSensitiveProperties)) |
| return StyleDifference::LayoutPositionedMovementOnly; |
| |
| if (changeRequiresLayerRepaint(other, changedContextSensitiveProperties)) |
| return StyleDifference::RepaintLayer; |
| |
| if (changeRequiresRepaint(other, changedContextSensitiveProperties)) |
| return StyleDifference::Repaint; |
| |
| if (changeRequiresRecompositeLayer(other, changedContextSensitiveProperties)) |
| return StyleDifference::RecompositeLayer; |
| |
| if (changeRequiresRepaintIfTextOrBorderOrOutline(other, changedContextSensitiveProperties)) |
| return StyleDifference::RepaintIfTextOrBorderOrOutline; |
| |
| // Cursors are not checked, since they will be set appropriately in response to mouse events, |
| // so they don't need to cause any repaint or layout. |
| |
| // Animations don't need to be checked either. We always set the new style on the RenderObject, so we will get a chance to fire off |
| // the resulting transition properly. |
| return StyleDifference::Equal; |
| } |
| |
| bool RenderStyle::diffRequiresLayerRepaint(const RenderStyle& style, bool isComposited) const |
| { |
| OptionSet<StyleDifferenceContextSensitiveProperty> changedContextSensitiveProperties; |
| |
| if (changeRequiresRepaint(style, changedContextSensitiveProperties)) |
| return true; |
| |
| if (isComposited && changeRequiresLayerRepaint(style, changedContextSensitiveProperties)) |
| return changedContextSensitiveProperties.contains(StyleDifferenceContextSensitiveProperty::ClipRect); |
| |
| return false; |
| } |
| |
| void RenderStyle::setClip(Length&& top, Length&& right, Length&& bottom, Length&& left) |
| { |
| auto& data = m_visualData.access(); |
| data.clip.top() = WTFMove(top); |
| data.clip.right() = WTFMove(right); |
| data.clip.bottom() = WTFMove(bottom); |
| data.clip.left() = WTFMove(left); |
| } |
| |
| void RenderStyle::addCursor(RefPtr<StyleImage>&& image, const IntPoint& hotSpot) |
| { |
| auto& cursorData = m_rareInheritedData.access().cursorData; |
| if (!cursorData) |
| cursorData = CursorList::create(); |
| cursorData->append(CursorData(WTFMove(image), hotSpot)); |
| } |
| |
| void RenderStyle::setCursorList(RefPtr<CursorList>&& list) |
| { |
| m_rareInheritedData.access().cursorData = WTFMove(list); |
| } |
| |
| void RenderStyle::setQuotes(RefPtr<QuotesData>&& q) |
| { |
| if (m_rareInheritedData->quotes == q || (m_rareInheritedData->quotes && q && *m_rareInheritedData->quotes == *q)) |
| return; |
| |
| m_rareInheritedData.access().quotes = WTFMove(q); |
| } |
| |
| void RenderStyle::setWillChange(RefPtr<WillChangeData>&& willChangeData) |
| { |
| if (arePointingToEqualData(m_rareNonInheritedData->willChange.get(), willChangeData.get())) |
| return; |
| |
| m_rareNonInheritedData.access().willChange = WTFMove(willChangeData); |
| } |
| |
| void RenderStyle::clearCursorList() |
| { |
| if (m_rareInheritedData->cursorData) |
| m_rareInheritedData.access().cursorData = nullptr; |
| } |
| |
| void RenderStyle::clearContent() |
| { |
| if (m_rareNonInheritedData->content) |
| m_rareNonInheritedData.access().content = nullptr; |
| } |
| |
| static inline ContentData& lastContent(ContentData& firstContent) |
| { |
| auto* lastContent = &firstContent; |
| for (auto* content = &firstContent; content; content = content->next()) |
| lastContent = content; |
| return *lastContent; |
| } |
| |
| void RenderStyle::setContent(std::unique_ptr<ContentData> contentData, bool add) |
| { |
| auto& data = m_rareNonInheritedData.access(); |
| if (add && data.content) |
| lastContent(*data.content).setNext(WTFMove(contentData)); |
| else { |
| data.content = WTFMove(contentData); |
| auto& altText = data.altText; |
| if (!altText.isNull()) |
| data.content->setAltText(altText); |
| } |
| } |
| |
| void RenderStyle::setContent(RefPtr<StyleImage>&& image, bool add) |
| { |
| if (!image) |
| return; |
| setContent(makeUnique<ImageContentData>(image.releaseNonNull()), add); |
| } |
| |
| void RenderStyle::setContent(const String& string, bool add) |
| { |
| auto& data = m_rareNonInheritedData.access(); |
| if (add && data.content) { |
| auto& last = lastContent(*data.content); |
| if (!is<TextContentData>(last)) |
| last.setNext(makeUnique<TextContentData>(string)); |
| else { |
| auto& textContent = downcast<TextContentData>(last); |
| textContent.setText(textContent.text() + string); |
| } |
| } else { |
| data.content = makeUnique<TextContentData>(string); |
| auto& altText = data.altText; |
| if (!altText.isNull()) |
| data.content->setAltText(altText); |
| } |
| } |
| |
| void RenderStyle::setContent(std::unique_ptr<CounterContent> counter, bool add) |
| { |
| if (!counter) |
| return; |
| setContent(makeUnique<CounterContentData>(WTFMove(counter)), add); |
| } |
| |
| void RenderStyle::setContent(QuoteType quote, bool add) |
| { |
| setContent(makeUnique<QuoteContentData>(quote), add); |
| } |
| |
| void RenderStyle::setContentAltText(const String& string) |
| { |
| auto& data = m_rareNonInheritedData.access(); |
| data.altText = string; |
| if (data.content) |
| data.content->setAltText(string); |
| } |
| |
| const String& RenderStyle::contentAltText() const |
| { |
| return m_rareNonInheritedData->altText; |
| } |
| |
| void RenderStyle::setHasAttrContent() |
| { |
| setUnique(); |
| SET_VAR(m_rareNonInheritedData, hasAttrContent, true); |
| } |
| |
| static inline bool requireTransformOrigin(const Vector<RefPtr<TransformOperation>>& transformOperations, RenderStyle::ApplyTransformOrigin applyOrigin) |
| { |
| // The transform-origin property brackets the transform with translate operations. |
| // When the only transform is a translation, the transform-origin is irrelevant. |
| |
| if (applyOrigin != RenderStyle::IncludeTransformOrigin) |
| return false; |
| |
| for (auto& operation : transformOperations) { |
| // FIXME: Use affectedByTransformOrigin(). |
| auto type = operation->type(); |
| if (type != TransformOperation::TRANSLATE |
| && type != TransformOperation::TRANSLATE_3D |
| && type != TransformOperation::TRANSLATE_X |
| && type != TransformOperation::TRANSLATE_Y |
| && type != TransformOperation::TRANSLATE_Z) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void RenderStyle::applyTransform(TransformationMatrix& transform, const FloatRect& boundingBox, ApplyTransformOrigin applyOrigin) const |
| { |
| auto& operations = m_rareNonInheritedData->transform->operations.operations(); |
| bool applyTransformOrigin = requireTransformOrigin(operations, applyOrigin); |
| |
| FloatPoint3D originTranslate; |
| if (applyTransformOrigin) { |
| originTranslate.setX(boundingBox.x() + floatValueForLength(transformOriginX(), boundingBox.width())); |
| originTranslate.setY(boundingBox.y() + floatValueForLength(transformOriginY(), boundingBox.height())); |
| originTranslate.setZ(transformOriginZ()); |
| transform.translate3d(originTranslate.x(), originTranslate.y(), originTranslate.z()); |
| } |
| |
| for (auto& operation : operations) |
| operation->apply(transform, boundingBox.size()); |
| |
| if (applyTransformOrigin) |
| transform.translate3d(-originTranslate.x(), -originTranslate.y(), -originTranslate.z()); |
| } |
| |
| void RenderStyle::setPageScaleTransform(float scale) |
| { |
| if (scale == 1) |
| return; |
| TransformOperations transform; |
| transform.operations().append(ScaleTransformOperation::create(scale, scale, ScaleTransformOperation::SCALE)); |
| setTransform(transform); |
| setTransformOriginX(Length(0, Fixed)); |
| setTransformOriginY(Length(0, Fixed)); |
| } |
| |
| void RenderStyle::setTextShadow(std::unique_ptr<ShadowData> shadowData, bool add) |
| { |
| ASSERT(!shadowData || (!shadowData->spread() && shadowData->style() == ShadowStyle::Normal)); |
| |
| auto& rareData = m_rareInheritedData.access(); |
| if (!add) { |
| rareData.textShadow = WTFMove(shadowData); |
| return; |
| } |
| |
| shadowData->setNext(WTFMove(rareData.textShadow)); |
| rareData.textShadow = WTFMove(shadowData); |
| } |
| |
| void RenderStyle::setBoxShadow(std::unique_ptr<ShadowData> shadowData, bool add) |
| { |
| auto& rareData = m_rareNonInheritedData.access(); |
| if (!add) { |
| rareData.boxShadow = WTFMove(shadowData); |
| return; |
| } |
| |
| shadowData->setNext(WTFMove(rareData.boxShadow)); |
| rareData.boxShadow = WTFMove(shadowData); |
| } |
| |
| static RoundedRect::Radii calcRadiiFor(const BorderData& border, const LayoutSize& size) |
| { |
| return { |
| sizeForLengthSize(border.topLeftRadius(), size), |
| sizeForLengthSize(border.topRightRadius(), size), |
| sizeForLengthSize(border.bottomLeftRadius(), size), |
| sizeForLengthSize(border.bottomRightRadius(), size) |
| }; |
| } |
| |
| StyleImage* RenderStyle::listStyleImage() const |
| { |
| return m_rareInheritedData->listStyleImage.get(); |
| } |
| |
| void RenderStyle::setListStyleImage(RefPtr<StyleImage>&& v) |
| { |
| if (m_rareInheritedData->listStyleImage != v) |
| m_rareInheritedData.access().listStyleImage = WTFMove(v); |
| } |
| |
| const Color& RenderStyle::color() const |
| { |
| return m_inheritedData->color; |
| } |
| |
| const Color& RenderStyle::visitedLinkColor() const |
| { |
| return m_inheritedData->visitedLinkColor; |
| } |
| |
| void RenderStyle::setColor(const Color& v) |
| { |
| SET_VAR(m_inheritedData, color, v); |
| } |
| |
| void RenderStyle::setVisitedLinkColor(const Color& v) |
| { |
| SET_VAR(m_inheritedData, visitedLinkColor, v); |
| } |
| |
| float RenderStyle::horizontalBorderSpacing() const |
| { |
| return m_inheritedData->horizontalBorderSpacing; |
| } |
| |
| float RenderStyle::verticalBorderSpacing() const |
| { |
| return m_inheritedData->verticalBorderSpacing; |
| } |
| |
| void RenderStyle::setHorizontalBorderSpacing(float v) |
| { |
| SET_VAR(m_inheritedData, horizontalBorderSpacing, v); |
| } |
| |
| void RenderStyle::setVerticalBorderSpacing(float v) |
| { |
| SET_VAR(m_inheritedData, verticalBorderSpacing, v); |
| } |
| |
| RoundedRect RenderStyle::getRoundedBorderFor(const LayoutRect& borderRect, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const |
| { |
| RoundedRect roundedRect(borderRect); |
| if (hasBorderRadius()) { |
| RoundedRect::Radii radii = calcRadiiFor(m_surroundData->border, borderRect.size()); |
| radii.scale(calcBorderRadiiConstraintScaleFor(borderRect, radii)); |
| roundedRect.includeLogicalEdges(radii, isHorizontalWritingMode(), includeLogicalLeftEdge, includeLogicalRightEdge); |
| } |
| return roundedRect; |
| } |
| |
| RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const |
| { |
| bool horizontal = isHorizontalWritingMode(); |
| LayoutUnit leftWidth { (!horizontal || includeLogicalLeftEdge) ? borderLeftWidth() : 0 }; |
| LayoutUnit rightWidth { (!horizontal || includeLogicalRightEdge) ? borderRightWidth() : 0 }; |
| LayoutUnit topWidth { (horizontal || includeLogicalLeftEdge) ? borderTopWidth() : 0 }; |
| LayoutUnit bottomWidth { (horizontal || includeLogicalRightEdge) ? borderBottomWidth() : 0 }; |
| return getRoundedInnerBorderFor(borderRect, topWidth, bottomWidth, leftWidth, rightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); |
| } |
| |
| RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect, LayoutUnit topWidth, LayoutUnit bottomWidth, |
| LayoutUnit leftWidth, LayoutUnit rightWidth, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const |
| { |
| RoundedRect roundedRect { { borderRect.x() + leftWidth, borderRect.y() + topWidth, |
| borderRect.width() - leftWidth - rightWidth, borderRect.height() - topWidth - bottomWidth } }; |
| if (hasBorderRadius()) { |
| auto radii = getRoundedBorderFor(borderRect).radii(); |
| radii.shrink(topWidth, bottomWidth, leftWidth, rightWidth); |
| roundedRect.includeLogicalEdges(radii, isHorizontalWritingMode(), includeLogicalLeftEdge, includeLogicalRightEdge); |
| } |
| return roundedRect; |
| } |
| |
| static bool allLayersAreFixed(const FillLayer& layers) |
| { |
| for (auto* layer = &layers; layer; layer = layer->next()) { |
| if (!(layer->image() && layer->attachment() == FillAttachment::FixedBackground)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool RenderStyle::hasEntirelyFixedBackground() const |
| { |
| return allLayersAreFixed(backgroundLayers()); |
| } |
| |
| const CounterDirectiveMap* RenderStyle::counterDirectives() const |
| { |
| return m_rareNonInheritedData->counterDirectives.get(); |
| } |
| |
| CounterDirectiveMap& RenderStyle::accessCounterDirectives() |
| { |
| auto& map = m_rareNonInheritedData.access().counterDirectives; |
| if (!map) |
| map = makeUnique<CounterDirectiveMap>(); |
| return *map; |
| } |
| |
| const AtomString& RenderStyle::hyphenString() const |
| { |
| ASSERT(hyphens() != Hyphens::None); |
| |
| auto& hyphenationString = m_rareInheritedData->hyphenationString; |
| if (!hyphenationString.isNull()) |
| return hyphenationString; |
| |
| // FIXME: This should depend on locale. |
| static NeverDestroyed<AtomString> hyphenMinusString(&hyphenMinus, 1); |
| static NeverDestroyed<AtomString> hyphenString(&hyphen, 1); |
| return fontCascade().primaryFont().glyphForCharacter(hyphen) ? hyphenString : hyphenMinusString; |
| } |
| |
| const AtomString& RenderStyle::textEmphasisMarkString() const |
| { |
| switch (textEmphasisMark()) { |
| case TextEmphasisMark::None: |
| return nullAtom(); |
| case TextEmphasisMark::Custom: |
| return textEmphasisCustomMark(); |
| case TextEmphasisMark::Dot: { |
| static NeverDestroyed<AtomString> filledDotString(&bullet, 1); |
| static NeverDestroyed<AtomString> openDotString(&whiteBullet, 1); |
| return textEmphasisFill() == TextEmphasisFill::Filled ? filledDotString : openDotString; |
| } |
| case TextEmphasisMark::Circle: { |
| static NeverDestroyed<AtomString> filledCircleString(&blackCircle, 1); |
| static NeverDestroyed<AtomString> openCircleString(&whiteCircle, 1); |
| return textEmphasisFill() == TextEmphasisFill::Filled ? filledCircleString : openCircleString; |
| } |
| case TextEmphasisMark::DoubleCircle: { |
| static NeverDestroyed<AtomString> filledDoubleCircleString(&fisheye, 1); |
| static NeverDestroyed<AtomString> openDoubleCircleString(&bullseye, 1); |
| return textEmphasisFill() == TextEmphasisFill::Filled ? filledDoubleCircleString : openDoubleCircleString; |
| } |
| case TextEmphasisMark::Triangle: { |
| static NeverDestroyed<AtomString> filledTriangleString(&blackUpPointingTriangle, 1); |
| static NeverDestroyed<AtomString> openTriangleString(&whiteUpPointingTriangle, 1); |
| return textEmphasisFill() == TextEmphasisFill::Filled ? filledTriangleString : openTriangleString; |
| } |
| case TextEmphasisMark::Sesame: { |
| static NeverDestroyed<AtomString> filledSesameString(&sesameDot, 1); |
| static NeverDestroyed<AtomString> openSesameString(&whiteSesameDot, 1); |
| return textEmphasisFill() == TextEmphasisFill::Filled ? filledSesameString : openSesameString; |
| } |
| case TextEmphasisMark::Auto: |
| ASSERT_NOT_REACHED(); |
| return nullAtom(); |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return nullAtom(); |
| } |
| |
| void RenderStyle::adjustAnimations() |
| { |
| auto* animationList = m_rareNonInheritedData->animations.get(); |
| if (!animationList) |
| return; |
| |
| // Get rid of empty animations and anything beyond them |
| for (size_t i = 0, size = animationList->size(); i < size; ++i) { |
| if (animationList->animation(i).isEmpty()) { |
| animationList->resize(i); |
| break; |
| } |
| } |
| |
| if (animationList->isEmpty()) { |
| clearAnimations(); |
| return; |
| } |
| |
| // Repeat patterns into layers that don't have some properties set. |
| animationList->fillUnsetProperties(); |
| } |
| |
| void RenderStyle::adjustTransitions() |
| { |
| auto* transitionList = m_rareNonInheritedData->transitions.get(); |
| if (!transitionList) |
| return; |
| |
| // Get rid of empty transitions and anything beyond them |
| for (size_t i = 0, size = transitionList->size(); i < size; ++i) { |
| if (transitionList->animation(i).isEmpty()) { |
| transitionList->resize(i); |
| break; |
| } |
| } |
| |
| if (transitionList->isEmpty()) { |
| clearTransitions(); |
| return; |
| } |
| |
| // Repeat patterns into layers that don't have some properties set. |
| transitionList->fillUnsetProperties(); |
| |
| // Make sure there are no duplicate properties. |
| // This is an O(n^2) algorithm but the lists tend to be short, so it is probably OK. |
| for (size_t i = 0; i < transitionList->size(); ++i) { |
| for (size_t j = i + 1; j < transitionList->size(); ++j) { |
| if (transitionList->animation(i).property() == transitionList->animation(j).property()) { |
| // toss i |
| transitionList->remove(i); |
| j = i; |
| } |
| } |
| } |
| } |
| |
| AnimationList& RenderStyle::ensureAnimations() |
| { |
| if (!m_rareNonInheritedData.access().animations) |
| m_rareNonInheritedData.access().animations = makeUnique<AnimationList>(); |
| return *m_rareNonInheritedData->animations; |
| } |
| |
| AnimationList& RenderStyle::ensureTransitions() |
| { |
| if (!m_rareNonInheritedData.access().transitions) |
| m_rareNonInheritedData.access().transitions = makeUnique<AnimationList>(); |
| return *m_rareNonInheritedData->transitions; |
| } |
| |
| const Animation* RenderStyle::transitionForProperty(CSSPropertyID property) const |
| { |
| auto* transitions = this->transitions(); |
| if (!transitions) |
| return nullptr; |
| for (size_t i = 0, size = transitions->size(); i < size; ++i) { |
| auto& animation = transitions->animation(i); |
| if (animation.animationMode() == Animation::AnimateAll || animation.property() == property) |
| return &animation; |
| } |
| return nullptr; |
| } |
| |
| const FontCascade& RenderStyle::fontCascade() const |
| { |
| return m_inheritedData->fontCascade; |
| } |
| |
| const FontMetrics& RenderStyle::fontMetrics() const |
| { |
| return m_inheritedData->fontCascade.fontMetrics(); |
| } |
| |
| const FontCascadeDescription& RenderStyle::fontDescription() const |
| { |
| return m_inheritedData->fontCascade.fontDescription(); |
| } |
| |
| float RenderStyle::specifiedFontSize() const |
| { |
| return fontDescription().specifiedSize(); |
| } |
| |
| float RenderStyle::computedFontSize() const |
| { |
| return fontDescription().computedSize(); |
| } |
| |
| unsigned RenderStyle::computedFontPixelSize() const |
| { |
| return fontDescription().computedPixelSize(); |
| } |
| |
| const Length& RenderStyle::wordSpacing() const |
| { |
| return m_rareInheritedData->wordSpacing; |
| } |
| |
| float RenderStyle::letterSpacing() const |
| { |
| return m_inheritedData->fontCascade.letterSpacing(); |
| } |
| |
| bool RenderStyle::setFontDescription(FontCascadeDescription&& description) |
| { |
| if (m_inheritedData->fontCascade.fontDescription() == description) |
| return false; |
| auto& cascade = m_inheritedData.access().fontCascade; |
| cascade = { WTFMove(description), cascade.letterSpacing(), cascade.wordSpacing() }; |
| return true; |
| } |
| |
| const Length& RenderStyle::specifiedLineHeight() const |
| { |
| #if ENABLE(TEXT_AUTOSIZING) |
| return m_inheritedData->specifiedLineHeight; |
| #else |
| return m_inheritedData->lineHeight; |
| #endif |
| } |
| |
| #if ENABLE(TEXT_AUTOSIZING) |
| |
| void RenderStyle::setSpecifiedLineHeight(Length&& height) |
| { |
| SET_VAR(m_inheritedData, specifiedLineHeight, WTFMove(height)); |
| } |
| |
| #endif |
| |
| const Length& RenderStyle::lineHeight() const |
| { |
| return m_inheritedData->lineHeight; |
| } |
| |
| void RenderStyle::setLineHeight(Length&& height) |
| { |
| SET_VAR(m_inheritedData, lineHeight, WTFMove(height)); |
| } |
| |
| int RenderStyle::computedLineHeight() const |
| { |
| const Length& lh = lineHeight(); |
| |
| // Negative value means the line height is not set. Use the font's built-in spacing. |
| if (lh.isNegative()) |
| return fontMetrics().lineSpacing(); |
| |
| if (lh.isPercentOrCalculated()) |
| return minimumValueForLength(lh, computedFontPixelSize()); |
| |
| return clampTo<int>(lh.value()); |
| } |
| |
| void RenderStyle::setWordSpacing(Length&& value) |
| { |
| float fontWordSpacing; |
| switch (value.type()) { |
| case Auto: |
| fontWordSpacing = 0; |
| break; |
| case Percent: |
| fontWordSpacing = value.percent() * fontCascade().spaceWidth() / 100; |
| break; |
| case Fixed: |
| fontWordSpacing = value.value(); |
| break; |
| case Calculated: |
| fontWordSpacing = value.nonNanCalculatedValue(maxValueForCssLength); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| fontWordSpacing = 0; |
| break; |
| } |
| m_inheritedData.access().fontCascade.setWordSpacing(fontWordSpacing); |
| m_rareInheritedData.access().wordSpacing = WTFMove(value); |
| } |
| |
| void RenderStyle::setLetterSpacing(float v) { m_inheritedData.access().fontCascade.setLetterSpacing(v); } |
| |
| void RenderStyle::setFontSize(float size) |
| { |
| // size must be specifiedSize if Text Autosizing is enabled, but computedSize if text |
| // zoom is enabled (if neither is enabled it's irrelevant as they're probably the same). |
| |
| ASSERT(std::isfinite(size)); |
| if (!std::isfinite(size) || size < 0) |
| size = 0; |
| else |
| size = std::min(maximumAllowedFontSize, size); |
| |
| FontSelector* currentFontSelector = fontCascade().fontSelector(); |
| auto description = fontDescription(); |
| description.setSpecifiedSize(size); |
| description.setComputedSize(size); |
| |
| setFontDescription(WTFMove(description)); |
| fontCascade().update(currentFontSelector); |
| } |
| |
| #if ENABLE(VARIATION_FONTS) |
| void RenderStyle::setFontVariationSettings(FontVariationSettings settings) |
| { |
| FontSelector* currentFontSelector = fontCascade().fontSelector(); |
| auto description = fontDescription(); |
| description.setVariationSettings(WTFMove(settings)); |
| |
| setFontDescription(WTFMove(description)); |
| fontCascade().update(currentFontSelector); |
| } |
| #endif |
| |
| void RenderStyle::setFontWeight(FontSelectionValue value) |
| { |
| FontSelector* currentFontSelector = fontCascade().fontSelector(); |
| auto description = fontDescription(); |
| description.setWeight(value); |
| |
| setFontDescription(WTFMove(description)); |
| fontCascade().update(currentFontSelector); |
| } |
| |
| void RenderStyle::setFontStretch(FontSelectionValue value) |
| { |
| FontSelector* currentFontSelector = fontCascade().fontSelector(); |
| auto description = fontDescription(); |
| description.setStretch(value); |
| |
| setFontDescription(WTFMove(description)); |
| fontCascade().update(currentFontSelector); |
| } |
| |
| void RenderStyle::setFontItalic(Optional<FontSelectionValue> value) |
| { |
| FontSelector* currentFontSelector = fontCascade().fontSelector(); |
| auto description = fontDescription(); |
| description.setItalic(value); |
| |
| setFontDescription(WTFMove(description)); |
| fontCascade().update(currentFontSelector); |
| } |
| |
| void RenderStyle::getShadowExtent(const ShadowData* shadow, LayoutUnit& top, LayoutUnit& right, LayoutUnit& bottom, LayoutUnit& left) const |
| { |
| top = 0; |
| right = 0; |
| bottom = 0; |
| left = 0; |
| |
| for ( ; shadow; shadow = shadow->next()) { |
| if (shadow->style() == ShadowStyle::Inset) |
| continue; |
| |
| auto extentAndSpread = shadow->paintingExtent() + shadow->spread(); |
| top = std::min<LayoutUnit>(top, shadow->y() - extentAndSpread); |
| right = std::max<LayoutUnit>(right, shadow->x() + extentAndSpread); |
| bottom = std::max<LayoutUnit>(bottom, shadow->y() + extentAndSpread); |
| left = std::min<LayoutUnit>(left, shadow->x() - extentAndSpread); |
| } |
| } |
| |
| LayoutBoxExtent RenderStyle::getShadowInsetExtent(const ShadowData* shadow) const |
| { |
| LayoutUnit top; |
| LayoutUnit right; |
| LayoutUnit bottom; |
| LayoutUnit left; |
| |
| for ( ; shadow; shadow = shadow->next()) { |
| if (shadow->style() == ShadowStyle::Normal) |
| continue; |
| |
| auto extentAndSpread = shadow->paintingExtent() + shadow->spread(); |
| top = std::max<LayoutUnit>(top, shadow->y() + extentAndSpread); |
| right = std::min<LayoutUnit>(right, shadow->x() - extentAndSpread); |
| bottom = std::min<LayoutUnit>(bottom, shadow->y() - extentAndSpread); |
| left = std::max<LayoutUnit>(left, shadow->x() + extentAndSpread); |
| } |
| |
| return LayoutBoxExtent(WTFMove(top), WTFMove(right), WTFMove(bottom), WTFMove(left)); |
| } |
| |
| void RenderStyle::getShadowHorizontalExtent(const ShadowData* shadow, LayoutUnit &left, LayoutUnit &right) const |
| { |
| left = 0; |
| right = 0; |
| |
| for ( ; shadow; shadow = shadow->next()) { |
| if (shadow->style() == ShadowStyle::Inset) |
| continue; |
| |
| auto extentAndSpread = shadow->paintingExtent() + shadow->spread(); |
| left = std::min<LayoutUnit>(left, shadow->x() - extentAndSpread); |
| right = std::max<LayoutUnit>(right, shadow->x() + extentAndSpread); |
| } |
| } |
| |
| void RenderStyle::getShadowVerticalExtent(const ShadowData* shadow, LayoutUnit &top, LayoutUnit &bottom) const |
| { |
| top = 0; |
| bottom = 0; |
| |
| for ( ; shadow; shadow = shadow->next()) { |
| if (shadow->style() == ShadowStyle::Inset) |
| continue; |
| |
| auto extentAndSpread = shadow->paintingExtent() + shadow->spread(); |
| top = std::min<LayoutUnit>(top, shadow->y() - extentAndSpread); |
| bottom = std::max<LayoutUnit>(bottom, shadow->y() + extentAndSpread); |
| } |
| } |
| |
| Color RenderStyle::colorIncludingFallback(CSSPropertyID colorProperty, bool visitedLink) const |
| { |
| Color result; |
| BorderStyle borderStyle = BorderStyle::None; |
| switch (colorProperty) { |
| case CSSPropertyBackgroundColor: |
| return visitedLink ? visitedLinkBackgroundColor() : backgroundColor(); // Background color doesn't fall back. |
| case CSSPropertyBorderLeftColor: |
| result = visitedLink ? visitedLinkBorderLeftColor() : borderLeftColor(); |
| borderStyle = borderLeftStyle(); |
| break; |
| case CSSPropertyBorderRightColor: |
| result = visitedLink ? visitedLinkBorderRightColor() : borderRightColor(); |
| borderStyle = borderRightStyle(); |
| break; |
| case CSSPropertyBorderTopColor: |
| result = visitedLink ? visitedLinkBorderTopColor() : borderTopColor(); |
| borderStyle = borderTopStyle(); |
| break; |
| case CSSPropertyBorderBottomColor: |
| result = visitedLink ? visitedLinkBorderBottomColor() : borderBottomColor(); |
| borderStyle = borderBottomStyle(); |
| break; |
| case CSSPropertyCaretColor: |
| result = visitedLink ? visitedLinkCaretColor() : caretColor(); |
| break; |
| case CSSPropertyColor: |
| result = visitedLink ? visitedLinkColor() : color(); |
| break; |
| case CSSPropertyOutlineColor: |
| result = visitedLink ? visitedLinkOutlineColor() : outlineColor(); |
| break; |
| case CSSPropertyColumnRuleColor: |
| result = visitedLink ? visitedLinkColumnRuleColor() : columnRuleColor(); |
| break; |
| case CSSPropertyTextDecorationColor: |
| // Text decoration color fallback is handled in RenderObject::decorationColor. |
| return visitedLink ? visitedLinkTextDecorationColor() : textDecorationColor(); |
| case CSSPropertyWebkitTextEmphasisColor: |
| result = visitedLink ? visitedLinkTextEmphasisColor() : textEmphasisColor(); |
| break; |
| case CSSPropertyWebkitTextFillColor: |
| result = visitedLink ? visitedLinkTextFillColor() : textFillColor(); |
| break; |
| case CSSPropertyWebkitTextStrokeColor: |
| result = visitedLink ? visitedLinkTextStrokeColor() : textStrokeColor(); |
| break; |
| case CSSPropertyStrokeColor: |
| result = visitedLink ? visitedLinkStrokeColor() : strokeColor(); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| |
| if (!result.isValid()) { |
| if (!visitedLink && (borderStyle == BorderStyle::Inset || borderStyle == BorderStyle::Outset || borderStyle == BorderStyle::Ridge || borderStyle == BorderStyle::Groove)) |
| result = Color(238, 238, 238); |
| else |
| result = visitedLink ? visitedLinkColor() : color(); |
| } |
| return result; |
| } |
| |
| Color RenderStyle::visitedDependentColor(CSSPropertyID colorProperty) const |
| { |
| Color unvisitedColor = colorIncludingFallback(colorProperty, false); |
| if (insideLink() != InsideLink::InsideVisited) |
| return unvisitedColor; |
| |
| Color visitedColor = colorIncludingFallback(colorProperty, true); |
| |
| // Text decoration color validity is preserved (checked in RenderObject::decorationColor). |
| if (colorProperty == CSSPropertyTextDecorationColor) |
| return visitedColor; |
| |
| // FIXME: Technically someone could explicitly specify the color transparent, but for now we'll just |
| // assume that if the background color is transparent that it wasn't set. Note that it's weird that |
| // we're returning unvisited info for a visited link, but given our restriction that the alpha values |
| // have to match, it makes more sense to return the unvisited background color if specified than it |
| // does to return black. This behavior matches what Firefox 4 does as well. |
| if (colorProperty == CSSPropertyBackgroundColor && visitedColor == Color::transparent) |
| return unvisitedColor; |
| |
| // Take the alpha from the unvisited color, but get the RGB values from the visited color. |
| return visitedColor.colorWithAlpha(unvisitedColor.alphaAsFloat()); |
| } |
| |
| Color RenderStyle::visitedDependentColorWithColorFilter(CSSPropertyID colorProperty) const |
| { |
| if (!hasAppleColorFilter()) |
| return visitedDependentColor(colorProperty); |
| |
| return colorByApplyingColorFilter(visitedDependentColor(colorProperty)); |
| } |
| |
| Color RenderStyle::colorByApplyingColorFilter(const Color& color) const |
| { |
| Color transformedColor = color; |
| appleColorFilter().transformColor(transformedColor); |
| return transformedColor; |
| } |
| |
| const BorderValue& RenderStyle::borderBefore() const |
| { |
| switch (writingMode()) { |
| case TopToBottomWritingMode: |
| return borderTop(); |
| case BottomToTopWritingMode: |
| return borderBottom(); |
| case LeftToRightWritingMode: |
| return borderLeft(); |
| case RightToLeftWritingMode: |
| return borderRight(); |
| } |
| ASSERT_NOT_REACHED(); |
| return borderTop(); |
| } |
| |
| const BorderValue& RenderStyle::borderAfter() const |
| { |
| switch (writingMode()) { |
| case TopToBottomWritingMode: |
| return borderBottom(); |
| case BottomToTopWritingMode: |
| return borderTop(); |
| case LeftToRightWritingMode: |
| return borderRight(); |
| case RightToLeftWritingMode: |
| return borderLeft(); |
| } |
| ASSERT_NOT_REACHED(); |
| return borderBottom(); |
| } |
| |
| const BorderValue& RenderStyle::borderStart() const |
| { |
| if (isHorizontalWritingMode()) |
| return isLeftToRightDirection() ? borderLeft() : borderRight(); |
| return isLeftToRightDirection() ? borderTop() : borderBottom(); |
| } |
| |
| const BorderValue& RenderStyle::borderEnd() const |
| { |
| if (isHorizontalWritingMode()) |
| return isLeftToRightDirection() ? borderRight() : borderLeft(); |
| return isLeftToRightDirection() ? borderBottom() : borderTop(); |
| } |
| |
| float RenderStyle::borderBeforeWidth() const |
| { |
| switch (writingMode()) { |
| case TopToBottomWritingMode: |
| return borderTopWidth(); |
| case BottomToTopWritingMode: |
| return borderBottomWidth(); |
| case LeftToRightWritingMode: |
| return borderLeftWidth(); |
| case RightToLeftWritingMode: |
| return borderRightWidth(); |
| } |
| ASSERT_NOT_REACHED(); |
| return borderTopWidth(); |
| } |
| |
| float RenderStyle::borderAfterWidth() const |
| { |
| switch (writingMode()) { |
| case TopToBottomWritingMode: |
| return borderBottomWidth(); |
| case BottomToTopWritingMode: |
| return borderTopWidth(); |
| case LeftToRightWritingMode: |
| return borderRightWidth(); |
| case RightToLeftWritingMode: |
| return borderLeftWidth(); |
| } |
| ASSERT_NOT_REACHED(); |
| return borderBottomWidth(); |
| } |
| |
| float RenderStyle::borderStartWidth() const |
| { |
| if (isHorizontalWritingMode()) |
| return isLeftToRightDirection() ? borderLeftWidth() : borderRightWidth(); |
| return isLeftToRightDirection() ? borderTopWidth() : borderBottomWidth(); |
| } |
| |
| float RenderStyle::borderEndWidth() const |
| { |
| if (isHorizontalWritingMode()) |
| return isLeftToRightDirection() ? borderRightWidth() : borderLeftWidth(); |
| return isLeftToRightDirection() ? borderBottomWidth() : borderTopWidth(); |
| } |
| |
| void RenderStyle::setMarginStart(Length&& margin) |
| { |
| if (isHorizontalWritingMode()) { |
| if (isLeftToRightDirection()) |
| setMarginLeft(WTFMove(margin)); |
| else |
| setMarginRight(WTFMove(margin)); |
| } else { |
| if (isLeftToRightDirection()) |
| setMarginTop(WTFMove(margin)); |
| else |
| setMarginBottom(WTFMove(margin)); |
| } |
| } |
| |
| void RenderStyle::setMarginEnd(Length&& margin) |
| { |
| if (isHorizontalWritingMode()) { |
| if (isLeftToRightDirection()) |
| setMarginRight(WTFMove(margin)); |
| else |
| setMarginLeft(WTFMove(margin)); |
| } else { |
| if (isLeftToRightDirection()) |
| setMarginBottom(WTFMove(margin)); |
| else |
| setMarginTop(WTFMove(margin)); |
| } |
| } |
| |
| TextEmphasisMark RenderStyle::textEmphasisMark() const |
| { |
| auto mark = static_cast<TextEmphasisMark>(m_rareInheritedData->textEmphasisMark); |
| if (mark != TextEmphasisMark::Auto) |
| return mark; |
| if (isHorizontalWritingMode()) |
| return TextEmphasisMark::Dot; |
| return TextEmphasisMark::Sesame; |
| } |
| |
| #if ENABLE(TOUCH_EVENTS) |
| |
| Color RenderStyle::initialTapHighlightColor() |
| { |
| return RenderTheme::tapHighlightColor(); |
| } |
| |
| #endif |
| |
| LayoutBoxExtent RenderStyle::imageOutsets(const NinePieceImage& image) const |
| { |
| return { |
| NinePieceImage::computeOutset(image.outset().top(), LayoutUnit(borderTopWidth())), |
| NinePieceImage::computeOutset(image.outset().right(), LayoutUnit(borderRightWidth())), |
| NinePieceImage::computeOutset(image.outset().bottom(), LayoutUnit(borderBottomWidth())), |
| NinePieceImage::computeOutset(image.outset().left(), LayoutUnit(borderLeftWidth())) |
| }; |
| } |
| |
| std::pair<FontOrientation, NonCJKGlyphOrientation> RenderStyle::fontAndGlyphOrientation() |
| { |
| // FIXME: TextOrientationSideways should map to sideways-left in vertical-lr, which is not supported yet. |
| |
| if (isHorizontalWritingMode()) |
| return { FontOrientation::Horizontal, NonCJKGlyphOrientation::Mixed }; |
| |
| switch (textOrientation()) { |
| case TextOrientation::Mixed: |
| return { FontOrientation::Vertical, NonCJKGlyphOrientation::Mixed }; |
| case TextOrientation::Upright: |
| return { FontOrientation::Vertical, NonCJKGlyphOrientation::Upright }; |
| case TextOrientation::Sideways: |
| return { FontOrientation::Horizontal, NonCJKGlyphOrientation::Mixed }; |
| default: |
| ASSERT_NOT_REACHED(); |
| return { FontOrientation::Horizontal, NonCJKGlyphOrientation::Mixed }; |
| } |
| } |
| |
| void RenderStyle::setBorderImageSource(RefPtr<StyleImage>&& image) |
| { |
| if (m_surroundData->border.m_image.image() == image.get()) |
| return; |
| m_surroundData.access().border.m_image.setImage(WTFMove(image)); |
| } |
| |
| void RenderStyle::setBorderImageSlices(LengthBox&& slices) |
| { |
| if (m_surroundData->border.m_image.imageSlices() == slices) |
| return; |
| m_surroundData.access().border.m_image.setImageSlices(WTFMove(slices)); |
| } |
| |
| void RenderStyle::setBorderImageWidth(LengthBox&& slices) |
| { |
| if (m_surroundData->border.m_image.borderSlices() == slices) |
| return; |
| m_surroundData.access().border.m_image.setBorderSlices(WTFMove(slices)); |
| } |
| |
| void RenderStyle::setBorderImageOutset(LengthBox&& outset) |
| { |
| if (m_surroundData->border.m_image.outset() == outset) |
| return; |
| m_surroundData.access().border.m_image.setOutset(WTFMove(outset)); |
| } |
| |
| void RenderStyle::setColumnStylesFromPaginationMode(const Pagination::Mode& paginationMode) |
| { |
| if (paginationMode == Pagination::Unpaginated) |
| return; |
| |
| setColumnFill(ColumnFill::Auto); |
| |
| switch (paginationMode) { |
| case Pagination::LeftToRightPaginated: |
| setColumnAxis(ColumnAxis::Horizontal); |
| if (isHorizontalWritingMode()) |
| setColumnProgression(isLeftToRightDirection() ? ColumnProgression::Normal : ColumnProgression::Reverse); |
| else |
| setColumnProgression(isFlippedBlocksWritingMode() ? ColumnProgression::Reverse : ColumnProgression::Normal); |
| break; |
| case Pagination::RightToLeftPaginated: |
| setColumnAxis(ColumnAxis::Horizontal); |
| if (isHorizontalWritingMode()) |
| setColumnProgression(isLeftToRightDirection() ? ColumnProgression::Reverse : ColumnProgression::Normal); |
| else |
| setColumnProgression(isFlippedBlocksWritingMode() ? ColumnProgression::Normal : ColumnProgression::Reverse); |
| break; |
| case Pagination::TopToBottomPaginated: |
| setColumnAxis(ColumnAxis::Vertical); |
| if (isHorizontalWritingMode()) |
| setColumnProgression(isFlippedBlocksWritingMode() ? ColumnProgression::Reverse : ColumnProgression::Normal); |
| else |
| setColumnProgression(isLeftToRightDirection() ? ColumnProgression::Normal : ColumnProgression::Reverse); |
| break; |
| case Pagination::BottomToTopPaginated: |
| setColumnAxis(ColumnAxis::Vertical); |
| if (isHorizontalWritingMode()) |
| setColumnProgression(isFlippedBlocksWritingMode() ? ColumnProgression::Normal : ColumnProgression::Reverse); |
| else |
| setColumnProgression(isLeftToRightDirection() ? ColumnProgression::Reverse : ColumnProgression::Normal); |
| break; |
| case Pagination::Unpaginated: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| } |
| |
| #if ENABLE(CSS_SCROLL_SNAP) |
| |
| ScrollSnapType RenderStyle::initialScrollSnapType() |
| { |
| return { }; |
| } |
| |
| ScrollSnapAlign RenderStyle::initialScrollSnapAlign() |
| { |
| return { }; |
| } |
| |
| const StyleScrollSnapArea& RenderStyle::scrollSnapArea() const |
| { |
| return *m_rareNonInheritedData->scrollSnapArea; |
| } |
| |
| const StyleScrollSnapPort& RenderStyle::scrollSnapPort() const |
| { |
| return *m_rareNonInheritedData->scrollSnapPort; |
| } |
| |
| const ScrollSnapType& RenderStyle::scrollSnapType() const |
| { |
| return m_rareNonInheritedData->scrollSnapPort->type; |
| } |
| |
| const LengthBox& RenderStyle::scrollPadding() const |
| { |
| return m_rareNonInheritedData->scrollSnapPort->scrollPadding; |
| } |
| |
| const Length& RenderStyle::scrollPaddingTop() const |
| { |
| return scrollPadding().top(); |
| } |
| |
| const Length& RenderStyle::scrollPaddingBottom() const |
| { |
| return scrollPadding().bottom(); |
| } |
| |
| const Length& RenderStyle::scrollPaddingLeft() const |
| { |
| return scrollPadding().left(); |
| } |
| |
| const Length& RenderStyle::scrollPaddingRight() const |
| { |
| return scrollPadding().right(); |
| } |
| |
| const ScrollSnapAlign& RenderStyle::scrollSnapAlign() const |
| { |
| return m_rareNonInheritedData->scrollSnapArea->alignment; |
| } |
| |
| const LengthBox& RenderStyle::scrollSnapMargin() const |
| { |
| return m_rareNonInheritedData->scrollSnapArea->scrollSnapMargin; |
| } |
| |
| const Length& RenderStyle::scrollSnapMarginTop() const |
| { |
| return scrollSnapMargin().top(); |
| } |
| |
| const Length& RenderStyle::scrollSnapMarginBottom() const |
| { |
| return scrollSnapMargin().bottom(); |
| } |
| |
| const Length& RenderStyle::scrollSnapMarginLeft() const |
| { |
| return scrollSnapMargin().left(); |
| } |
| |
| const Length& RenderStyle::scrollSnapMarginRight() const |
| { |
| return scrollSnapMargin().right(); |
| } |
| |
| void RenderStyle::setScrollSnapType(const ScrollSnapType& type) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapPort, type, type); |
| } |
| |
| void RenderStyle::setScrollPaddingTop(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapPort, scrollPadding.top(), WTFMove(length)); |
| } |
| |
| void RenderStyle::setScrollPaddingBottom(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapPort, scrollPadding.bottom(), WTFMove(length)); |
| } |
| |
| void RenderStyle::setScrollPaddingLeft(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapPort, scrollPadding.left(), WTFMove(length)); |
| } |
| |
| void RenderStyle::setScrollPaddingRight(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapPort, scrollPadding.right(), WTFMove(length)); |
| } |
| |
| void RenderStyle::setScrollSnapAlign(const ScrollSnapAlign& alignment) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapArea, alignment, alignment); |
| } |
| |
| void RenderStyle::setScrollSnapMarginTop(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapArea, scrollSnapMargin.top(), WTFMove(length)); |
| } |
| |
| void RenderStyle::setScrollSnapMarginBottom(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapArea, scrollSnapMargin.bottom(), WTFMove(length)); |
| } |
| |
| void RenderStyle::setScrollSnapMarginLeft(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapArea, scrollSnapMargin.left(), WTFMove(length)); |
| } |
| |
| void RenderStyle::setScrollSnapMarginRight(Length&& length) |
| { |
| SET_NESTED_VAR(m_rareNonInheritedData, scrollSnapArea, scrollSnapMargin.right(), WTFMove(length)); |
| } |
| |
| #endif |
| |
| bool RenderStyle::hasReferenceFilterOnly() const |
| { |
| if (!hasFilter()) |
| return false; |
| auto& filterOperations = m_rareNonInheritedData->filter->operations; |
| return filterOperations.size() == 1 && filterOperations.at(0)->type() == FilterOperation::REFERENCE; |
| } |
| |
| float RenderStyle::outlineWidth() const |
| { |
| if (m_backgroundData->outline.style() == BorderStyle::None) |
| return 0; |
| if (outlineStyleIsAuto() == OutlineIsAuto::On) |
| return std::max(m_backgroundData->outline.width(), RenderTheme::platformFocusRingWidth()); |
| return m_backgroundData->outline.width(); |
| } |
| |
| float RenderStyle::outlineOffset() const |
| { |
| if (m_backgroundData->outline.style() == BorderStyle::None) |
| return 0; |
| if (outlineStyleIsAuto() == OutlineIsAuto::On) |
| return (m_backgroundData->outline.offset() + RenderTheme::platformFocusRingOffset(outlineWidth())); |
| return m_backgroundData->outline.offset(); |
| } |
| |
| bool RenderStyle::shouldPlaceBlockDirectionScrollbarOnLeft() const |
| { |
| return !isLeftToRightDirection() && isHorizontalWritingMode(); |
| } |
| |
| Vector<PaintType, 3> RenderStyle::paintTypesForPaintOrder(PaintOrder order) |
| { |
| Vector<PaintType, 3> paintOrder; |
| switch (order) { |
| case PaintOrder::Normal: |
| FALLTHROUGH; |
| case PaintOrder::Fill: |
| paintOrder.append(PaintType::Fill); |
| paintOrder.append(PaintType::Stroke); |
| paintOrder.append(PaintType::Markers); |
| break; |
| case PaintOrder::FillMarkers: |
| paintOrder.append(PaintType::Fill); |
| paintOrder.append(PaintType::Markers); |
| paintOrder.append(PaintType::Stroke); |
| break; |
| case PaintOrder::Stroke: |
| paintOrder.append(PaintType::Stroke); |
| paintOrder.append(PaintType::Fill); |
| paintOrder.append(PaintType::Markers); |
| break; |
| case PaintOrder::StrokeMarkers: |
| paintOrder.append(PaintType::Stroke); |
| paintOrder.append(PaintType::Markers); |
| paintOrder.append(PaintType::Fill); |
| break; |
| case PaintOrder::Markers: |
| paintOrder.append(PaintType::Markers); |
| paintOrder.append(PaintType::Fill); |
| paintOrder.append(PaintType::Stroke); |
| break; |
| case PaintOrder::MarkersStroke: |
| paintOrder.append(PaintType::Markers); |
| paintOrder.append(PaintType::Stroke); |
| paintOrder.append(PaintType::Fill); |
| break; |
| }; |
| return paintOrder; |
| } |
| |
| float RenderStyle::computedStrokeWidth(const IntSize& viewportSize) const |
| { |
| // Use the stroke-width and stroke-color value combination only if stroke-color has been explicitly specified. |
| // Since there will be no visible stroke when stroke-color is not specified (transparent by default), we fall |
| // back to the legacy Webkit text stroke combination in that case. |
| if (!hasExplicitlySetStrokeColor()) |
| return textStrokeWidth(); |
| |
| const Length& length = strokeWidth(); |
| |
| if (length.isPercent()) { |
| // According to the spec, https://drafts.fxtf.org/paint/#stroke-width, the percentage is relative to the scaled viewport size. |
| // The scaled viewport size is the geometric mean of the viewport width and height. |
| ExceptionOr<float> result = length.value() * (viewportSize.width() + viewportSize.height()) / 200.0f; |
| if (result.hasException()) |
| return 0; |
| return result.releaseReturnValue(); |
| } |
| |
| if (length.isAuto() || !length.isSpecified()) |
| return 0; |
| |
| return floatValueForLength(length, viewportSize.width()); |
| } |
| |
| bool RenderStyle::hasPositiveStrokeWidth() const |
| { |
| if (!hasExplicitlySetStrokeWidth()) |
| return textStrokeWidth() > 0; |
| |
| return strokeWidth().isPositive(); |
| } |
| |
| Color RenderStyle::computedStrokeColor() const |
| { |
| CSSPropertyID propertyID = CSSPropertyStrokeColor; |
| if (!hasExplicitlySetStrokeColor()) |
| propertyID = CSSPropertyWebkitTextStrokeColor; |
| return visitedDependentColor(propertyID); |
| } |
| |
| } // namespace WebCore |