Regression: Event#stopPropagation() does not halt bubbling for webkitTransitionEnd
authoralexis@webkit.org <alexis@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 May 2013 03:26:58 +0000 (03:26 +0000)
committeralexis@webkit.org <alexis@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 May 2013 03:26:58 +0000 (03:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=115656

Reviewed by Darin Adler.

Source/WebCore:

If we create a prefixed event to dispatch it (in the case we have only
prefixed event listeners in client's code) then we need to make sure to
keep it in sync with the original unprefixed event after it has been
dispatched. While being dispatched the event can be modified by
client's code and when propagated back to outer elements, attributes were
not updated. This patch changes the old design of creating a separate event
for the prefixed case and now change the type of the event (so the name) before
dispatching it, keeping the attributes if changed and then rename it
back to unprefixed when the dispatching is finished.

Tests: transitions/transition-end-event-prefixed-01.html
       transitions/transition-end-event-prefixed-02.html
       transitions/transition-end-event-prefixed-03.html

* dom/Event.h:
(WebCore::Event::setType):
* dom/EventTarget.cpp:
(WebCore::EventTarget::fireEventListeners):

LayoutTests:

Added new tests to cover the bug.

* transitions/transition-end-event-prefixed-03-expected.txt: Added.
* transitions/transition-end-event-prefixed-03.html: Added.
* transitions/transition-end-event-prefixed-02-expected.txt: Added.
* transitions/transition-end-event-prefixed-02.html: Added.
* transitions/transition-end-event-prefixed-01-expected.txt: Added.
* transitions/transition-end-event-prefixed-01.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/transitions/transition-end-event-prefixed-01-expected.txt [new file with mode: 0644]
LayoutTests/transitions/transition-end-event-prefixed-01.html [new file with mode: 0644]
LayoutTests/transitions/transition-end-event-prefixed-02-expected.txt [new file with mode: 0644]
LayoutTests/transitions/transition-end-event-prefixed-02.html [new file with mode: 0644]
LayoutTests/transitions/transition-end-event-prefixed-03-expected.txt [new file with mode: 0644]
LayoutTests/transitions/transition-end-event-prefixed-03.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Event.h
Source/WebCore/dom/EventTarget.cpp

index 04d9f6f..336f030 100644 (file)
@@ -1,3 +1,19 @@
+2013-05-16  Alexis Menard  <alexis@webkit.org>
+
+        Regression: Event#stopPropagation() does not halt bubbling for webkitTransitionEnd
+        https://bugs.webkit.org/show_bug.cgi?id=115656
+
+        Reviewed by Darin Adler.
+
+        Added new tests to cover the bug.
+
+        * transitions/transition-end-event-prefixed-03-expected.txt: Added.
+        * transitions/transition-end-event-prefixed-03.html: Added.
+        * transitions/transition-end-event-prefixed-02-expected.txt: Added.
+        * transitions/transition-end-event-prefixed-02.html: Added.
+        * transitions/transition-end-event-prefixed-01-expected.txt: Added.
+        * transitions/transition-end-event-prefixed-01.html: Added.
+
 2013-05-16  Ryosuke Niwa  <rniwa@webkit.org>
 
         Add failing test expectations to http/tests/cache/willsendrequest-returns-null-for-memory-cache-load.html per bug 116259.
diff --git a/LayoutTests/transitions/transition-end-event-prefixed-01-expected.txt b/LayoutTests/transitions/transition-end-event-prefixed-01-expected.txt
new file mode 100644 (file)
index 0000000..992de62
--- /dev/null
@@ -0,0 +1,22 @@
+Test to make sure that if prefixed transition events are modified we correctly modify unprefixed events.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS transitionEventContainer.type is transitionEventBox.type
+PASS transitionEventContainer.bubbles is transitionEventBox.bubbles
+PASS transitionEventContainer.timeStamp is transitionEventBox.timeStamp
+PASS transitionEventContainer.cancelable is transitionEventBox.cancelable
+PASS transitionEventContainer.srcElement is transitionEventBox.srcElement
+PASS transitionEventContainer.returnValue is transitionEventBox.returnValue
+PASS transitionEventContainer.cancelBubble is transitionEventBox.cancelBubble
+PASS transitionEventContainer.defaultPrevented is transitionEventBox.defaultPrevented
+PASS transitionEventContainer.target is transitionEventBox.target
+PASS transitionEventContainer.currentTarget is testContainer
+PASS transitionEventContainer.pseudoElement is transitionEventBox.pseudoElement
+PASS transitionEventContainer.elapsedTime is transitionEventBox.elapsedTime
+PASS transitionEventContainer.propertyName is transitionEventBox.propertyName
+
diff --git a/LayoutTests/transitions/transition-end-event-prefixed-01.html b/LayoutTests/transitions/transition-end-event-prefixed-01.html
new file mode 100644 (file)
index 0000000..ce537b6
--- /dev/null
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+    <style>
+    .box {
+        position: relative;
+        left: 0;
+        height: 100px;
+        width: 100px;
+        margin: 10px;
+        background-color: blue;
+        -webkit-transition-property: width;
+        -webkit-transition-duration: 0.5s;
+    }
+    </style>
+    <script src="transition-end-event-helpers.js"></script>
+    <script src="../fast/js/resources/js-test-pre.js"></script>
+    <script type="text/javascript">
+    function runAnimation() {
+        var box = document.getElementById('box1');
+        box.style.width = '200px';
+    }
+  </script>
+</head>
+<body onLoad="runAnimation()">
+<script type="text/javascript">
+    description("Test to make sure that if prefixed transition events are modified we correctly modify unprefixed events.");
+
+    if (window.testRunner)
+        testRunner.waitUntilDone();
+
+    var testContainer = document.createElement("div");
+    document.body.appendChild(testContainer);
+
+    testContainer.innerHTML = '<div id="box1" class="box"></div>';
+    var box = document.getElementById('box1');
+
+    var transitionEventContainer;
+    var transitionEventBox;
+
+    function innerTransitionEndEvent(e)
+    {
+        transitionEventBox = e;
+    }
+
+    function outerTransitionEndEvent(e)
+    {
+        transitionEventContainer = e;
+        shouldBe("transitionEventContainer.type", "transitionEventBox.type");
+        shouldBe("transitionEventContainer.bubbles", "transitionEventBox.bubbles");
+        shouldBe("transitionEventContainer.timeStamp", "transitionEventBox.timeStamp");
+        shouldBe("transitionEventContainer.cancelable", "transitionEventBox.cancelable");
+        shouldBe("transitionEventContainer.srcElement", "transitionEventBox.srcElement");
+        shouldBe("transitionEventContainer.returnValue", "transitionEventBox.returnValue");
+        shouldBe("transitionEventContainer.cancelBubble", "transitionEventBox.cancelBubble");
+        shouldBe("transitionEventContainer.defaultPrevented", "transitionEventBox.defaultPrevented");
+        shouldBe("transitionEventContainer.target", "transitionEventBox.target");
+        shouldBe("transitionEventContainer.currentTarget", "testContainer");
+        // TransitionEnd event specific properties.
+        shouldBe("transitionEventContainer.pseudoElement", "transitionEventBox.pseudoElement");
+        shouldBe("transitionEventContainer.elapsedTime", "transitionEventBox.elapsedTime");
+        shouldBe("transitionEventContainer.propertyName", "transitionEventBox.propertyName");
+        if (window.testRunner)
+            testRunner.notifyDone();
+        document.body.removeChild(testContainer);
+    }
+
+    testContainer.addEventListener('webkitTransitionEnd', outerTransitionEndEvent, false);
+    box.addEventListener('webkitTransitionEnd', innerTransitionEndEvent, false);
+</script>
+<script src="../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/transitions/transition-end-event-prefixed-02-expected.txt b/LayoutTests/transitions/transition-end-event-prefixed-02-expected.txt
new file mode 100644 (file)
index 0000000..218fb9e
--- /dev/null
@@ -0,0 +1,10 @@
+Test to make sure that if prefixed transition events are modified we correctly modify unprefixed events.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS transitionEventBox.currentTarget is transitionEventBox.target
+
diff --git a/LayoutTests/transitions/transition-end-event-prefixed-02.html b/LayoutTests/transitions/transition-end-event-prefixed-02.html
new file mode 100644 (file)
index 0000000..85ed794
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+    <style>
+    .box {
+        position: relative;
+        left: 0;
+        height: 100px;
+        width: 100px;
+        margin: 10px;
+        background-color: blue;
+        -webkit-transition-property: width;
+        -webkit-transition-duration: 0.5s;
+    }
+    </style>
+    <script src="transition-end-event-helpers.js"></script>
+    <script src="../fast/js/resources/js-test-pre.js"></script>
+    <script type="text/javascript">
+    function runAnimation() {
+        var box = document.getElementById('box1');
+        box.style.width = '200px';
+    }
+  </script>
+</head>
+<body onLoad="runAnimation()">
+<script type="text/javascript">
+    description("Test to make sure that if prefixed transition events are modified we correctly modify unprefixed events.");
+
+    if (window.testRunner)
+        testRunner.waitUntilDone();
+
+    var testContainer = document.createElement("div");
+    document.body.appendChild(testContainer);
+
+    testContainer.innerHTML = '<div id="box1" class="box"></div>';
+    var box = document.getElementById('box1');
+
+    var transitionEventBox;
+
+    function transitionEndEvent(e)
+    {
+        e.stopPropagation();
+        transitionEventBox = e;
+        shouldBe("transitionEventBox.currentTarget", "transitionEventBox.target");
+        if (window.testRunner)
+            testRunner.notifyDone();
+        document.body.removeChild(testContainer);
+    }
+
+    testContainer.addEventListener('webkitTransitionEnd', transitionEndEvent, false);
+    box.addEventListener('webkitTransitionEnd', transitionEndEvent, false);
+</script>
+<script src="../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/transitions/transition-end-event-prefixed-03-expected.txt b/LayoutTests/transitions/transition-end-event-prefixed-03-expected.txt
new file mode 100644 (file)
index 0000000..992de62
--- /dev/null
@@ -0,0 +1,22 @@
+Test to make sure that if prefixed transition events are modified we correctly modify unprefixed events.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS transitionEventContainer.type is transitionEventBox.type
+PASS transitionEventContainer.bubbles is transitionEventBox.bubbles
+PASS transitionEventContainer.timeStamp is transitionEventBox.timeStamp
+PASS transitionEventContainer.cancelable is transitionEventBox.cancelable
+PASS transitionEventContainer.srcElement is transitionEventBox.srcElement
+PASS transitionEventContainer.returnValue is transitionEventBox.returnValue
+PASS transitionEventContainer.cancelBubble is transitionEventBox.cancelBubble
+PASS transitionEventContainer.defaultPrevented is transitionEventBox.defaultPrevented
+PASS transitionEventContainer.target is transitionEventBox.target
+PASS transitionEventContainer.currentTarget is testContainer
+PASS transitionEventContainer.pseudoElement is transitionEventBox.pseudoElement
+PASS transitionEventContainer.elapsedTime is transitionEventBox.elapsedTime
+PASS transitionEventContainer.propertyName is transitionEventBox.propertyName
+
diff --git a/LayoutTests/transitions/transition-end-event-prefixed-03.html b/LayoutTests/transitions/transition-end-event-prefixed-03.html
new file mode 100644 (file)
index 0000000..146b0fd
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+    <style>
+    .box {
+        position: relative;
+        left: 0;
+        height: 100px;
+        width: 100px;
+        margin: 10px;
+        background-color: blue;
+        -webkit-transition-property: width;
+        -webkit-transition-duration: 0.5s;
+    }
+    </style>
+    <script src="transition-end-event-helpers.js"></script>
+    <script src="../fast/js/resources/js-test-pre.js"></script>
+    <script type="text/javascript">
+    function runAnimation() {
+        var box = document.getElementById('box1');
+        box.style.width = '200px';
+    }
+  </script>
+</head>
+<body onLoad="runAnimation()">
+<script type="text/javascript">
+    description("Test to make sure that if prefixed transition events are modified we correctly modify unprefixed events.");
+
+    if (window.testRunner)
+        testRunner.waitUntilDone();
+
+    var testContainer = document.createElement("div");
+    document.body.appendChild(testContainer);
+
+    testContainer.innerHTML = '<div id="box1" class="box"></div>';
+    var box = document.getElementById('box1');
+
+    var transitionEventContainer;
+    var transitionEventBox;
+
+    function innerTransitionEndEvent(e)
+    {
+        transitionEventBox = e;
+        e.preventDefault();
+    }
+
+    function outerTransitionEndEvent(e)
+    {
+        transitionEventContainer = e;
+        shouldBe("transitionEventContainer.type", "transitionEventBox.type");
+        shouldBe("transitionEventContainer.bubbles", "transitionEventBox.bubbles");
+        shouldBe("transitionEventContainer.timeStamp", "transitionEventBox.timeStamp");
+        shouldBe("transitionEventContainer.cancelable", "transitionEventBox.cancelable");
+        shouldBe("transitionEventContainer.srcElement", "transitionEventBox.srcElement");
+        shouldBe("transitionEventContainer.returnValue", "transitionEventBox.returnValue");
+        shouldBe("transitionEventContainer.cancelBubble", "transitionEventBox.cancelBubble");
+        shouldBe("transitionEventContainer.defaultPrevented", "transitionEventBox.defaultPrevented");
+        shouldBe("transitionEventContainer.target", "transitionEventBox.target");
+        shouldBe("transitionEventContainer.currentTarget", "testContainer");
+        // TransitionEnd event specific properties.
+        shouldBe("transitionEventContainer.pseudoElement", "transitionEventBox.pseudoElement");
+        shouldBe("transitionEventContainer.elapsedTime", "transitionEventBox.elapsedTime");
+        shouldBe("transitionEventContainer.propertyName", "transitionEventBox.propertyName");
+        if (window.testRunner)
+            testRunner.notifyDone();
+        document.body.removeChild(testContainer);
+    }
+
+    testContainer.addEventListener('webkitTransitionEnd', outerTransitionEndEvent, false);
+    box.addEventListener('webkitTransitionEnd', innerTransitionEndEvent, false);
+</script>
+<script src="../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 7e4440a..ece7d3a 100644 (file)
@@ -1,3 +1,29 @@
+2013-05-16  Alexis Menard  <alexis@webkit.org>
+
+        Regression: Event#stopPropagation() does not halt bubbling for webkitTransitionEnd
+        https://bugs.webkit.org/show_bug.cgi?id=115656
+
+        Reviewed by Darin Adler.
+
+        If we create a prefixed event to dispatch it (in the case we have only
+        prefixed event listeners in client's code) then we need to make sure to
+        keep it in sync with the original unprefixed event after it has been
+        dispatched. While being dispatched the event can be modified by
+        client's code and when propagated back to outer elements, attributes were
+        not updated. This patch changes the old design of creating a separate event
+        for the prefixed case and now change the type of the event (so the name) before
+        dispatching it, keeping the attributes if changed and then rename it
+        back to unprefixed when the dispatching is finished.
+
+        Tests: transitions/transition-end-event-prefixed-01.html
+               transitions/transition-end-event-prefixed-02.html
+               transitions/transition-end-event-prefixed-03.html
+
+        * dom/Event.h:
+        (WebCore::Event::setType):
+        * dom/EventTarget.cpp:
+        (WebCore::EventTarget::fireEventListeners):
+
 2013-05-16  Mike Fenton  <mifenton@rim.com>
 
         [BlackBerry] Strip invoke URLs when writing to the clipboard.
index a9d1299..4725ae3 100644 (file)
@@ -93,6 +93,7 @@ public:
     void initEvent(const AtomicString& type, bool canBubble, bool cancelable);
 
     const AtomicString& type() const { return m_type; }
+    void setType(const AtomicString& type) { m_type = type; }
     
     EventTarget* target() const { return m_target.get(); }
     void setTarget(PassRefPtr<EventTarget>);
index 2669b1a..8b79d3a 100644 (file)
@@ -162,20 +162,6 @@ void EventTarget::uncaughtExceptionInEventHandler()
 {
 }
 
-static PassRefPtr<Event> createMatchingPrefixedEvent(const Event* event)
-{
-    if (event->type() == eventNames().transitionendEvent) {
-        const WebKitTransitionEvent* transitionEvent = static_cast<const WebKitTransitionEvent*>(event);
-        RefPtr<Event> prefixedEvent = WebKitTransitionEvent::create(eventNames().webkitTransitionEndEvent, transitionEvent->propertyName(), transitionEvent->elapsedTime(), transitionEvent->pseudoElement());
-        prefixedEvent->setTarget(event->target());
-        prefixedEvent->setCurrentTarget(event->currentTarget());
-        prefixedEvent->setEventPhase(event->eventPhase());
-        return prefixedEvent.release();
-    }
-    ASSERT_NOT_REACHED();
-    return 0;
-}
-
 static AtomicString prefixedType(const Event* event)
 {
     if (event->type() == eventNames().transitionendEvent)
@@ -202,8 +188,12 @@ bool EventTarget::fireEventListeners(Event* event)
 
     if (listenerUnprefixedVector)
         fireEventListeners(event, d, *listenerUnprefixedVector);
-    else if (listenerPrefixedVector)
-        fireEventListeners(createMatchingPrefixedEvent(event).get(), d, *listenerPrefixedVector);
+    else if (listenerPrefixedVector) {
+        AtomicString unprefixedTypeName = event->type();
+        event->setType(prefixedTypeName);
+        fireEventListeners(event, d, *listenerPrefixedVector);
+        event->setType(unprefixedTypeName);
+    }
 
     if (!prefixedTypeName.isEmpty()) {
         ScriptExecutionContext* context = scriptExecutionContext();