Monday, September 1st, 2008
foreignObject: Hey, you’ve got HTML in my SVG!
<p>The SVGforeignObject tag allows you to mix non-SVG content into your page. For example, you could drop some HTML in the middle of an SVG element.
Firefox 3 recently announced support for foreignObject; both Safari and Opera have supported this tag for awhile.
From Mark Finkle comes two examples (1, 2). The first embeds some HTML text into SVG shapes to take advantage of HTML's nice reflowing and text abilities:
Here's the code that accomplishes this:
-
<?xml version="1.0" standalone="yes"?>
-
<svg xmlns = "http://www.w3.org/2000/svg">
-
<rect x="10" y="10" width="100" height="150" fill="gray"/>
-
<foreignobject x="10" y="10" width="100" height="150">
-
<body xmlns="http://www.w3.org/1999/xhtml">
-
<div>Here is a <strong>paragraph</strong> that requires <em>word wrap</em></div>
-
</body>
-
-
</foreignobject>
-
-
<circle cx="200" cy="200" r="100" fill="blue" stoke="red"/>
-
<foreignobject x="120" y="120" width="180" height="180">
-
<body xmlns="http://www.w3.org/1999/xhtml">
-
<div>
-
<ul>
-
<li><strong>First</strong> item</li>
-
-
<li><em>Second</em> item</li>
-
<li>Thrid item</li>
-
</ul>
-
</div>
-
</body>
-
</foreignobject>
-
</svg>
The second embeds a full iframe, and then rotates it using SVG:
The first example works fine on both Safari and Firefox 3. On Safari, the second example initially has a display glitch which fixes itself when you resize the browser; I'm sure the SVG source could be changed to accomodate this to work correctly initially, perhaps by changing the viewBox attribute.
From Ted Mielczarek comes an even more ambitious example. He embeds a Google Map into a foreignObject in an SVG document, and then uses an SVG transform to rotate the entire Google Map! Here is a short video showing this in action; I can even click and drag the map as it rotates!
Safari can display this as well, but the behavior is a bit different: the entire map rotates out of view quickly; again, I'm still learning cross-browser SVG but I believe that this could be fixed perhaps by changing the SVG and testing on Safari:
The code that achieves this:
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml">
-
<head>
-
<script type="text/javascript">
-
//<![CDATA[
-
var angle = 0;
-
function rotate()
-
{
-
angle++;
-
if(angle> 360)
-
angle = 0;
-
-
document.getElementById("g").setAttribute("transform", "rotate(" + angle + ",1000,1000)");
-
}
-
-
function doResize()
-
{
-
var w = window.innerWidth/2 - 1000;
-
var h = window.innerHeight/2 - 1000;
-
document.getElementById("g0").setAttribute("transform","translate("+w+","+h+")");
-
}
-
-
function setup()
-
{
-
window.addEventListener("resize", doResize, false);
-
doResize();
-
setInterval(rotate,100);
-
}
-
-
//]]>
-
</script>
-
</head>
-
-
<body onload="setup()">
-
<svg :svg xmlns:svg="http://www.w3.org/2000/svg" version="1.1" width="100%" height="96%">
-
</svg><svg :g id="g0" transform="translate(-1000,-1000)">
-
</svg><svg :g id="g">
-
</svg><svg :foreignObject width="2000" height="2000">
-
<iframe style="border: none" src="fo.html" width="2000" height="2000">
-
</iframe>
-
</svg>
-
-
-
-
-
</body>
-
</html>
Where fo.html is simply a normal HTML file that embeds a Google Map.
For the foreignObject support in Firefox 3, there are some provisos. From a comment by Robert Longson:
There are some caveats to using HTML text instead of SVG text.
a) SVG text does not scale independently of the SVG image so if you increase or decrease the text size in the browser it has no effect on your SVG text. If you use HTML text however this will change size, possibly obscuring parts of your drawing.
b) SVG text ignores the browser’s minimum font size. I.e. Tools->Options->Fonts & Colours(Advanced)->Minimum Font Size. If you have this set your HTML replacement text may come out bigger than you expect on your drawing.
To see the full new set of SVG tags in Firefox 3 (including SVG filters), see here.
Related Content:













“The first example works fine on both Safari and Firefox 3.” Now that’s an overstatement. Safari has a rather serious display glitch that renders the element pretty unusable. Firefox 3 does it well. Everything works as it should, although it’s slow. But still, faster than the other browsers.
Opera does render it, however, it treats it as an image, so you can’t interact with it (Meaning no links, no scroll, etc).
alert(document.cookie)
And one more way to embed malicious code inside SVGs… working fine in Opera, FF2 (without really rendering the svg of course), FF3 and Safari.
Here’s the stripped code: http://phpfi.com/349332
x00mario: I don’t think you know what you’re talking about. SVG supports the script tag & ECMAScript naitively. (they call it ECMAScript on a technicality, its still JS)
Example:
alert(document.cookie);
Oops, pardon me. I misused the code tag, I guess.
http://phpfi.com/349371
rethinker: I think you don’t know what I was talking about. I am pretty aware of the multiple ways to execute JS from within a SVG file. I was just ranting about the addition of another and IMHO useless way in major browser which makes filtering and parsing way harder for security affine developers.
Why is it that you have access to the DOM of the embedding page when you execute the JS in the SVG? Why can’t there be a default sandbox model that is capable of making it more secure to deal with user uploaded SVG files etc.?
And, one of the examples from my OSCON talk:
http://xdraw.org/CSS_slides/examples/foreignobject.svg