blob: a1638a79133f9ff59efc3992fadc2e5e22a7f105 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
#include "third_party/blink/renderer/core/css/anchor_evaluator.h"
#include "third_party/blink/renderer/core/css/container_query.h"
#include "third_party/blink/renderer/core/css/container_query_evaluator.h"
#include "third_party/blink/renderer/core/css/css_resolution_units.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/font_size_style.h"
namespace blink {
namespace {
std::optional<double> FindSizeForContainerAxis(
PhysicalAxes requested_axis,
Element* context_element,
const ScopedCSSName* container_name = nullptr) {
DCHECK(requested_axis == kPhysicalAxesHorizontal ||
requested_axis == kPhysicalAxesVertical);
ContainerSelector selector;
const TreeScope* tree_scope = nullptr;
if (container_name) {
selector = ContainerSelector(container_name->GetName(), requested_axis,
kLogicalAxesNone);
tree_scope = container_name->GetTreeScope();
} else {
selector = ContainerSelector(requested_axis);
tree_scope = context_element ? &context_element->GetTreeScope() : nullptr;
}
for (Element* container = ContainerQueryEvaluator::FindContainer(
context_element, selector, tree_scope);
container;
container = ContainerQueryEvaluator::FindContainer(
container->ParentOrShadowHostElement(), selector, tree_scope)) {
ContainerQueryEvaluator& evaluator =
container->EnsureContainerQueryEvaluator();
evaluator.SetReferencedByUnit();
std::optional<double> size = requested_axis == kPhysicalAxesHorizontal
? evaluator.Width()
: evaluator.Height();
if (!size.has_value()) {
continue;
}
return size;
}
return std::nullopt;
}
} // namespace
float CSSToLengthConversionData::FontSizes::Ex(float zoom) const {
DCHECK(font_);
const SimpleFontData* font_data = font_->PrimaryFont();
if (!font_data || !font_data->GetFontMetrics().HasXHeight()) {
return em_ / 2.0f;
}
// Font-metrics-based units are pre-zoomed with a factor of `font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return font_data->GetFontMetrics().XHeight() / font_zoom_ * zoom;
}
float CSSToLengthConversionData::FontSizes::Rex(float zoom) const {
DCHECK(root_font_);
const SimpleFontData* font_data = root_font_->PrimaryFont();
if (!font_data || !font_data->GetFontMetrics().HasXHeight()) {
return rem_ / 2.0f;
}
// Font-metrics-based units are pre-zoomed with a factor of `root_font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return font_data->GetFontMetrics().XHeight() / root_font_zoom_ * zoom;
}
float CSSToLengthConversionData::FontSizes::Ch(float zoom) const {
DCHECK(font_);
const SimpleFontData* font_data = font_->PrimaryFont();
if (!font_data) {
return 0;
}
// Font-metrics-based units are pre-zoomed with a factor of `font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return font_data->GetFontMetrics().ZeroWidth() / font_zoom_ * zoom;
}
float CSSToLengthConversionData::FontSizes::Rch(float zoom) const {
DCHECK(root_font_);
const SimpleFontData* font_data = root_font_->PrimaryFont();
if (!font_data) {
return 0;
}
// Font-metrics-based units are pre-zoomed with a factor of `root_font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return font_data->GetFontMetrics().ZeroWidth() / root_font_zoom_ * zoom;
}
float CSSToLengthConversionData::FontSizes::Ic(float zoom) const {
DCHECK(font_);
const SimpleFontData* font_data = font_->PrimaryFont();
std::optional<float> full_width;
if (font_data) {
full_width = font_data->IdeographicInlineSize();
}
if (!full_width.has_value()) {
return Em(zoom);
}
// Font-metrics-based units are pre-zoomed with a factor of `font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return full_width.value() / font_zoom_ * zoom;
}
float CSSToLengthConversionData::FontSizes::Ric(float zoom) const {
DCHECK(root_font_);
const SimpleFontData* font_data = root_font_->PrimaryFont();
std::optional<float> full_width;
if (font_data) {
full_width = font_data->IdeographicInlineSize();
}
if (!full_width.has_value()) {
return Rem(zoom);
}
// Font-metrics-based units are pre-zoomed with a factor of `font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return full_width.value() / root_font_zoom_ * zoom;
}
float CSSToLengthConversionData::FontSizes::Cap(float zoom) const {
CHECK(font_);
const SimpleFontData* font_data = font_->PrimaryFont();
if (!font_data) {
return 0.0f;
}
// Font-metrics-based units are pre-zoomed with a factor of `font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return font_data->GetFontMetrics().CapHeight() / font_zoom_ * zoom;
}
float CSSToLengthConversionData::FontSizes::Rcap(float zoom) const {
CHECK(root_font_);
const SimpleFontData* font_data = root_font_->PrimaryFont();
if (!font_data) {
return 0.0f;
}
// Font-metrics-based units are pre-zoomed with a factor of `root_font_zoom_`,
// we need to unzoom using that factor before applying the target zoom.
return font_data->GetFontMetrics().CapHeight() / root_font_zoom_ * zoom;
}
CSSToLengthConversionData::LineHeightSize::LineHeightSize(
const FontSizeStyle& style,
const ComputedStyle* root_style)
: LineHeightSize(
style.SpecifiedLineHeight(),
root_style ? root_style->SpecifiedLineHeight()
: style.SpecifiedLineHeight(),
&style.GetFont(),
root_style ? &root_style->GetFont() : &style.GetFont(),
style.EffectiveZoom(),
root_style ? root_style->EffectiveZoom() : style.EffectiveZoom()) {}
float CSSToLengthConversionData::LineHeightSize::Lh(float zoom) const {
if (!font_) {
return 0;
}
// Like font-metrics-based units, lh is also based on pre-zoomed font metrics.
// We therefore need to unzoom using the font zoom before applying the target
// zoom.
return ComputedStyle::ComputedLineHeight(line_height_, *font_) / font_zoom_ *
zoom;
}
float CSSToLengthConversionData::LineHeightSize::Rlh(float zoom) const {
if (!root_font_) {
return 0;
}
// Like font-metrics-based units, rlh is also based on pre-zoomed font
// metrics. We therefore need to unzoom using the font zoom before applying
// the target zoom.
return ComputedStyle::ComputedLineHeight(root_line_height_, *root_font_) /
root_font_zoom_ * zoom;
}
CSSToLengthConversionData::ViewportSize::ViewportSize(
const LayoutView* layout_view) {
if (layout_view) {
gfx::SizeF large_size = layout_view->LargeViewportSizeForViewportUnits();
large_width_ = large_size.width();
large_height_ = large_size.height();
gfx::SizeF small_size = layout_view->SmallViewportSizeForViewportUnits();
small_width_ = small_size.width();
small_height_ = small_size.height();
gfx::SizeF dynamic_size =
layout_view->DynamicViewportSizeForViewportUnits();
dynamic_width_ = dynamic_size.width();
dynamic_height_ = dynamic_size.height();
}
}
CSSToLengthConversionData::ContainerSizes
CSSToLengthConversionData::ContainerSizes::PreCachedCopy() const {
ContainerSizes copy = *this;
copy.Width();
copy.Height();
DCHECK(!copy.context_element_ || copy.cached_width_.has_value());
DCHECK(!copy.context_element_ || copy.cached_height_.has_value());
// We don't need to keep the container since we eagerly fetched both values.
copy.context_element_ = nullptr;
return copy;
}
void CSSToLengthConversionData::ContainerSizes::Trace(Visitor* visitor) const {
visitor->Trace(context_element_);
}
bool CSSToLengthConversionData::ContainerSizes::SizesEqual(
const ContainerSizes& other) const {
return (Width() == other.Width()) && (Height() == other.Height());
}
std::optional<double> CSSToLengthConversionData::ContainerSizes::Width() const {
CacheSizeIfNeeded(PhysicalAxes(kPhysicalAxesHorizontal), cached_width_);
return cached_width_;
}
std::optional<double> CSSToLengthConversionData::ContainerSizes::Height()
const {
CacheSizeIfNeeded(PhysicalAxes(kPhysicalAxesVertical), cached_height_);
return cached_height_;
}
std::optional<double> CSSToLengthConversionData::ContainerSizes::Width(
const ScopedCSSName& container_name) const {
return FindNamedSize(container_name, PhysicalAxes(kPhysicalAxesHorizontal));
}
std::optional<double> CSSToLengthConversionData::ContainerSizes::Height(
const ScopedCSSName& container_name) const {
return FindNamedSize(container_name, PhysicalAxes(kPhysicalAxesVertical));
}
void CSSToLengthConversionData::ContainerSizes::CacheSizeIfNeeded(
PhysicalAxes requested_axis,
std::optional<double>& cache) const {
if ((cached_physical_axes_ & requested_axis) == requested_axis) {
return;
}
cached_physical_axes_ |= requested_axis;
cache = FindSizeForContainerAxis(requested_axis, context_element_);
}
std::optional<double> CSSToLengthConversionData::ContainerSizes::FindNamedSize(
const ScopedCSSName& container_name,
PhysicalAxes requested_axis) const {
return FindSizeForContainerAxis(requested_axis, context_element_,
&container_name);
}
CSSToLengthConversionData::AnchorData::AnchorData(
AnchorEvaluator* evaluator,
const ScopedCSSName* position_anchor,
const std::optional<InsetAreaOffsets>& inset_area_offsets)
: evaluator_(evaluator),
position_anchor_(position_anchor),
inset_area_offsets_(inset_area_offsets) {}
CSSToLengthConversionData::CSSToLengthConversionData(
WritingMode writing_mode,
const FontSizes& font_sizes,
const LineHeightSize& line_height_size,
const ViewportSize& viewport_size,
const ContainerSizes& container_sizes,
const AnchorData& anchor_data,
float zoom,
Flags& flags)
: CSSLengthResolver(
ClampTo<float>(zoom, std::numeric_limits<float>::denorm_min())),
writing_mode_(writing_mode),
font_sizes_(font_sizes),
line_height_size_(line_height_size),
viewport_size_(viewport_size),
container_sizes_(container_sizes),
anchor_data_(anchor_data),
flags_(&flags) {}
float CSSToLengthConversionData::EmFontSize(float zoom) const {
SetFlag(Flag::kEm);
return font_sizes_.Em(zoom);
}
float CSSToLengthConversionData::RemFontSize(float zoom) const {
SetFlag(Flag::kRootFontRelative);
return font_sizes_.Rem(zoom);
}
float CSSToLengthConversionData::ExFontSize(float zoom) const {
SetFlag(Flag::kGlyphRelative);
return font_sizes_.Ex(zoom);
}
float CSSToLengthConversionData::RexFontSize(float zoom) const {
// Need to mark the current element's ComputedStyle as having glyph relative
// styles, even if it is not relative to the current element's font because
// the invalidation that happens when a web font finishes loading for the root
// element does not necessarily cause a style difference for the root element,
// hence will not cause an invalidation of root font relative dependent
// styles. See also Node::MarkSubtreeNeedsStyleRecalcForFontUpdates().
SetFlag(Flag::kGlyphRelative);
SetFlag(Flag::kRootFontRelative);
return font_sizes_.Rex(zoom);
}
float CSSToLengthConversionData::ChFontSize(float zoom) const {
SetFlag(Flag::kGlyphRelative);
return font_sizes_.Ch(zoom);
}
float CSSToLengthConversionData::RchFontSize(float zoom) const {
// Need to mark the current element's ComputedStyle as having glyph relative
// styles, even if it is not relative to the current element's font because
// the invalidation that happens when a web font finishes loading for the root
// element does not necessarily cause a style difference for the root element,
// hence will not cause an invalidation of root font relative dependent
// styles. See also Node::MarkSubtreeNeedsStyleRecalcForFontUpdates().
SetFlag(Flag::kGlyphRelative);
SetFlag(Flag::kRootFontRelative);
return font_sizes_.Rch(zoom);
}
float CSSToLengthConversionData::IcFontSize(float zoom) const {
SetFlag(Flag::kGlyphRelative);
return font_sizes_.Ic(zoom);
}
float CSSToLengthConversionData::RicFontSize(float zoom) const {
// Need to mark the current element's ComputedStyle as having glyph relative
// styles, even if it is not relative to the current element's font because
// the invalidation that happens when a web font finishes loading for the root
// element does not necessarily cause a style difference for the root element,
// hence will not cause an invalidation of root font relative dependent
// styles. See also Node::MarkSubtreeNeedsStyleRecalcForFontUpdates().
SetFlag(Flag::kGlyphRelative);
SetFlag(Flag::kRootFontRelative);
return font_sizes_.Ric(zoom);
}
float CSSToLengthConversionData::LineHeight(float zoom) const {
SetFlag(Flag::kGlyphRelative);
SetFlag(Flag::kLineHeightRelative);
return line_height_size_.Lh(zoom);
}
float CSSToLengthConversionData::RootLineHeight(float zoom) const {
// Need to mark the current element's ComputedStyle as having glyph relative
// styles, even if it is not relative to the current element's font because
// the invalidation that happens when a web font finishes loading for the root
// element does not necessarily cause a style difference for the root element,
// hence will not cause an invalidation of root font relative dependent
// styles. See also Node::MarkSubtreeNeedsStyleRecalcForFontUpdates().
SetFlag(Flag::kGlyphRelative);
SetFlag(Flag::kRootFontRelative);
SetFlag(Flag::kLineHeightRelative);
return line_height_size_.Rlh(zoom);
}
float CSSToLengthConversionData::CapFontSize(float zoom) const {
// Need to mark the current element's ComputedStyle as having glyph relative
// styles, even if it is not relative to the current element's font because
// the invalidation that happens when a web font finishes loading for the root
// element does not necessarily cause a style difference for the root element,
// hence will not cause an invalidation of root font relative dependent
// styles. See also Node::MarkSubtreeNeedsStyleRecalcForFontUpdates().
SetFlag(Flag::kGlyphRelative);
return font_sizes_.Cap(zoom);
}
float CSSToLengthConversionData::RcapFontSize(float zoom) const {
// Need to mark the current element's ComputedStyle as having glyph relative
// styles, even if it is not relative to the current element's font because
// the invalidation that happens when a web font finishes loading for the root
// element does not necessarily cause a style difference for the root element,
// hence will not cause an invalidation of root font relative dependent
// styles. See also Node::MarkSubtreeNeedsStyleRecalcForFontUpdates().
SetFlag(Flag::kGlyphRelative);
SetFlag(Flag::kRootFontRelative);
return font_sizes_.Rcap(zoom);
}
double CSSToLengthConversionData::ViewportWidth() const {
SetFlag(Flag::kStaticViewport);
return viewport_size_.LargeWidth();
}
double CSSToLengthConversionData::ViewportHeight() const {
SetFlag(Flag::kStaticViewport);
return viewport_size_.LargeHeight();
}
double CSSToLengthConversionData::SmallViewportWidth() const {
SetFlag(Flag::kStaticViewport);
return viewport_size_.SmallWidth();
}
double CSSToLengthConversionData::SmallViewportHeight() const {
SetFlag(Flag::kStaticViewport);
return viewport_size_.SmallHeight();
}
double CSSToLengthConversionData::LargeViewportWidth() const {
SetFlag(Flag::kStaticViewport);
return viewport_size_.LargeWidth();
}
double CSSToLengthConversionData::LargeViewportHeight() const {
SetFlag(Flag::kStaticViewport);
return viewport_size_.LargeHeight();
}
double CSSToLengthConversionData::DynamicViewportWidth() const {
SetFlag(Flag::kDynamicViewport);
return viewport_size_.DynamicWidth();
}
double CSSToLengthConversionData::DynamicViewportHeight() const {
SetFlag(Flag::kDynamicViewport);
return viewport_size_.DynamicHeight();
}
double CSSToLengthConversionData::ContainerWidth() const {
SetFlag(Flag::kContainerRelative);
return container_sizes_.Width().value_or(SmallViewportWidth());
}
double CSSToLengthConversionData::ContainerHeight() const {
SetFlag(Flag::kContainerRelative);
return container_sizes_.Height().value_or(SmallViewportHeight());
}
double CSSToLengthConversionData::ContainerWidth(
const ScopedCSSName& container_name) const {
SetFlag(Flag::kContainerRelative);
return container_sizes_.Width(container_name).value_or(SmallViewportWidth());
}
double CSSToLengthConversionData::ContainerHeight(
const ScopedCSSName& container_name) const {
SetFlag(Flag::kContainerRelative);
return container_sizes_.Height(container_name)
.value_or(SmallViewportHeight());
}
WritingMode CSSToLengthConversionData::GetWritingMode() const {
// This method is called by CSSLengthResolver only when resolving
// logical direction relative units, so we can set the flag
// indicating the presence of such units.
SetFlag(Flag::kLogicalDirectionRelative);
return writing_mode_;
}
CSSToLengthConversionData::ContainerSizes
CSSToLengthConversionData::PreCachedContainerSizesCopy() const {
SetFlag(Flag::kContainerRelative);
return container_sizes_.PreCachedCopy();
}
void CSSToLengthConversionData::ReferenceTreeScope() const {
SetFlag(Flag::kTreeScopedReference);
}
void CSSToLengthConversionData::ReferenceAnchor() const {
SetFlag(Flag::kAnchorRelative);
}
} // namespace blink