Title: [130267] trunk
Revision
130267
Author
[email protected]
Date
2012-10-03 02:45:35 -0700 (Wed, 03 Oct 2012)

Log Message

Make sure that user gestures can't be consumed twice
https://bugs.webkit.org/show_bug.cgi?id=97483

Reviewed by Adam Barth.

Source/WebCore:

Instead of a simple counter, use a ref counted token to track how many
user gestures happened and where consumed. When creating a timer that
is supposed to forward the user gesture, take a reference to this token
and reinstantiate the UserGestureIndicator with that token when the
timer is triggered.

Tests: platform/chromium/fast/events/popup-forwarded-gesture-blocked.html
       platform/chromium/fast/events/popup-forwarded-gesture.html

* dom/UserGestureIndicator.cpp:
(WebCore):
(WebCore::UserGestureIndicator::UserGestureIndicator):
(WebCore::UserGestureIndicator::~UserGestureIndicator):
(WebCore::UserGestureIndicator::processingUserGesture):
(WebCore::UserGestureIndicator::consumeUserGesture):
(WebCore::UserGestureIndicator::currentToken):
* dom/UserGestureIndicator.h:
(Token):
(WebCore::UserGestureIndicator::Token::~Token):
(UserGestureIndicator):
* page/DOMTimer.cpp:
(WebCore::DOMTimer::DOMTimer):
(WebCore::DOMTimer::fired):
* page/DOMTimer.h:
(DOMTimer):

LayoutTests:

* platform/chromium/fast/events/popup-forwarded-gesture-blocked-expected.txt: Added.
* platform/chromium/fast/events/popup-forwarded-gesture-blocked.html: Added.
* platform/chromium/fast/events/popup-forwarded-gesture-expected.txt: Added.
* platform/chromium/fast/events/popup-forwarded-gesture.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (130266 => 130267)


--- trunk/LayoutTests/ChangeLog	2012-10-03 09:23:30 UTC (rev 130266)
+++ trunk/LayoutTests/ChangeLog	2012-10-03 09:45:35 UTC (rev 130267)
@@ -1,3 +1,15 @@
+2012-10-03  Jochen Eisinger  <[email protected]>
+
+        Make sure that user gestures can't be consumed twice
+        https://bugs.webkit.org/show_bug.cgi?id=97483
+
+        Reviewed by Adam Barth.
+
+        * platform/chromium/fast/events/popup-forwarded-gesture-blocked-expected.txt: Added.
+        * platform/chromium/fast/events/popup-forwarded-gesture-blocked.html: Added.
+        * platform/chromium/fast/events/popup-forwarded-gesture-expected.txt: Added.
+        * platform/chromium/fast/events/popup-forwarded-gesture.html: Added.
+
 2012-10-03  Dominic Mazzoni  <[email protected]>
 
         AX: Heap-use-after-free when deleting a ContainerNode with an AX object

Added: trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-blocked-expected.txt (0 => 130267)


--- trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-blocked-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-blocked-expected.txt	2012-10-03 09:45:35 UTC (rev 130267)
@@ -0,0 +1,4 @@
+Test that a forwarded user gesture that is consumed before the time fires is resurrected.
+
+Click Here
+PASSED

Added: trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-blocked.html (0 => 130267)


--- trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-blocked.html	                        (rev 0)
+++ trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-blocked.html	2012-10-03 09:45:35 UTC (rev 130267)
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+    <body>
+        <p>
+            Test that a forwarded user gesture that is consumed before the
+            time fires is resurrected.
+        </p>
+        <button id="button" _onclick_="popup()">Click Here</button>
+        <div id="console"></div>
+        <script>
+            function timer() {
+                window.open("about:blank", "window2");
+                if (window.testRunner) {
+                    if (testRunner.windowCount() == windowCount + 1)
+                        document.getElementById("console").innerText = "PASSED";
+                    else
+                        document.getElementById("console").innerText = "FAILED";
+                    testRunner.notifyDone();
+                }
+            }
+
+            function popup() {
+                setTimeout(timer, 0);
+                window.open("about:blank", "window1");
+            }
+
+            if (window.testRunner) {
+                testRunner.dumpAsText();
+                testRunner.setCanOpenWindows();
+                testRunner.setPopupBlockingEnabled(true);
+                testRunner.setCloseRemainingWindowsWhenComplete(true);
+                testRunner.waitUntilDone();
+                windowCount = testRunner.windowCount();
+
+                var button = document.getElementById("button");
+
+                if (window.eventSender) {
+                    eventSender.mouseMoveTo(button.offsetLeft + button.offsetWidth / 2, button.offsetTop + button.offsetHeight / 2);
+                    eventSender.mouseDown();
+                    eventSender.mouseUp();
+                }
+            }
+        </script>
+    </body>
+</html>

Added: trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-expected.txt (0 => 130267)


--- trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture-expected.txt	2012-10-03 09:45:35 UTC (rev 130267)
@@ -0,0 +1,4 @@
+Test that a forwarded user gesture can be consumed by any timeout, not just the first.
+
+Click Here
+PASSED

Added: trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture.html (0 => 130267)


--- trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture.html	                        (rev 0)
+++ trunk/LayoutTests/platform/chromium/fast/events/popup-forwarded-gesture.html	2012-10-03 09:45:35 UTC (rev 130267)
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+    <body>
+        <p>
+            Test that a forwarded user gesture can be consumed by any timeout,
+            not just the first.
+        </p>
+        <button id="button" _onclick_="popup()">Click Here</button>
+        <div id="console"></div>
+        <script>
+            function timer1() {
+            }
+            function timer2() {
+                var win = window.open("about:blank", "window");
+                if (!win)
+                    document.getElementById("console").innerText = "FAILED";
+                else
+                    document.getElementById("console").innerText = "PASSED";
+                if (window.testRunner)
+                    testRunner.notifyDone();
+            }
+
+            function popup() {
+                setTimeout(timer1, 0);
+                setTimeout(timer2, 10);
+            }
+
+            if (window.testRunner) {
+                testRunner.dumpAsText();
+                testRunner.setCanOpenWindows();
+                testRunner.setPopupBlockingEnabled(true);
+                testRunner.setCloseRemainingWindowsWhenComplete(true);
+                testRunner.waitUntilDone();
+
+                var button = document.getElementById("button");
+
+                if (window.eventSender) {
+                    eventSender.mouseMoveTo(button.offsetLeft + button.offsetWidth / 2, button.offsetTop + button.offsetHeight / 2);
+                    eventSender.mouseDown();
+                    eventSender.mouseUp();
+                }
+            }
+        </script>
+    </body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (130266 => 130267)


--- trunk/Source/WebCore/ChangeLog	2012-10-03 09:23:30 UTC (rev 130266)
+++ trunk/Source/WebCore/ChangeLog	2012-10-03 09:45:35 UTC (rev 130267)
@@ -1,3 +1,36 @@
+2012-10-03  Jochen Eisinger  <[email protected]>
+
+        Make sure that user gestures can't be consumed twice
+        https://bugs.webkit.org/show_bug.cgi?id=97483
+
+        Reviewed by Adam Barth.
+
+        Instead of a simple counter, use a ref counted token to track how many
+        user gestures happened and where consumed. When creating a timer that
+        is supposed to forward the user gesture, take a reference to this token
+        and reinstantiate the UserGestureIndicator with that token when the
+        timer is triggered.
+
+        Tests: platform/chromium/fast/events/popup-forwarded-gesture-blocked.html
+               platform/chromium/fast/events/popup-forwarded-gesture.html
+
+        * dom/UserGestureIndicator.cpp:
+        (WebCore):
+        (WebCore::UserGestureIndicator::UserGestureIndicator):
+        (WebCore::UserGestureIndicator::~UserGestureIndicator):
+        (WebCore::UserGestureIndicator::processingUserGesture):
+        (WebCore::UserGestureIndicator::consumeUserGesture):
+        (WebCore::UserGestureIndicator::currentToken):
+        * dom/UserGestureIndicator.h:
+        (Token):
+        (WebCore::UserGestureIndicator::Token::~Token):
+        (UserGestureIndicator):
+        * page/DOMTimer.cpp:
+        (WebCore::DOMTimer::DOMTimer):
+        (WebCore::DOMTimer::fired):
+        * page/DOMTimer.h:
+        (DOMTimer):
+
 2012-10-03  Dominic Mazzoni  <[email protected]>
 
         AX: Heap-use-after-free when deleting a ContainerNode with an AX object

Modified: trunk/Source/WebCore/dom/UserGestureIndicator.cpp (130266 => 130267)


--- trunk/Source/WebCore/dom/UserGestureIndicator.cpp	2012-10-03 09:23:30 UTC (rev 130266)
+++ trunk/Source/WebCore/dom/UserGestureIndicator.cpp	2012-10-03 09:45:35 UTC (rev 130267)
@@ -28,40 +28,104 @@
 
 namespace WebCore {
 
+namespace {
+
+class GestureToken : public UserGestureIndicator::Token {
+public:
+    static PassRefPtr<UserGestureIndicator::Token> create() { return adoptRef(new GestureToken); }
+
+    virtual ~GestureToken() { }
+
+    void addGesture() { m_consumableGestures++; }
+    bool consumeGesture()
+    {
+        if (!m_consumableGestures)
+            return false;
+        m_consumableGestures--;
+        return true;
+    }
+    bool hasGestures() const { return m_consumableGestures > 0; }
+
+private:
+    GestureToken()
+        : m_consumableGestures(0)
+    {
+    }
+
+    size_t m_consumableGestures;
+};
+
+}
+
 static bool isDefinite(ProcessingUserGestureState state)
 {
     return state == DefinitelyProcessingUserGesture || state == DefinitelyNotProcessingUserGesture;
 }
 
 ProcessingUserGestureState UserGestureIndicator::s_state = DefinitelyNotProcessingUserGesture;
-size_t UserGestureIndicator::s_consumableGestures = 0;
+UserGestureIndicator* UserGestureIndicator::s_topmostIndicator = 0;
 
 UserGestureIndicator::UserGestureIndicator(ProcessingUserGestureState state)
     : m_previousState(s_state)
 {
     // We overwrite s_state only if the caller is definite about the gesture state.
-    if (isDefinite(state))
+    if (isDefinite(state)) {
+        if (!s_topmostIndicator) {
+            s_topmostIndicator = this;
+            m_token = GestureToken::create();
+        } else
+            m_token = s_topmostIndicator->currentToken();
         s_state = state;
+    }
 
-    if (s_state == DefinitelyProcessingUserGesture)
-        s_consumableGestures++;
+    if (state == DefinitelyProcessingUserGesture)
+        static_cast<GestureToken*>(m_token.get())->addGesture();
     ASSERT(isDefinite(s_state));
 }
 
+UserGestureIndicator::UserGestureIndicator(PassRefPtr<UserGestureIndicator::Token> token)
+    : m_previousState(s_state)
+{
+    if (token && static_cast<GestureToken*>(token.get())->hasGestures()) {
+        if (!s_topmostIndicator) {
+            s_topmostIndicator = this;
+            m_token = token;
+        } else {
+            m_token = s_topmostIndicator->currentToken();
+            static_cast<GestureToken*>(m_token.get())->addGesture();
+            static_cast<GestureToken*>(token.get())->consumeGesture();
+        }
+        s_state = DefinitelyProcessingUserGesture;
+    }
+
+    ASSERT(isDefinite(s_state));
+}
+
 UserGestureIndicator::~UserGestureIndicator()
 {
-    if (s_consumableGestures && s_state == DefinitelyProcessingUserGesture)
-        s_consumableGestures--;
     s_state = m_previousState;
+    if (s_topmostIndicator == this)
+        s_topmostIndicator = 0;
     ASSERT(isDefinite(s_state));
 }
 
+bool UserGestureIndicator::processingUserGesture()
+{
+    return s_topmostIndicator && static_cast<GestureToken*>(s_topmostIndicator->currentToken())->hasGestures() && s_state == DefinitelyProcessingUserGesture;
+}
+
 bool UserGestureIndicator::consumeUserGesture()
 {
-    if (!s_consumableGestures)
+    if (!s_topmostIndicator)
         return false;
-    s_consumableGestures--;
-    return true;
+    return static_cast<GestureToken*>(s_topmostIndicator->currentToken())->consumeGesture();
 }
 
+UserGestureIndicator::Token* UserGestureIndicator::currentToken()
+{
+    if (!s_topmostIndicator)
+        return 0;
+    return s_topmostIndicator->m_token.get();
 }
+
+}

Modified: trunk/Source/WebCore/dom/UserGestureIndicator.h (130266 => 130267)


--- trunk/Source/WebCore/dom/UserGestureIndicator.h	2012-10-03 09:23:30 UTC (rev 130266)
+++ trunk/Source/WebCore/dom/UserGestureIndicator.h	2012-10-03 09:45:35 UTC (rev 130267)
@@ -27,6 +27,8 @@
 #define UserGestureIndicator_h
 
 #include <wtf/Noncopyable.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
 
 namespace WebCore {
 
@@ -39,16 +41,25 @@
 class UserGestureIndicator {
     WTF_MAKE_NONCOPYABLE(UserGestureIndicator);
 public:
-    static bool processingUserGesture() { return s_consumableGestures && s_state == DefinitelyProcessingUserGesture; }
+    class Token : public RefCounted<Token> {
+    public:
+        virtual ~Token() { }
+    };
+
+    static bool processingUserGesture();
     static bool consumeUserGesture();
+    static Token* currentToken();
 
     explicit UserGestureIndicator(ProcessingUserGestureState);
+    explicit UserGestureIndicator(PassRefPtr<Token>);
     ~UserGestureIndicator();
 
+
 private:
     static ProcessingUserGestureState s_state;
-    static size_t s_consumableGestures;
+    static UserGestureIndicator* s_topmostIndicator;
     ProcessingUserGestureState m_previousState;
+    RefPtr<Token> m_token;
 };
 
 }

Modified: trunk/Source/WebCore/page/DOMTimer.cpp (130266 => 130267)


--- trunk/Source/WebCore/page/DOMTimer.cpp	2012-10-03 09:23:30 UTC (rev 130266)
+++ trunk/Source/WebCore/page/DOMTimer.cpp	2012-10-03 09:45:35 UTC (rev 130267)
@@ -30,7 +30,6 @@
 #include "InspectorInstrumentation.h"
 #include "ScheduledAction.h"
 #include "ScriptExecutionContext.h"
-#include "UserGestureIndicator.h"
 #include <wtf/HashSet.h>
 #include <wtf/StdLibExtras.h>
 
@@ -68,8 +67,9 @@
     , m_nestingLevel(timerNestingLevel + 1)
     , m_action(action)
     , m_originalInterval(interval)
-    , m_shouldForwardUserGesture(shouldForwardUserGesture(interval, m_nestingLevel))
 {
+    if (shouldForwardUserGesture(interval, m_nestingLevel))
+        m_userGestureToken = UserGestureIndicator::currentToken();
     scriptExecutionContext()->addTimeout(m_timeoutId, this);
 
     double intervalMilliseconds = intervalClampedToMinimum(interval, context->minimumTimerInterval());
@@ -116,10 +116,8 @@
     ScriptExecutionContext* context = scriptExecutionContext();
     timerNestingLevel = m_nestingLevel;
     ASSERT(!context->activeDOMObjectsAreSuspended());
-    UserGestureIndicator gestureIndicator(m_shouldForwardUserGesture ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture);
-    
     // Only the first execution of a multi-shot timer should get an affirmative user gesture indicator.
-    m_shouldForwardUserGesture = false;
+    UserGestureIndicator gestureIndicator(m_userGestureToken.release());
 
     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireTimer(context, m_timeoutId);
 

Modified: trunk/Source/WebCore/page/DOMTimer.h (130266 => 130267)


--- trunk/Source/WebCore/page/DOMTimer.h	2012-10-03 09:23:30 UTC (rev 130266)
+++ trunk/Source/WebCore/page/DOMTimer.h	2012-10-03 09:45:35 UTC (rev 130267)
@@ -28,6 +28,7 @@
 #define DOMTimer_h
 
 #include "SuspendableTimer.h"
+#include "UserGestureIndicator.h"
 #include <wtf/OwnPtr.h>
 #include <wtf/PassOwnPtr.h>
 
@@ -69,7 +70,7 @@
         int m_nestingLevel;
         OwnPtr<ScheduledAction> m_action;
         int m_originalInterval;
-        bool m_shouldForwardUserGesture;
+        RefPtr<UserGestureIndicator::Token> m_userGestureToken;
         static double s_minDefaultTimerInterval;
     };
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to