[SVG] load events shouldn't be fired during Node::insrtedInto()
authormorrita@google.com <morrita@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Aug 2012 04:16:38 +0000 (04:16 +0000)
committermorrita@google.com <morrita@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Aug 2012 04:16:38 +0000 (04:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=92969

Reviewed by Ryosuke Niwa.

Source/WebCore:

Event dispatches during insertedInto() allow event handlers to
break DOM tree cosistency. This chagne makes them async for load
events which are dispatched during insertedInto() call. This
prevents event handlers from breaking tree consistency while the
notification traversal.

Test: svg/custom/loadevents-async.html

* svg/SVGElement.cpp:
(WebCore::SVGElement::sendSVGLoadEventIfPossibleAsynchronously): Added.
(WebCore):
(WebCore::SVGElement::svgLoadEventTimerFired): Added.
(WebCore::SVGElement::svgLoadEventTimer):
- Added a stub. Implemented in SVGScriptElement, SVGStopElement, SVGUseElement
  where the load event happens.
* svg/SVGElement.h:
(SVGElement):
* svg/SVGExternalResourcesRequired.cpp:
(WebCore::SVGExternalResourcesRequired::insertedIntoDocument):
- Replaces event dispatch call with async version.
* svg/SVGScriptElement.h:
* svg/SVGStyleElement.h:
* svg/SVGUseElement.h:

LayoutTests:

* svg/custom/loadevents-async-expected.txt: Added.
* svg/custom/loadevents-async.html: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/svg/custom/loadevents-async-expected.txt [new file with mode: 0644]
LayoutTests/svg/custom/loadevents-async.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/svg/SVGElement.cpp
Source/WebCore/svg/SVGElement.h
Source/WebCore/svg/SVGExternalResourcesRequired.cpp
Source/WebCore/svg/SVGScriptElement.cpp
Source/WebCore/svg/SVGScriptElement.h
Source/WebCore/svg/SVGStyleElement.cpp
Source/WebCore/svg/SVGStyleElement.h
Source/WebCore/svg/SVGUseElement.cpp
Source/WebCore/svg/SVGUseElement.h

index aabc105..81d578e 100644 (file)
@@ -1,3 +1,13 @@
+2012-08-08  MORITA Hajime  <morrita@google.com>
+
+        [SVG] load events shouldn't be fired during Node::insrtedInto()
+        https://bugs.webkit.org/show_bug.cgi?id=92969
+
+        Reviewed by Ryosuke Niwa.
+
+        * svg/custom/loadevents-async-expected.txt: Added.
+        * svg/custom/loadevents-async.html: Added.
+
 2012-08-08  Tony Chang  <tony@chromium.org>
 
         css3/flexbox/content-height-with-scrollbars.html failing on non-fractional pixel layout machines
diff --git a/LayoutTests/svg/custom/loadevents-async-expected.txt b/LayoutTests/svg/custom/loadevents-async-expected.txt
new file mode 100644 (file)
index 0000000..554c0ed
--- /dev/null
@@ -0,0 +1,11 @@
+This test ensures that tree mutation on the load doesn't break consistency.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS invoked is false
+PASS invoked is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/svg/custom/loadevents-async.html b/LayoutTests/svg/custom/loadevents-async.html
new file mode 100644 (file)
index 0000000..65976e5
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+description("This test ensures that tree mutation on the load doesn't break consistency.");
+var invoked = false;
+var spanElement = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+var divElement = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+var useElement = document.createElementNS("http://www.w3.org/2000/svg", "use");
+var emptyDocument = document.implementation.createDocument("", "", null);
+
+document.body.appendChild(spanElement);
+divElement.appendChild(useElement);
+useElement.addEventListener("load", function () { emptyDocument.adoptNode(useElement); invoked = true; }, false);
+spanElement.appendChild(divElement);
+document.body.appendChild(useElement);
+shouldBeFalse("invoked");
+
+jsTestIsAsync = true;
+setTimeout(function() {
+    shouldBeTrue("invoked");
+    finishJSTest();
+}, 1);
+
+</script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+<body>
+</html>
index fe06167..147ba03 100644 (file)
@@ -1,3 +1,34 @@
+2012-08-08  MORITA Hajime  <morrita@google.com>
+
+        [SVG] load events shouldn't be fired during Node::insrtedInto()
+        https://bugs.webkit.org/show_bug.cgi?id=92969
+
+        Reviewed by Ryosuke Niwa.
+
+        Event dispatches during insertedInto() allow event handlers to
+        break DOM tree cosistency. This chagne makes them async for load
+        events which are dispatched during insertedInto() call. This
+        prevents event handlers from breaking tree consistency while the
+        notification traversal.
+
+        Test: svg/custom/loadevents-async.html
+
+        * svg/SVGElement.cpp:
+        (WebCore::SVGElement::sendSVGLoadEventIfPossibleAsynchronously): Added.
+        (WebCore):
+        (WebCore::SVGElement::svgLoadEventTimerFired): Added.
+        (WebCore::SVGElement::svgLoadEventTimer):
+        - Added a stub. Implemented in SVGScriptElement, SVGStopElement, SVGUseElement
+          where the load event happens.
+        * svg/SVGElement.h:
+        (SVGElement):
+        * svg/SVGExternalResourcesRequired.cpp:
+        (WebCore::SVGExternalResourcesRequired::insertedIntoDocument):
+        - Replaces event dispatch call with async version.
+        * svg/SVGScriptElement.h:
+        * svg/SVGStyleElement.h:
+        * svg/SVGUseElement.h:
+
 2012-08-08  Adam Barth  <abarth@webkit.org>
 
         Implement JSDOMWindow*::allowsAccessFrom* in terms of BindingSecurity
index c80b257..1c09c61 100644 (file)
@@ -477,6 +477,22 @@ void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents)
     }
 }
 
+void SVGElement::sendSVGLoadEventIfPossibleAsynchronously()
+{
+    svgLoadEventTimer()->startOneShot(0);
+}
+
+void SVGElement::svgLoadEventTimerFired(Timer<SVGElement>*)
+{
+    sendSVGLoadEventIfPossible();
+}
+
+Timer<SVGElement>* SVGElement::svgLoadEventTimer()
+{
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
 void SVGElement::finishParsingChildren()
 {
     StyledElement::finishParsingChildren();
index 470235a..900b614 100644 (file)
@@ -27,6 +27,7 @@
 #include "SVGParsingError.h"
 #include "SVGPropertyInfo.h"
 #include "StyledElement.h"
+#include "Timer.h"
 #include <wtf/HashMap.h>
 
 namespace WebCore {
@@ -72,6 +73,9 @@ public:
     virtual void animatedPropertyTypeForAttribute(const QualifiedName&, Vector<AnimatedPropertyType>&);
 
     void sendSVGLoadEventIfPossible(bool sendParentLoadEvents = false);
+    void sendSVGLoadEventIfPossibleAsynchronously();
+    void svgLoadEventTimerFired(Timer<SVGElement>*);
+    virtual Timer<SVGElement>* svgLoadEventTimer();
 
     virtual AffineTransform* supplementalTransform() { return 0; }
 
index 469eb87..e186c53 100644 (file)
@@ -103,7 +103,7 @@ void SVGExternalResourcesRequired::insertedIntoDocument(SVGElement* targetElemen
     if (externalResourcesRequiredBaseValue())
         return;
     setHaveFiredLoadEvent(true);
-    targetElement->sendSVGLoadEventIfPossible();
+    targetElement->sendSVGLoadEventIfPossibleAsynchronously();
 }
 
 void SVGExternalResourcesRequired::finishParsingChildren()
index ff9834c..de2a58c 100644 (file)
@@ -47,6 +47,7 @@ END_REGISTER_ANIMATED_PROPERTIES
 inline SVGScriptElement::SVGScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool alreadyStarted)
     : SVGElement(tagName, document)
     , ScriptElement(this, wasInsertedByParser, alreadyStarted)
+    , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired)
 {
     ASSERT(hasTagName(SVGNames::scriptTag));
     registerAnimatedPropertiesForSVGScriptElement();
index 503598f..05506ec 100644 (file)
@@ -75,6 +75,7 @@ private:
     virtual void setHaveFiredLoadEvent(bool haveFiredLoadEvent) { ScriptElement::setHaveFiredLoadEvent(haveFiredLoadEvent); }
     virtual bool isParserInserted() const { return ScriptElement::isParserInserted(); }
     virtual bool haveFiredLoadEvent() const { return ScriptElement::haveFiredLoadEvent(); }
+    virtual Timer<SVGElement>* svgLoadEventTimer() OVERRIDE { return &m_svgLoadEventTimer; }
 
     BEGIN_DECLARE_ANIMATED_PROPERTIES(SVGScriptElement)
         DECLARE_ANIMATED_STRING(Href, href)
@@ -82,6 +83,7 @@ private:
     END_DECLARE_ANIMATED_PROPERTIES
 
     String m_type;
+    Timer<SVGElement> m_svgLoadEventTimer;
 };
 
 } // namespace WebCore
index ce00232..e4d6f6b 100644 (file)
@@ -37,6 +37,7 @@ namespace WebCore {
 inline SVGStyleElement::SVGStyleElement(const QualifiedName& tagName, Document* document, bool createdByParser)
     : SVGElement(tagName, document)
     , StyleElement(document, createdByParser)
+    , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired)
 {
     ASSERT(hasTagName(SVGNames::styleTag));
 }
index 3cf5b9f..6688365 100644 (file)
@@ -63,6 +63,9 @@ private:
     virtual bool isLoading() const { return StyleElement::isLoading(); }
     virtual bool sheetLoaded() { return StyleElement::sheetLoaded(document()); }
     virtual void startLoadingDynamicSheet() { StyleElement::startLoadingDynamicSheet(document()); }
+    virtual Timer<SVGElement>* svgLoadEventTimer() OVERRIDE { return &m_svgLoadEventTimer; }
+
+    Timer<SVGElement> m_svgLoadEventTimer;
 };
 
 } // namespace WebCore
index 562c083..d19ea19 100755 (executable)
@@ -89,6 +89,7 @@ inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* docu
     , m_wasInsertedByParser(wasInsertedByParser)
     , m_haveFiredLoadEvent(false)
     , m_needsShadowTreeRecreation(false)
+    , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired)
 {
     ASSERT(hasCustomCallbacks());
     ASSERT(hasTagName(SVGNames::useTag));
index 8eb6469..46ee8b3 100755 (executable)
@@ -124,12 +124,14 @@ private:
     virtual void setHaveFiredLoadEvent(bool haveFiredLoadEvent) { m_haveFiredLoadEvent = haveFiredLoadEvent; }
     virtual bool isParserInserted() const { return m_wasInsertedByParser; }
     virtual bool haveFiredLoadEvent() const { return m_haveFiredLoadEvent; }
+    virtual Timer<SVGElement>* svgLoadEventTimer() OVERRIDE { return &m_svgLoadEventTimer; }
 
     bool m_wasInsertedByParser;
     bool m_haveFiredLoadEvent;
     bool m_needsShadowTreeRecreation;
     RefPtr<SVGElementInstance> m_targetElementInstance;
     CachedResourceHandle<CachedSVGDocument> m_cachedDocument;
+    Timer<SVGElement> m_svgLoadEventTimer;
 };
 
 }