Bug 139451 - Only when the SVG is inline and only when a shape is referenced before it is defined, this shape will not be drawn
Summary: Only when the SVG is inline and only when a shape is referenced before it is ...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: SVG (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Said Abou-Hallawa
URL:
Keywords: InRadar
: 105257 136128 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-12-09 11:06 PST by Said Abou-Hallawa
Modified: 2014-12-19 10:36 PST (History)
10 users (show)

See Also:


Attachments
<defs> after <use> (inline) (432 bytes, text/html)
2014-12-09 11:06 PST, Said Abou-Hallawa
no flags Details
<defs> after <use> (non inline) (376 bytes, image/svg+xml)
2014-12-09 11:06 PST, Said Abou-Hallawa
no flags Details
<defs> before <use> (inline) (430 bytes, text/html)
2014-12-09 11:07 PST, Said Abou-Hallawa
no flags Details
<defs> after <use> (inline - xhtml) (432 bytes, application/xhtml+xml)
2014-12-11 12:10 PST, Said Abou-Hallawa
no flags Details
Patch (6.62 KB, patch)
2014-12-11 19:14 PST, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (5.43 KB, patch)
2014-12-12 12:11 PST, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Said Abou-Hallawa 2014-12-09 11:06:03 PST
Created attachment 242942 [details]
<defs> after <use> (inline)

Open the attached test cases in WebKit.

Result: When the SVG is inline (included in an HTML) and when a shape is referenced before it is defined, this shape will not be drawn.  When the same SVG (the one which has the <defs> comes after the <use>) is opened by itself in WebKit, everything is drawn as expected.
Expected: Regardless whether the SVG is inline or not and regardless whether the shapes are defined before they are referenced or after they are referenced, the shapes should be drawn.
Comment 1 Said Abou-Hallawa 2014-12-09 11:06:48 PST
Created attachment 242943 [details]
<defs> after <use> (non inline)
Comment 2 Said Abou-Hallawa 2014-12-09 11:07:26 PST
Created attachment 242944 [details]
<defs> before <use> (inline)
Comment 3 Said Abou-Hallawa 2014-12-09 11:11:07 PST
NOTE: FireFox and Chrome render the first test case as expected.
Comment 4 Radar WebKit Bug Importer 2014-12-09 11:12:05 PST
<rdar://problem/19192678>
Comment 5 Said Abou-Hallawa 2014-12-11 12:10:58 PST
Created attachment 243135 [details]
<defs> after <use> (inline - xhtml)
Comment 6 Said Abou-Hallawa 2014-12-11 12:11:55 PST
It looks like this bug is only related to the html parser. It does not happen if the file extension is xhtml
Comment 7 Said Abou-Hallawa 2014-12-11 19:14:11 PST
Created attachment 243173 [details]
Patch
Comment 8 Dirk Schulze 2014-12-12 04:20:23 PST
Comment on attachment 243173 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=243173&action=review

> Source/WebCore/ChangeLog:34
> +        This actually might not be the most efficient solution here. This invalidate/
> +        update mechanism is very aggressive. While parsing an inline SVG and with
> +        every child gets appended to a target element we will invalidate/update all
> +        shadow trees of the referencing elements till all the children elements get
> +        inserted. If the target SVG element has an 'n' children nodes and there 'm'
> +        other nodes referencing this target elment then invalidating and updating the
> +        shadow tree nodes will be O(n*n*m).

I do not understand what that has to do with the characterData node?

> Source/WebCore/svg/SVGElement.cpp:1099
> +    if (hasID() || change.source != ChildChangeSourceParser)
> +        SVGElementInstance::invalidateAllInstancesOfElement(this);

Ok, so if I understand this part and the comment above, we are invalidating more often. Every time a child changes and the element has an id we always mark all instance for invalidation and rebuild the trees, right?  To your comment above, this is one of the critical code paths. <use> element is specifically slow in WebKit.

If there is a difference between XML and HTML on characterData, why do we fix the issue here?

> LayoutTests/svg/in-html/defs-after-use-expected.html:4
> +    <g transform="translate(10,10)">

Is the transform needed? just putting x="10" y="10" should work for rectangles. (There might be AA issues on circles and curves though.)

> LayoutTests/svg/in-html/defs-after-use.html:16
> +    <defs>
> +      <g id="red-rect">
> +        <rect x="0" y="0" width="100" height="50" fill="red"/>
> +      </g>
> +    </defs>
> +    <use x="10" y="10" xlink:href="#red-rect"/>

IMO this can just use the rect directly. If that actual test works we don't know if this referencing works. The second <use> overdraws this one.
Comment 9 Said Abou-Hallawa 2014-12-12 12:11:06 PST
Created attachment 243209 [details]
Patch
Comment 10 Said Abou-Hallawa 2014-12-12 12:41:32 PST
(In reply to comment #8)
> Comment on attachment 243173 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=243173&action=review
> 
> > Source/WebCore/ChangeLog:34
> > +        This actually might not be the most efficient solution here. This invalidate/
> > +        update mechanism is very aggressive. While parsing an inline SVG and with
> > +        every child gets appended to a target element we will invalidate/update all
> > +        shadow trees of the referencing elements till all the children elements get
> > +        inserted. If the target SVG element has an 'n' children nodes and there 'm'
> > +        other nodes referencing this target elment then invalidating and updating the
> > +        shadow tree nodes will be O(n*n*m).
> 
> I do not understand what that has to do with the characterData node?

When parsing the following SVG:

  <svg xmlns="http://www.w3.org/2000/svg"
  	  xmlns:xlink="http://www.w3.org/1999/xlink"
  	  width="1000px" height="1000px"
  	  viewBox="0 0 1000 1000">
    <use x="425" y="80" xlink:href="#helmet"/>
    <defs>
      <g id="helmet">
        <path d="M 0 0 L 150 0 A75,75 0 0,0 0 0 z" fill="#6E808B"/>
        <rect x="0" y="0" width="150" height="20" fill="#48565e"/>
      </g>
    </defs>
  </svg>

We get the following DOM tree:

#document  0x116818600 (renderer 0x11650c7f0) (child needs style recalc)
  svg 0x119f02460 (renderer 0x10c171670) (child needs style recalc)
    #text  0x628000080190 "\n "
    use 0x111204e20 (renderer 0x640000180b60) 
      #document-fragment 0x6480001c0e10 (renderer 0x0) 
        g  0x111009000 (renderer 0x6400001809c0) 
          #text  0x640000080820 "\n   "
          path  0x111014500 (renderer 0x6400001e0100) 
          #text  0x640000080910 "\n   "
          rect  0x11100a190 (renderer 0x11100f170) 
          #text  0x640000080780 "\n  "
    #text  0x648000082670 "\n "
    defs  0x110705860 (renderer 0x600000105220) (child needs style recalc)
      #text  0x610000081ea0 "\n  "
      g  0x111311580 (renderer 0x600000185210) 
        #text  0x650000080f50 "\n   "
        path  0x10c176380 (renderer 0x6080001e2f00) 
        #text  0x60000008ef60 "\n   "
        rect  0x10c6259c0 (renderer 0x116517480) 
        #text  0x608000087940 "\n  "
      #text  0x640000080a50 "\n "
    #text  0x650000080ff0 "\n"

The HTML and the XML parsers create a Text element after each SVG element. In the XML code path and when we start processing a new tag, we append the buffered text to the leaf text element which is XMLDocumentParser::m_leafTextNode, see XMLDocumentParser::exitText().  This function causes the following stack of calls:

#0	WebCore::SVGElement::childrenChanged() 
#1	WebCore::CharacterData::dispatchModifiedEvent() 
#2	WebCore::CharacterData::setDataAndUpdate()
#3	WebCore::CharacterData::appendData() 
#4	WebCore::XMLDocumentParser::exitText() 
#5	WebCore::XMLDocumentParser::startElementNs()

WebCore::CharacterData::dispatchModifiedEvent() sends the source of change as ContainerNode::ChildChangeSourceAPI to SVGElement::childrenChanged() which considers this as a legitimate reason to invalidate the referencing elements because it assumes the children were changed by something but not the parser. This might be a bug but it should not be specific to the SVG shadow tree invalidate/update.

> 
> > Source/WebCore/svg/SVGElement.cpp:1099
> > +    if (hasID() || change.source != ChildChangeSourceParser)
> > +        SVGElementInstance::invalidateAllInstancesOfElement(this);
> 
> Ok, so if I understand this part and the comment above, we are invalidating
> more often. Every time a child changes and the element has an id we always
> mark all instance for invalidation and rebuild the trees, right?  To your
> comment above, this is one of the critical code paths. <use> element is
> specifically slow in WebKit.
> 
> If there is a difference between XML and HTML on characterData, why do we
> fix the issue here?
> 

Fixed: I moved the invalidation to SVGElement::finishParsingChildren() which gets called when all the children are parsed. And for the difference between HTML and XML, I explained above why the bug was not repro in XML code path which, I think, happened accidentally. Although we are doing more invalidation for the XML with this change than we do for the HTML, I think this is the right place to have the invalidation. We should fix the XML Text element issue to not cause any update. But I think this problem should be addressed in a separate bug.

> > LayoutTests/svg/in-html/defs-after-use-expected.html:4
> > +    <g transform="translate(10,10)">
> 
> Is the transform needed? just putting x="10" y="10" should work for
> rectangles. (There might be AA issues on circles and curves though.)
> 

Done.

> > LayoutTests/svg/in-html/defs-after-use.html:16
> > +    <defs>
> > +      <g id="red-rect">
> > +        <rect x="0" y="0" width="100" height="50" fill="red"/>
> > +      </g>
> > +    </defs>
> > +    <use x="10" y="10" xlink:href="#red-rect"/>
> 
> IMO this can just use the rect directly. If that actual test works we don't
> know if this referencing works. The second <use> overdraws this one.

Done. But I changed the test a little to include two <use> elements:
1st: references two <rect>s, the first <rect> comes before the user and the second <rect> comes after the <use>.
2nd: references two <rect>s, the two <rect>s come after the <use>.
Comment 11 Said Abou-Hallawa 2014-12-12 12:58:26 PST
*** Bug 105257 has been marked as a duplicate of this bug. ***
Comment 12 Said Abou-Hallawa 2014-12-12 12:59:42 PST
*** Bug 136128 has been marked as a duplicate of this bug. ***
Comment 13 WebKit Commit Bot 2014-12-19 10:36:36 PST
Comment on attachment 243209 [details]
Patch

Clearing flags on attachment: 243209

Committed r177576: <http://trac.webkit.org/changeset/177576>
Comment 14 WebKit Commit Bot 2014-12-19 10:36:42 PST
All reviewed patches have been landed.  Closing bug.