Bug 1351148 Part3: Synthesize native input events with priority. f=kats,smaug. r=smaug.

The test helper_touch_action_regions.html uses nsDOMWindowUtils to synthesize native input events and creates some runnables to trigger the test. It expects the runnables which synthesize native input events are processed first, then the runnables to continue the test, and finally the input events are forwarded from chrome process to content process. Enabling event prioritization may change the execution order.
Wraps those runnables to synthesize native input events as priority=input and dispatches those runnables to continue the test with priority=input to make sure the execution order is as expected.

MozReview-Commit-ID: 8hkaB1FRW9T
This commit is contained in:
Stone Shih 2017-05-19 15:41:24 +08:00
parent 9573b6e439
commit 5c6d99d228
7 changed files with 143 additions and 44 deletions

View File

@ -174,6 +174,30 @@ private:
nsSize mSize;
};
namespace {
class NativeInputRunnable final : public PrioritizableRunnable
{
explicit NativeInputRunnable(already_AddRefed<nsIRunnable>&& aEvent);
~NativeInputRunnable() {}
public:
static already_AddRefed<nsIRunnable> Create(already_AddRefed<nsIRunnable>&& aEvent);
};
NativeInputRunnable::NativeInputRunnable(already_AddRefed<nsIRunnable>&& aEvent)
: PrioritizableRunnable(Move(aEvent), nsIRunnablePriority::PRIORITY_INPUT)
{
}
/* static */ already_AddRefed<nsIRunnable>
NativeInputRunnable::Create(already_AddRefed<nsIRunnable>&& aEvent)
{
nsCOMPtr<nsIRunnable> event(new NativeInputRunnable(Move(aEvent)));
return event.forget();
}
} // unnamed namespace
LinkedList<OldWindowSize> OldWindowSize::sList;
NS_INTERFACE_MAP_BEGIN(nsDOMWindowUtils)
@ -1119,7 +1143,7 @@ nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout,
if (!widget)
return NS_ERROR_FAILURE;
NS_DispatchToMainThread(
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<int32_t,
int32_t,
uint32_t,
@ -1133,7 +1157,7 @@ nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout,
aModifiers,
aCharacters,
aUnmodifiedCharacters,
aObserver));
aObserver)));
return NS_OK;
}
@ -1150,7 +1174,7 @@ nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX,
if (!widget)
return NS_ERROR_FAILURE;
NS_DispatchToMainThread(
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<LayoutDeviceIntPoint, int32_t, int32_t, nsIObserver*>(
"nsIWidget::SynthesizeNativeMouseEvent",
widget,
@ -1158,7 +1182,7 @@ nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX,
LayoutDeviceIntPoint(aScreenX, aScreenY),
aNativeMessage,
aModifierFlags,
aObserver));
aObserver)));
return NS_OK;
}
@ -1173,12 +1197,13 @@ nsDOMWindowUtils::SendNativeMouseMove(int32_t aScreenX,
if (!widget)
return NS_ERROR_FAILURE;
NS_DispatchToMainThread(NewRunnableMethod<LayoutDeviceIntPoint, nsIObserver*>(
"nsIWidget::SynthesizeNativeMouseMove",
widget,
&nsIWidget::SynthesizeNativeMouseMove,
LayoutDeviceIntPoint(aScreenX, aScreenY),
aObserver));
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<LayoutDeviceIntPoint, nsIObserver*>(
"nsIWidget::SynthesizeNativeMouseMove",
widget,
&nsIWidget::SynthesizeNativeMouseMove,
LayoutDeviceIntPoint(aScreenX, aScreenY),
aObserver)));
return NS_OK;
}
@ -1200,25 +1225,26 @@ nsDOMWindowUtils::SendNativeMouseScrollEvent(int32_t aScreenX,
return NS_ERROR_FAILURE;
}
NS_DispatchToMainThread(NewRunnableMethod<mozilla::LayoutDeviceIntPoint,
uint32_t,
double,
double,
double,
uint32_t,
uint32_t,
nsIObserver*>(
"nsIWidget::SynthesizeNativeMouseScrollEvent",
widget,
&nsIWidget::SynthesizeNativeMouseScrollEvent,
LayoutDeviceIntPoint(aScreenX, aScreenY),
aNativeMessage,
aDeltaX,
aDeltaY,
aDeltaZ,
aModifierFlags,
aAdditionalFlags,
aObserver));
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<mozilla::LayoutDeviceIntPoint,
uint32_t,
double,
double,
double,
uint32_t,
uint32_t,
nsIObserver*>(
"nsIWidget::SynthesizeNativeMouseScrollEvent",
widget,
&nsIWidget::SynthesizeNativeMouseScrollEvent,
LayoutDeviceIntPoint(aScreenX, aScreenY),
aNativeMessage,
aDeltaX,
aDeltaY,
aDeltaZ,
aModifierFlags,
aAdditionalFlags,
aObserver)));
return NS_OK;
}
@ -1240,7 +1266,7 @@ nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId,
return NS_ERROR_INVALID_ARG;
}
NS_DispatchToMainThread(
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<uint32_t,
nsIWidget::TouchPointerState,
LayoutDeviceIntPoint,
@ -1254,7 +1280,7 @@ nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId,
LayoutDeviceIntPoint(aScreenX, aScreenY),
aPressure,
aOrientation,
aObserver));
aObserver)));
return NS_OK;
}
@ -1269,14 +1295,14 @@ nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX,
return NS_ERROR_FAILURE;
}
NS_DispatchToMainThread(
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<LayoutDeviceIntPoint, bool, nsIObserver*>(
"nsIWidget::SynthesizeNativeTouchTap",
widget,
&nsIWidget::SynthesizeNativeTouchTap,
LayoutDeviceIntPoint(aScreenX, aScreenY),
aLongTap,
aObserver));
aObserver)));
return NS_OK;
}
@ -1298,11 +1324,11 @@ nsDOMWindowUtils::ClearNativeTouchSequence(nsIObserver* aObserver)
return NS_ERROR_FAILURE;
}
NS_DispatchToMainThread(
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<nsIObserver*>("nsIWidget::ClearNativeTouchSequence",
widget,
&nsIWidget::ClearNativeTouchSequence,
aObserver));
aObserver)));
return NS_OK;
}

View File

@ -93,6 +93,11 @@ function waitFor(eventType, count) {
return true;
}
function RunAfterProcessedQueuedInputEvents(aCallback) {
let tm = SpecialPowers.Services.tm;
tm.dispatchToMainThread(aCallback, SpecialPowers.Ci.nsIRunnablePriority.PRIORITY_INPUT);
}
function* test(testDriver) {
// The main part of this test should run completely before the child process'
// main-thread deals with the touch event, so check to make sure that happens.
@ -139,8 +144,8 @@ function* test(testDriver) {
// scrolling even though we know we haven't yet processed the DOM touch events
// in the child process yet.
//
// Note that the "async callback" we use here is SpecialPowers.executeSoon,
// because nothing else does exactly what we want:
// Note that the "async callback" we use here is SpecialPowers.tm.dispatchToMainThread
// with priority = input, because nothing else does exactly what we want:
// - setTimeout(..., 0) does not maintain ordering, because it respects the
// time delta provided (i.e. the callback can jump the queue to meet its
// deadline).
@ -149,6 +154,9 @@ function* test(testDriver) {
// round-trip time.
// - SimpleTest.executeSoon has a codepath that delegates to setTimeout, so
// is less reliable if it ever decides to switch to that codepath.
// - SpecialPowers.executeSoon dispatches a task to main thread. However,
// normal runnables may be preempted by input events and be executed in an
// unexpected order.
// The other problem we need to deal with is the asynchronicity in the chrome
// process. That is, we might request a snapshot before the chrome process has
@ -169,13 +177,13 @@ function* test(testDriver) {
// Set up the child process events and callbacks
var scroller = document.getElementById('scroller');
synthesizeNativeTouch(scroller, 10, 110, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
SpecialPowers.executeSoon(testDriver);
RunAfterProcessedQueuedInputEvents(testDriver);
for (var i = 1; i < 10; i++) {
synthesizeNativeTouch(scroller, 10, 110 - (i * 10), SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
SpecialPowers.executeSoon(testDriver);
RunAfterProcessedQueuedInputEvents(testDriver);
}
synthesizeNativeTouch(scroller, 10, 10, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, null, 0);
SpecialPowers.executeSoon(testDriver);
RunAfterProcessedQueuedInputEvents(testDriver);
ok(true, "Finished setting up event queue");
// Get our baseline snapshot

View File

@ -18,7 +18,7 @@ interface nsIRunnable : nsISupports
void run();
};
[uuid(e75aa42a-80a9-11e6-afb5-e89d87348e2c)]
[scriptable, uuid(e75aa42a-80a9-11e6-afb5-e89d87348e2c)]
interface nsIRunnablePriority : nsISupports
{
const unsigned short PRIORITY_NORMAL = 0;

View File

@ -92,7 +92,7 @@ interface nsIThreadManager : nsISupports
* .currentThread.dispatch(runnable, Ci.nsIEventTarget.DISPATCH_NORMAL);
* C++ callers should instead use NS_DispatchToMainThread.
*/
void dispatchToMainThread(in nsIRunnable event);
void dispatchToMainThread(in nsIRunnable event, [optional] in uint32_t priority);
/**
* This queues a runnable to the main thread's idle queue.

View File

@ -386,7 +386,7 @@ nsThreadManager::GetHighestNumberOfThreads()
}
NS_IMETHODIMP
nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent)
nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent, uint32_t aPriority)
{
// Note: C++ callers should instead use NS_DispatchToMainThread.
MOZ_ASSERT(NS_IsMainThread());
@ -395,7 +395,11 @@ nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent)
if (NS_WARN_IF(!mMainThread)) {
return NS_ERROR_NOT_INITIALIZED;
}
if (aPriority != nsIRunnablePriority::PRIORITY_NORMAL) {
nsCOMPtr<nsIRunnable> event(aEvent);
return mMainThread->DispatchFromScript(
new PrioritizableRunnable(event.forget(), aPriority), 0);
}
return mMainThread->DispatchFromScript(aEvent, 0);
}

View File

@ -97,6 +97,47 @@ already_AddRefed<nsITimer> CreateTimer()
} // namespace detail
} // namespace mozilla
NS_IMPL_ISUPPORTS_INHERITED(PrioritizableRunnable, Runnable,
nsIRunnablePriority)
PrioritizableRunnable::PrioritizableRunnable(already_AddRefed<nsIRunnable>&& aRunnable,
uint32_t aPriority)
// Real runnable name is managed by overridding the GetName function.
: Runnable("PrioritizableRunnable")
, mRunnable(Move(aRunnable))
, mPriority(aPriority)
{
#if DEBUG
nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(mRunnable);
MOZ_ASSERT(!runnablePrio);
#endif
}
NS_IMETHODIMP
PrioritizableRunnable::GetName(nsACString& aName)
{
// Try to get a name from the underlying runnable.
nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable);
if (named) {
named->GetName(aName);
}
return NS_OK;
}
NS_IMETHODIMP
PrioritizableRunnable::Run()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
return mRunnable->Run();
}
NS_IMETHODIMP
PrioritizableRunnable::GetPriority(uint32_t* aPriority)
{
*aPriority = mPriority;
return NS_OK;
}
#endif // XPCOM_GLUE_AVOID_NSPR
//-----------------------------------------------------------------------------

View File

@ -486,6 +486,26 @@ private:
IdleRunnable& operator=(const IdleRunnable&&) = delete;
};
// This class is designed to be a wrapper of a real runnable to support event
// prioritizable.
class PrioritizableRunnable : public Runnable, public nsIRunnablePriority
{
public:
PrioritizableRunnable(already_AddRefed<nsIRunnable>&& aRunnable,
uint32_t aPriority);
NS_IMETHOD GetName(nsACString& aName) override;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
NS_DECL_NSIRUNNABLEPRIORITY
protected:
virtual ~PrioritizableRunnable() {};
nsCOMPtr<nsIRunnable> mRunnable;
uint32_t mPriority;
};
namespace detail {
// An event that can be used to call a C++11 functions or function objects,