Skip to content

Commit

Permalink
LibWeb: Teach HTMLObjectElement to negotiate natural size with SVGs
Browse files Browse the repository at this point in the history
We do this by generalizing the code previously used for SVGSVGBox into
something that can be used for NavigableViewportContainer as well.
  • Loading branch information
awesomekling committed Nov 26, 2024
1 parent 673230d commit 47957d0
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 358 deletions.
6 changes: 6 additions & 0 deletions Libraries/LibWeb/HTML/Navigable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1931,6 +1931,12 @@ void finalize_a_cross_document_navigation(GC::Ref<Navigable> navigable, HistoryH

// 10. Apply the push/replace history step targetStep to traversable.
traversable->apply_the_push_or_replace_history_step(target_step, history_handling, TraversableNavigable::SynchronousNavigation::No);

// AD-HOC: If we're inside a navigable container, let's trigger a relayout in the container document.
// This allows size negotiation between the containing document and SVG documents to happen.
if (auto container = navigable->container()) {
container->document().set_needs_layout();
}
}

// https://html.spec.whatwg.org/multipage/browsing-the-web.html#url-and-history-update-steps
Expand Down
11 changes: 11 additions & 0 deletions Libraries/LibWeb/Layout/NavigableContainerViewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Layout/NavigableContainerViewport.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Painting/NavigableContainerViewportPaintable.h>
#include <LibWeb/SVG/SVGSVGElement.h>

namespace Web::Layout {

Expand All @@ -22,6 +24,15 @@ NavigableContainerViewport::~NavigableContainerViewport() = default;

void NavigableContainerViewport::prepare_for_replaced_layout()
{
if (auto const* content_document = dom_node().content_document_without_origin_check()) {
if (auto const* root_element = content_document->document_element(); root_element && root_element->is_svg_svg_element()) {
auto natural_metrics = SVG::SVGSVGElement::negotiate_natural_metrics(static_cast<SVG::SVGSVGElement const&>(*root_element));
set_natural_width(natural_metrics.width);
set_natural_height(natural_metrics.height);
set_natural_aspect_ratio(natural_metrics.aspect_ratio);
return;
}
}
// FIXME: Do proper error checking, etc.
set_natural_width(dom_node().get_attribute_value(HTML::AttributeNames::width).to_number<int>().value_or(300));
set_natural_height(dom_node().get_attribute_value(HTML::AttributeNames::height).to_number<int>().value_or(150));
Expand Down
56 changes: 4 additions & 52 deletions Libraries/LibWeb/Layout/SVGSVGBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,58 +27,10 @@ GC::Ptr<Painting::Paintable> SVGSVGBox::create_paintable() const

void SVGSVGBox::prepare_for_replaced_layout()
{
// https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS

// The intrinsic dimensions must also be determined from the width and height sizing properties.
// If either width or height are not specified, the used value is the initial value 'auto'.
// 'auto' and percentage lengths must not be used to determine an intrinsic width or intrinsic height.

Optional<CSSPixels> natural_width;
if (auto width = dom_node().width_style_value_from_attribute(); width && width->is_length() && width->as_length().length().is_absolute()) {
natural_width = width->as_length().length().absolute_length_to_px();
}

Optional<CSSPixels> natural_height;
if (auto height = dom_node().height_style_value_from_attribute(); height && height->is_length() && height->as_length().length().is_absolute()) {
natural_height = height->as_length().length().absolute_length_to_px();
}

// The intrinsic aspect ratio must be calculated using the following algorithm. If the algorithm returns null, then there is no intrinsic aspect ratio.
auto natural_aspect_ratio = [&]() -> Optional<CSSPixelFraction> {
// 1. If the width and height sizing properties on the ‘svg’ element are both absolute values:
if (natural_width.has_value() && natural_height.has_value()) {
if (natural_width != 0 && natural_height != 0) {
// 1. return width / height
return *natural_width / *natural_height;
}
return {};
}

// FIXME: 2. If an SVG View is active:
// FIXME: 1. let viewbox be the viewbox defined by the active SVG View
// FIXME: 2. return viewbox.width / viewbox.height

// 3. If the ‘viewBox’ on the ‘svg’ element is correctly specified:
if (dom_node().view_box().has_value()) {
// 1. let viewbox be the viewbox defined by the ‘viewBox’ attribute on the ‘svg’ element
auto const& viewbox = dom_node().view_box().value();

// 2. return viewbox.width / viewbox.height
auto viewbox_width = CSSPixels::nearest_value_for(viewbox.width);
auto viewbox_height = CSSPixels::nearest_value_for(viewbox.height);
if (viewbox_width != 0 && viewbox_height != 0)
return viewbox_width / viewbox_height;

return {};
}

// 4. return null
return {};
}();

set_natural_width(natural_width);
set_natural_height(natural_height);
set_natural_aspect_ratio(natural_aspect_ratio);
auto natural_metrics = SVG::SVGSVGElement::negotiate_natural_metrics(dom_node());
set_natural_width(natural_metrics.width);
set_natural_height(natural_metrics.height);
set_natural_aspect_ratio(natural_metrics.aspect_ratio);
}

}
54 changes: 54 additions & 0 deletions Libraries/LibWeb/SVG/SVGSVGElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,58 @@ GC::Ref<SVGTransform> SVGSVGElement::create_svg_transform() const
return SVGTransform::create(realm());
}

SVGSVGElement::NaturalMetrics SVGSVGElement::negotiate_natural_metrics(SVG::SVGSVGElement const& svg_root)
{
// https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS

NaturalMetrics natural_metrics;

// The intrinsic dimensions must also be determined from the width and height sizing properties.
// If either width or height are not specified, the used value is the initial value 'auto'.
// 'auto' and percentage lengths must not be used to determine an intrinsic width or intrinsic height.

if (auto width = svg_root.width_style_value_from_attribute(); width && width->is_length() && width->as_length().length().is_absolute()) {
natural_metrics.width = width->as_length().length().absolute_length_to_px();
}

if (auto height = svg_root.height_style_value_from_attribute(); height && height->is_length() && height->as_length().length().is_absolute()) {
natural_metrics.height = height->as_length().length().absolute_length_to_px();
}

// The intrinsic aspect ratio must be calculated using the following algorithm. If the algorithm returns null, then there is no intrinsic aspect ratio.
natural_metrics.aspect_ratio = [&]() -> Optional<CSSPixelFraction> {
// 1. If the width and height sizing properties on the ‘svg’ element are both absolute values:
if (natural_metrics.width.has_value() && natural_metrics.height.has_value()) {
if (natural_metrics.width != 0 && natural_metrics.height != 0) {
// 1. return width / height
return *natural_metrics.width / *natural_metrics.height;
}
return {};
}

// FIXME: 2. If an SVG View is active:
// FIXME: 1. let viewbox be the viewbox defined by the active SVG View
// FIXME: 2. return viewbox.width / viewbox.height

// 3. If the ‘viewBox’ on the ‘svg’ element is correctly specified:
if (svg_root.view_box().has_value()) {
// 1. let viewbox be the viewbox defined by the ‘viewBox’ attribute on the ‘svg’ element
auto const& viewbox = svg_root.view_box().value();

// 2. return viewbox.width / viewbox.height
auto viewbox_width = CSSPixels::nearest_value_for(viewbox.width);
auto viewbox_height = CSSPixels::nearest_value_for(viewbox.height);
if (viewbox_width != 0 && viewbox_height != 0)
return viewbox_width / viewbox_height;

return {};
}

// 4. return null
return {};
}();

return natural_metrics;
}

}
8 changes: 8 additions & 0 deletions Libraries/LibWeb/SVG/SVGSVGElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ class SVGSVGElement final : public SVGGraphicsElement
[[nodiscard]] RefPtr<CSS::CSSStyleValue> width_style_value_from_attribute() const;
[[nodiscard]] RefPtr<CSS::CSSStyleValue> height_style_value_from_attribute() const;

struct NaturalMetrics {
Optional<CSSPixels> width;
Optional<CSSPixels> height;
Optional<CSSPixelFraction> aspect_ratio;
};

static NaturalMetrics negotiate_natural_metrics(SVGSVGElement const&);

private:
SVGSVGElement(DOM::Document&, DOM::QualifiedName);

Expand Down
Loading

0 comments on commit 47957d0

Please sign in to comment.