Skip to content

Commit 0e66815

Browse files
progerschromium-wpt-export-bot
authored andcommitted
[html-in-canvas] Skip transforms from drawable element to canvas
Canvas element transforms map a drawable element's border box directly to the canvas coordinate space. For nested drawable elements, ancestor transforms, offsets, and clips between the drawable element and the canvas should be skipped. This patch updates the element canvas transform paint property node to be parented directly under the canvas. Similarly, this patch updates MapLocalToAncestor, MapAncestorToLocal, and MapToVisualRectInAncestorSpaceInternal to jump directly between the drawable element and the canvas. Additionally, GetUsedCanvasTransform has been changed to require a valid CanvasForDrawing. Additionally, pseudo elements have been prevented from being drawable. Bug: 532229486 Change-Id: I37fa0a15b5156574bdd5f4bd879272ca05e467be Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8266994 Reviewed-by: Stefan Zager <szager@chromium.org> Commit-Queue: Philip Rogers <pdr@chromium.org> Cr-Commit-Position: refs/heads/main@{#1683625}
1 parent 0ff01d5 commit 0e66815

1 file changed

Lines changed: 216 additions & 67 deletions

File tree

html/canvas/element/manual/draw-element-image/canvas-transform-get-bounding-client-rect.tentative.html

Lines changed: 216 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,81 +5,230 @@
55
<link rel="help" href="https://github.com/WICG/html-in-canvas">
66
<script src="/resources/testharness.js"></script>
77
<script src="/resources/testharnessreport.js"></script>
8+
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
89
</head>
9-
<body style="margin: 0;">
10+
<body style="margin: 0;" onload="runTest()">
1011
<canvas id="canvas" style="position: absolute; left: 10px; top: 20px; width: 400px; height: 400px;" layoutsubtree>
11-
<div id="target" style="position: absolute; left: 0; top: 0; width: 100px; height: 50px;"></div>
12+
<div id="target" style="position: absolute; left: 0; top: 0; width: 100px; height: 50px;">
13+
<span id="nested-inline" drawable style="font: 25px/1 Ahem;">X</span>
14+
<div id="nested-block" drawable style="width: 20px; height: 30px;"></div>
15+
<div id="nested-non-drawable" style="width: 15px; height: 25px;"></div>
16+
</div>
1217
</canvas>
1318
<div id="outside" style="position: absolute; left: 10px; top: 10px; width: 100px; height: 50px;"></div>
1419

1520
<script>
1621
'use strict';
1722

18-
// TODO(crbug.com/532229486): Add a test of inlines.
19-
20-
test(() => {
21-
const target = document.getElementById('target');
22-
let rect = target.getBoundingClientRect();
23-
assert_equals(rect.left, 10);
24-
assert_equals(rect.top, 20);
25-
26-
// Translate.
27-
target.setCanvasTransform(new DOMMatrix().translate(30, 40));
28-
rect = target.getBoundingClientRect();
29-
assert_equals(rect.left, 40);
30-
assert_equals(rect.top, 60);
31-
32-
// Scale and translate.
33-
target.setCanvasTransform(new DOMMatrix().translate(50, 60).scale(2, 3));
34-
rect = target.getBoundingClientRect();
35-
assert_equals(rect.left, 60);
36-
assert_equals(rect.top, 80);
37-
assert_equals(rect.width, 200);
38-
assert_equals(rect.height, 150);
39-
40-
// Compound with CSS transform: Canvas transform applies before CSS transform.
41-
// With CSS transform scale(2) around center (50, 25), target bounds are
42-
// [-50..150, -25..75] relative to (0, 0).
43-
// Canvas transform translates by (30, 40), giving [-20..180, 15..115].
44-
// Adding canvas position (10, 20) yields rect left = -10, top = 35.
45-
// If canvas transform were applied after CSS transform, left would be 20, top would be 75.
46-
target.setCanvasTransform(new DOMMatrix().translate(30, 40));
47-
target.style.transform = 'scale(2)';
48-
rect = target.getBoundingClientRect();
49-
assert_approx_equals(rect.left, 10 + 30 - 50, 0.01);
50-
assert_approx_equals(rect.top, 20 + 40 - 25, 0.01);
51-
assert_approx_equals(rect.width, 200, 0.01);
52-
assert_approx_equals(rect.height, 100, 0.01);
53-
54-
// Compound with CSS translate and transform properties: Canvas transform
55-
// applies before all CSS transforms, including individual transform properties.
56-
target.setCanvasTransform(new DOMMatrix().translate(30, 40));
57-
target.style.translate = '5px 10px';
58-
target.style.transform = 'scale(2)';
59-
rect = target.getBoundingClientRect();
60-
assert_approx_equals(rect.left, 10 + 30 + 5 - 50, 0.01);
61-
assert_approx_equals(rect.top, 20 + 40 + 10 - 25, 0.01);
62-
assert_approx_equals(rect.width, 200, 0.01);
63-
assert_approx_equals(rect.height, 100, 0.01);
64-
65-
// Reset.
66-
target.style.translate = '';
67-
target.style.transform = '';
68-
target.setCanvasTransform(new DOMMatrix());
69-
rect = target.getBoundingClientRect();
70-
assert_equals(rect.left, 10);
71-
assert_equals(rect.top, 20);
72-
assert_equals(rect.width, 100);
73-
assert_equals(rect.height, 50);
74-
}, 'setCanvasTransform() affects getBoundingClientRect() on canvas child');
75-
76-
test(() => {
77-
const outside = document.getElementById('outside');
78-
outside.setCanvasTransform(new DOMMatrix().translate(50, 50));
79-
const rect = outside.getBoundingClientRect();
80-
assert_equals(rect.left, 10);
81-
assert_equals(rect.top, 10);
82-
}, 'setCanvasTransform() does not affect getBoundingClientRect() outside canvas subtree');
23+
const runTest = () => {
24+
test(() => {
25+
const target = document.getElementById('target');
26+
let rect = target.getBoundingClientRect();
27+
assert_equals(rect.left, 10);
28+
assert_equals(rect.top, 20);
29+
30+
// Translate.
31+
target.setCanvasTransform(new DOMMatrix().translate(30, 40));
32+
rect = target.getBoundingClientRect();
33+
assert_equals(rect.left, 40);
34+
assert_equals(rect.top, 60);
35+
36+
// Scale and translate.
37+
target.setCanvasTransform(new DOMMatrix().translate(50, 60).scale(2, 3));
38+
rect = target.getBoundingClientRect();
39+
assert_equals(rect.left, 60);
40+
assert_equals(rect.top, 80);
41+
assert_equals(rect.width, 200);
42+
assert_equals(rect.height, 150);
43+
44+
// Compound with CSS transform: Canvas transform applies before CSS transform.
45+
// With CSS transform scale(2) around center (50, 25), target bounds are
46+
// [-50..150, -25..75] relative to (0, 0).
47+
// Canvas transform translates by (30, 40), giving [-20..180, 15..115].
48+
// Adding canvas position (10, 20) yields rect left = -10, top = 35.
49+
// If canvas transform were applied after CSS transform, left would be 20, top would be 75.
50+
target.setCanvasTransform(new DOMMatrix().translate(30, 40));
51+
target.style.transform = 'scale(2)';
52+
rect = target.getBoundingClientRect();
53+
assert_approx_equals(rect.left, 10 + 30 - 50, 0.01);
54+
assert_approx_equals(rect.top, 20 + 40 - 25, 0.01);
55+
assert_approx_equals(rect.width, 200, 0.01);
56+
assert_approx_equals(rect.height, 100, 0.01);
57+
58+
// Compound with CSS translate and transform properties: Canvas transform
59+
// applies before all CSS transforms, including individual transform properties.
60+
target.setCanvasTransform(new DOMMatrix().translate(30, 40));
61+
target.style.translate = '5px 10px';
62+
target.style.transform = 'scale(2)';
63+
rect = target.getBoundingClientRect();
64+
assert_approx_equals(rect.left, 10 + 30 + 5 - 50, 0.01);
65+
assert_approx_equals(rect.top, 20 + 40 + 10 - 25, 0.01);
66+
assert_approx_equals(rect.width, 200, 0.01);
67+
assert_approx_equals(rect.height, 100, 0.01);
68+
69+
// Reset.
70+
target.style.translate = '';
71+
target.style.transform = '';
72+
target.setCanvasTransform(new DOMMatrix());
73+
rect = target.getBoundingClientRect();
74+
assert_equals(rect.left, 10);
75+
assert_equals(rect.top, 20);
76+
assert_equals(rect.width, 100);
77+
assert_equals(rect.height, 50);
78+
}, 'setCanvasTransform() affects getBoundingClientRect() on canvas child');
79+
80+
test(() => {
81+
const nestedInline = document.getElementById('nested-inline');
82+
let rect = nestedInline.getBoundingClientRect();
83+
assert_equals(rect.left, 10);
84+
assert_equals(rect.top, 20);
85+
assert_equals(rect.width, 25);
86+
assert_equals(rect.height, 25);
87+
88+
// Translate.
89+
nestedInline.setCanvasTransform(new DOMMatrix().translate(30, 40));
90+
rect = nestedInline.getBoundingClientRect();
91+
assert_equals(rect.left, 10 + 30);
92+
assert_equals(rect.top, 20 + 40);
93+
assert_equals(rect.width, 25);
94+
assert_equals(rect.height, 25);
95+
96+
// Scale and translate.
97+
nestedInline.setCanvasTransform(new DOMMatrix().translate(50, 60).scale(2, 3));
98+
rect = nestedInline.getBoundingClientRect();
99+
assert_equals(rect.left, 10 + 50);
100+
assert_equals(rect.top, 20 + 60);
101+
assert_equals(rect.width, 2 * 25);
102+
assert_equals(rect.height, 3 * 25);
103+
104+
// Test that canvas transform is unaffected by CSS margin.
105+
nestedInline.setCanvasTransform(new DOMMatrix().translate(50, 60).scale(2, 3));
106+
nestedInline.style.marginLeft = '12px';
107+
rect = nestedInline.getBoundingClientRect();
108+
assert_equals(rect.left, 10 + 50);
109+
assert_equals(rect.top, 20 + 60);
110+
assert_equals(rect.width, 2 * 25);
111+
assert_equals(rect.height, 3 * 25);
112+
113+
// Reset.
114+
nestedInline.style.marginLeft = '0';
115+
nestedInline.setCanvasTransform(new DOMMatrix());
116+
rect = nestedInline.getBoundingClientRect();
117+
assert_equals(rect.left, 10);
118+
assert_equals(rect.top, 20);
119+
assert_equals(rect.width, 25);
120+
assert_equals(rect.height, 25);
121+
}, 'setCanvasTransform() affects getBoundingClientRect() on an inline nested drawable');
122+
123+
test(() => {
124+
const target = document.getElementById('target');
125+
const nestedBlock = document.getElementById('nested-block');
126+
127+
// When ancestor has a canvas transform set, a nested drawable without its own
128+
// canvas transform does not inherit the ancestor's canvas transform.
129+
target.setCanvasTransform(new DOMMatrix().translate(100, 200));
130+
let rect = nestedBlock.getBoundingClientRect();
131+
assert_equals(rect.left, 10);
132+
assert_equals(rect.top, 20);
133+
134+
// When the nested drawable also sets a canvas transform, it is relative to
135+
// the canvas and does not include the ancestor's canvas transform.
136+
nestedBlock.setCanvasTransform(new DOMMatrix().translate(30, 40));
137+
rect = nestedBlock.getBoundingClientRect();
138+
assert_equals(rect.left, 10 + 30);
139+
assert_equals(rect.top, 20 + 40);
140+
assert_equals(rect.width, 20);
141+
assert_equals(rect.height, 30);
142+
143+
// Reset.
144+
target.setCanvasTransform(new DOMMatrix());
145+
nestedBlock.setCanvasTransform(new DOMMatrix());
146+
}, 'setCanvasTransform() on ancestor and nested drawable');
147+
148+
test(() => {
149+
const nestedBlock = document.getElementById('nested-block');
150+
let rect = nestedBlock.getBoundingClientRect();
151+
assert_equals(rect.left, 10);
152+
assert_equals(rect.top, 20);
153+
assert_equals(rect.width, 20);
154+
assert_equals(rect.height, 30);
155+
156+
// Translate.
157+
nestedBlock.setCanvasTransform(new DOMMatrix().translate(30, 40));
158+
rect = nestedBlock.getBoundingClientRect();
159+
assert_equals(rect.left, 10 + 30);
160+
assert_equals(rect.top, 20 + 40);
161+
162+
// Scale and translate.
163+
nestedBlock.setCanvasTransform(new DOMMatrix().translate(50, 60).scale(2, 3));
164+
rect = nestedBlock.getBoundingClientRect();
165+
assert_equals(rect.left, 10 + 50);
166+
assert_equals(rect.top, 20 + 60);
167+
assert_equals(rect.width, 2 * 20);
168+
assert_equals(rect.height, 3 * 30);
169+
170+
// Test that canvas transform is unaffected by CSS margin.
171+
nestedBlock.setCanvasTransform(new DOMMatrix().translate(50, 60).scale(2, 3));
172+
nestedBlock.style.marginLeft = '12px';
173+
rect = nestedBlock.getBoundingClientRect();
174+
assert_equals(rect.left, 10 + 50);
175+
assert_equals(rect.top, 20 + 60);
176+
assert_equals(rect.width, 2 * 20);
177+
assert_equals(rect.height, 3 * 30);
178+
nestedBlock.style.marginLeft = '';
179+
180+
// Test that canvas transform applies before CSS transform.
181+
// If canvas transform applied after CSS transform, the translation of
182+
// 50, 60 would be scaled 2x.
183+
nestedBlock.setCanvasTransform(new DOMMatrix().translate(50, 60).scale(2, 3));
184+
nestedBlock.style.transform = 'scale(2)';
185+
nestedBlock.style.transformOrigin = 'top left';
186+
rect = nestedBlock.getBoundingClientRect();
187+
assert_equals(rect.left, 10 + 50);
188+
assert_equals(rect.top, 20 + 60);
189+
assert_equals(rect.width, 2 * 2 * 20);
190+
assert_equals(rect.height, 2 * 3 * 30);
191+
192+
// Reset.
193+
nestedBlock.style.marginLeft = '';
194+
nestedBlock.style.transform = '';
195+
nestedBlock.style.transformOrigin = '';
196+
nestedBlock.setCanvasTransform(new DOMMatrix());
197+
rect = nestedBlock.getBoundingClientRect();
198+
assert_equals(rect.left, 10);
199+
assert_equals(rect.top, 20);
200+
assert_equals(rect.width, 20);
201+
assert_equals(rect.height, 30);
202+
}, 'setCanvasTransform() affects getBoundingClientRect() on a block nested drawable');
203+
204+
test(() => {
205+
const target = document.getElementById('target');
206+
const nestedNonDrawable = document.getElementById('nested-non-drawable');
207+
208+
// A non-drawable descendant inherits its ancestor's canvas transform.
209+
const initialRect = nestedNonDrawable.getBoundingClientRect();
210+
target.setCanvasTransform(new DOMMatrix().translate(100, 200));
211+
let rect = nestedNonDrawable.getBoundingClientRect();
212+
assert_equals(rect.left, initialRect.left + 100);
213+
assert_equals(rect.top, initialRect.top + 200);
214+
assert_equals(rect.width, initialRect.width);
215+
assert_equals(rect.height, initialRect.height);
216+
217+
// Reset.
218+
target.setCanvasTransform(new DOMMatrix());
219+
rect = nestedNonDrawable.getBoundingClientRect();
220+
assert_equals(rect.left, initialRect.left);
221+
assert_equals(rect.top, initialRect.top);
222+
}, 'A non-drawable descendant inherits its ancestor\'s canvas transform');
223+
224+
test(() => {
225+
const outside = document.getElementById('outside');
226+
outside.setCanvasTransform(new DOMMatrix().translate(50, 50));
227+
const rect = outside.getBoundingClientRect();
228+
assert_equals(rect.left, 10);
229+
assert_equals(rect.top, 10);
230+
}, 'setCanvasTransform() does not affect getBoundingClientRect() outside canvas subtree');
231+
}
83232
</script>
84233
</body>
85234
</html>

0 commit comments

Comments
 (0)