Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 5 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /css/cssom-view/cssom-geometryutils-getBoxQuads-options.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<title>CSSOM View - getBoxQuads() options</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/geometry-utils.js"></script>
<style>
html, body {
margin: 0;
}
#target {
position: absolute;
left: 40px;
top: 30px;
width: 200px;
height: 160px;
margin: 11px 13px 17px 19px;
border: solid;
border-width: 2px 4px 6px 8px;
padding: 3px 5px 7px 9px;
box-sizing: content-box;
background: lightblue;
}
#relative {
position: absolute;
left: 20px;
top: 15px;
width: 300px;
height: 220px;
border: 10px solid gray;
padding: 12px;
}
#child {
position: absolute;
left: 50px;
top: 45px;
width: 80px;
height: 40px;
margin: 10px 14px 18px 22px;
border: solid;
border-width: 1px 3px 5px 7px;
padding: 2px 4px 6px 8px;
box-sizing: content-box;
background: gold;
}
#rtl {
position: absolute;
left: 320px;
top: 30px;
width: 60px;
height: 25px;
direction: rtl;
writing-mode: vertical-rl;
border: 4px solid black;
padding: 6px;
}
#inline-container {
position: absolute;
left: 420px;
top: 30px;
width: 115px;
font: 18px/2 sans-serif;
}
#inline {
margin: 7px 11px;
border: solid;
border-width: 3px 5px;
padding: 2px 4px;
}
#scroll-spacer {
width: calc(100vw + 200px);
height: calc(100vh + 200px);
}
</style>
<div id="target"></div>
<div id="relative">
<div id="child"></div>
</div>
<div id="rtl"></div>
<div id="inline-container"><span id="inline">inline content wraps here</span></div>
<div id="scroll-spacer"></div>
<script>
const boxes = ["margin", "border", "padding", "content"];
const target = document.getElementById("target");
const relative = document.getElementById("relative");
const child = document.getElementById("child");
const rtl = document.getElementById("rtl");
const inline = document.getElementById("inline");
test(() => {
assert_geometry_utils_exist();
}, "GeometryUtils APIs exist on Element");
for (const box of boxes) {
test(() => {
const quad = target.getBoxQuads({box})[0];
const expected = local_box_rect(target, box);
const borderRect = target.getBoundingClientRect();
assert_point_approx_equals(quad.p1,
new DOMPoint(borderRect.left + expected.x,
borderRect.top + expected.y),
`${box} p1`);
assert_point_approx_equals(quad.p3,
new DOMPoint(borderRect.left + expected.x +
expected.width,
borderRect.top + expected.y +
expected.height),
`${box} p3`);
assert_approx_equals(quad.getBounds().width, expected.width,
GEOMETRY_UTILS_EPSILON, `${box} width`);
assert_approx_equals(quad.getBounds().height, expected.height,
GEOMETRY_UTILS_EPSILON, `${box} height`);
}, `getBoxQuads({box: "${box}"}) returns the requested CSS box`);
}
test(() => {
const defaultQuad = target.getBoxQuads()[0];
const borderQuad = target.getBoxQuads({box: "border"})[0];
assert_quad_approx_equals(defaultQuad, borderQuad,
"default getBoxQuads() border box");
}, "getBoxQuads() defaults to the border box");
test(() => {
const quad = child.getBoxQuads({relativeTo: relative})[0];
const childRect = child.getBoundingClientRect();
const relativeRect = relative.getBoundingClientRect();
assert_point_approx_equals(quad.p1,
new DOMPoint(childRect.left - relativeRect.left,
childRect.top - relativeRect.top),
"child border p1 relative to relative border box");
assert_point_approx_equals(quad.p3,
new DOMPoint(childRect.right - relativeRect.left,
childRect.bottom - relativeRect.top),
"child border p3 relative to relative border box");
}, "getBoxQuads({relativeTo}) uses the relative element border-box origin");
test(() => {
const quad = child.getBoxQuads({box: "content", relativeTo: relative})[0];
const childRect = child.getBoundingClientRect();
const relativeRect = relative.getBoundingClientRect();
const contentRect = local_box_rect(child, "content");
assert_point_approx_equals(quad.p1,
new DOMPoint(childRect.left - relativeRect.left +
contentRect.x,
childRect.top - relativeRect.top +
contentRect.y),
"child content p1 relative to relative");
assert_point_approx_equals(quad.p3,
new DOMPoint(childRect.left - relativeRect.left +
contentRect.x + contentRect.width,
childRect.top - relativeRect.top +
contentRect.y + contentRect.height),
"child content p3 relative to relative");
}, "getBoxQuads() combines box and relativeTo options");
test(() => {
const quad = rtl.getBoxQuads()[0];
assert_true(quad.p1.x < quad.p2.x, "p1 remains the physical left corner");
assert_true(quad.p1.y < quad.p4.y, "p1 remains the physical top corner");
}, "getBoxQuads() point order is physical for bidi and writing-mode");
for (const box of boxes) {
test(() => {
const style = getComputedStyle(inline);
const borderRects = inline.getClientRects();
const quads = inline.getBoxQuads({box});
assert_greater_than(quads.length, 1, "inline produces fragments");
assert_equals(quads.length, borderRects.length, "fragment count");
let left = 0;
let top = 0;
let right = 0;
let bottom = 0;
if (box === "margin") {
left = i => i === 0 ? -parseFloat(style.marginLeft) : 0;
top = -parseFloat(style.marginTop);
right = i => i === quads.length - 1 ? parseFloat(style.marginRight) : 0;
bottom = parseFloat(style.marginBottom);
} else if (box === "padding" || box === "content") {
left = i => i === 0 ? parseFloat(style.borderLeftWidth) : 0;
top = parseFloat(style.borderTopWidth);
right = i => i === quads.length - 1
? -parseFloat(style.borderRightWidth) : 0;
bottom = -parseFloat(style.borderBottomWidth);
if (box === "content") {
const borderLeft = left;
const borderRight = right;
left = i => borderLeft(i) +
(i === 0 ? parseFloat(style.paddingLeft) : 0);
top += parseFloat(style.paddingTop);
right = i => borderRight(i) -
(i === quads.length - 1 ? parseFloat(style.paddingRight) : 0);
bottom -= parseFloat(style.paddingBottom);
}
}
for (let i = 0; i < quads.length; ++i) {
const rect = borderRects[i];
const fragmentLeft = typeof left === "function" ? left(i) : left;
const fragmentRight = typeof right === "function" ? right(i) : right;
assert_rect_approx_equals(
quads[i].getBounds(),
new DOMRect(rect.left + fragmentLeft, rect.top + top,
rect.width + fragmentRight - fragmentLeft,
rect.height + bottom - top),
`${box} fragment ${i}`
);
}
}, `getBoxQuads({box: "${box}"}) applies box edges to inline fragments`);
}
for (const box of boxes) {
test(() => {
assert_greater_than(document.scrollingElement.scrollWidth,
document.documentElement.clientWidth,
"the document has horizontal overflow");
assert_greater_than(document.scrollingElement.scrollHeight,
document.documentElement.clientHeight,
"the document has vertical overflow");
const quad = document.getBoxQuads({box})[0];
assert_point_approx_equals(quad.p1, new DOMPoint(0, 0),
"document quad origin");
assert_approx_equals(quad.getBounds().width,
document.documentElement.clientWidth,
GEOMETRY_UTILS_EPSILON,
"document quad excludes the vertical scrollbar");
assert_approx_equals(quad.getBounds().height,
document.documentElement.clientHeight,
GEOMETRY_UTILS_EPSILON,
"document quad excludes the horizontal scrollbar");
}, `Document.getBoxQuads({box: "${box}"}) returns the layout viewport`);
}
</script>