mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
script: Implement Range::getClientRects
and Range::getBoundingClientRect
(#35993)
* Add doc comments to boundary point Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Allow querying content box of text fragments Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Implement Range::getBoundingClientRect Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update WPT expectations Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
07e06f5635
commit
6be7612d16
8 changed files with 73 additions and 29 deletions
|
@ -155,6 +155,7 @@ impl FragmentTree {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Fragment::Positioning(fragment) => fragment.borrow().rect.cast_unit(),
|
Fragment::Positioning(fragment) => fragment.borrow().rect.cast_unit(),
|
||||||
|
Fragment::Text(text_fragment) => text_fragment.borrow().rect,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -94,10 +94,13 @@ impl AbstractRangeMethods<crate::DomTypeHolder> for AbstractRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://dom.spec.whatwg.org/#concept-range-bp>
|
||||||
#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
|
#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
|
||||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||||
pub(crate) struct BoundaryPoint {
|
pub(crate) struct BoundaryPoint {
|
||||||
|
/// <https://dom.spec.whatwg.org/#boundary-point-node>
|
||||||
node: MutDom<Node>,
|
node: MutDom<Node>,
|
||||||
|
/// <https://dom.spec.whatwg.org/#concept-range-bp-offset>
|
||||||
offset: Cell<u32>,
|
offset: Cell<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::cmp::{Ordering, PartialOrd};
|
use std::cmp::{Ordering, PartialOrd};
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use js::jsapi::JSTracer;
|
use js::jsapi::JSTracer;
|
||||||
|
@ -29,9 +30,11 @@ use crate::dom::bindings::weakref::{WeakRef, WeakRefVec};
|
||||||
use crate::dom::characterdata::CharacterData;
|
use crate::dom::characterdata::CharacterData;
|
||||||
use crate::dom::document::Document;
|
use crate::dom::document::Document;
|
||||||
use crate::dom::documentfragment::DocumentFragment;
|
use crate::dom::documentfragment::DocumentFragment;
|
||||||
|
use crate::dom::domrect::DOMRect;
|
||||||
|
use crate::dom::domrectlist::DOMRectList;
|
||||||
use crate::dom::element::Element;
|
use crate::dom::element::Element;
|
||||||
use crate::dom::htmlscriptelement::HTMLScriptElement;
|
use crate::dom::htmlscriptelement::HTMLScriptElement;
|
||||||
use crate::dom::node::{Node, ShadowIncluding, UnbindContext};
|
use crate::dom::node::{Node, NodeTraits, ShadowIncluding, UnbindContext};
|
||||||
use crate::dom::selection::Selection;
|
use crate::dom::selection::Selection;
|
||||||
use crate::dom::text::Text;
|
use crate::dom::text::Text;
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
|
@ -324,6 +327,23 @@ impl Range {
|
||||||
pub(crate) fn collapsed(&self) -> bool {
|
pub(crate) fn collapsed(&self) -> bool {
|
||||||
self.abstract_range().Collapsed()
|
self.abstract_range().Collapsed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_rects(
|
||||||
|
&self,
|
||||||
|
can_gc: CanGc,
|
||||||
|
) -> impl Iterator<Item = euclid::Rect<app_units::Au, euclid::UnknownUnit>> {
|
||||||
|
// FIXME: For text nodes that are only partially selected, this should return the client
|
||||||
|
// rect of the selected part, not the whole text node.
|
||||||
|
let start = self.start_container();
|
||||||
|
let end = self.end_container();
|
||||||
|
let document = start.owner_doc();
|
||||||
|
let end_clone = end.clone();
|
||||||
|
start
|
||||||
|
.following_nodes(document.upcast::<Node>())
|
||||||
|
.take_while(move |node| node != &end)
|
||||||
|
.chain(iter::once(end_clone))
|
||||||
|
.flat_map(move |node| node.content_boxes(can_gc))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RangeMethods<crate::DomTypeHolder> for Range {
|
impl RangeMethods<crate::DomTypeHolder> for Range {
|
||||||
|
@ -1105,6 +1125,51 @@ impl RangeMethods<crate::DomTypeHolder> for Range {
|
||||||
// Step 5.
|
// Step 5.
|
||||||
Ok(fragment_node)
|
Ok(fragment_node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/cssom-view/#dom-range-getclientrects>
|
||||||
|
fn GetClientRects(&self, can_gc: CanGc) -> DomRoot<DOMRectList> {
|
||||||
|
let start = self.start_container();
|
||||||
|
let window = start.owner_window();
|
||||||
|
|
||||||
|
let client_rects = self
|
||||||
|
.client_rects(can_gc)
|
||||||
|
.map(|rect| {
|
||||||
|
DOMRect::new(
|
||||||
|
window.upcast(),
|
||||||
|
rect.origin.x.to_f64_px(),
|
||||||
|
rect.origin.y.to_f64_px(),
|
||||||
|
rect.size.width.to_f64_px(),
|
||||||
|
rect.size.height.to_f64_px(),
|
||||||
|
can_gc,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
DOMRectList::new(&window, client_rects, can_gc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/cssom-view/#dom-range-getboundingclientrect>
|
||||||
|
fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> {
|
||||||
|
let window = self.start_container().owner_window();
|
||||||
|
|
||||||
|
// Step 1. Let list be the result of invoking getClientRects() on the same range this method was invoked on.
|
||||||
|
let list = self.client_rects(can_gc);
|
||||||
|
|
||||||
|
// Step 2. If list is empty return a DOMRect object whose x, y, width and height members are zero.
|
||||||
|
// Step 3. If all rectangles in list have zero width or height, return the first rectangle in list.
|
||||||
|
// Step 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.
|
||||||
|
let bounding_rect = list.fold(euclid::Rect::zero(), |acc, rect| acc.union(&rect));
|
||||||
|
|
||||||
|
DOMRect::new(
|
||||||
|
window.upcast(),
|
||||||
|
bounding_rect.origin.x.to_f64_px(),
|
||||||
|
bounding_rect.origin.y.to_f64_px(),
|
||||||
|
bounding_rect.size.width.to_f64_px(),
|
||||||
|
bounding_rect.size.height.to_f64_px(),
|
||||||
|
can_gc,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct WeakRangeVec {
|
pub(crate) struct WeakRangeVec {
|
||||||
|
|
|
@ -469,7 +469,7 @@ DOMInterfaces = {
|
||||||
},
|
},
|
||||||
|
|
||||||
'Range': {
|
'Range': {
|
||||||
'canGc': ['CloneContents', 'CloneRange', 'CreateContextualFragment', 'ExtractContents', 'SurroundContents', 'InsertNode'],
|
'canGc': ['CloneContents', 'CloneRange', 'CreateContextualFragment', 'ExtractContents', 'SurroundContents', 'InsertNode', 'GetClientRects', 'GetBoundingClientRect'],
|
||||||
'weakReferenceable': True,
|
'weakReferenceable': True,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,6 @@ partial interface Range {
|
||||||
|
|
||||||
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-range-interface
|
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-range-interface
|
||||||
partial interface Range {
|
partial interface Range {
|
||||||
// sequence<DOMRect> getClientRects();
|
DOMRectList getClientRects();
|
||||||
// [NewObject]
|
[NewObject] DOMRect getBoundingClientRect();
|
||||||
// DOMRect getBoundingClientRect();
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[DOMRectList.html]
|
|
||||||
[Range getClientRects()]
|
|
||||||
expected: FAIL
|
|
|
@ -1,9 +0,0 @@
|
||||||
[getBoundingClientRect-svg.html]
|
|
||||||
[Element.getBoundingClientRect() and Range.getBoudingClientRect() should match for an SVG <text>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Element.getBoundingClientRect() and Range.getBoudingClientRect() should match for an SVG <text> with a transform]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Element.getBoundingClientRect() and Range.getBoudingClientRect() should match for an SVG <text> with a rotate]
|
|
||||||
expected: FAIL
|
|
|
@ -26,9 +26,6 @@
|
||||||
[Element interface: document.createElement("div") must inherit property "getBoxQuads(BoxQuadOptions)" with the proper type]
|
[Element interface: document.createElement("div") must inherit property "getBoxQuads(BoxQuadOptions)" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Range interface: new Range() must inherit property "getBoundingClientRect()" with the proper type]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Element interface: calling convertRectFromNode(DOMRectReadOnly, GeometryNode, ConvertCoordinateOptions) on document.createElementNS("x", "y") with too few arguments must throw TypeError]
|
[Element interface: calling convertRectFromNode(DOMRectReadOnly, GeometryNode, ConvertCoordinateOptions) on document.createElementNS("x", "y") with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -62,9 +59,6 @@
|
||||||
[CaretPosition interface: attribute offset]
|
[CaretPosition interface: attribute offset]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Range interface: operation getBoundingClientRect()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Partial dictionary MouseEventInit: member names are unique]
|
[Partial dictionary MouseEventInit: member names are unique]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -98,9 +92,6 @@
|
||||||
[CaretPosition interface: existence and properties of interface prototype object]
|
[CaretPosition interface: existence and properties of interface prototype object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Range interface: operation getClientRects()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Element interface: document.createElementNS("x", "y") must inherit property "convertRectFromNode(DOMRectReadOnly, GeometryNode, ConvertCoordinateOptions)" with the proper type]
|
[Element interface: document.createElementNS("x", "y") must inherit property "convertRectFromNode(DOMRectReadOnly, GeometryNode, ConvertCoordinateOptions)" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -272,9 +263,6 @@
|
||||||
[Element interface: document.createElement("img") must inherit property "convertPointFromNode(DOMPointInit, GeometryNode, ConvertCoordinateOptions)" with the proper type]
|
[Element interface: document.createElement("img") must inherit property "convertPointFromNode(DOMPointInit, GeometryNode, ConvertCoordinateOptions)" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Range interface: new Range() must inherit property "getClientRects()" with the proper type]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Window interface: window must inherit property "screenLeft" with the proper type]
|
[Window interface: window must inherit property "screenLeft" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue