WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
Bug 195802
Prefer null namespace 'href' over 'xlink:href' on SVG elements
https://bugs.webkit.org/show_bug.cgi?id=195802
Summary
Prefer null namespace 'href' over 'xlink:href' on SVG elements
Masataka Yakura
Reported
2019-03-15 07:24:45 PDT
https://trac.webkit.org/changeset/234683/webkit
added support for null-namespace 'href' attribute on SVG elements; however, it doesn't look to conform the spec when there are both 'href' and 'xlink:href' attributes. The spec says:
> When the ‘href’ attribute is present in both the XLink namespace and without a namespace, the value of the attribute without a namespace shall be used. The attribute in the XLink namespace shall be ignored.
-
https://svgwg.org/svg2-draft/linking.html#XLinkRefAttrs
But
https://w3c-test.org/svg/linking/reftests/href-use-element.html
, which is the wpt for, falis on STP (77).
Attachments
Patch
(9.60 KB, patch)
2019-09-02 01:45 PDT
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Archive of layout-test-results from ews213 for win-future
(13.48 MB, application/zip)
2019-09-02 03:28 PDT
,
EWS Watchlist
no flags
Details
Patch
(9.70 KB, patch)
2019-09-03 09:00 PDT
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(12.61 KB, patch)
2019-09-05 18:22 PDT
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(12.36 KB, patch)
2019-09-06 09:13 PDT
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Patch
(12.33 KB, patch)
2019-09-06 12:55 PDT
,
Said Abou-Hallawa
no flags
Details
Formatted Diff
Diff
Show Obsolete
(5)
View All
Add attachment
proposed patch, testcase, etc.
Radar WebKit Bug Importer
Comment 1
2019-03-16 12:07:22 PDT
<
rdar://problem/48955175
>
Binyamin
Comment 2
2019-04-08 05:17:39 PDT
Safari/iOS are the only ones missing this support, even old IE supports it. Details in
https://css-tricks.com/on-xlinkhref-being-deprecated-in-svg/
And FYI "xlink:href is deprecated"
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
, meaning xlink:href eventually must be dropped. Support testcase
https://codepen.io/laukstein/full/MRjPxW
Said Abou-Hallawa
Comment 3
2019-09-02 01:45:09 PDT
Created
attachment 377846
[details]
Patch
EWS Watchlist
Comment 4
2019-09-02 03:28:22 PDT
Comment on
attachment 377846
[details]
Patch
Attachment 377846
[details]
did not pass win-ews (win): Output:
https://webkit-queues.webkit.org/results/12990812
New failing tests: fast/selectors/any-link-basics-2.html
EWS Watchlist
Comment 5
2019-09-02 03:28:25 PDT
Created
attachment 377849
[details]
Archive of layout-test-results from ews213 for win-future The attached test failures were seen while running run-webkit-tests on the win-ews. Bot: ews213 Port: win-future Platform: CYGWIN_NT-10.0-17763-3.0.5-338.x86_64-x86_64-64bit
Said Abou-Hallawa
Comment 6
2019-09-03 09:00:11 PDT
Created
attachment 377898
[details]
Patch
Darin Adler
Comment 7
2019-09-03 11:44:01 PDT
Comment on
attachment 377898
[details]
Patch Is it correct to have special cases for empty strings? What about a string with a single space in it, for example? I agree that null strings, which are used to represent lack of the attribute, are a special case. But I don’t understand why we would special case empty strings, which indicate the attribute without a value or with a value that is explicitly "".
Darin Adler
Comment 8
2019-09-03 11:44:56 PDT
Comment on
attachment 377898
[details]
Patch Aren’t there other call sites where we call getAttribute on href? I seem to remember one using the multiple-argument getAttribute function.
Nikolas Zimmermann
Comment 9
2019-09-03 13:44:32 PDT
(In reply to Darin Adler from
comment #8
)
> Comment on
attachment 377898
[details]
> Patch > > Aren’t there other call sites where we call getAttribute on href? I seem to > remember one using the multiple-argument getAttribute function.
Hi Darin, nice to see you again :-) Oh yes, plenty of call sites: dom/Element.cpp: linkAttribute = getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr); dom/VisitedLinkState.cpp: return &element.getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr); svg/SVGAElement.cpp: m_storedVisitedLinkHash = computeVisitedLinkHash(document().baseURL(), getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr)); svg/SVGAltGlyphElement.cpp: auto target = targetElementFromIRIString(getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr), document()); svg/SVGFontFaceUriElement.cpp: auto src = CSSFontFaceSrcValue::create(getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr), LoadedFromOpaqueSource::No); svg/SVGFontFaceUriElement.cpp: const AtomString& href = getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr); svg/SVGGlyphRefElement.cpp: auto target = targetElementFromIRIString(getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr), document()); svg/SVGImageElement.cpp: return getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr); svg/animation/SVGSMILElement.cpp: auto& href = getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr);
Nikolas Zimmermann
Comment 10
2019-09-03 13:52:11 PDT
(In reply to Darin Adler from
comment #7
)
> Comment on
attachment 377898
[details]
> Patch > > Is it correct to have special cases for empty strings? What about a string > with a single space in it, for example? I agree that null strings, which are > used to represent lack of the attribute, are a special case. But I don’t > understand why we would special case empty strings, which indicate the > attribute without a value or with a value that is explicitly "".
I agree, this seems awkward. Currently both SVGNames::hrefAttr and XLinkNames::hrefAttr are mapped to the same SVGURIReference::m_href object: PropertyRegistry::registerProperty<SVGNames::hrefAttr, &SVGURIReference::m_href>(); PropertyRegistry::registerProperty<XLinkNames::hrefAttr, &SVGURIReference::m_href>(); In an ideal world, we would split this into "m_href" and "m_xlinkHref" and indicate the attribute presence as non-null (opposed to non-empty) strings. Then we could implement the desired SVG2 behaviour right in a custom href() accessor that either returns m_href or m_xlinkHref...
youenn fablet
Comment 11
2019-09-04 00:32:02 PDT
Comment on
attachment 377898
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=377898&action=review
> Source/WebCore/svg/SVGURIReference.cpp:50 > + SVGElement* contextElement = m_href->contextElement();
auto*
> Source/WebCore/svg/SVGURIReference.cpp:52 > + String xLinkHref = contextElement->getAttribute(XLinkNames::hrefAttr);
auto& might be more efficient if possible.
> Source/WebCore/svg/SVGURIReference.cpp:53 > + if (value.isEmpty() && !xLinkHref.isEmpty())
Should it be isEmpty or isNull? Looking at
https://svgwg.org/svg2-draft/linking.html#XLinkRefAttrs
, the spec mostly talks about attribute presence, which I interpret as an isNull check. If so, we could simplify the check here to write: if (name.matches(SVGNames::hrefAttr)) { m_href->setBaseValInternal(value); return; }
> Source/WebCore/svg/SVGURIReference.cpp:56 > + m_href->setBaseValInternal(value);
We could return early here since the if XLinkNames::hrefAttr check should always be false if name is SVGNames::hrefAttr.
> Source/WebCore/svg/SVGURIReference.cpp:60 > + String href = contextElement->getAttribute(SVGNames::hrefAttr);
auto& might be more efficient if possible.
> Source/WebCore/svg/SVGURIReference.cpp:62 > + m_href->setBaseValInternal(value);
Should it be isNull?
> LayoutTests/svg/custom/href-xlink-href-gradient-element.svg:12 > + <linearGradient id="gradient4" xlink:href="#red-gradient" href="#green-gradient"/>
Nice tests! Can we also add tests for href/xlink:href attributes with empty values, and maybe check what other browsers are doing in that specific case?
Said Abou-Hallawa
Comment 12
2019-09-05 18:22:14 PDT
Created
attachment 378144
[details]
Patch
Said Abou-Hallawa
Comment 13
2019-09-05 18:24:22 PDT
(In reply to Darin Adler from
comment #7
)
> Comment on
attachment 377898
[details]
> Patch > > Is it correct to have special cases for empty strings? What about a string > with a single space in it, for example? I agree that null strings, which are > used to represent lack of the attribute, are a special case. But I don’t > understand why we would special case empty strings, which indicate the > attribute without a value or with a value that is explicitly "".
You are right. I changed the code to check for the existence of the attribute, i.e. checking the string isNull().
Said Abou-Hallawa
Comment 14
2019-09-05 23:24:10 PDT
(In reply to Nikolas Zimmermann from
comment #10
)
> (In reply to Darin Adler from
comment #7
) > > Comment on
attachment 377898
[details]
> > Patch > > > > Is it correct to have special cases for empty strings? What about a string > > with a single space in it, for example? I agree that null strings, which are > > used to represent lack of the attribute, are a special case. But I don’t > > understand why we would special case empty strings, which indicate the > > attribute without a value or with a value that is explicitly "". > > I agree, this seems awkward. > > Currently both SVGNames::hrefAttr and XLinkNames::hrefAttr are mapped to the > same SVGURIReference::m_href object: > > PropertyRegistry::registerProperty<SVGNames::hrefAttr, > &SVGURIReference::m_href>(); > PropertyRegistry::registerProperty<XLinkNames::hrefAttr, > &SVGURIReference::m_href>(); > > In an ideal world, we would split this into "m_href" and "m_xlinkHref" and > indicate the attribute presence as non-null (opposed to non-empty) strings. > Then we could implement the desired SVG2 behaviour right in a custom href() > accessor that either returns m_href or m_xlinkHref...
I do not think this solution will work well with the href property. Consider this example: <use id="use" xlink:href="#red-rect"/> <script> var use = document.getElementById("use"); // href1 will be linked to m_xlinkHref whose baseVal is equal to "#red-rect" var href1 = use.href; use.setAttribute("href", "#green-rect"); // href2 will be linked to m_href which is equal to "#green-rect" var href2 = use.href; assert_false(href1.baseVal != href2.baseVal); </script> So in this example, although href1 and href2 are initialized with the same value "use.href", they are referencing two different SVGAnimatedStrings. This is because the custom SVGURIReference::href() will decide to return m_xlinkHref for href1 but will return m_href for href2.
youenn fablet
Comment 15
2019-09-05 23:26:37 PDT
Comment on
attachment 378144
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=378144&action=review
> Source/WebCore/svg/SVGURIReference.cpp:55 > + m_href->setBaseValInternal(value);
We could write this if/else it as a ternary maybe.
> Source/WebCore/svg/SVGURIReference.cpp:58 > + if (name.matches(XLinkNames::hrefAttr)) {
We should either use 'else if' or return inside the next if check.
> Source/WebCore/svg/SVGURIReference.cpp:59 > + if (contextElement->getAttribute(SVGNames::hrefAttr).isNull())
Can we use contextElement->hasAttribute(SVGNames::hrefAttr)?
Said Abou-Hallawa
Comment 16
2019-09-05 23:38:22 PDT
Comment on
attachment 377898
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=377898&action=review
>> Source/WebCore/svg/SVGURIReference.cpp:50 >> + SVGElement* contextElement = m_href->contextElement(); > > auto*
Done.
>> Source/WebCore/svg/SVGURIReference.cpp:52 >> + String xLinkHref = contextElement->getAttribute(XLinkNames::hrefAttr); > > auto& might be more efficient if possible.
I changed the if-statement below such that it does not need to check xLinkHref.isEmpty(). So contextElement->getAttribute(...) was inlined inside setBaseValInternal().
>> Source/WebCore/svg/SVGURIReference.cpp:53 >> + if (value.isEmpty() && !xLinkHref.isEmpty()) > > Should it be isEmpty or isNull? > Looking at
https://svgwg.org/svg2-draft/linking.html#XLinkRefAttrs
, the spec mostly talks about attribute presence, which I interpret as an isNull check. > If so, we could simplify the check here to write: > if (name.matches(SVGNames::hrefAttr)) { > m_href->setBaseValInternal(value); > return; > }
I do not think this suggestion is correct. We need to set m_href to the Xlink::hrefAttr if value.isNull(). Otherwise a case like this will fail <use id="use8" xlink:href="#green-rect" href="#red-rect"/> <script> var use8 = document.getElementById("use8"); // Before this statement, m_herf has the value of the href attribute which is "#red-rect" // After this statement, m_herf has to be set to the value of Xlink::href which is "#green-rect" use8.removeAttribute("href"); </script>
>> Source/WebCore/svg/SVGURIReference.cpp:56 >> + m_href->setBaseValInternal(value); > > We could return early here since the if XLinkNames::hrefAttr check should always be false if name is SVGNames::hrefAttr.
Done.
>> Source/WebCore/svg/SVGURIReference.cpp:62 >> + m_href->setBaseValInternal(value); > > Should it be isNull?
Done.
>> LayoutTests/svg/custom/href-xlink-href-gradient-element.svg:12 >> + <linearGradient id="gradient4" xlink:href="#red-gradient" href="#green-gradient"/> > > Nice tests! > Can we also add tests for href/xlink:href attributes with empty values, and maybe check what other browsers are doing in that specific case?
Done. ast/selectors/any-link-basics-2.html covers many error cases for both href and Xlink::href.
Nikolas Zimmermann
Comment 17
2019-09-06 01:00:21 PDT
(In reply to Said Abou-Hallawa from
comment #14
)
> So in this example, although href1 and href2 are initialized with the same > value "use.href", they are referencing two different SVGAnimatedStrings. > This is because the custom SVGURIReference::href() will decide to return > m_xlinkHref for href1 but will return m_href for href2.
Fully correct, "in an ideal world" was meant to read: it is not possible due to some constraints (which you correctly pointed out). The current solution - last patch - is just fine!
Darin Adler
Comment 18
2019-09-06 09:07:16 PDT
(In reply to Said Abou-Hallawa from
comment #16
)
> I do not think this suggestion is correct. We need to set m_href to the > Xlink::hrefAttr if value.isNull(). Otherwise a case like this will fail > > <use id="use8" xlink:href="#green-rect" href="#red-rect"/> > <script> > var use8 = document.getElementById("use8"); > // Before this statement, m_herf has the value of the href attribute > which is "#red-rect" > // After this statement, m_herf has to be set to the value of > Xlink::href which is "#green-rect" > use8.removeAttribute("href"); > </script>
Do we have a test like this?
Said Abou-Hallawa
Comment 19
2019-09-06 09:13:08 PDT
Created
attachment 378195
[details]
Patch
Said Abou-Hallawa
Comment 20
2019-09-06 09:19:41 PDT
(In reply to Darin Adler from
comment #18
)
> (In reply to Said Abou-Hallawa from
comment #16
) > > I do not think this suggestion is correct. We need to set m_href to the > > Xlink::hrefAttr if value.isNull(). Otherwise a case like this will fail > > > > <use id="use8" xlink:href="#green-rect" href="#red-rect"/> > > <script> > > var use8 = document.getElementById("use8"); > > // Before this statement, m_herf has the value of the href attribute > > which is "#red-rect" > > // After this statement, m_herf has to be set to the value of > > Xlink::href which is "#green-rect" > > use8.removeAttribute("href"); > > </script> > > Do we have a test like this?
Yes. I copied it from the new test svg/custom/href-xlink-href-use-element.svg. And I confirmed this test case fails in WebKit without this patch while it passes in Chrome and FireFox.
WebKit Commit Bot
Comment 21
2019-09-06 12:04:23 PDT
Comment on
attachment 378195
[details]
Patch Rejecting
attachment 378195
[details]
from commit-queue. Failed to run "['/Volumes/Data/EWS/WebKit/Tools/Scripts/webkit-patch', '--status-host=webkit-queues.webkit.org', '--bot-id=webkit-cq-03', 'validate-changelog', '--check-oops', '--non-interactive', 378195, '--port=mac']" exit_code: 1 cwd: /Volumes/Data/EWS/WebKit ChangeLog entry in Source/WebCore/ChangeLog contains OOPS!. Full output:
https://webkit-queues.webkit.org/results/13006551
Said Abou-Hallawa
Comment 22
2019-09-06 12:55:40 PDT
Created
attachment 378220
[details]
Patch
WebKit Commit Bot
Comment 23
2019-09-06 14:57:38 PDT
Comment on
attachment 378220
[details]
Patch Clearing flags on attachment: 378220 Committed
r249593
: <
https://trac.webkit.org/changeset/249593
>
WebKit Commit Bot
Comment 24
2019-09-06 14:57:40 PDT
All reviewed patches have been landed. Closing bug.
Darin Adler
Comment 25
2019-09-06 18:58:48 PDT
Comment on
attachment 378220
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=378220&action=review
I really like the final version of this patch. The idiom seems good!
> Source/WebCore/svg/SVGURIReference.cpp:50 > + auto* contextElement = m_href->contextElement();
What guarantees that contextElement is non-null? And if it’s guaranteed to be non-null, why is this a pointer rather than a reference?
Said Abou-Hallawa
Comment 26
2019-09-09 17:57:17 PDT
Comment on
attachment 378220
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=378220&action=review
>> Source/WebCore/svg/SVGURIReference.cpp:50 >> + auto* contextElement = m_href->contextElement(); > > What guarantees that contextElement is non-null? And if it’s guaranteed to be non-null, why is this a pointer rather than a reference?
Yes it is guaranteed that contextElement is not null. In fact m_href->contextElement() is equal to 'this'. SVGURIReference has to be created as a base class for a concrete SVGElement like SVGImageElement. When the concrete SVGElement is created it passes 'this' as the contextElement to SVGURIReference. SVGURIReference passes the contextElement to its m_href. m_href keeps this back pointer as long as the contextElement lives. The purpose of having contextElement owned by m_href is to give it the ability to synchronize its new baseVal to the contextElement. <script> var use = document.getElementById("use"); var href = use.href; href.baseVal = "
http://example.com
"; console.log(use.getAttribute("href")); // This should show "
http://example.com
". use.remove(); // href should exist after removing its contextElement but no attribute synchronization will be done from now on. </script> -- Why doesn't m_href->contextElement() return SVGElement& instead of SVGElement*? Because the m_href can be detached if the contextElement is deleted. In this case m_href->m_contextElement has to be set to null. -- Why doesn't SVGURIReference retains a SVGElement& instead of getting it through the contextElement() method of any property it owns? It was done this way to reduce the memory size of SVGURIReference by the size of a raw pointer. -- Can't SVGURIReference use downcast<SVGElement>(this) to get the contextElement? No it can't because SVGElement is not a superclass of SVGURIReference? -- Can't we ASSERT(this == m_href->contextElement())? No we can't because the compiler does not send the pointer of the concrete class, e.g. SVGImageElement, as the value of 'this' when SVGURIReference::parseAttribute() is called. It sends a shifted pointer such that it points to the beginning of the data block of SVGURIReference. So although the debugger may show 'this' and m_href->contextElement() have the same value, the expression 'this == m_href->contextElement()' will be devalued to false.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug