Monday 7 November 2011
Drawing DOM Content To Canvas
I see that one feature Web developers are asking for is the ability to draw DOM objects to an HTML canvas. I've got good news and bad news about that. The bad news is that such a feature has to be designed very carefully or it's a security risk. The good news is that this feature already exists in Web standards, and it works in Firefox and Chrome (at least)!
Here's a demo of rendering HTML elements into a canvas. The trick is to create an SVG image containing the content you want to render, in this case a <foreignObject> containing HTML, and draw that image to the canvas. Constructing the SVG image as a data: URI lets you compose the content dynamically without requiring an external resource load. There is nothing tricky going on here; this feature works exactly as it should according to the specs.
The resulting canvas should even be "origin-clean" so the toDataURL() method works on it. Currently that isn't true in Firefox or Chrome. In Firefox, that's because of bug 672013; we clear the origin-clean flag whenever we draw an SVG image to a canvas. We'll fix that shortly. The problem with Chrome is a general Webkit bug that documents loaded from data: URIs are considered to have a different origin from the containing document, contrary to the spec.
One obvious question is how this feature can be secure, in light of the concerns I raised above. We're relying on the fact that our implementation of SVG images is very restrictive. For example, we do not allow an SVG image to load any external resource, even one that appears to be from the same domain. Resources such as JPEGs or IFRAMEs in an SVG image have to be included as data: URIs so they're inline in the image. Scripting in an SVG image is not allowed, there is no way to access the DOM of an SVG image from other scripts, and DOM elements in SVG images cannot receive input events. Thus there is no way to load privileged information into a form control (such as a full path in a <input type="file">) and render it. We don't apply visited-link styles in SVG images, and we don't render native themes in SVG images. Some of those limitations probably make this technique unusable for some use-cases, especially the restriction that script can't directly touch DOM nodes that get rendered to the canvas --- but that restriction is important for security.
Another limitation that's a bit annoying is that you have to use XML/XHTML syntax for the SVG image. It would be nice if you could use HTML syntax for the HTML content at least. Maybe it would be worth defining an "HTML image format" (image/html?) that uses the HTML5 parser to parse the image contents. Another issue is having to wait for the onload event to fire, drawing asynchronously. It might make sense to add a convenience API like "drawHTML(string, x, y, w, h)" which behaves like drawing an SVG image containing <foreignObject> (so doesn't introduce any new security concerns) but uses the HTML5 parser and is much more convenient. On the other hand, it might be better for developers to experiment with the existing solution and use that experience when designing a better API.
Comments