Bug 43911

Summary: <foreignObject> descendants are stripped from <use> shadow trees
Product: WebKit Reporter: Silvia Pfeiffer <silviapfeiffer1>
Component: SVGAssignee: Nobody <webkit-unassigned>
Status: NEW    
Severity: Normal CC: agafvv, karlcow, krit, lck60980, shirzadmahdi902, simon.fraser, zimmermann
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: PC   
OS: OS X 10.5   
Attachments:
Description Flags
testcase-reduced.svg
none
rendering in safari, firefox, chrome
none
testcase control
none
rendering in safari, firefox, chrome (control) none

Silvia Pfeiffer
Reported 2010-08-12 06:06:36 PDT
Google Chrome: 6.0.472.25 dev Safari/Webkit: Version 5.0.1 (6533.17.8, r63640) I have a foreignObject in a <defs> and cannot apply a mask to it, because the foreignObject keeps getting rendered fully. Here is the example: <?xml version="1.0"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="270px" width="480px"> <mask id="c1" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse"> <circle id="circle" cx="240" cy="135" r="135" fill="white"/> </mask> <defs> <g id="videoGroup"> <foreignObject width="100%" height="100%"> <body xmlns="http://www.w3.org/1999/xhtml"> <video id="vid" class="target" height="270px" width="480px" controls="controls" autoplay="autoplay"> <source src="http://annodex.net/~silvia/itext/chocolate_rain/chocolate_rain.mp4" type="video/mp4"/> <source src="http://annodex.net/~silvia/itext/chocolate_rain/chocolate_rain.ogv" type="video/ogg"/> </video> </body> </foreignObject> </g> </defs> <use xlink:href="#videoGroup" mask="url(#c1)"/> </svg>
Attachments
testcase-reduced.svg (1.15 KB, image/svg+xml)
2026-05-11 02:14 PDT, Karl Dubost
no flags
rendering in safari, firefox, chrome (126.45 KB, image/png)
2026-05-11 02:15 PDT, Karl Dubost
no flags
testcase control (919 bytes, image/svg+xml)
2026-05-11 02:17 PDT, Karl Dubost
no flags
rendering in safari, firefox, chrome (control) (249.97 KB, image/png)
2026-05-11 02:18 PDT, Karl Dubost
no flags
Karl Dubost
Comment 2 2026-05-11 02:14:45 PDT
Created attachment 479594 [details] testcase-reduced.svg This is a testcase without video.
Karl Dubost
Comment 3 2026-05-11 02:15:57 PDT
Created attachment 479595 [details] rendering in safari, firefox, chrome Firefox shows a disk with the content of the ForeignObject Chrome and Safari do not show anything.
Karl Dubost
Comment 4 2026-05-11 02:17:24 PDT
Created attachment 479596 [details] testcase control Same thing but without using defs.
Karl Dubost
Comment 5 2026-05-11 02:18:26 PDT
Created attachment 479597 [details] rendering in safari, firefox, chrome (control) This time it renders everywhere.
Karl Dubost
Comment 6 2026-05-11 02:19:18 PDT
CONTROL. <svg xmlns="http://www.w3.org/2000/svg" height="270" width="480"> <mask id="c1" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse"> <circle cx="240" cy="135" r="135" fill="white"/> </mask> <g mask="url(#c1)"> <foreignObject width="100%" height="100%"> <body xmlns="http://www.w3.org/1999/xhtml" style="margin:0; width:480px; height:270px; background: linear-gradient(90deg, #f0a, #0af); font: bold 48px sans-serif; color: white; display: flex; align-items: center; justify-content: center;"> foreignObject content </body> </foreignObject> </g> </svg> TESTCASE <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="270" width="480"> <mask id="c1" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse"> <circle cx="240" cy="135" r="135" fill="white"/> </mask> <defs> <g id="fobjGroup"> <foreignObject width="100%" height="100%"> <body xmlns="http://www.w3.org/1999/xhtml" style="margin:0; width:480px; height:270px; background: linear-gradient(90deg, #f0a, #0af); font: bold 48px sans-serif; color: white; display: flex; align-items: center; justify-content: center;"> foreignObject content </body> </foreignObject> </g> </defs> <use xlink:href="#fobjGroup" mask="url(#c1)"/> </svg>
Karl Dubost
Comment 7 2026-05-11 02:34:05 PDT
*** Bug 91515 has been marked as a duplicate of this bug. ***
Karl Dubost
Comment 8 2026-05-11 03:04:30 PDT
I changed the title to be closer to the real issue. Neither <defs> nor the mask is load-bearing in the repro; both are red herrings. The same bug is reported in bug 91515 (2012) with a simpler testcase. Minimal reproduction: <svg xmlns="http://www.w3.org/2000/svg"> <defs> <g id="g"> <foreignObject width="100" height="100"> <body xmlns="http://www.w3.org/1999/xhtml">hello</body> </foreignObject> </g> </defs> <use href="#g"/> </svg> Expected: "hello" renders (as it does in Firefox). Observed in WebKit (and Chrome): nothing renders. Root cause <foreignObject> is explicitly disallowed inside <use> shadow trees. SVGUseElement::isDisallowedElement() at Source/WebCore/svg/SVGUseElement.cpp:223 is an allow-list of SVG template elements (a, circle, g, path, rect, svg, symbol, text, use, …). foreignObject is deliberately absent, so removeDisallowedElementsFromSubtree() strips it when cloning the referenced subtree into the shadow tree. No RenderSVGForeignObject is ever created; there is nothing to mask, nothing to paint. https://searchfox.org/wubkat/rev/d221d71a6a560cb45bb3e9b23e526b6bcfa9e4b3/Source/WebCore/svg/SVGUseElement.cpp#223-258 ```cpp static inline bool NODELETE isDisallowedElement(const SVGElement& element) { using namespace ElementNames; // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used // (i.e., "instanced") in the SVG document via a 'use' element." // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text' // Excluded are anything that is used by reference or that only make sense to appear once in a document. switch (element.elementName()) { case SVG::a: case SVG::circle: case SVG::desc: case SVG::ellipse: case SVG::g: case SVG::image: case SVG::line: case SVG::metadata: case SVG::path: case SVG::polygon: case SVG::polyline: case SVG::rect: case SVG::svg: case SVG::switch_: case SVG::symbol: case SVG::text: case SVG::textPath: case SVG::title: case SVG::tref: case SVG::tspan: case SVG::use: return false; default: break; } return true; } ``` Existing coverage that codifies the current behavior: - LayoutTests/svg/custom/use-on-disallowed-foreign-object-{1..6}.svg See https://searchfox.org/wubkat/source/LayoutTests/svg/custom/use-on-disallowed-foreign-object-1.svg and following tests This looks hard to fix. Per comments 1 and 3 on bug 91515: 1. Whitelisting foreignObjectTag alone is insufficient. HTML descendants inside the foreignObject (<body>, <p>, <video>, <div>, …) are also not in the allow-list, so they would be stripped too. removeDisallowedElementsFromSubtree() needs to be reworked to permit the foreignObject subtree wholesale once we enter HTML content. 2. Allowing HTML inside <use> shadow trees opens reference-cycle classes that WebKit's current <use>-cycle detector does not cover. Examples: <foreignObject><iframe src="other.html"></foreignObject> // other.html embeds containing SVG via <img> <foreignObject><div style="background-image: url(blub.svg)"></foreignObject> <foreignObject><object data="foo.svg"></foreignObject> Cycle detection would need to extend to every HTML element/attribute that can reference external resources (iframe @src, img @src, CSS url(), object, embed, link, script, …). This is the security-relevant piece of the work. Spec The spec does not forbid foreignObject in <use> referenced content. SVG 2 §5.5 ("The 'use' element") describes the shadow tree as a clone of the referenced subtree; there is no exception for foreignObject. https://w3c.github.io/svgwg/svg2-draft/struct.html#UseElement Browser landscape - Firefox: renders as expected. - WebKit, Chrome: strip the foreignObject during cloning. If anyone tackles the fix, it is a two-part project: 1. teach removeDisallowedElementsFromSubtree() to treat the foreignObject boundary as an HTML island, 2. extend the <use> cycle detector to cover HTML resource-reference attributes. And other areas do not help: * In-tree <use> → <use> cycles are caught by walking the correspondingElement lineage of the cloned shadow tree; if an ancestor was already cloned from the same target, the new clone is refused. * Cross-document SVG cycles (SVG loaded as an image via <img>, background-image, or SVG <image>) have no cycle detection at all — they are prevented by total isolation: an SVGImage runs in a maximum-sandbox Page and is blocked from making any non-data: subresource request.
Note You need to log in before you can comment on or make changes to this bug.