RESOLVED FIXED Bug 91790
SVG Fragment is not rendered if it is the css background image of an HTML element
https://bugs.webkit.org/show_bug.cgi?id=91790
Summary SVG Fragment is not rendered if it is the css background image of an HTML ele...
Ken Collins
Reported 2012-07-19 15:02:57 PDT
Created attachment 153357 [details] Version 13.0.1 of Firefox renders the SVG fragment identifier in CSS background correctly. A upcoming technique for SVG sprites, dubbed stacks, has been described here. http://simurai.com/post/20251013889/svg-stacks It relies on using an SVG fragment identifier (target) in the IRI. Tho Webkit supports this in many other places, it does not support the fragment identifier when used in a CSS background. Here is a JSFiddle linked form the above article that demonstrates the issue. http://jsfiddle.net/simurai/7GCGr/
Attachments
Version 13.0.1 of Firefox renders the SVG fragment identifier in CSS background correctly. (122.25 KB, image/png)
2012-07-19 15:02 PDT, Ken Collins
no flags
Patch (8.64 KB, patch)
2014-02-25 09:37 PST, Antoine Quint
no flags
Patch (10.62 KB, patch)
2015-06-09 10:31 PDT, Said Abou-Hallawa
no flags
Patch (11.51 KB, patch)
2015-06-09 16:07 PDT, Said Abou-Hallawa
no flags
Dirk Schulze
Comment 1 2013-03-04 06:35:19 PST
SVG stacks will not be supported for CSS properties taking CSS Image values. This includes, but is not limited to, background-image, mask-image, border-image. This is a resolution of the SVG and CSS WG to differ between resources (like SVG gradients, masks, clipPath) and image values during parse time of CSS. This is a security requirement to protect the users privacy and safety. See following discussions for further information: http://lists.w3.org/Archives/Public/www-style/2012Oct/0406.html http://lists.w3.org/Archives/Public/www-style/2012Oct/0765.html As an alternative, use SVG image sprites. They work exactly like normal image sprites. For CSS properties: Short-term: use background-position to shift your sprites Long-term: the fragment identifier #xywh=0,0,100,100 will allow to get a sprite out of an image. For image element: Short-term: use the fragment identifier #viewbox(0,0,100,100) Long-term: the fragment identifier #xywh=0,0,100,100 will allow to get a sprite out of an image.
Antoine Quint
Comment 2 2014-02-24 07:13:14 PST
Dirk, you say "This is a resolution of the SVG and CSS WG to differ between resources (like SVG gradients, masks, clipPath) and image values during parse time of CSS. This is a security requirement to protect the users privacy and safety." In the example, the reference is not to a paint server, but to an SVG image which should respect the :target pseudo-class to display only the element referenced by the identifier in the URL. It seems odd to me that opening http://example.com/sprite.svg#foo would render something if opened as a standalone document but wouldn't if referenced as a CSS background-image. I'll also note that the <img src=""> fails as well. I think both of these should just work…
Dirk Schulze
Comment 3 2014-02-24 08:03:09 PST
(In reply to comment #2) > Dirk, you say "This is a resolution of the SVG and CSS WG to differ between resources (like SVG gradients, masks, clipPath) and image values during parse time of CSS. This is a security requirement to protect the users privacy and safety." > > In the example, the reference is not to a paint server, but to an SVG image which should respect the :target pseudo-class to display only the element referenced by the identifier in the URL. It seems odd to me that opening http://example.com/sprite.svg#foo would render something if opened as a standalone document but wouldn't if referenced as a CSS background-image. > > I'll also note that the <img src=""> fails as well. I think both of these should just work… The resolution changed. The UA doesn't deal with it at parse time anymore. (Which makes implementing URL correctly a bit harder in WebKit for CSS though.) Instead, all resources (image or SVG resources like <linearGradient>, <clipPath> or <mask>) are loaded with the same restrictions. On rendering we shall decide if we interpret the resources as image or SVG resources. The restrictions say that the resource in question is not allowed to fetch any other resources (CSS, images or scripts) and is not allowed to execute scripts. This keep the door open for SVG stacks IMO. Even if me comments was correct at the time, it isn't anymore. Note that the original resolution just affected CSS's url() function. <img src=""> was never affected. Reopening the bug.
Antoine Quint
Comment 4 2014-02-25 07:02:20 PST
When loading an SVG file standalone, the fragment identifier is parsed and results in a call to Document::setCSSTarget() as a result of shouldPerformFragmentNavigation() returning true in FrameLoader::loadWithDocumentLoader(). This doesn't happen when loading the same URL through an <img>.
Antoine Quint
Comment 5 2014-02-25 07:56:15 PST
I think the problem actually stems from the fact that when we create the SVGDocument for an SVG image loaded by an <img>, SVGImage::dataChanged() is called higher up in the call stack and calls the following line: loader.activeDocumentLoader()->writer().begin(URL()); // create the empty document Note the empty URL, which eventually causes us to bail in FrameView::scrollToFragment() when we notice that we don't have a fragment identifier for the URL nor do we have a pre-set CSS target. I think we might be able to deal with this in SVGImage::dataChanged() by getting the loaded resource's URL through the image observer's resource request.
Antoine Quint
Comment 6 2014-02-25 09:12:46 PST
My proposed fix is to pass a new URL parameter when creating an SVGImage in CachedImage::createImage(). Patch coming up.
Antoine Quint
Comment 7 2014-02-25 09:37:10 PST
Antoine Quint
Comment 8 2014-02-25 10:20:27 PST
Hmm, as it turns out the original test case at http://jsfiddle.net/simurai/7GCGr/ isn't fixed, it seems SVG resources served through the network fail to load completely.
Antoine Quint
Comment 9 2014-02-26 06:18:36 PST
MemoryCache::removeFragmentIdentifierIfNeeded() is stripping the resource identifier when loading an SVG resource for an <img> element via HTTP: URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL) { if (!originalURL.hasFragmentIdentifier()) return originalURL; // Strip away fragment identifier from HTTP URLs. // Data URLs must be unmodified. For file and custom URLs clients may expect resources // to be unique even when they differ by the fragment identifier only. if (!originalURL.protocolIsInHTTPFamily()) return originalURL; URL url = originalURL; url.removeFragmentIdentifier(); return url; }
Antoine Quint
Comment 10 2014-02-26 13:30:21 PST
Comment on attachment 225157 [details] Patch Marking my original patch as obsolete, it would only work with non-HTTP URLs (file and custom protocols). A complete patch will be more complex, I'll break this into individual tasks for the various paths needed, starting with <img src> support.
Antoine Quint
Comment 11 2014-02-26 13:55:16 PST
I'll start with https://bugs.webkit.org/show_bug.cgi?id=129387 to make fragment identifiers work with SVG images via <img>.
Radar WebKit Bug Importer
Comment 12 2015-02-16 15:42:56 PST
Said Abou-Hallawa
Comment 13 2015-06-09 10:31:59 PDT
Darin Adler
Comment 14 2015-06-09 11:13:51 PDT
Comment on attachment 254578 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=254578&action=review > Source/WebCore/ChangeLog:3 > + SVG Fragment Identifier With CSS Backgrounds Not A Good Bug Title; might be worth fixing it to state the problem we are fixing > Source/WebCore/ChangeLog:23 > + (WebCore::CachedImage::load): > + (WebCore::CachedImage::checkShouldPaintBrokenImage): being consistent. Sorry, don’t understand this comment and rationale for changing these. Is the url() function exactly the same as resourceRequest().url(), or is there a possible behavior change? > Source/WebCore/loader/cache/CachedImage.cpp:345 > + RefPtr<SVGImage> svgImage = SVGImage::create(this, url()); As long as you are changing this, I suggest changing the argument type to ImageObserver& instead of ImageObserver*, since null is not legal. > Source/WebCore/svg/graphics/SVGImageCache.cpp:68 > + Image* image = imageForRenderer(renderer); > + return image == Image::nullImage() ? m_svgImage->size() : image->size(); Seems messy to map null pointers to nullImage inside imageForRenderer and then have to check explicitly for nullImage. Maybe a helper function named findImageForRenderer or something that returns null, and imageForRenderer can be a wrapper that adds in the nullImage. > Source/WebCore/svg/graphics/SVGImageCache.cpp:78 > + ImageForContainerMap::const_iterator it = m_imageForContainerMap.find(renderer); Better to use auto here than a specific type. > Source/WebCore/svg/graphics/SVGImageCache.cpp:82 > RefPtr<SVGImageForContainer> imageForContainer = it->value; Type here should be auto&; no need to copy the RefPtr out of the map. I’m not even sure we should bother with a local variable for this. > Source/WebCore/svg/graphics/SVGImageCache.cpp:84 > return imageForContainer.get(); I think this whole function could be written like this: auto* image = renderer ? m_imageForContainerMap.get(renderer) : nullptr; return image ? image : Image::nullImage(); I understand that the code wants to assert that the image size is not empty, but I really think the two line version is way easier to read than the version with many lines of code that uses find and iterators. If the assertion is really needed, we could do this five line version: auto* image = renderer ? m_imageForContainerMap.get(renderer) : nullptr; if (!image) image = Image::nullImage(); ASSERT(!image->size().isEmpty()); return image; > Source/WebCore/svg/graphics/SVGImageCache.h:50 > + Image* imageForRenderer(const RenderObject*) const; Does this really need to be callable on a null renderer? Might be worth looking at the call sites and figuring out if the type can be perhaps const RenderElement& instead of const RenderObject*. Probably a project for later.
Said Abou-Hallawa
Comment 15 2015-06-09 16:07:44 PDT
Said Abou-Hallawa
Comment 16 2015-06-09 16:14:46 PDT
(In reply to comment #14) > Comment on attachment 254578 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=254578&action=review > > > Source/WebCore/ChangeLog:3 > > + SVG Fragment Identifier With CSS Backgrounds > > Not A Good Bug Title; might be worth fixing it to state the problem we are > fixing > Done. The bug title was changed. > > Source/WebCore/ChangeLog:23 > > + (WebCore::CachedImage::load): > > + (WebCore::CachedImage::checkShouldPaintBrokenImage): being consistent. > > Sorry, don’t understand this comment and rationale for changing these. Is > the url() function exactly the same as resourceRequest().url(), or is there > a possible behavior change? > Done. The comment was fixed. And yes, url(), resourceRequest().url() and m_resourceRequest.url() are all the same. > > Source/WebCore/loader/cache/CachedImage.cpp:345 > > + RefPtr<SVGImage> svgImage = SVGImage::create(this, url()); > > As long as you are changing this, I suggest changing the argument type to > ImageObserver& instead of ImageObserver*, since null is not legal. > Done. Type of the argument was changed to be reference. > > Source/WebCore/svg/graphics/SVGImageCache.cpp:68 > > + Image* image = imageForRenderer(renderer); > > + return image == Image::nullImage() ? m_svgImage->size() : image->size(); > > Seems messy to map null pointers to nullImage inside imageForRenderer and > then have to check explicitly for nullImage. Maybe a helper function named > findImageForRenderer or something that returns null, and imageForRenderer > can be a wrapper that adds in the nullImage. > Done. A new helper function: findImageForRenderer() was added. > > Source/WebCore/svg/graphics/SVGImageCache.cpp:78 > > + ImageForContainerMap::const_iterator it = m_imageForContainerMap.find(renderer); > > Better to use auto here than a specific type. > > > Source/WebCore/svg/graphics/SVGImageCache.cpp:82 > > RefPtr<SVGImageForContainer> imageForContainer = it->value; > > Type here should be auto&; no need to copy the RefPtr out of the map. I’m > not even sure we should bother with a local variable for this. > > > Source/WebCore/svg/graphics/SVGImageCache.cpp:84 > > return imageForContainer.get(); > > I think this whole function could be written like this: > > auto* image = renderer ? m_imageForContainerMap.get(renderer) : nullptr; > return image ? image : Image::nullImage(); > > I understand that the code wants to assert that the image size is not empty, > but I really think the two line version is way easier to read than the > version with many lines of code that uses find and iterators. If the > assertion is really needed, we could do this five line version: > > auto* image = renderer ? m_imageForContainerMap.get(renderer) : nullptr; > if (!image) > image = Image::nullImage(); > ASSERT(!image->size().isEmpty()); > return image; > Done. I kept the assertion but I think we should return if (!image) immediately since we should not assert in this case. > > Source/WebCore/svg/graphics/SVGImageCache.h:50 > > + Image* imageForRenderer(const RenderObject*) const; > > Does this really need to be callable on a null renderer? Might be worth > looking at the call sites and figuring out if the type can be perhaps const > RenderElement& instead of const RenderObject*. Probably a project for later. Yes. I confirmed that this function can be called with a null renderer. So I kept the check for null renderer as is.
WebKit Commit Bot
Comment 17 2015-06-09 17:46:31 PDT
Comment on attachment 254611 [details] Patch Clearing flags on attachment: 254611 Committed r185395: <http://trac.webkit.org/changeset/185395>
WebKit Commit Bot
Comment 18 2015-06-09 17:46:36 PDT
All reviewed patches have been landed. Closing bug.
Steven Vachon
Comment 19 2016-10-20 13:01:24 PDT
How is this fixed? The fragments are ignored and all are rendered: http://codepen.io/chriscoyier/pen/GndhE
Said Abou-Hallawa
Comment 20 2016-10-21 15:03:54 PDT
(In reply to comment #19) > How is this fixed? The fragments are ignored and all are rendered: > http://codepen.io/chriscoyier/pen/GndhE Thanks for reporting this problem and providing a test case. There are two issues here: 1. The SVG fragment is not respected in the <img> element if the source is an HTTP URL (i.e. <img src="some_http_url#fragment_id">). It happens because the SVGImage gets only the image URL without the fragment in the case of HTTP URL. See MemoryCache::shouldRemoveFragmentIdentifier(). The reason for removing the fragment is the CachedImage in this case is loaded from the network only once. The CachedImage and hence the SVGImage are shared among multiple RenderImages. Because the SVGImage gets the URL without the fragment, it does not call FrameView::scrollToFragment() when drawing. I filed https://bugs.webkit.org/show_bug.cgi?id=163811 to track this issue. 2. The SVG fragment is not respected if the SVG image is drawn as the background of an element and the fragment is the id of a <view> element (i.e. <div style= "background: url(some_url#some_view_element_id);"></div>). The reason is the following. We call SVGSVGElement::scrollToAnchor() to set the viewBox of the root <svg> to the viewBox of the <view> element and we call RenderSVGResource::markForLayoutAndParentResourceInvalidation() for the root renderer but we never run the layout for the SVGImage before drawing it. I filed https://bugs.webkit.org/show_bug.cgi?id=163812 to track this issue.
Said Abou-Hallawa
Comment 21 2016-10-21 17:18:18 PDT
Steven Vachon
Comment 22 2016-10-21 17:42:16 PDT
Thanks!!
Ian Yang
Comment 23 2017-09-04 01:27:31 PDT
I'm confused. This bug still hasn't been fixed (http://caniuse.com/#feat=svg-fragment), but the Status has been changed to RESOLVED FIXED?
Said Abou-Hallawa
Comment 24 2017-09-05 13:11:01 PDT
(In reply to Ian Yang from comment #23) > I'm confused. This bug still hasn't been fixed > (http://caniuse.com/#feat=svg-fragment), but the Status has been changed to > RESOLVED FIXED? I am not sure if I understand your question. Do you have a test case which does not work after the resolution of the bug? If this is the case, please attach it to this bug or open a new bug and describe what you expect and what WebKit currently renders.
Pavel
Comment 25 2017-09-15 07:05:16 PDT
(In reply to Said Abou-Hallawa from comment #24) > (In reply to Ian Yang from comment #23) > > I'm confused. This bug still hasn't been fixed > > (http://caniuse.com/#feat=svg-fragment), but the Status has been changed to > > RESOLVED FIXED? > > I am not sure if I understand your question. Do you have a test case which > does not work after the resolution of the bug? If this is the case, please > attach it to this bug or open a new bug and describe what you expect and > what WebKit currently renders. Testcase: http://plnkr.co/edit/oaifzmh2g8pMkPtisyFP?p=preview Not working on Safari with 603. Nothing being rendered at all.
Silac
Comment 26 2018-03-27 01:30:48 PDT
11.0.3, original test case is still relevant http://jsfiddle.net/simurai/7GCGr/
Note You need to log in before you can comment on or make changes to this bug.