- 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;
};