blob: 92cdabf56df5a6d1d30bdc897bf9163a3d9662ca [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll ([email protected])
* Copyright (C) 2004-2005 Allan Sandfeld Jensen ([email protected])
* Copyright (C) 2006, 2007 Nicholas Shanks ([email protected])
* Copyright (C) 2005-2022 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alexey Proskuryakov <[email protected]>
* Copyright (C) 2007, 2008 Eric Seidel <[email protected]>
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
* Copyright (C) 2012, 2013 Google Inc. All rights reserved.
* Copyright (C) 2014 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "StyleBuilderState.h"
#include "StyleBuilderStateInlines.h"
#include "CSSCalcRandomCachingKey.h"
#include "CSSCanvasValue.h"
#include "CSSColorValue.h"
#include "CSSCrossfadeValue.h"
#include "CSSCursorImageValue.h"
#include "CSSFilterImageValue.h"
#include "CSSFontSelector.h"
#include "CSSFunctionValue.h"
#include "CSSGradientValue.h"
#include "CSSImageSetValue.h"
#include "CSSImageValue.h"
#include "CSSNamedImageValue.h"
#include "CSSPaintImageValue.h"
#include "DocumentInlines.h"
#include "DocumentView.h"
#include "ElementInlines.h"
#include "ElementTraversal.h"
#include "FontCache.h"
#include "FrameDestructionObserverInlines.h"
#include "HTMLElement.h"
#include "LocalFrame.h"
#include "RenderStyle+SettersInlines.h"
#include "RenderTheme.h"
#include "SVGElementTypeHelpers.h"
#include "SVGSVGElement.h"
#include "Settings.h"
#include "StyleBuilder.h"
#include "StyleCachedImage.h"
#include "StyleCanvasImage.h"
#include "StyleColor.h"
#include "StyleCrossfadeImage.h"
#include "StyleCursorImage.h"
#include "StyleFilterImage.h"
#include "StyleFontSizeFunctions.h"
#include "StyleGeneratedImage.h"
#include "StyleGradientImage.h"
#include "StyleImageSet.h"
#include "StyleNamedImage.h"
#include "StylePaintImage.h"
#include "StylePrimitiveNumericTypes+Conversions.h"
#include "StylePrimitiveNumericTypes+Evaluation.h"
namespace WebCore {
namespace Style {
WTF_MAKE_TZONE_ALLOCATED_IMPL(BuilderState);
BuilderState::BuilderState(RenderStyle& style)
: m_style(style)
{
}
BuilderState::BuilderState(RenderStyle& style, BuilderContext&& context)
: m_style(style)
, m_context(WTF::move(context))
, m_cssToLengthConversionData(style, *this)
{
}
float BuilderState::zoomWithTextZoomFactor()
{
if (RefPtr frame = document().frame()) {
float textZoomFactor = style().textZoom() != TextZoom::Reset ? frame->textZoomFactor() : 1.0f;
float usedZoom = evaluationTimeZoomEnabled(*this) ? 1.0f : style().usedZoom();
return usedZoom * textZoomFactor;
}
return cssToLengthConversionData().zoom();
}
// SVG handles zooming in a different way compared to CSS. The whole document is scaled instead
// of each individual length value in the render style / tree. CSSPrimitiveValue::resolveAsLength*()
// multiplies each resolved length with the zoom multiplier - so for SVG we need to disable that.
// Though all CSS values that can be applied to outermost <svg> elements (width/height/border/padding...)
// need to respect the scaling. RenderBox (the parent class of LegacyRenderSVGRoot) grabs values like
// width/height/border/padding/... from the RenderStyle -> for SVG these values would never scale,
// if we'd pass a 1.0 zoom factor everywhere. So we only pass a zoom factor of 1.0 for specific
// properties that are NOT allowed to scale within a zoomed SVG document (letter/word-spacing/font-size).
bool BuilderState::useSVGZoomRules() const
{
return is<SVGElement>(element());
}
bool BuilderState::useSVGZoomRulesForLength() const
{
return is<SVGElement>(element()) && !(is<SVGSVGElement>(*element()) && element()->parentNode());
}
RefPtr<StyleImage> BuilderState::createStyleImage(const CSSValue& value) const
{
if (auto* imageValue = dynamicDowncast<CSSImageValue>(value))
return imageValue->createStyleImage(*this);
if (auto* imageSetValue = dynamicDowncast<CSSImageSetValue>(value))
return imageSetValue->createStyleImage(*this);
if (auto* imageValue = dynamicDowncast<CSSCursorImageValue>(value))
return imageValue->createStyleImage(*this);
if (auto* imageValue = dynamicDowncast<CSSNamedImageValue>(value))
return imageValue->createStyleImage(*this);
if (auto* cssCanvasValue = dynamicDowncast<CSSCanvasValue>(value))
return cssCanvasValue->createStyleImage(*this);
if (auto* crossfadeValue = dynamicDowncast<CSSCrossfadeValue>(value))
return crossfadeValue->createStyleImage(*this);
if (auto* filterImageValue = dynamicDowncast<CSSFilterImageValue>(value))
return filterImageValue->createStyleImage(*this);
if (auto* gradientValue = dynamicDowncast<CSSGradientValue>(value))
return gradientValue->createStyleImage(*this);
if (auto* paintImageValue = dynamicDowncast<CSSPaintImageValue>(value))
return paintImageValue->createStyleImage(*this);
return nullptr;
}
void BuilderState::registerContentAttribute(const AtomString& attributeLocalName)
{
if (style().pseudoElementType() == PseudoElementType::Before || style().pseudoElementType() == PseudoElementType::After)
m_registeredContentAttributes.append(attributeLocalName);
}
void BuilderState::adjustStyleForInterCharacterRuby()
{
if (!m_style.isInterCharacterRubyPosition() || !element() || !element()->hasTagName(HTMLNames::rtTag))
return;
m_style.setTextAlign(TextAlign::Center);
if (!m_style.writingMode().isVerticalTypographic())
m_style.setWritingMode(StyleWritingMode::VerticalLr);
}
void BuilderState::updateFont()
{
Ref fontSelector = const_cast<Document&>(document()).fontSelector();
auto needsUpdate = [&] {
return m_fontDirty || !m_style.fontCascade().fonts();
};
if (!needsUpdate())
return;
#if ENABLE(TEXT_AUTOSIZING)
updateFontForTextSizeAdjust();
#endif
updateFontForGenericFamilyChange();
updateFontForZoomChange();
updateFontForOrientationChange();
updateFontForSizeChange();
m_style.fontCascade().update(fontSelector.ptr());
m_fontDirty = false;
}
#if ENABLE(TEXT_AUTOSIZING)
void BuilderState::updateFontForTextSizeAdjust()
{
if (m_style.textSizeAdjust().isAuto()
|| !document().settings().textAutosizingEnabled()
|| (document().settings().textAutosizingUsesIdempotentMode()
&& !m_style.textSizeAdjust().isNone()
&& !document().settings().idempotentModeAutosizingOnlyHonorsPercentages()))
return;
auto newFontDescription = m_style.fontDescription();
if (!m_style.textSizeAdjust().isNone())
newFontDescription.setComputedSize(newFontDescription.specifiedSize() * m_style.textSizeAdjust().multiplier());
else
newFontDescription.setComputedSize(newFontDescription.specifiedSize());
m_style.setFontDescriptionWithoutUpdate(WTF::move(newFontDescription));
}
#endif
void BuilderState::updateFontForZoomChange()
{
if (m_style.usedZoom() == parentStyle().usedZoom() && m_style.textZoom() == parentStyle().textZoom())
return;
setFontDescriptionFontSize(m_style.fontDescription().specifiedSize());
}
void BuilderState::updateFontForGenericFamilyChange()
{
const auto& childFont = m_style.fontDescription();
if (childFont.isAbsoluteSize())
return;
const auto& parentFont = parentStyle().fontDescription();
if (childFont.useFixedDefaultSize() == parentFont.useFixedDefaultSize())
return;
// We know the parent is monospace or the child is monospace, and that font
// size was unspecified. We want to scale our font size as appropriate.
// If the font uses a keyword size, then we refetch from the table rather than
// multiplying by our scale factor.
float size = [&] {
if (CSSValueID sizeIdentifier = childFont.keywordSizeAsIdentifier())
return Style::fontSizeForKeyword(sizeIdentifier, childFont.useFixedDefaultSize(), document());
auto fixedSize = document().settings().defaultFixedFontSize();
auto defaultSize = document().settings().defaultFontSize();
float fixedScaleFactor = (fixedSize && defaultSize) ? static_cast<float>(fixedSize) / defaultSize : 1;
return parentFont.useFixedDefaultSize() ? childFont.specifiedSize() / fixedScaleFactor : childFont.specifiedSize() * fixedScaleFactor;
}();
auto newFontDescription = childFont;
setFontSize(newFontDescription, size);
m_style.setFontDescriptionWithoutUpdate(WTF::move(newFontDescription));
}
void BuilderState::updateFontForOrientationChange()
{
auto [fontOrientation, glyphOrientation] = m_style.fontAndGlyphOrientation();
const auto& fontDescription = m_style.fontDescription();
if (fontDescription.orientation() == fontOrientation && fontDescription.nonCJKGlyphOrientation() == glyphOrientation)
return;
auto newFontDescription = fontDescription;
newFontDescription.setNonCJKGlyphOrientation(glyphOrientation);
newFontDescription.setOrientation(fontOrientation);
m_style.setFontDescriptionWithoutUpdate(WTF::move(newFontDescription));
}
void BuilderState::updateFontForSizeChange()
{
m_style.synchronizeLetterSpacingWithFontCascadeWithoutUpdate();
m_style.synchronizeWordSpacingWithFontCascadeWithoutUpdate();
}
void BuilderState::setFontSize(FontCascadeDescription& fontDescription, float size)
{
fontDescription.setSpecifiedSize(size);
auto computedFontSize = Style::computedFontSizeFromSpecifiedSize(size, fontDescription.isAbsoluteSize(), useSVGZoomRules(), style(), document());
fontDescription.setComputedSize(computedFontSize.size, computedFontSize.usedZoomFactor);
}
CSSPropertyID BuilderState::cssPropertyID() const
{
return m_currentProperty ? m_currentProperty->id : CSSPropertyInvalid;
}
bool BuilderState::isCurrentPropertyInvalidAtComputedValueTime() const
{
return m_invalidAtComputedValueTimeProperties.get(cssPropertyID());
}
void BuilderState::setCurrentPropertyInvalidAtComputedValueTime()
{
m_invalidAtComputedValueTimeProperties.set(cssPropertyID());
}
void BuilderState::setUsesViewportUnits()
{
m_style.setUsesViewportUnits();
}
void BuilderState::setUsesContainerUnits()
{
m_style.setUsesContainerUnits();
}
double BuilderState::lookupCSSRandomBaseValue(const CSSCalc::RandomCachingKey& key, std::optional<CSS::Keyword::ElementShared> elementShared) const
{
if (!elementShared)
return element()->lookupCSSRandomBaseValue(style().pseudoElementIdentifier(), key);
return document().lookupCSSRandomBaseValue(key);
}
// MARK: - Tree Counting Functions
unsigned BuilderState::siblingCount()
{
// https://drafts.csswg.org/css-values-5/#funcdef-sibling-count
ASSERT(element());
RefPtr parent = element()->parentElement();
if (!parent)
return 1;
m_style.setUsesTreeCountingFunctions();
parent->setChildrenAffectedByBackwardPositionalRules();
parent->setChildrenAffectedByForwardPositionalRules();
unsigned count = 1;
for (const auto* sibling = ElementTraversal::previousSibling(*element()); sibling; sibling = ElementTraversal::previousSibling(*sibling))
++count;
for (const auto* sibling = ElementTraversal::nextSibling(*element()); sibling; sibling = ElementTraversal::nextSibling(*sibling))
++count;
return count;
}
unsigned BuilderState::siblingIndex()
{
// https://drafts.csswg.org/css-values-5/#funcdef-sibling-index
ASSERT(element());
RefPtr parent = element()->parentElement();
if (!parent)
return 1;
m_style.setUsesTreeCountingFunctions();
parent->setChildrenAffectedByBackwardPositionalRules();
parent->setChildrenAffectedByForwardPositionalRules();
unsigned count = 1;
for (const auto* sibling = ElementTraversal::previousSibling(*element()); sibling; sibling = ElementTraversal::previousSibling(*sibling))
++count;
return count;
}
void BuilderState::disableNativeAppearanceIfNeeded(CSSPropertyID propertyID, PropertyCascade::Origin origin)
{
auto shouldDisable = [&] {
if (origin != PropertyCascade::Origin::Author)
return false;
if (!CSSProperty::disablesNativeAppearance(propertyID))
return false;
if (!applyPropertyToRegularStyle())
return false;
return element()->isDevolvableWidget() || RenderTheme::hasAppearanceForElementTypeFromUAStyle(*element());
};
if (shouldDisable())
style().setNativeAppearanceDisabled(true);
}
} // namespace Style
} // namespace WebCore