Skip to content

Add HTML-in-Canvas APIs - #11588

Open
foolip wants to merge 129 commits into
mainfrom
foolip/html-in-canvas
Open

foolip wants to merge 129 commits into
mainfrom
foolip/html-in-canvas

Conversation

@foolip

@foolip foolip commented Aug 21, 2025

Copy link
Copy Markdown
Member

(See WHATWG Working Mode: Changes for more details.)


/browsers.html ( diff )
/canvas.html ( diff )
/dnd.html ( diff )
/dom.html ( diff )
/index.html ( diff )
/indices.html ( diff )
/infrastructure.html ( diff )
/interaction.html ( diff )
/popover.html ( diff )
/references.html ( diff )
/rendering.html ( diff )
/webappapis.html ( diff )

Handwavy things that need fleshing out are marked with 👋
@foolip
foolip marked this pull request as draft August 21, 2025 12:52

@Kaiido Kaiido left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad to see this being worked on, thanks.

Not quite sure how much discussion should be held at this stage. So to note, this doesn't seem to fully match the latest state of https://github.com/WICG/html-in-canvas. e.g. the rename to drawHTMLElement. The layoutsubtree attribute is also missing along with the implications to the existing fallback contents.
Still, thanks for making this move.

Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
@foolip foolip changed the title Stub out canvas.drawElement() Stub out canvas.drawHTMLElement() Aug 29, 2025
@foolip

foolip commented Aug 29, 2025

Copy link
Copy Markdown
Member Author

@Kaiido thank you for the review! I've fleshed things out more, renaming to drawHTMLElement() (because drawElement() will probably not be workable in WebGL) and adding the layoutsubtree attribute.

There's still some handwaving going on of course, in particular what causes the subtree to be laid out but not painted.

@foolip foolip mentioned this pull request Sep 2, 2025
Comment thread source Outdated
@foolip foolip changed the title Stub out canvas.drawHTMLElement() Stub out canvas.drawElementImage() Sep 16, 2025
@foolip foolip changed the title Stub out canvas.drawElementImage() Add HTML-in-Canvas APIs Sep 17, 2025
@foolip

foolip commented Sep 17, 2025

Copy link
Copy Markdown
Member Author

I've fleshed this out some more now, in particular the hit testing.

@Kaiido Kaiido left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One common complain with the use of dictionaries in the Canvas2D API is that this makes GC kick in very often during animations which has a non-negligible performance cost.
This API shape makes a big use of such dictionaries with one for the wrapper CanvasElementHitTestRegion and then a nested one for the CanvasHitTestRect, and I guess there will be scenarios where multiple of these will need to be updated at every frame. Since the values are copied over from the passed objects to new internal objects, it's unclear if even a careful author, who would try to reuse the same objects, could avoid GC at all here.

On the other hand, I really like how this API shape enables future additions like using a Path2D, or even a bitmap mask, instead of a CanvasHitTestRect. (btw can we bikeshed on rect for that purpose?)

It's not my area of expertise, but would an actual exposed interface allow for non copy from JS, so that authors can just update the regions instead of setting new ones?

Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source
<h3>The <dfn element-attr for="html-global"><code
data-x="attr-drawable">drawable</code></dfn> attribute</h3>

<p>All <span>HTML elements</span> may have the <code data-x="attr-drawable">drawable</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't it supposed to be set on Element? (WICG/html-in-canvas#134 (comment))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, should be elements, not "HTML elements", so SVG and MathML can be drawable.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are three parts to how making drawable work on any element:

  • The algorithms that create and draw snapshots should just talk about elements with the drawable attribute. This is already the case, so per current spec it should work.
  • Where drawable is reflected. Instead of putting things on Element we more often use HTMLOrSVGOrMathMLElement, and I think that's probably the right call here too.
  • Conformance or requirements for authors. It should really be up to the SVG and MathML specs to say where drawable attributes can appear. I'll file issues on those specs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that MathML and SVG agree to allow drawable, which it seems like they will, what do we say here? That is, assuming everything @foolip says above happens, what would this text say? Is it just "All ELements ..."?

Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment on lines +149046 to +149047
<li><p>When an element is drawn onto the <code>canvas</code> (or has its <span
data-x="concept-element-canvas-transform">canvas transform</span> set), the element's position is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean that if we draw an element on a canvas, then move it back into another container without calling clearElementGeometry, that element will still have the offset for hit testing?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kaiido -- No, that's not the intention. We are still dialing in the behavior on this, but our intention is that the canvas transform associated with an element will be cleared when a DOM change occurs that makes the element no longer eligible to be drawn in the canvas.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@szager-chromium can you give me the details of what needs to be spec'd here? Removal steps that look clear the canvas transform and any associated snapshot? Should the same also happen (synchronously) when the drawable attribute is removed?

Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
Comment thread source
};</code></pre>

<dl class="domintro">
<dt><code data-x=""><var>elementImage</var>.<span subdfn data-x="dom-ElementImage-width">width</span></code></dt>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need more explication of width and height. Here's some non-conforming text:

These dimensions, when used as the dx and dy parameters to drawElementImage, will cause the image to be drawn at the same size it would be were it placed outside the canvas. when the dx and dy parameters are omitted from a call to drawElementImage, these are the default values that are used.

For the WebGL and WebGPU interfaces, if these dimensions are used as the destination size when drawing to a texture; and the texture is then blitted directly into the canvas (i.e., 1:1 texel-to-canvas-pixel ratio), then the element will appear in the canvas with the same size it would have were it placed outside the canvas.

I think it would be nifty if these definitions could somehow be worked into the existing definition of "natural dimensions", but that's beyond my spec-fu.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will cause the image to be drawn at the same size it would be were it placed outside the canvas

Should note (if not noted already somewhere) whether this is assuming the default canvas sizing (1 canvas bitmap pixel = 1 CSS px) or assumes that the developer has made their canvas hidpi (which can be approximated with devicePixelRatio or done precisely using ResizeObserver).

I think it would be nifty if these definitions could somehow be worked into the existing definition of "natural dimensions", but that's beyond my spec-fu.

I'm not an expert in the CSS/HTML sizing stuff either, but I think you'll want a different concept than "natural dimensions". Imagine for example <canvas layoutsubtree><img src="/api/v1/web-embed/proxy?proxyUrl=https%3A%2F%2Fgithub.com%2Fwhatwg%2Fhtml%2Fpull%2Fmy5x5.png&mode=full" style="width:10px"></canvas>. The img is an "object" with "natural dimensions" of 5x5, but it's also an HTML element with a layout size of 10x10 (CSS) px (IDK the spec term for this). Since HIC views the DOM subtree and not the object, I think it should see a size of 10x10 (or 10*devicePixelRatio or whatever)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "natural size" makes no assumptions about the CSS-to-canvas-grid ratio.

Agreed that we can't just use "natural dimensions" outright, but this does feel like a related concept so I hope it can be proximal to "natural dimensions".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it, the key problem is that we can't say "border box width in CSS pixels" because that's not what it is. It's actually the border box width/height multiplied by the ratio of output bitmap pixel size to CSS pixel size. Is that actually sufficiently descriptive to use?

We have the necessary language down below in the "Update the Rendering" section. I think the cleanest way to do this would be to create concepts called "snapshot width" and "snapshot height", then define them using the language from "Update the Rendering", and then you can refer to the concept every time you need this particular width and height.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@schenney-chromium Snapshots already have a width and height, see right after the definition of "element image snapshot". See also the line starting with "Let width be element's border box's width in CSS pixels multiplied by scaleX".

Does this already do the right thing, then?

Comment thread source

<div algorithm>
<p>The <dfn method for="HTMLCanvasElement"><code
data-x="dom-canvas-updateElementGeometry">updateElementGeometry(<var>element</var>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument should be ElementOrElementImage, and the error conditions need to be updated as well. Same goes for OffscreenCanvas.updateElementGeometry, below.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument already is (Element or ElementImage) in the IDL and the "get element image snapshot" algorithm handles both, so this already seems correct to me. Do I misunderstand?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @foolip.

Comment thread source Outdated
<p>The <dfn element-attr for="canvas"><code
data-x="attr-canvas-layoutsubtree">layoutsubtree</code></dfn> attribute is a <span>boolean
attribute</span>. If present, <span data-x="concept-tree-descendant">descendants</span> of the
<code>canvas</code> element are laid out, so that those with the <code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly wrong now, I think? Only drawable elements (and their descendants) are laid out, not all canvas descendants.

Comment thread source Outdated
data-x="attr-canvas-layoutsubtree">layoutsubtree</code></dfn> attribute is a <span>boolean
attribute</span>. If present, <span data-x="concept-tree-descendant">descendants</span> of the
<code>canvas</code> element are laid out, so that those with the <code
data-x="attr-drawable">drawable</code> attribute specified can be drawn using <code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have a definition for the drawable element, so we're not having to refer to them verbosely in several places. The explainer just uses "drawable element", which I think works, and then also "drawable subtree" for the descendants that aren't part of another drawable tree.

Comment thread source
<span>output bitmap</span> pixels), a <dfn
data-x="concept-element-image-snapshot-translation-matrix">translation matrix</dfn>, and a <dfn
data-x="concept-element-image-snapshot-scaling-matrix">scaling matrix</dfn>. A snapshot captures
the <span>border box</span> of the element it was created from. Descendants of the element that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we want to reuse the same language as "capture the image" in View Transitions here, with appropriate adjustments (only rendering the drawable subtree, probably adjusting which properties are ignored). It even has the right pattern for the nested drawables.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, probably want to remove the "border box" text here. The border-box is used to define the origin of the drawing coords, and the default width/height, but the snapshot isn't actually limited to it in any way.

@schenney-chromium schenney-chromium Sep 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we want to reuse the same language as "capture the image" in View Transitions here, with appropriate adjustments (only rendering the drawable subtree, probably adjusting which properties are ignored). It even has the right pattern for the nested drawables.

I don't agree with this. The view transitions capture is different in almost all of the details that it discusses. captureElementImage cannot capture the document root, does not necessarily capture ink overflow, still captures scrollbars, does not apply effects like opacity and filters from the ancestor chain that would otherwise affect the rendering. And we do not produce an image.

@schenney-chromium schenney-chromium Sep 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, probably want to remove the "border box" text here. The border-box is used to define the origin of the drawing coords, and the default width/height, but the snapshot isn't actually limited to it in any way.

We do want to remove border box but it needs to be replaced with some clear statement like "everything within the src rect that is generated by drawing the element and its descendants".

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source rect has no place here, there is no source rect associated with paint snaphsot. The paint snapshot has no clipping, and it always includes all sources of ink overflow regardless of extent. For example, if the element has a box-shadow with blur, then the paint snapshot includes all the information necessary to rasterize the element including the infinite box shadow.

Comment thread source
data-x="concept-element-image-originating-canvas">originating <code>canvas</code> element</span>
is not <span>this</span>, then throw a <code>TypeError</code>.</p></li>

<li><p>Let <var>targetElement</var> be <var>snapshot</var>'s <span

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just the element argument? This feels like a strange bit of indirection. (We will need to fetch the originating element if an ElementImage is passed, tho.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment above.

Comment thread source
method steps are:</p>

<ol>
<li><p>Let <var>snapshot</var> be the result of <span>get element image snapshot</span> given

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we capturing a snapshot here, when we're just fetching data from the element?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "get element image snapshot" algorithm only gets the snapshot from the Element-or-ElementImage argument, it doesn't capture a snapshot. That only happens in the rendering loop before "paint" events are fired.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Tab's point is that if step 1 is get element image snapshot given element then the snapshot's element must be element, then we just get the element again and set something on it. Why go round the circle? Is it to handle Element or ElementImage, in which case the algorithm should switch on which one we have.

If <var>element</var> is an Element:
  Set originatingElement to <var>element</var>
Otherwise
  Set originatingElement to <var>element</var>'s <span data-x="concept-element-image-snapshot-originating-element">originating element</span>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is roundabout and targetElement can end up being the same as element, but the snapshot is also how we make sure that the element belongs to the canvas (this). I've tried to do all checks like this, and never check if there's a drawable attribute or walk a tree to check preconditions in the API, instead leaving that entirely to snapshot creation time in the rendering loop.

Does that match the Chromium implementation? Concretely, can elm.updateElementGeometry() be called immediately after elm is inserted as a canvas descendent, or does the rendering loop have to spin once first?

Comment thread source
<code>TypeError</code>.</p></li>

<li>
<p>Paint the area of <var>snapshot</var> given by <var>sourceRect</var> to the specified

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming sourceRect is passed to the snapshot-capturing algo instead, the mention of it here can be dropped; the image you get will be perfectly sized already.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, "specified rectangular area" isn't actually specified. You need to construct this area from dx/y/w/h.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you need to define the destination rect and then refer to it here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's similar language (search "specified rect") for clearRect() and fillRect(), but it's not as obvious here, so I'll use more words.

Comment thread source
<p>Paint the area of <var>snapshot</var> given by <var>sourceRect</var> to the specified
rectangular area.</p>

<p>The element image must be painted in its original orientation, even if <var>dw</var> or

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this imply that the top left corner of the source isn't necessarily painted at (dx, dy), but might be dx+dw and/or dy+dh if w/h are negative? Or is it always at dx/dy and you ignore the sign of width/height, or something else?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means the top left may not be at dx/dy, yes. This is a deliberate choice to match the behavior of drawImage.

Comment thread source
<h3>The <dfn element-attr for="html-global"><code
data-x="attr-drawable">drawable</code></dfn> attribute</h3>

<p>All <span>HTML elements</span> may have the <code data-x="attr-drawable">drawable</code>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, should be elements, not "HTML elements", so SVG and MathML can be drawable.

Comment thread source

<ul>
<li>
<p>Its descendants are laid out. For the purposes of this layout, the <code>canvas</code> parent

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only drawable descendants.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We lay out everything under a content=drawable canvas because technically we need to. Other browsers would also have this requirement.

Comment thread source

<li><p>Descendants are visible to assistive technologies (ATs) by default, but are marked as
offscreen (allowing assistive technologies to filter them). When an element with a <code
data-x="attr-drawable">drawable</code> attribute specified is drawn onto the <code>canvas</code>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only if {preserveElementGeometry:true} wasn't passed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it's updated from updateElementGeometry() even if not drawn.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Maybe use "has non-null canvas transform" or "is in the drawn elements list" or whatever that concept was.

@schenney-chromium schenney-chromium left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most significant things here are improving the concept of the canvas content state and clearing up what width and height are for snapshots.

It is editorial in the sense that it's about spec clarity rather than the behaviors.

Comment thread source
<ul class="brief">
<li>The <dfn data-x-href="https://drafts.csswg.org/css-transforms/#transform-property">'transform'</dfn> property</li>
<li>The <dfn data-x-href="https://drafts.csswg.org/css-transforms/#transform-origin-property">'transform-origin'</dfn> property</li>
<li>The <dfn data-x-href="https://drafts.csswg.org/css-transforms/#transformation-matrix">transformation matrix</dfn></li>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<li>The <dfn data-x-href="/api/v1/web-embed/proxy?proxyUrl=https%3A%2F%2Fdrafts.csswg.org%2Fcss-transforms%2F%23transformation-matrix&mode=full">transformation matrix</dfn></li>
<li>The <dfn data-x-href="/api/v1/web-embed/proxy?proxyUrl=https%3A%2F%2Fdrafts.csswg.org%2Fcss-transforms%2F%23transformation-matrix&mode=full">'transformation matrix'</dfn></li>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't seem right, "transformation matrix" is a concept, not the name of a property.

Comment thread source
undefined <span data-x="dom-canvas-toBlob">toBlob</span>(<span>BlobCallback</span> _callback, optional DOMString type = "image/png", optional any quality);
<span>OffscreenCanvas</span> <span data-x="dom-canvas-transferControlToOffscreen">transferControlToOffscreen</span>();

[<span>CEReactions</span>, <span data-x="xattr-Reflect">Reflect</span>] attribute DOMString <span data-x="dom-canvas-content">content</span>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this up to right below width and height to keep the attributes together.

Comment thread source
data-x="concept-canvas-webgpu">webgpu</dfn> by algorithms defined in this specification.</p>

<p>To keep track of when the <code data-x="event-paint">paint</code> event needs to be fired, a
<code>canvas</code> element also has a <dfn data-x="concept-canvas-force-paint-event">force paint

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<code>canvas</code> element also has a <dfn data-x="concept-canvas-force-paint-event">force paint
<code>canvas</code> element has a <dfn data-x="concept-canvas-force-paint-event">force paint

My interpretation of the "also" in the prose above this is that "in addition to a rendering context bound to the canvas, it also has a canvas context mode ..." So for the things we are adding I would not include the "also".

Comment thread source
<span>output bitmap</span> pixels), a <dfn
data-x="concept-element-image-snapshot-translation-matrix">translation matrix</dfn>, and a <dfn
data-x="concept-element-image-snapshot-scaling-matrix">scaling matrix</dfn>. A snapshot captures
the <span>border box</span> of the element it was created from. Descendants of the element that

@schenney-chromium schenney-chromium Sep 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we want to reuse the same language as "capture the image" in View Transitions here, with appropriate adjustments (only rendering the drawable subtree, probably adjusting which properties are ignored). It even has the right pattern for the nested drawables.

I don't agree with this. The view transitions capture is different in almost all of the details that it discusses. captureElementImage cannot capture the document root, does not necessarily capture ink overflow, still captures scrollbars, does not apply effects like opacity and filters from the ancestor chain that would otherwise affect the rendering. And we do not produce an image.

Comment thread source
<span>output bitmap</span> pixels), a <dfn
data-x="concept-element-image-snapshot-translation-matrix">translation matrix</dfn>, and a <dfn
data-x="concept-element-image-snapshot-scaling-matrix">scaling matrix</dfn>. A snapshot captures
the <span>border box</span> of the element it was created from. Descendants of the element that

@schenney-chromium schenney-chromium Sep 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, probably want to remove the "border box" text here. The border-box is used to define the origin of the drawing coords, and the default width/height, but the snapshot isn't actually limited to it in any way.

We do want to remove border box but it needs to be replaced with some clear statement like "everything within the src rect that is generated by drawing the element and its descendants".

Comment thread source
<dt><code data-x=""><var>event</var>.<span subdfn data-x="dom-CanvasPaintEvent-changedElements">changedElements</span></code></dt>

<dd>
<p>Returns a <span>frozen array</span> of the elements that have changed.</p>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"... and may need to be drawn into the canvas to update their appearance".

Comment thread source
<h3>The <dfn element-attr for="html-global"><code
data-x="attr-drawable">drawable</code></dfn> attribute</h3>

<p>All <span>HTML elements</span> may have the <code data-x="attr-drawable">drawable</code>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that MathML and SVG agree to allow drawable, which it seems like they will, what do we say here? That is, assuming everything @foolip says above happens, what would this text say? Is it just "All ELements ..."?

Comment thread source
<p>All <span>HTML elements</span> may have the <code data-x="attr-drawable">drawable</code>
content attribute set. The <code data-x="attr-drawable">drawable</code> attribute is a
<span>boolean attribute</span>. When present on an element that is a descendant of a
<code>canvas</code> element whose <code data-x="attr-canvas-content">content</code> attribute is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should refer to the internal state I talked about way back near the top, rather than the attribute.

Comment thread source

<ul>
<li>
<p>Its descendants are laid out. For the purposes of this layout, the <code>canvas</code> parent

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We lay out everything under a content=drawable canvas because technically we need to. Other browsers would also have this requirement.

Comment thread source

<li><p>Descendants are visible to assistive technologies (ATs) by default, but are marked as
offscreen (allowing assistive technologies to filter them). When an element with a <code
data-x="attr-drawable">drawable</code> attribute specified is drawn onto the <code>canvas</code>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Maybe use "has non-null canvas transform" or "is in the drawn elements list" or whatever that concept was.

@schenney-chromium schenney-chromium left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some clarification after chatting with Stefan.

Comment thread source
<span>OffscreenCanvas</span> <span data-x="dom-canvas-transferControlToOffscreen">transferControlToOffscreen</span>();

[<span>CEReactions</span>, <span data-x="xattr-Reflect">Reflect</span>] attribute DOMString <span data-x="dom-canvas-content">content</span>;
<span>ElementImage</span> <span data-x="dom-canvas-captureElementImage">captureElementImage</span>(<span>Element</span> element);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can capture ink overflow etc do we need to specify a src rect here after all? Or are we actually capturing a snapshot with width/height equal to the ink overflow rect?

Comment thread source
data-x="concept-element-image-snapshot-width">width</span> and <span
data-x="concept-element-image-snapshot-height">height</span>, respectively.</p></li>

<li><p>If not given, <var>sourceRect</var> must default to a source rectangle at (0, 0) with

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can kill the step because there is no src rect for createElementImage and we want to be able to use the same algorithm. The default src rect for createElementImage is not the same as the one for drawElementImage, or is it? If it is the same, is it impossible to get ink overflow with createElementImage?

Comment thread source
snapshot is immutable once created and is not affected by any later changes to the element.</p>

<p>The internal representation of an <span data-x="concept-element-image-snapshot">element image
snapshot</span> is <span>implementation-defined</span>, but must preserve crisp text and vector

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something more rigorous than "must preserve crisp text and vector graphics"? Like "must be scalable without introducing pixelation effects"?

Comment thread source
reference, an <dfn data-x="concept-element-image-originating-canvas">originating
<code>canvas</code> element</dfn> weak reference, <dfn
data-x="concept-element-image-snapshot-width">width</dfn> and <dfn
data-x="concept-element-image-snapshot-height">height</dfn> (dimensions in the <span

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be better to omit "dimensions in the originating canvas element's output bitmap pixels" here. That's sort of half an explanation, which is probably worse than no explanation at all. Somewhere in the spec (but probably not here) we should have a clear explanation of the geometry concepts, and we should link to it wherever geometry comes into play.

I'm going to offer up some possible language for describing the geometry here, but I'm not sure where it should go in the spec...

First, we need to rigorously define a term which is conceptually similar to the density-corrected natural width and height of <img>'s. I'm going to use the term natural size here, but that overlaps with CSS natural dimensions so we need a differentiated term.

"""
By default, the area of an element drawn by a call to drawElementImage is its border box. Any regular or ink overflow that extends beyond the border box is clipped out. The sx, sy, swidth, and sheight parameters provide a way to adjust the default border box clip -- it can be expanded to include (regular and ink) overflow, or contracted to some area within the border box.

The dwidth and dheight arguments apply a scaling factor when drawing an element. If the dwidth and dheight arguments are not specified in a call to drawElementImage at a time when the canvas's current transformation matrix is the identity matrix, and regardless of any values provided for the sx, sy, swidth, and sheight parameters, the implementation must ensure that the element appears in the canvas with the same on-screen proportions as it would have were it placed outside the canvas with identical computed style.

The natural size of an element is defined as the values, when supplied as the dwidth and dheight arguments to drawElementImage, at a time when the canvas's current transformation matrix is the identity matrix, and with no values provided for the sx, sy, swidth, or sheight arguments, which will cause the element to appear in the canvas with the same on-screen proportions as the element would have were it placed outside the canvas with identical computed style.

The width and height properties of ElementImage contain the natural size of the element at the time the ElementImage was generated.
"""

This is the bedrock principle of the geometry stuff, and everything pretty much derives from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

HTML-in-Canvas