Make SVGUseElement respect & support externalResourcesRequired
authorzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Mar 2012 16:59:16 +0000 (16:59 +0000)
committerzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Mar 2012 16:59:16 +0000 (16:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81109

Reviewed by Rob Buis.

Source/WebCore:

Generalize the existing externalResourcesRequired support from SVGScriptElement
into SVGExternalResourcesRequired, so it can be shared with SVGUseElement, which
was lacking proper externalResourcesRequired support.

<use xlink:href="external.svg" onload="alert('hi')" externalResourcesRequired="true"/>
The onload handler now fires _after_ the external resources loaded, making it possible
to write reliable tests that switch from internal to external resources or the other
way around. Converted the new tests in svg/dynamic-updates/SVGUseElement* to listen
to SVGLoad events, to make them reliable.

We may be able to generalize this to cover SVGImageElement as well (the only other class which
is currently supporting externalResourcesRequired - but that's more involved, so I left it TODO).

Covered by existing tests and the changes to the new SVGUseElement tests.

* svg/SVGElement.h:
(SVGElement): Make haveLoadedRequiredResources() public so SVGExternalResourcesRequired can call it.
* svg/SVGExternalResourcesRequired.cpp: Refactored code from SVGScriptElement, 1:1 copies w/o changes.
(WebCore::SVGExternalResourcesRequired::handleAttributeChange):
(WebCore::SVGExternalResourcesRequired::dispatchLoadEvent):
(WebCore::SVGExternalResourcesRequired::insertedIntoDocument):
(WebCore::SVGExternalResourcesRequired::finishParsingChildren):
(WebCore::SVGExternalResourcesRequired::haveLoadedRequiredResources):
* svg/SVGExternalResourcesRequired.h: Add new virtual functions, that must be available in classes that inherit from SVGExternalResourcesRequired.
(WebCore::SVGExternalResourcesRequired::setHaveFiredLoadEvent):
(WebCore::SVGExternalResourcesRequired::isParserInserted):
(WebCore::SVGExternalResourcesRequired::haveFiredLoadEvent):
* svg/SVGScriptElement.cpp: Refactored externalResourcesRequired handling into SVGExternalResourcesRequired.
(WebCore::SVGScriptElement::svgAttributeChanged):
(WebCore::SVGScriptElement::insertedIntoDocument):
(WebCore::SVGScriptElement::finishParsingChildren):
* svg/SVGScriptElement.h: Ditto.
(WebCore::SVGScriptElement::haveLoadedRequiredResources):
(WebCore::SVGScriptElement::dispatchLoadEvent):
(WebCore::SVGScriptElement::setHaveFiredLoadEvent):
(WebCore::SVGScriptElement::isParserInserted):
(WebCore::SVGScriptElement::haveFiredLoadEvent):
* svg/SVGTests.cpp: Fix obvious typo, that leads to an assertion. Always return true if we know the attributeName.
(WebCore::SVGTests::handleAttributeChange):
* svg/SVGTests.h:
* svg/SVGTextPathElement.cpp:
(WebCore::SVGTextPathElement::insertedIntoDocument): Remove ambigous call warning.
* svg/SVGUseElement.cpp: Support externalResourcesRequired="true/false" + dynamic changes of it. Covered by existing tests.
(WebCore::SVGUseElement::SVGUseElement):
(WebCore::SVGUseElement::create):
(WebCore::SVGUseElement::insertedIntoDocument):
(WebCore::SVGUseElement::svgAttributeChanged):
(WebCore::SVGUseElement::notifyFinished):
(WebCore::SVGUseElement::finishParsingChildren):
(WebCore):
* svg/SVGUseElement.h: Ditto.
(WebCore::SVGUseElement::haveLoadedRequiredResources):
(WebCore::SVGUseElement::setHaveFiredLoadEvent):
(WebCore::SVGUseElement::isParserInserted):
(WebCore::SVGUseElement::haveFiredLoadEvent):
* svg/svgtags.in: Pass "bool wasInsertedByParser" to SVGUseElement constructor.

LayoutTests:

Fix flakiness of new external <use> tests introduced in r110676.

* platform/mac/svg/batik/masking/maskRegions-expected.png:
* platform/mac/svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.png:
* platform/mac/svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.png:
* svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.txt:
* svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.txt:
* svg/dynamic-updates/script-tests/SVGUseElement-dom-href1-attr.js:
(repaintTest):
* svg/dynamic-updates/script-tests/SVGUseElement-dom-href2-attr.js:
(repaintTest):
(externalLoadDone):
* svg/dynamic-updates/script-tests/SVGUseElement-svgdom-href1-prop.js:
(repaintTest):
* svg/dynamic-updates/script-tests/SVGUseElement-svgdom-href2-prop.js:
(repaintTest):
(externalLoadDone):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@110711 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/svg/batik/masking/maskRegions-expected.png
LayoutTests/platform/mac/svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.png
LayoutTests/platform/mac/svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.png
LayoutTests/svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.txt
LayoutTests/svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.txt
LayoutTests/svg/dynamic-updates/script-tests/SVGUseElement-dom-href1-attr.js
LayoutTests/svg/dynamic-updates/script-tests/SVGUseElement-dom-href2-attr.js
LayoutTests/svg/dynamic-updates/script-tests/SVGUseElement-svgdom-href1-prop.js
LayoutTests/svg/dynamic-updates/script-tests/SVGUseElement-svgdom-href2-prop.js
Source/WebCore/ChangeLog
Source/WebCore/svg/SVGElement.h
Source/WebCore/svg/SVGExternalResourcesRequired.cpp
Source/WebCore/svg/SVGExternalResourcesRequired.h
Source/WebCore/svg/SVGScriptElement.cpp
Source/WebCore/svg/SVGScriptElement.h
Source/WebCore/svg/SVGTests.cpp
Source/WebCore/svg/SVGTests.h
Source/WebCore/svg/SVGTextPathElement.cpp
Source/WebCore/svg/SVGUseElement.cpp
Source/WebCore/svg/SVGUseElement.h
Source/WebCore/svg/svgtags.in

index 8050b77..ee35481 100644 (file)
@@ -1,3 +1,28 @@
+2012-03-14  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Make SVGUseElement respect & support externalResourcesRequired
+        https://bugs.webkit.org/show_bug.cgi?id=81109
+
+        Reviewed by Rob Buis.
+
+        Fix flakiness of new external <use> tests introduced in r110676.
+
+        * platform/mac/svg/batik/masking/maskRegions-expected.png:
+        * platform/mac/svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.png:
+        * platform/mac/svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.png:
+        * svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.txt:
+        * svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.txt:
+        * svg/dynamic-updates/script-tests/SVGUseElement-dom-href1-attr.js:
+        (repaintTest):
+        * svg/dynamic-updates/script-tests/SVGUseElement-dom-href2-attr.js:
+        (repaintTest):
+        (externalLoadDone):
+        * svg/dynamic-updates/script-tests/SVGUseElement-svgdom-href1-prop.js:
+        (repaintTest):
+        * svg/dynamic-updates/script-tests/SVGUseElement-svgdom-href2-prop.js:
+        (repaintTest):
+        (externalLoadDone):
+
 2012-03-14  Jessie Berlin  <jberlin@apple.com>
 
         Mac WK2: editing/pasteboard/dataTransfer-setData-getData.html fails
index 7e8c224..6e42e9a 100644 (file)
Binary files a/LayoutTests/platform/mac/svg/batik/masking/maskRegions-expected.png and b/LayoutTests/platform/mac/svg/batik/masking/maskRegions-expected.png differ
index 33335b1..4742007 100644 (file)
Binary files a/LayoutTests/platform/mac/svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.png and b/LayoutTests/platform/mac/svg/dynamic-updates/SVGUseElement-dom-href2-attr-expected.png differ
index 6c2ef33..bf818fe 100644 (file)
Binary files a/LayoutTests/platform/mac/svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.png and b/LayoutTests/platform/mac/svg/dynamic-updates/SVGUseElement-svgdom-href2-prop-expected.png differ
index f13d573..f8e0286 100644 (file)
@@ -5,7 +5,6 @@ Tests dynamic updates of the 'href' attribute of the SVGUseElement object
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS useElement.getAttributeNS('http://www.w3.org/1999/xlink', 'href') is "../custom/resources/rgb.svg#R"
 PASS useElement.getAttributeNS('http://www.w3.org/1999/xlink', 'href') is "#MyRect"
 PASS successfullyParsed is true
 
index 0a2ab3e..db3b300 100644 (file)
@@ -5,7 +5,6 @@ Tests dynamic updates of the 'href' property of the SVGUseElement object
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS useElement.href.baseVal is "../custom/resources/rgb.svg#R"
 PASS useElement.href.baseVal is "#MyRect"
 PASS successfullyParsed is true
 
index 224b5b3..ffc8c3b 100644 (file)
@@ -10,6 +10,8 @@ rootSVGElement.appendChild(defsElement);
 var useElement = createSVGElement("use");
 useElement.setAttribute("x", "10");
 useElement.setAttribute("y", "10");
+useElement.setAttribute("externalResourcesRequired", "true");
+useElement.setAttribute("onload", "completeTest()");
 useElement.setAttributeNS(xlinkNS, "xlink:href", "#MyRect");
 
 var rectElement = createSVGElement("rect");
@@ -31,8 +33,6 @@ shouldBeEqualToString("useElement.getAttributeNS('" + xlinkNS + "', 'href')", "#
 function repaintTest() {
     useElement.setAttributeNS(xlinkNS, "xlink:href", "../custom/resources/rgb.svg#G");
     shouldBeEqualToString("useElement.getAttributeNS('" + xlinkNS + "', 'href')", "../custom/resources/rgb.svg#G");
-
-    completeTest();
 }
 
 var successfullyParsed = true;
index e9bfa66..7578514 100644 (file)
@@ -1,4 +1,4 @@
-// [Name] SVGUseElement-dom-href1-attr.js
+// [Name] SVGUseElement-dom-href2-attr.js
 // [Expected rendering result] A use element first with an external then with an internal referenced document - and a series of PASS messages
 
 description("Tests dynamic updates of the 'href' attribute of the SVGUseElement object")
@@ -10,7 +10,8 @@ rootSVGElement.appendChild(defsElement);
 var useElement = createSVGElement("use");
 useElement.setAttribute("x", "10");
 useElement.setAttribute("y", "10");
-useElement.setAttributeNS(xlinkNS, "xlink:href", "../custom/resources/rgb.svg#R");
+useElement.setAttribute("externalResourcesRequired", "true");
+useElement.setAttribute("onload", "externalLoadDone()");
 
 var rectElement = createSVGElement("rect");
 rectElement.setAttribute("id", "MyRect");
@@ -21,14 +22,16 @@ rectElement.setAttribute("height", "64");
 rectElement.setAttribute("fill", "green");
 
 defsElement.appendChild(rectElement);
-defsElement.appendChild(useElement);
 
 rootSVGElement.setAttribute("height", "200");
 rootSVGElement.appendChild(useElement);
 
-shouldBeEqualToString("useElement.getAttributeNS('" + xlinkNS + "', 'href')", "../custom/resources/rgb.svg#R");
-
 function repaintTest() {
+    // Start loading external resource, wait for it, then switch back to internal.
+       useElement.setAttributeNS(xlinkNS, "xlink:href", "../custom/resources/rgb.svg#R");
+}
+
+function externalLoadDone() {
     useElement.setAttributeNS(xlinkNS, "xlink:href", "#MyRect");
     shouldBeEqualToString("useElement.getAttributeNS('" + xlinkNS + "', 'href')", "#MyRect");
 
index 6bb70c2..bc156ce 100644 (file)
@@ -10,6 +10,8 @@ rootSVGElement.appendChild(defsElement);
 var useElement = createSVGElement("use");
 useElement.setAttribute("x", "10");
 useElement.setAttribute("y", "10");
+useElement.setAttribute("externalResourcesRequired", "true");
+useElement.setAttribute("onload", "completeTest()");
 useElement.setAttributeNS(xlinkNS, "xlink:href", "#MyRect");
 
 var rectElement = createSVGElement("rect");
@@ -31,8 +33,6 @@ shouldBeEqualToString("useElement.href.baseVal", "#MyRect");
 function repaintTest() {
     useElement.href.baseVal = "../custom/resources/rgb.svg#G";
     shouldBeEqualToString("useElement.href.baseVal", "../custom/resources/rgb.svg#G");
-
-    completeTest();
 }
 
 var successfullyParsed = true;
index 75e0fc2..a432bad 100644 (file)
@@ -10,7 +10,8 @@ rootSVGElement.appendChild(defsElement);
 var useElement = createSVGElement("use");
 useElement.setAttribute("x", "10");
 useElement.setAttribute("y", "10");
-useElement.setAttributeNS(xlinkNS, "xlink:href", "../custom/resources/rgb.svg#R");
+useElement.setAttribute("externalResourcesRequired", "true");
+useElement.setAttribute("onload", "externalLoadDone()");
 
 var rectElement = createSVGElement("rect");
 rectElement.setAttribute("id", "MyRect");
@@ -21,14 +22,16 @@ rectElement.setAttribute("height", "64");
 rectElement.setAttribute("fill", "green");
 
 defsElement.appendChild(rectElement);
-defsElement.appendChild(useElement);
 
 rootSVGElement.setAttribute("height", "200");
 rootSVGElement.appendChild(useElement);
 
-shouldBeEqualToString("useElement.href.baseVal", "../custom/resources/rgb.svg#R");
-
 function repaintTest() {
+    // Start loading external resource, wait for it, then switch back to internal.
+    useElement.setAttributeNS(xlinkNS, "xlink:href", "../custom/resources/rgb.svg#R");
+}
+
+function externalLoadDone() {
     useElement.href.baseVal = "#MyRect";
     shouldBeEqualToString("useElement.href.baseVal", "#MyRect");
 
index 2e86808..4ff46b8 100644 (file)
@@ -1,3 +1,67 @@
+2012-03-14  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Make SVGUseElement respect & support externalResourcesRequired
+        https://bugs.webkit.org/show_bug.cgi?id=81109
+
+        Reviewed by Rob Buis.
+
+        Generalize the existing externalResourcesRequired support from SVGScriptElement
+        into SVGExternalResourcesRequired, so it can be shared with SVGUseElement, which
+        was lacking proper externalResourcesRequired support.
+
+        <use xlink:href="external.svg" onload="alert('hi')" externalResourcesRequired="true"/>
+        The onload handler now fires _after_ the external resources loaded, making it possible
+        to write reliable tests that switch from internal to external resources or the other
+        way around. Converted the new tests in svg/dynamic-updates/SVGUseElement* to listen
+        to SVGLoad events, to make them reliable.
+
+        We may be able to generalize this to cover SVGImageElement as well (the only other class which
+        is currently supporting externalResourcesRequired - but that's more involved, so I left it TODO).
+
+        Covered by existing tests and the changes to the new SVGUseElement tests.
+
+        * svg/SVGElement.h:
+        (SVGElement): Make haveLoadedRequiredResources() public so SVGExternalResourcesRequired can call it.
+        * svg/SVGExternalResourcesRequired.cpp: Refactored code from SVGScriptElement, 1:1 copies w/o changes.
+        (WebCore::SVGExternalResourcesRequired::handleAttributeChange):
+        (WebCore::SVGExternalResourcesRequired::dispatchLoadEvent):
+        (WebCore::SVGExternalResourcesRequired::insertedIntoDocument):
+        (WebCore::SVGExternalResourcesRequired::finishParsingChildren):
+        (WebCore::SVGExternalResourcesRequired::haveLoadedRequiredResources):
+        * svg/SVGExternalResourcesRequired.h: Add new virtual functions, that must be available in classes that inherit from SVGExternalResourcesRequired.
+        (WebCore::SVGExternalResourcesRequired::setHaveFiredLoadEvent):
+        (WebCore::SVGExternalResourcesRequired::isParserInserted):
+        (WebCore::SVGExternalResourcesRequired::haveFiredLoadEvent):
+        * svg/SVGScriptElement.cpp: Refactored externalResourcesRequired handling into SVGExternalResourcesRequired.
+        (WebCore::SVGScriptElement::svgAttributeChanged):
+        (WebCore::SVGScriptElement::insertedIntoDocument):
+        (WebCore::SVGScriptElement::finishParsingChildren):
+        * svg/SVGScriptElement.h: Ditto.
+        (WebCore::SVGScriptElement::haveLoadedRequiredResources):
+        (WebCore::SVGScriptElement::dispatchLoadEvent):
+        (WebCore::SVGScriptElement::setHaveFiredLoadEvent):
+        (WebCore::SVGScriptElement::isParserInserted):
+        (WebCore::SVGScriptElement::haveFiredLoadEvent):
+        * svg/SVGTests.cpp: Fix obvious typo, that leads to an assertion. Always return true if we know the attributeName.
+        (WebCore::SVGTests::handleAttributeChange):
+        * svg/SVGTests.h:
+        * svg/SVGTextPathElement.cpp:
+        (WebCore::SVGTextPathElement::insertedIntoDocument): Remove ambigous call warning.
+        * svg/SVGUseElement.cpp: Support externalResourcesRequired="true/false" + dynamic changes of it. Covered by existing tests.
+        (WebCore::SVGUseElement::SVGUseElement):
+        (WebCore::SVGUseElement::create):
+        (WebCore::SVGUseElement::insertedIntoDocument):
+        (WebCore::SVGUseElement::svgAttributeChanged):
+        (WebCore::SVGUseElement::notifyFinished):
+        (WebCore::SVGUseElement::finishParsingChildren):
+        (WebCore):
+        * svg/SVGUseElement.h: Ditto.
+        (WebCore::SVGUseElement::haveLoadedRequiredResources):
+        (WebCore::SVGUseElement::setHaveFiredLoadEvent):
+        (WebCore::SVGUseElement::isParserInserted):
+        (WebCore::SVGUseElement::haveFiredLoadEvent):
+        * svg/svgtags.in: Pass "bool wasInsertedByParser" to SVGUseElement constructor.
+
 2012-03-14  Andrew Lo  <anlo@rim.com>
 
         [BlackBerry] Implement REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR using AnimationFrameRateController
index 87b9835..5e20cd8 100644 (file)
@@ -110,6 +110,8 @@ public:
     StylePropertySet* animatedSMILStyleProperties() const;
     StylePropertySet* ensureAnimatedSMILStyleProperties();
 
+    virtual bool haveLoadedRequiredResources();
+
 protected:
     SVGElement(const QualifiedName&, Document*, ConstructionType = CreateSVGElement);
 
@@ -136,7 +138,6 @@ private:
     void mapInstanceToElement(SVGElementInstance*);
     void removeInstanceMapping(SVGElementInstance*);
 
-    virtual bool haveLoadedRequiredResources();
 };
 
 struct SVGAttributeHashTranslator {
index 498566f..3d33f04 100644 (file)
@@ -48,6 +48,76 @@ void SVGExternalResourcesRequired::addSupportedAttributes(HashSet<QualifiedName>
     supportedAttributes.add(SVGNames::externalResourcesRequiredAttr);
 }
 
+bool SVGExternalResourcesRequired::handleAttributeChange(SVGElement* targetElement, const QualifiedName& attrName)
+{
+    ASSERT(targetElement);
+    if (!isKnownAttribute(attrName))
+        return false;
+    if (!targetElement->inDocument())
+        return true;
+
+    // Handle dynamic updates of the 'externalResourcesRequired' attribute. Only possible case: changing from 'true' to 'false'
+    // causes an immediate dispatch of the SVGLoad event. If the attribute value was 'false' before inserting the script element
+    // in the document, the SVGLoad event has already been dispatched.
+    if (!externalResourcesRequiredBaseValue() && !haveFiredLoadEvent() && !isParserInserted()) {
+        setHaveFiredLoadEvent(true);
+        ASSERT(targetElement->haveLoadedRequiredResources());
+
+        targetElement->sendSVGLoadEventIfPossible();
+    }
+
+    return true;
+}
+
+void SVGExternalResourcesRequired::dispatchLoadEvent(SVGElement* targetElement)
+{
+    bool externalResourcesRequired = externalResourcesRequiredBaseValue();
+
+    if (isParserInserted())
+        ASSERT(externalResourcesRequired != haveFiredLoadEvent());
+    else if (haveFiredLoadEvent())
+        return;
+
+    // HTML and SVG differ completely in the 'onload' event handling of <script> elements.
+    // HTML fires the 'load' event after it sucessfully loaded a remote resource, otherwise an error event.
+    // SVG fires the SVGLoad event immediately after parsing the <script> element, if externalResourcesRequired
+    // is set to 'false', otherwise it dispatches the 'SVGLoad' event just after loading the remote resource.
+    if (!externalResourcesRequired)
+        return;
+
+    ASSERT(!haveFiredLoadEvent());
+
+    // Dispatch SVGLoad event
+    setHaveFiredLoadEvent(true);
+    ASSERT(targetElement->haveLoadedRequiredResources());
+
+    targetElement->sendSVGLoadEventIfPossible();
+}
+
+void SVGExternalResourcesRequired::insertedIntoDocument(SVGElement* targetElement)
+{
+    if (isParserInserted())
+        return;
+
+    // Eventually send SVGLoad event now for the dynamically inserted script element.
+    if (externalResourcesRequiredBaseValue())
+        return;
+    setHaveFiredLoadEvent(true);
+    targetElement->sendSVGLoadEventIfPossible();
+}
+
+void SVGExternalResourcesRequired::finishParsingChildren()
+{
+    // A SVGLoad event has been fired by SVGElement::finishParsingChildren.
+    if (!externalResourcesRequiredBaseValue())
+        setHaveFiredLoadEvent(true);
+}
+
+bool SVGExternalResourcesRequired::haveLoadedRequiredResources() const
+{
+    return !externalResourcesRequiredBaseValue() || haveFiredLoadEvent();
+}
+
 }
 
 #endif // ENABLE(SVG)
index 8b11ebb..ddc8999 100644 (file)
@@ -42,9 +42,21 @@ public:
     bool parseAttribute(Attribute*);
     bool isKnownAttribute(const QualifiedName&);
     void addSupportedAttributes(HashSet<QualifiedName>&);
+    bool handleAttributeChange(SVGElement*, const QualifiedName&);
 
 protected:
+    // These types look a bit awkward, but have to match the generic types of the SVGAnimatedProperty macros.
     virtual void setExternalResourcesRequiredBaseValue(const bool&) = 0;
+    virtual bool& externalResourcesRequiredBaseValue() const = 0;
+
+    virtual void setHaveFiredLoadEvent(bool) { }
+    virtual bool isParserInserted() const { return false; }
+    virtual bool haveFiredLoadEvent() const { return false; }
+
+    void dispatchLoadEvent(SVGElement*);
+    void insertedIntoDocument(SVGElement*);
+    void finishParsingChildren();
+    bool haveLoadedRequiredResources() const;
 };
 
 } // namespace WebCore
index 8a48239..000d481 100644 (file)
@@ -111,18 +111,8 @@ void SVGScriptElement::svgAttributeChanged(const QualifiedName& attrName)
         return;
     }
 
-    if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) {
-        // Handle dynamic updates of the 'externalResourcesRequired' attribute. Only possible case: changing from 'true' to 'false'
-        // causes an immediate dispatch of the SVGLoad event. If the attribute value was 'false' before inserting the script element
-        // in the document, the SVGLoad event has already been dispatched.
-        if (!externalResourcesRequiredBaseValue() && !haveFiredLoadEvent() && !isParserInserted()) {
-            setHaveFiredLoadEvent(true);
-            ASSERT(haveLoadedRequiredResources());
-
-            sendSVGLoadEventIfPossible();
-        }
+    if (SVGExternalResourcesRequired::handleAttributeChange(this, attrName))
         return;
-    }
 
     ASSERT_NOT_REACHED();
 }
@@ -131,15 +121,7 @@ void SVGScriptElement::insertedIntoDocument()
 {
     SVGElement::insertedIntoDocument();
     ScriptElement::insertedIntoDocument();
-
-    if (isParserInserted())
-        return;
-
-    // Eventually send SVGLoad event now for the dynamically inserted script element
-    if (!externalResourcesRequiredBaseValue()) {
-        setHaveFiredLoadEvent(true);
-        sendSVGLoadEventIfPossible();
-    }
+    SVGExternalResourcesRequired::insertedIntoDocument(this);
 }
 
 void SVGScriptElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
@@ -156,10 +138,7 @@ bool SVGScriptElement::isURLAttribute(Attribute* attr) const
 void SVGScriptElement::finishParsingChildren()
 {
     SVGElement::finishParsingChildren();
-
-    // A SVGLoad event has been fired by SVGElement::finishParsingChildren.
-    if (!externalResourcesRequiredBaseValue())
-        setHaveFiredLoadEvent(true);
+    SVGExternalResourcesRequired::finishParsingChildren();
 }
 
 String SVGScriptElement::type() const
@@ -179,11 +158,6 @@ void SVGScriptElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) cons
     addSubresourceURL(urls, document()->completeURL(href()));
 }
 
-bool SVGScriptElement::haveLoadedRequiredResources()
-{
-    return !externalResourcesRequiredBaseValue() || haveFiredLoadEvent();
-}
-
 String SVGScriptElement::sourceAttributeValue() const
 {
     return href();
@@ -229,34 +203,6 @@ bool SVGScriptElement::hasSourceAttribute() const
     return hasAttribute(XLinkNames::hrefAttr);
 }
 
-void SVGScriptElement::dispatchLoadEvent()
-{
-    bool externalResourcesRequired = externalResourcesRequiredBaseValue();
-
-    if (isParserInserted())
-        ASSERT(externalResourcesRequired != haveFiredLoadEvent());
-    else if (haveFiredLoadEvent()) {
-        // If we've already fired an load event and externalResourcesRequired is set to 'true'
-        // externalResourcesRequired has been modified while loading the <script>. Don't dispatch twice.
-        if (externalResourcesRequired)
-            return;
-    }
-
-    // HTML and SVG differ completly in the 'onload' event handling of <script> elements.
-    // HTML fires the 'load' event after it sucessfully loaded a remote resource, otherwhise an error event.
-    // SVG fires the SVGLoad event immediately after parsing the <script> element, if externalResourcesRequired
-    // is set to 'false', otherwhise it dispatches the 'SVGLoad' event just after loading the remote resource.
-    if (externalResourcesRequired) {
-        ASSERT(!haveFiredLoadEvent());
-
-        // Dispatch SVGLoad event
-        setHaveFiredLoadEvent(true);
-        ASSERT(haveLoadedRequiredResources());
-
-        sendSVGLoadEventIfPossible();
-    }
-}
-
 PassRefPtr<Element> SVGScriptElement::cloneElementWithoutAttributesAndChildren()
 {
     return adoptRef(new SVGScriptElement(tagQName(), document(), false, alreadyStarted()));
index f7335d2..8a8c260 100644 (file)
@@ -55,7 +55,7 @@ private:
 
     virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const;
 
-    virtual bool haveLoadedRequiredResources();
+    virtual bool haveLoadedRequiredResources() { return SVGExternalResourcesRequired::haveLoadedRequiredResources(); }
 
     virtual String sourceAttributeValue() const;
     virtual String charsetAttributeValue() const;
@@ -67,10 +67,15 @@ private:
     virtual bool deferAttributeValue() const;
     virtual bool hasSourceAttribute() const;
 
-    virtual void dispatchLoadEvent();
+    virtual void dispatchLoadEvent() { SVGExternalResourcesRequired::dispatchLoadEvent(this); }
 
     virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren();
 
+    // SVGExternalResourcesRequired
+    virtual void setHaveFiredLoadEvent(bool haveFiredLoadEvent) { ScriptElement::setHaveFiredLoadEvent(haveFiredLoadEvent); }
+    virtual bool isParserInserted() const { return ScriptElement::isParserInserted(); }
+    virtual bool haveFiredLoadEvent() const { return ScriptElement::haveFiredLoadEvent(); }
+
     BEGIN_DECLARE_ANIMATED_PROPERTIES(SVGScriptElement)
         DECLARE_ANIMATED_STRING(Href, href)
         DECLARE_ANIMATED_BOOLEAN(ExternalResourcesRequired, externalResourcesRequired)
index e527308..b6da357 100644 (file)
@@ -145,19 +145,18 @@ bool SVGTests::isKnownAttribute(const QualifiedName& attrName)
         || attrName == SVGNames::systemLanguageAttr;
 }
 
-bool SVGTests::handleAttributeChange(const SVGElement* targetElement, const QualifiedName& attrName)
+bool SVGTests::handleAttributeChange(SVGElement* targetElement, const QualifiedName& attrName)
 {
+    ASSERT(targetElement);
     if (!isKnownAttribute(attrName))
         return false;
     if (!targetElement->inDocument())
-        return false;
-    SVGElement* svgElement = const_cast<SVGElement*>(targetElement);
-    ASSERT(svgElement);
-    bool valid = svgElement->isValid();
-    if (valid && !svgElement->attached())
-        svgElement->attach();
-    if (!valid && svgElement->attached())
-        svgElement->detach();
+        return true;
+    bool valid = targetElement->isValid();
+    if (valid && !targetElement->attached())
+        targetElement->attach();
+    if (!valid && targetElement->attached())
+        targetElement->detach();
     return true;
 }
 
index 4d7d829..1851786 100644 (file)
@@ -45,7 +45,7 @@ public:
     bool isKnownAttribute(const QualifiedName&);
 
     void addSupportedAttributes(HashSet<QualifiedName>&);
-    bool handleAttributeChange(const SVGElement*, const QualifiedName&);
+    bool handleAttributeChange(SVGElement*, const QualifiedName&);
 
     static SVGAttributeToPropertyMap& attributeToPropertyMap();
 
index c8cfdd2..3febdd7 100644 (file)
@@ -141,7 +141,7 @@ bool SVGTextPathElement::rendererIsNeeded(const NodeRenderingContext& context)
 
 void SVGTextPathElement::insertedIntoDocument()
 {
-    SVGTextContentElement::insertedIntoDocument();
+    SVGStyledElement::insertedIntoDocument();
 
     String id;
     Element* targetElement = SVGURIReference::targetElementFromIRIString(href(), document(), &id);
index e7d56ef..50c8e82 100755 (executable)
@@ -80,12 +80,14 @@ BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGUseElement)
     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
 END_REGISTER_ANIMATED_PROPERTIES
 
-inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* document)
+inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser)
     : SVGStyledTransformableElement(tagName, document)
     , m_x(LengthModeWidth)
     , m_y(LengthModeHeight)
     , m_width(LengthModeWidth)
     , m_height(LengthModeHeight)
+    , m_wasInsertedByParser(wasInsertedByParser)
+    , m_haveFiredLoadEvent(false)
     , m_needsShadowTreeRecreation(false)
 {
     ASSERT(hasTagName(SVGNames::useTag));
@@ -94,10 +96,10 @@ inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* docu
     setHasCustomWillOrDidRecalcStyle();
 }
 
-PassRefPtr<SVGUseElement> SVGUseElement::create(const QualifiedName& tagName, Document* document)
+PassRefPtr<SVGUseElement> SVGUseElement::create(const QualifiedName& tagName, Document* document, bool wasInsertedByParser)
 {
     // Always build a #shadow-root for SVGUseElement.
-    RefPtr<SVGUseElement> use = adoptRef(new SVGUseElement(tagName, document));
+    RefPtr<SVGUseElement> use = adoptRef(new SVGUseElement(tagName, document, wasInsertedByParser));
     use->ensureShadowRoot();
     return use.release();
 }
@@ -180,6 +182,7 @@ void SVGUseElement::insertedIntoDocument()
     ASSERT(!m_targetElementInstance || !isWellFormedDocument(document()));
     ASSERT(!hasPendingResources() || !isWellFormedDocument(document()));
     buildPendingResource();
+    SVGExternalResourcesRequired::insertedIntoDocument(this);
 }
 
 void SVGUseElement::removedFromDocument()
@@ -230,6 +233,9 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
     if (SVGTests::handleAttributeChange(this, attrName))
         return;
 
+    if (SVGExternalResourcesRequired::handleAttributeChange(this, attrName))
+        return;
+
     if (SVGURIReference::isKnownAttribute(attrName)) {
         bool isExternalReference = isExternalURIReference(href(), document());
         if (isExternalReference) {
@@ -911,12 +917,16 @@ bool SVGUseElement::selfHasRelativeLengths() const
     return static_cast<SVGStyledElement*>(element)->hasRelativeLengths();
 }
 
-void SVGUseElement::notifyFinished(CachedResource*)
+void SVGUseElement::notifyFinished(CachedResource* resource)
 {
     if (!inDocument())
         return;
 
     invalidateShadowTree();
+    if (resource->errorOccurred())
+        dispatchEvent(Event::create(eventNames().errorEvent, false, false));
+    else if (!resource->wasCanceled())
+        SVGExternalResourcesRequired::dispatchLoadEvent(this);
 }
 
 bool SVGUseElement::cachedDocumentIsStillLoading()
@@ -939,6 +949,12 @@ bool SVGUseElement::instanceTreeIsLoading(SVGElementInstance* targetElementInsta
     return false;
 }
 
+void SVGUseElement::finishParsingChildren()
+{
+    SVGStyledTransformableElement::finishParsingChildren();
+    SVGExternalResourcesRequired::finishParsingChildren();
+}
+
 }
 
 #endif // ENABLE(SVG)
index 6e817de..dd6533c 100755 (executable)
@@ -43,7 +43,7 @@ class SVGUseElement : public SVGStyledTransformableElement,
                       public SVGURIReference,
                       public CachedSVGDocumentClient {
 public:
-    static PassRefPtr<SVGUseElement> create(const QualifiedName&, Document*);
+    static PassRefPtr<SVGUseElement> create(const QualifiedName&, Document*, bool wasInsertedByParser);
     virtual ~SVGUseElement();
 
     SVGElementInstance* instanceRoot();
@@ -54,7 +54,7 @@ public:
     RenderObject* rendererClipChild() const;
 
 private:
-    SVGUseElement(const QualifiedName&, Document*);
+    SVGUseElement(const QualifiedName&, Document*, bool wasInsertedByParser);
 
     virtual bool isValid() const { return SVGTests::isValid(); }
     virtual bool supportsFocus() const { return true; }
@@ -76,6 +76,9 @@ private:
     void buildShadowAndInstanceTree(SVGElement* target);
     void detachInstance();
 
+    virtual bool haveLoadedRequiredResources() { return SVGExternalResourcesRequired::haveLoadedRequiredResources(); }
+
+    virtual void finishParsingChildren();
     virtual bool selfHasRelativeLengths() const;
 
     // Instance tree handling
@@ -115,6 +118,13 @@ private:
     virtual void synchronizeRequiredExtensions() { SVGTests::synchronizeRequiredExtensions(this); }
     virtual void synchronizeSystemLanguage() { SVGTests::synchronizeSystemLanguage(this); }
 
+    // SVGExternalResourcesRequired
+    virtual void setHaveFiredLoadEvent(bool haveFiredLoadEvent) { m_haveFiredLoadEvent = haveFiredLoadEvent; }
+    virtual bool isParserInserted() const { return m_wasInsertedByParser; }
+    virtual bool haveFiredLoadEvent() const { return m_haveFiredLoadEvent; }
+
+    bool m_wasInsertedByParser;
+    bool m_haveFiredLoadEvent;
     bool m_needsShadowTreeRecreation;
     RefPtr<SVGElementInstance> m_targetElementInstance;
     CachedResourceHandle<CachedSVGDocument> m_cachedDocument;
index 9c64229..368eec5 100644 (file)
@@ -93,7 +93,7 @@ textPath
 title
 tref interfaceName=SVGTRefElement
 tspan interfaceName=SVGTSpanElement
-use
+use constructorNeedsCreatedByParser
 view
 #if ENABLE_SVG_FONTS
 vkern interfaceName=SVGVKernElement