Match inline font fragment geometry
This commit is contained in:
1 parent
61d61feb50
commit
3e377cef67
9 files changed
+561
-20
No files matched your search
Vendored
+9
@@ -3044,6 +3044,14 @@ class Element extends Node {
|
||||
const geometry = this._renderBoxGeometry();
|
||||
if (geometry === null) return new DOMRectList([]);
|
||||
if (geometry !== undefined) {
|
||||
if (Array.isArray(geometry.clientRects)) {
|
||||
return new DOMRectList(geometry.clientRects.map(
|
||||
rect => this._rectFromRenderGeometry({
|
||||
...rect,
|
||||
viewportFixed: geometry.viewportFixed,
|
||||
})
|
||||
));
|
||||
}
|
||||
return new DOMRectList([this._rectFromRenderGeometry(geometry)]);
|
||||
}
|
||||
return new DOMRectList([this.getBoundingClientRect()]);
|
||||
@@ -5945,6 +5953,7 @@ globalThis.getComputedStyle = (el) => {
|
||||
'margin-top': '0px', 'margin-right': '0px', 'margin-bottom': '0px', 'margin-left': '0px',
|
||||
'padding-top': '0px', 'padding-right': '0px', 'padding-bottom': '0px', 'padding-left': '0px',
|
||||
'font-size': '16px', 'line-height': 'normal', 'font-weight': '400',
|
||||
'letter-spacing': 'normal',
|
||||
'font-family': 'Times',
|
||||
color: 'rgb(0, 0, 0)', 'background-color': 'rgba(0, 0, 0, 0)',
|
||||
'border-width': '0px', 'border-style': 'none', 'border-color': 'rgb(0, 0, 0)',
|
||||
|
||||
@@ -2427,10 +2427,11 @@ pub(crate) fn clamp_scroll_offset(state: &mut ObscuraState, requested: (f32, f32
|
||||
/// cache. The cache is computed lazily on first read and cleared on navigation
|
||||
/// (see `set_dom`). Coordinates are viewport-relative after the shared root
|
||||
/// scroll offset, except for viewport-fixed subtrees. Returns JSON
|
||||
/// `{"x","y","width","height","clientWidth","clientHeight"}` in CSS pixels,
|
||||
/// or an empty string when the node has no box. The client dimensions are the
|
||||
/// unscaled padding box used by CSSOM View, whereas the rect is the visual
|
||||
/// viewport-relative border box. Feature-gated.
|
||||
/// `{"x","y","width","height","clientWidth","clientHeight","clientRects"}`
|
||||
/// in CSS pixels, or an empty string when the node has no box. The client
|
||||
/// dimensions are the unscaled padding box used by CSSOM View, `clientRects`
|
||||
/// retains every inline continuation, and the top-level rect is their visual
|
||||
/// viewport-relative bounding union. Feature-gated.
|
||||
#[cfg(feature = "render")]
|
||||
#[op2]
|
||||
#[string]
|
||||
@@ -2447,17 +2448,32 @@ fn op_layout_geometry(state: &OpState, #[string] nid_str: String) -> String {
|
||||
let Some((client_width, client_height)) = prepared.client_size(nid) else {
|
||||
return String::new();
|
||||
};
|
||||
let Some(client_rects) = prepared.viewport_client_rects(nid, scroll) else {
|
||||
return String::new();
|
||||
};
|
||||
let client_rects = client_rects
|
||||
.into_iter()
|
||||
.map(|rect| {
|
||||
serde_json::json!({
|
||||
"x": rect.x,
|
||||
"y": rect.y,
|
||||
"width": rect.width,
|
||||
"height": rect.height,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let viewport_fixed = prepared.viewport_fixed_nodes().contains(&nid);
|
||||
return format!(
|
||||
"{{\"x\":{},\"y\":{},\"width\":{},\"height\":{},\"clientWidth\":{},\"clientHeight\":{},\"viewportFixed\":{}}}",
|
||||
rect.x,
|
||||
rect.y,
|
||||
rect.width,
|
||||
rect.height,
|
||||
client_width,
|
||||
client_height,
|
||||
viewport_fixed
|
||||
);
|
||||
return serde_json::json!({
|
||||
"x": rect.x,
|
||||
"y": rect.y,
|
||||
"width": rect.width,
|
||||
"height": rect.height,
|
||||
"clientWidth": client_width,
|
||||
"clientHeight": client_height,
|
||||
"clientRects": client_rects,
|
||||
"viewportFixed": viewport_fixed,
|
||||
})
|
||||
.to_string();
|
||||
}
|
||||
String::new()
|
||||
}
|
||||
|
||||
@@ -2275,6 +2275,9 @@ mod tests {
|
||||
rect: [rect.x, rect.y, rect.width, rect.height],
|
||||
after: [afterRect.x, afterRect.y],
|
||||
client: [token.clientWidth, token.clientHeight],
|
||||
clientRects: Array.from(token.getClientRects(), r => [
|
||||
r.x, r.y, r.width, r.height
|
||||
]),
|
||||
atomic: [atomic.width, atomic.height],
|
||||
replaced: [replaced.width, replaced.height],
|
||||
item: [item.width, item.height, getComputedStyle(item).display]
|
||||
@@ -2298,6 +2301,19 @@ mod tests {
|
||||
);
|
||||
assert!(token_height < 40.0, "ignored block size leaked into geometry");
|
||||
assert_eq!(result["client"], serde_json::json!([0, 0]));
|
||||
let client_rects = result["clientRects"].as_array().unwrap();
|
||||
assert_eq!(client_rects.len(), 1);
|
||||
let client_rect = client_rects[0].as_array().unwrap();
|
||||
for (actual, expected) in client_rect
|
||||
.iter()
|
||||
.map(|value| value.as_f64().unwrap())
|
||||
.zip([token_x, token_y, token_width, token_height])
|
||||
{
|
||||
assert!(
|
||||
(actual - expected).abs() < 0.001,
|
||||
"getClientRects must expose the renderer's inline fragments"
|
||||
);
|
||||
}
|
||||
let after = result["after"].as_array().unwrap();
|
||||
assert!(after[0].as_f64().unwrap() >= token_x + token_width - 0.01);
|
||||
assert!((after[1].as_f64().unwrap() - token_y).abs() < 0.01);
|
||||
@@ -2377,6 +2393,49 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
#[test]
|
||||
fn computed_typography_uses_resolved_renderer_values() {
|
||||
let dom = parse_html(
|
||||
r#"<html><head><style>
|
||||
#parent {
|
||||
font-size:20px; line-height:1.5;
|
||||
letter-spacing:-.05em; white-space:pre-wrap;
|
||||
text-align:end
|
||||
}
|
||||
#child { font-size:10px }
|
||||
#zero { letter-spacing:0px; white-space:break-spaces }
|
||||
</style></head><body>
|
||||
<div id="parent"><span id="child">child</span></div>
|
||||
<div id="zero">zero</div>
|
||||
</body></html>"#,
|
||||
);
|
||||
let mut rt = ObscuraJsRuntime::new();
|
||||
rt.set_dom(dom);
|
||||
rt.set_viewport(400.0, 200.0);
|
||||
rt.run_page_init();
|
||||
|
||||
let result = rt
|
||||
.evaluate(
|
||||
r#"
|
||||
const sample = id => {
|
||||
const s = getComputedStyle(document.getElementById(id));
|
||||
return [s.lineHeight, s.letterSpacing, s.whiteSpace, s.textAlign];
|
||||
};
|
||||
return [sample("parent"), sample("child"), sample("zero")];
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
serde_json::json!([
|
||||
["30px", "-1px", "pre-wrap", "end"],
|
||||
["15px", "-1px", "pre-wrap", "end"],
|
||||
["normal", "normal", "break-spaces", "start"],
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
#[test]
|
||||
fn computed_style_exposes_cascaded_custom_properties_and_invalidates() {
|
||||
|
||||
Reference in new issue
Block a user