diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 30893551f3a..bf170ec5784 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -4585,7 +4585,7 @@ void Document::queue_an_intersection_observer_entry(IntersectionObserver::Inters } // https://www.w3.org/TR/intersection-observer/#compute-the-intersection -static GC::Ref compute_intersection(GC::Ref target, IntersectionObserver::IntersectionObserver const& observer) +static CSSPixelRect compute_intersection(GC::Ref target, IntersectionObserver::IntersectionObserver const& observer) { // 1. Let intersectionRect be the result of getting the bounding box for target. auto intersection_rect = target->get_bounding_client_rect(); @@ -4604,12 +4604,7 @@ static GC::Ref compute_intersection(GC::Ref // 5. Update intersectionRect by intersecting it with the root intersection rectangle. // FIXME: Pass in target so we can properly apply rootMargin. auto root_intersection_rectangle = observer.root_intersection_rectangle(); - CSSPixelRect intersection_rect_as_pixel_rect(intersection_rect->x(), intersection_rect->y(), intersection_rect->width(), intersection_rect->height()); - intersection_rect_as_pixel_rect.intersect(root_intersection_rectangle); - intersection_rect->set_x(static_cast(intersection_rect_as_pixel_rect.x())); - intersection_rect->set_y(static_cast(intersection_rect_as_pixel_rect.y())); - intersection_rect->set_width(static_cast(intersection_rect_as_pixel_rect.width())); - intersection_rect->set_height(static_cast(intersection_rect_as_pixel_rect.height())); + intersection_rect.intersect(root_intersection_rectangle); // FIXME: 6. Map intersectionRect to the coordinate space of the viewport of the document containing target. @@ -4647,10 +4642,10 @@ void Document::run_the_update_intersection_observations_steps(HighResolutionTime bool is_intersecting = false; // targetRect be a DOMRectReadOnly with x, y, width, and height set to 0. - auto target_rect = Geometry::DOMRectReadOnly::construct_impl(realm, 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors(); + CSSPixelRect target_rect { 0, 0, 0, 0 }; // intersectionRect be a DOMRectReadOnly with x, y, width, and height set to 0. - auto intersection_rect = Geometry::DOMRectReadOnly::construct_impl(realm, 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors(); + CSSPixelRect intersection_rect { 0, 0, 0, 0 }; // SPEC ISSUE: It doesn't pass in intersection ratio to "queue an IntersectionObserverEntry" despite needing it. // This is default 0, as isIntersecting is default false, see step 9. @@ -4673,20 +4668,19 @@ void Document::run_the_update_intersection_observations_steps(HighResolutionTime intersection_rect = compute_intersection(target, observer); // 6. Let targetArea be targetRect’s area. - auto target_area = target_rect->width() * target_rect->height(); + auto target_area = target_rect.width() * target_rect.height(); // 7. Let intersectionArea be intersectionRect’s area. - auto intersection_area = intersection_rect->width() * intersection_rect->height(); + auto intersection_area = intersection_rect.size().area(); // 8. Let isIntersecting be true if targetRect and rootBounds intersect or are edge-adjacent, even if the // intersection has zero area (because rootBounds or targetRect have zero area). - CSSPixelRect target_rect_as_pixel_rect(target_rect->x(), target_rect->y(), target_rect->width(), target_rect->height()); - is_intersecting = target_rect_as_pixel_rect.edge_adjacent_intersects(root_bounds); + is_intersecting = target_rect.edge_adjacent_intersects(root_bounds); // 9. If targetArea is non-zero, let intersectionRatio be intersectionArea divided by targetArea. // Otherwise, let intersectionRatio be 1 if isIntersecting is true, or 0 if isIntersecting is false. if (target_area != 0.0) - intersection_ratio = intersection_area / target_area; + intersection_ratio = (intersection_area / target_area).to_double(); else intersection_ratio = is_intersecting ? 1.0 : 0.0; @@ -4716,7 +4710,9 @@ void Document::run_the_update_intersection_observations_steps(HighResolutionTime auto root_bounds_as_dom_rect = Geometry::DOMRectReadOnly::construct_impl(realm, static_cast(root_bounds.x()), static_cast(root_bounds.y()), static_cast(root_bounds.width()), static_cast(root_bounds.height())).release_value_but_fixme_should_propagate_errors(); // SPEC ISSUE: It doesn't pass in intersectionRatio, but it's required. - queue_an_intersection_observer_entry(observer, time, root_bounds_as_dom_rect, target_rect, intersection_rect, is_intersecting, intersection_ratio, target); + auto target_dom_rect = MUST(Geometry::DOMRectReadOnly::construct_impl(realm, static_cast(target_rect.x()), static_cast(target_rect.y()), static_cast(target_rect.width()), static_cast(target_rect.height()))); + auto intersection_dom_rect = MUST(Geometry::DOMRectReadOnly::construct_impl(realm, static_cast(intersection_rect.x()), static_cast(intersection_rect.y()), static_cast(intersection_rect.width()), static_cast(intersection_rect.height()))); + queue_an_intersection_observer_entry(observer, time, root_bounds_as_dom_rect, target_dom_rect, intersection_dom_rect, is_intersecting, intersection_ratio, target); } // 15. Assign thresholdIndex to intersectionObserverRegistration’s previousThresholdIndex property. diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index d8a50e62e12..290a1ce26d8 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -954,46 +954,62 @@ bool Element::serializes_as_void() const } // https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect -GC::Ref Element::get_bounding_client_rect() const +GC::Ref Element::get_bounding_client_rect_for_bindings() const +{ + auto rect = get_bounding_client_rect(); + return MUST(Geometry::DOMRect::construct_impl(realm(), static_cast(rect.x()), static_cast(rect.y()), static_cast(rect.width()), static_cast(rect.height()))); +} + +// https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect +CSSPixelRect Element::get_bounding_client_rect() const { // 1. Let list be the result of invoking getClientRects() on element. auto list = get_client_rects(); // 2. If the list is empty return a DOMRect object whose x, y, width and height members are zero. - if (list->length() == 0) - return Geometry::DOMRect::construct_impl(realm(), 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors(); + if (list.size() == 0) + return { 0, 0, 0, 0 }; // 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) { + for (auto i = 0u; i < list.size(); ++i) { + auto const& rect = list.at(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 GC::Ref { *const_cast(list->item(0)) }; + return list.at(0); // 4. Otherwise, return a DOMRect object describing the smallest rectangle that includes all of the rectangles in // list of which the height or width is not zero. - auto const* first_rect = list->item(0); - auto bounding_rect = Gfx::Rect { first_rect->x(), first_rect->y(), first_rect->width(), first_rect->height() }; - for (auto i = 1u; i < list->length(); ++i) { - auto const& rect = list->item(i); - if (rect->width() == 0 || rect->height() == 0) + auto bounding_rect = list.at(0); + for (auto i = 1u; i < list.size(); ++i) { + auto const& rect = list.at(i); + if (rect.width() == 0 || rect.height() == 0) continue; - bounding_rect.unite({ rect->x(), rect->y(), rect->width(), rect->height() }); + bounding_rect.unite(rect); } - return Geometry::DOMRect::create(realm(), bounding_rect.to_type()); + return bounding_rect; } // https://drafts.csswg.org/cssom-view/#dom-element-getclientrects -GC::Ref Element::get_client_rects() const +GC::Ref Element::get_client_rects_for_bindings() const +{ + Vector> rects; + for (auto const& rect : get_client_rects()) { + rects.append(MUST(Geometry::DOMRect::construct_impl(realm(), static_cast(rect.x()), static_cast(rect.y()), static_cast(rect.width()), static_cast(rect.height())))); + } + return Geometry::DOMRectList::create(realm(), move(rects)); +} + +// https://drafts.csswg.org/cssom-view/#dom-element-getclientrects +Vector Element::get_client_rects() const { auto navigable = document().navigable(); if (!navigable) - return Geometry::DOMRectList::create(realm(), {}); + return {}; // NOTE: Ensure that layout is up-to-date before looking at metrics. const_cast(document()).update_layout(UpdateLayoutReason::ElementGetClientRects); @@ -1001,7 +1017,7 @@ GC::Ref Element::get_client_rects() const // 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. if (!layout_node()) - return Geometry::DOMRectList::create(realm(), {}); + return {}; // FIXME: 2. If the element has an associated SVG layout box return a DOMRectList object containing a single // DOMRect object that describes the bounding box of the element as defined by the SVG specification, @@ -1022,7 +1038,7 @@ GC::Ref Element::get_client_rects() const CSSPixelPoint scroll_offset; auto const* paintable = this->paintable(); - Vector> rects; + Vector rects; if (auto const* paintable_box = this->paintable_box()) { transform = Gfx::extract_2d_affine_transform(paintable_box->transform()); for (auto const* containing_block = paintable->containing_block(); !containing_block->is_viewport(); containing_block = containing_block->containing_block()) { @@ -1038,12 +1054,12 @@ GC::Ref Element::get_client_rects() const .to_type() .translated(paintable_box->transform_origin()) .translated(-scroll_offset); - rects.append(Geometry::DOMRect::create(realm(), transformed_rect.to_type())); + rects.append(transformed_rect); } else if (paintable) { dbgln("FIXME: Failed to get client rects for element ({})", debug_description()); } - return Geometry::DOMRectList::create(realm(), move(rects)); + return rects; } int Element::client_top() const @@ -1969,24 +1985,24 @@ static CSSPixelPoint determine_the_scroll_into_view_position(Element& target, Bi // 2. Let scrolling box edge A be the beginning edge in the block flow direction of scrolling box, and // let element edge A be target bounding border box’s edge on the same physical side as that of // scrolling box edge A. - CSSPixels element_edge_a = CSSPixels::nearest_value_for(target_bounding_border_box->top()); + CSSPixels element_edge_a = target_bounding_border_box.top(); CSSPixels scrolling_box_edge_a = scrolling_box_rect.top(); // 3. Let scrolling box edge B be the ending edge in the block flow direction of scrolling box, and let // element edge B be target bounding border box’s edge on the same physical side as that of scrolling // box edge B. - CSSPixels element_edge_b = CSSPixels::nearest_value_for(target_bounding_border_box->bottom()); + CSSPixels element_edge_b = target_bounding_border_box.bottom(); CSSPixels scrolling_box_edge_b = scrolling_box_rect.bottom(); // 4. Let scrolling box edge C be the beginning edge in the inline base direction of scrolling box, and // let element edge C be target bounding border box’s edge on the same physical side as that of scrolling // box edge C. - CSSPixels element_edge_c = CSSPixels::nearest_value_for(target_bounding_border_box->left()); + CSSPixels element_edge_c = target_bounding_border_box.left(); CSSPixels scrolling_box_edge_c = scrolling_box_rect.left(); // 5. Let scrolling box edge D be the ending edge in the inline base direction of scrolling box, and let element // edge D be target bounding border box’s edge on the same physical side as that of scrolling box edge D. - CSSPixels element_edge_d = CSSPixels::nearest_value_for(target_bounding_border_box->right()); + CSSPixels element_edge_d = target_bounding_border_box.right(); CSSPixels scrolling_box_edge_d = scrolling_box_rect.right(); // 6. Let element height be the distance between element edge A and element edge B. diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index bab7dd93e25..573a199e1c0 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -254,8 +254,11 @@ public: bool is_void_element() const; bool serializes_as_void() const; - GC::Ref get_bounding_client_rect() const; - GC::Ref get_client_rects() const; + [[nodiscard]] CSSPixelRect get_bounding_client_rect() const; + [[nodiscard]] GC::Ref get_bounding_client_rect_for_bindings() const; + + [[nodiscard]] Vector get_client_rects() const; + [[nodiscard]] GC::Ref get_client_rects_for_bindings() const; virtual GC::Ptr create_layout_node(GC::Ref); virtual void adjust_computed_style(CSS::ComputedProperties&) { } diff --git a/Libraries/LibWeb/DOM/Element.idl b/Libraries/LibWeb/DOM/Element.idl index 022d33c138a..e7df24216c4 100644 --- a/Libraries/LibWeb/DOM/Element.idl +++ b/Libraries/LibWeb/DOM/Element.idl @@ -84,8 +84,8 @@ interface Element : Node { readonly attribute Element? previousElementSibling; // https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface - DOMRectList getClientRects(); - DOMRect getBoundingClientRect(); + [ImplementedAs=get_client_rects_for_bindings] DOMRectList getClientRects(); + [ImplementedAs=get_bounding_client_rect_for_bindings] DOMRect getBoundingClientRect(); boolean checkVisibility(optional CheckVisibilityOptions options = {}); diff --git a/Libraries/LibWeb/DOM/Range.cpp b/Libraries/LibWeb/DOM/Range.cpp index 98a73eef119..9cc417a057c 100644 --- a/Libraries/LibWeb/DOM/Range.cpp +++ b/Libraries/LibWeb/DOM/Range.cpp @@ -1180,11 +1180,9 @@ GC::Ref Range::get_client_rects() // areas returned by invoking getClientRects() on the element. if (contains_node(*node) && !contains_node(*node->parent())) { auto const& element = static_cast(*node); - GC::Ref const element_rects = element.get_client_rects(); - for (u32 i = 0; i < element_rects->length(); i++) { - auto rect = element_rects->item(i); - rects.append(Geometry::DOMRect::create(realm(), - Gfx::FloatRect(rect->x(), rect->y(), rect->width(), rect->height()))); + auto const element_rects = element.get_client_rects(); + for (auto& rect : element_rects) { + rects.append(MUST(Geometry::DOMRect::construct_impl(realm(), static_cast(rect.x()), static_cast(rect.y()), static_cast(rect.width()), static_cast(rect.height())))); } } } else if (node_type == NodeType::TEXT_NODE) { diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 7b47ae5e6ad..8031eaf6d64 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -1238,7 +1238,7 @@ void HTMLInputElement::create_range_input_shadow_tree() double minimum = *min(); double maximum = *max(); // FIXME: Snap new value to input steps - MUST(set_value_as_number(clamp(round(((client_x - rect->left()) / rect->width()) * (maximum - minimum) + minimum), minimum, maximum))); + MUST(set_value_as_number(clamp(round(((client_x - rect.left().to_double()) / rect.width().to_double()) * (maximum - minimum) + minimum), minimum, maximum))); user_interaction_did_change_input_value(); }; diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp index b27f42b13a8..42147beb00a 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -499,8 +499,8 @@ void HTMLSelectElement::show_the_picker_if_applicable() // Request select dropdown auto weak_element = make_weak_ptr(); auto rect = get_bounding_client_rect(); - auto position = document().navigable()->to_top_level_position(Web::CSSPixelPoint { rect->x(), rect->y() + rect->height() }); - document().page().did_request_select_dropdown(weak_element, position, CSSPixels(rect->width()), m_select_items); + auto position = document().navigable()->to_top_level_position(Web::CSSPixelPoint { rect.x(), rect.bottom() }); + document().page().did_request_select_dropdown(weak_element, position, rect.width(), m_select_items); set_is_open(true); } diff --git a/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp b/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp index 90b329b5a61..e79b497ae9d 100644 --- a/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp +++ b/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp @@ -283,8 +283,7 @@ CSSPixelRect IntersectionObserver::root_intersection_rectangle() const // Otherwise, // it’s the result of getting the bounding box for the intersection root. - auto bounding_client_rect = element->get_bounding_client_rect(); - rect = CSSPixelRect(bounding_client_rect->x(), bounding_client_rect->y(), bounding_client_rect->width(), bounding_client_rect->height()); + rect = element->get_bounding_client_rect(); } // When calculating the root intersection rectangle for a same-origin-domain target, the rectangle is then diff --git a/Libraries/LibWeb/WebDriver/Actions.cpp b/Libraries/LibWeb/WebDriver/Actions.cpp index bed4590e1a3..1b8f96a47eb 100644 --- a/Libraries/LibWeb/WebDriver/Actions.cpp +++ b/Libraries/LibWeb/WebDriver/Actions.cpp @@ -160,8 +160,8 @@ static CSSPixelPoint get_parent_offset(HTML::BrowsingContext const& browsing_con // 9. Add containerRect.left + borderLeftWidth to offsetLeft. // 10. Add containerRect.top + borderTopWidth to offsetTop. offset.translate_by( - CSSPixels { container_rect->left() } + border_left_width, - CSSPixels { container_rect->top() } + border_top_width); + container_rect.left() + border_left_width, + container_rect.top() + border_top_width); } // 5. Return (offsetLeft, offsetTop). diff --git a/Libraries/LibWeb/WebDriver/ElementReference.cpp b/Libraries/LibWeb/WebDriver/ElementReference.cpp index 802f25a9a54..b409103f825 100644 --- a/Libraries/LibWeb/WebDriver/ElementReference.cpp +++ b/Libraries/LibWeb/WebDriver/ElementReference.cpp @@ -393,7 +393,7 @@ GC::RootVector> pointer_interactable_tree(Web::HTML:: auto rectangles = element.get_client_rects(); // 3. If rectangles has the length of 0, return an empty sequence. - if (rectangles->length() == 0) + if (rectangles.is_empty()) return GC::RootVector>(browsing_context.heap()); // 4. Let center point be the in-view center point of the first indexed element in rectangles. @@ -530,20 +530,19 @@ String element_rendered_text(DOM::Node& node) CSSPixelPoint in_view_center_point(DOM::Element const& element, CSSPixelRect viewport) { // 1. Let rectangle be the first element of the DOMRect sequence returned by calling getClientRects() on element. - auto const* rectangle = element.get_client_rects()->item(0); - VERIFY(rectangle); + auto rectangle = element.get_client_rects().first(); // 2. Let left be max(0, min(x coordinate, x coordinate + width dimension)). - auto left = max(0.0, min(rectangle->x(), rectangle->x() + rectangle->width())); + auto left = max(CSSPixels(0), min(rectangle.x(), rectangle.x() + rectangle.width())); // 3. Let right be min(innerWidth, max(x coordinate, x coordinate + width dimension)). - auto right = min(viewport.width().to_double(), max(rectangle->x(), rectangle->x() + rectangle->width())); + auto right = min(viewport.width(), max(rectangle.x(), rectangle.x() + rectangle.width())); // 4. Let top be max(0, min(y coordinate, y coordinate + height dimension)). - auto top = max(0.0, min(rectangle->y(), rectangle->y() + rectangle->height())); + auto top = max(CSSPixels(0), min(rectangle.y(), rectangle.y() + rectangle.height())); // 5. Let bottom be min(innerHeight, max(y coordinate, y coordinate + height dimension)). - auto bottom = min(viewport.height().to_double(), max(rectangle->y(), rectangle->y() + rectangle->height())); + auto bottom = min(viewport.height(), max(rectangle.y(), rectangle.y() + rectangle.height())); // 6. Let x be floor((left + right) ÷ 2.0). auto x = floor((left + right) / 2.0); diff --git a/Services/WebContent/WebDriverConnection.cpp b/Services/WebContent/WebDriverConnection.cpp index 93ded1ccbc1..0a0305100f6 100644 --- a/Services/WebContent/WebDriverConnection.cpp +++ b/Services/WebContent/WebDriverConnection.cpp @@ -3090,7 +3090,7 @@ void WebDriverConnection::delete_cookies(Optional const& name) } // https://w3c.github.io/webdriver/#dfn-calculate-the-absolute-position -Gfx::IntPoint WebDriverConnection::calculate_absolute_position_of_element(GC::Ref rect) +Gfx::IntPoint WebDriverConnection::calculate_absolute_position_of_element(Web::CSSPixelRect rect) { // 1. Let rect be the value returned by calling getBoundingClientRect(). @@ -3098,10 +3098,10 @@ Gfx::IntPoint WebDriverConnection::calculate_absolute_position_of_element(GC::Re auto const* window = current_top_level_browsing_context()->active_window(); // 3. Let x be (scrollX of window + rect’s x coordinate). - auto x = (window ? static_cast(window->scroll_x()) : 0) + static_cast(rect->x()); + auto x = (window ? static_cast(window->scroll_x()) : 0) + static_cast(rect.x()); // 4. Let y be (scrollY of window + rect’s y coordinate). - auto y = (window ? static_cast(window->scroll_y()) : 0) + static_cast(rect->y()); + auto y = (window ? static_cast(window->scroll_y()) : 0) + static_cast(rect.y()); // 5. Return a pair of (x, y). return { x, y }; @@ -3115,8 +3115,8 @@ Gfx::IntRect WebDriverConnection::calculate_absolute_rect_of_element(Web::DOM::E return { coordinates.x(), coordinates.y(), - static_cast(bounding_rect->width()), - static_cast(bounding_rect->height()) + static_cast(bounding_rect.width()), + static_cast(bounding_rect.height()) }; } diff --git a/Services/WebContent/WebDriverConnection.h b/Services/WebContent/WebDriverConnection.h index b7243421132..9321d3607a3 100644 --- a/Services/WebContent/WebDriverConnection.h +++ b/Services/WebContent/WebDriverConnection.h @@ -137,7 +137,7 @@ private: using OnNavigationComplete = GC::Ref>; void wait_for_navigation_to_complete(OnNavigationComplete); - Gfx::IntPoint calculate_absolute_position_of_element(GC::Ref rect); + Gfx::IntPoint calculate_absolute_position_of_element(Web::CSSPixelRect); Gfx::IntRect calculate_absolute_rect_of_element(Web::DOM::Element const& element); using GetStartNode = GC::Ref, Web::WebDriver::Error>()>>;