Commit ca1aea26 authored by jochen@chromium.org's avatar jochen@chromium.org

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.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@130267 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a09b3cd4
2012-10-03 Jochen Eisinger <jochen@chromium.org>
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 <dmazzoni@google.com>
AX: Heap-use-after-free when deleting a ContainerNode with an AX object
......
Test that a forwarded user gesture that is consumed before the time fires is resurrected.
Click Here
PASSED
<!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>
Test that a forwarded user gesture can be consumed by any timeout, not just the first.
Click Here
PASSED
<!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>
2012-10-03 Jochen Eisinger <jochen@chromium.org>
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 <dmazzoni@google.com>
AX: Heap-use-after-free when deleting a ContainerNode with an AX object
......
......@@ -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 (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;
}
if (s_state == DefinitelyProcessingUserGesture)
s_consumableGestures++;
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();
}
}
......@@ -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 @@ enum ProcessingUserGestureState {
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;
};
}
......
......@@ -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 @@ DOMTimer::DOMTimer(ScriptExecutionContext* context, PassOwnPtr<ScheduledAction>
, 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 @@ void DOMTimer::fired()
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);
......
......@@ -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 @@ namespace WebCore {
int m_nestingLevel;
OwnPtr<ScheduledAction> m_action;
int m_originalInterval;
bool m_shouldForwardUserGesture;
RefPtr<UserGestureIndicator::Token> m_userGestureToken;
static double s_minDefaultTimerInterval;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment