mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:07:35 +00:00
LibWeb: Follow the spec more precisely in Element::getClientRects()
Now, `Element::getBoundingClientRect()` implementation depends on `Element::getClientRects()`, as defined in the specification.
This commit is contained in:
parent
16f1962f10
commit
768b8415f2
3 changed files with 78 additions and 31 deletions
2
Tests/LibWeb/Text/expected/element-get-client-rects.txt
Normal file
2
Tests/LibWeb/Text/expected/element-get-client-rects.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
inline {"0":{"x":8,"y":500,"width":784,"height":150,"top":500,"right":792,"bottom":650,"left":8}}
|
||||||
|
{"0":{"x":8,"y":650,"width":41.296875,"height":17,"top":650,"right":49.296875,"bottom":667,"left":8}}
|
22
Tests/LibWeb/Text/input/element-get-client-rects.html
Normal file
22
Tests/LibWeb/Text/input/element-get-client-rects.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<style type="text/css">
|
||||||
|
#box {
|
||||||
|
margin-top: 500px;
|
||||||
|
padding-top: 100px;
|
||||||
|
background-color: navy;
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="box"></div>
|
||||||
|
<a id="inline">inline</a>
|
||||||
|
<script src="include.js"></script>
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
const box_rect = document.getElementById("box").getClientRects();
|
||||||
|
println(JSON.stringify(box_rect));
|
||||||
|
|
||||||
|
const inline_rect = document.getElementById("inline").getClientRects();
|
||||||
|
println(JSON.stringify(inline_rect));
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -849,30 +849,36 @@ bool Element::serializes_as_void() const
|
||||||
// https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect
|
// https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect
|
||||||
JS::NonnullGCPtr<Geometry::DOMRect> Element::get_bounding_client_rect() const
|
JS::NonnullGCPtr<Geometry::DOMRect> Element::get_bounding_client_rect() const
|
||||||
{
|
{
|
||||||
// // NOTE: Ensure that layout is up-to-date before looking at metrics.
|
// 1. Let list be the result of invoking getClientRects() on element.
|
||||||
const_cast<Document&>(document()).update_layout();
|
auto list = get_client_rects();
|
||||||
VERIFY(document().navigable());
|
|
||||||
auto viewport_offset = document().navigable()->viewport_scroll_offset();
|
|
||||||
|
|
||||||
if (auto const* paintable_box = this->paintable_box()) {
|
// 2. If the list is empty return a DOMRect object whose x, y, width and height members are zero.
|
||||||
auto absolute_rect = paintable_box->absolute_border_box_rect();
|
if (list->length() == 0)
|
||||||
absolute_rect.translate_by(-viewport_offset.x(), -viewport_offset.y());
|
return Geometry::DOMRect::construct_impl(realm(), 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
|
||||||
return Geometry::DOMRect::create(realm(), absolute_rect.to_type<float>());
|
|
||||||
|
// 3. If all rectangles in list have zero width or height, return the first rectangle in list.
|
||||||
|
auto all_rectangle_has_zero_width_or_height = true;
|
||||||
|
for (auto i = 0u; i < list->length(); ++i) {
|
||||||
|
auto const& rect = list->item(i);
|
||||||
|
if (rect->width() != 0 && rect->height() != 0) {
|
||||||
|
all_rectangle_has_zero_width_or_height = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (all_rectangle_has_zero_width_or_height)
|
||||||
|
return JS::NonnullGCPtr { *const_cast<Geometry::DOMRect*>(list->item(0)) };
|
||||||
|
|
||||||
auto const* paintable = this->paintable();
|
// 4. Otherwise, return a DOMRect object describing the smallest rectangle that includes all of the rectangles in
|
||||||
if (paintable && is<Painting::InlinePaintable>(*paintable)) {
|
// list of which the height or width is not zero.
|
||||||
auto const& inline_paintable = static_cast<Painting::InlinePaintable const&>(*paintable);
|
auto const* first_rect = list->item(0);
|
||||||
auto absolute_rect = inline_paintable.bounding_rect();
|
auto bounding_rect = Gfx::Rect { first_rect->x(), first_rect->y(), first_rect->width(), first_rect->height() };
|
||||||
absolute_rect.translate_by(-viewport_offset.x(), -viewport_offset.y());
|
for (auto i = 1u; i < list->length(); ++i) {
|
||||||
return Geometry::DOMRect::create(realm(), absolute_rect.to_type<float>());
|
auto const& rect = list->item(i);
|
||||||
|
if (rect->width() == 0 || rect->height() == 0)
|
||||||
|
continue;
|
||||||
|
bounding_rect = bounding_rect.united({ rect->x(), rect->y(), rect->width(), rect->height() });
|
||||||
}
|
}
|
||||||
|
return Geometry::DOMRect::create(realm(), bounding_rect.to_type<float>());
|
||||||
if (paintable) {
|
|
||||||
dbgln("FIXME: Failed to get bounding client rect for element ({})", debug_description());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Geometry::DOMRect::construct_impl(realm(), 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom-view/#dom-element-getclientrects
|
// https://drafts.csswg.org/cssom-view/#dom-element-getclientrects
|
||||||
|
@ -883,22 +889,39 @@ JS::NonnullGCPtr<Geometry::DOMRectList> Element::get_client_rects() const
|
||||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||||
const_cast<Document&>(document()).update_layout();
|
const_cast<Document&>(document()).update_layout();
|
||||||
|
|
||||||
// 1. If the element on which it was invoked does not have an associated layout box return an empty DOMRectList object and stop this algorithm.
|
// 1. If the element on which it was invoked does not have an associated layout box return an empty DOMRectList
|
||||||
if (!layout_node() || !layout_node()->is_box())
|
// object and stop this algorithm.
|
||||||
|
if (!layout_node())
|
||||||
return Geometry::DOMRectList::create(realm(), move(rects));
|
return Geometry::DOMRectList::create(realm(), move(rects));
|
||||||
|
|
||||||
// FIXME: 2. If the element has an associated SVG layout box return a DOMRectList object containing a single DOMRect object that describes
|
// FIXME: 2. If the element has an associated SVG layout box return a DOMRectList object containing a single
|
||||||
// the bounding box of the element as defined by the SVG specification, applying the transforms that apply to the element and its ancestors.
|
// DOMRect object that describes the bounding box of the element as defined by the SVG specification,
|
||||||
|
// applying the transforms that apply to the element and its ancestors.
|
||||||
|
|
||||||
// FIXME: 3. Return a DOMRectList object containing DOMRect objects in content order, one for each box fragment,
|
// 3. Return a DOMRectList object containing DOMRect objects in content order, one for each box fragment,
|
||||||
// describing its border area (including those with a height or width of zero) with the following constraints:
|
// describing its border area (including those with a height or width of zero) with the following constraints:
|
||||||
// - Apply the transforms that apply to the element and its ancestors.
|
// FIXME: - Apply the transforms that apply to the element and its ancestors.
|
||||||
// - If the element on which the method was invoked has a computed value for the display property of table
|
// FIXME: - If the element on which the method was invoked has a computed value for the display property of table
|
||||||
// or inline-table include both the table box and the caption box, if any, but not the anonymous container box.
|
// or inline-table include both the table box and the caption box, if any, but not the anonymous container box.
|
||||||
// - Replace each anonymous block box with its child box(es) and repeat this until no anonymous block boxes are left in the final list.
|
// FIXME: - Replace each anonymous block box with its child box(es) and repeat this until no anonymous block boxes
|
||||||
|
// are left in the final list.
|
||||||
|
const_cast<Document&>(document()).update_layout();
|
||||||
|
VERIFY(document().navigable());
|
||||||
|
auto viewport_offset = document().navigable()->viewport_scroll_offset();
|
||||||
|
auto const* paintable = this->paintable();
|
||||||
|
if (auto const* paintable_box = this->paintable_box()) {
|
||||||
|
auto absolute_rect = paintable_box->absolute_border_box_rect();
|
||||||
|
absolute_rect.translate_by(-viewport_offset.x(), -viewport_offset.y());
|
||||||
|
rects.append(Geometry::DOMRect::create(realm(), absolute_rect.to_type<float>()));
|
||||||
|
} else if (paintable && is<Painting::InlinePaintable>(*paintable)) {
|
||||||
|
auto const& inline_paintable = static_cast<Painting::InlinePaintable const&>(*paintable);
|
||||||
|
auto absolute_rect = inline_paintable.bounding_rect();
|
||||||
|
absolute_rect.translate_by(-viewport_offset.x(), -viewport_offset.y());
|
||||||
|
rects.append(Geometry::DOMRect::create(realm(), absolute_rect.to_type<float>()));
|
||||||
|
} else if (paintable) {
|
||||||
|
dbgln("FIXME: Failed to get client rects for element ({})", debug_description());
|
||||||
|
}
|
||||||
|
|
||||||
auto bounding_rect = get_bounding_client_rect();
|
|
||||||
rects.append(*bounding_rect);
|
|
||||||
return Geometry::DOMRectList::create(realm(), move(rects));
|
return Geometry::DOMRectList::create(realm(), move(rects));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue