Bug 1389314 Part2: Support enabling and disabling the input priority events in runtime. r=smaug.

MozReview-Commit-ID: 3a2TNVqguVb
This commit is contained in:
Stone Shih 2017-07-28 15:14:54 +08:00
parent 60d437ddc2
commit 2c638fc04f
20 changed files with 488 additions and 81 deletions

View File

@ -190,6 +190,7 @@ NativeInputRunnable::NativeInputRunnable(already_AddRefed<nsIRunnable>&& aEvent)
/* static */ already_AddRefed<nsIRunnable>
NativeInputRunnable::Create(already_AddRefed<nsIRunnable>&& aEvent)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIRunnable> event(new NativeInputRunnable(Move(aEvent)));
return event.forget();
}

View File

@ -1203,9 +1203,6 @@ ContentChild::InitXPCOM(const XPCOMInitData& aXPCOMInit,
GfxInfoBase::SetFeatureStatus(aXPCOMInit.gfxFeatureStatus());
DataStorage::SetCachedStorageEntries(aXPCOMInit.dataStorage());
// Enable input event prioritization.
nsThreadManager::get().EnableMainThreadEventPrioritization();
}
mozilla::ipc::IPCResult
@ -3599,6 +3596,34 @@ ContentChild::RecvResetCodeCoverageCounters()
#endif
}
mozilla::ipc::IPCResult
ContentChild::RecvSetInputEventQueueEnabled()
{
nsThreadManager::get().EnableMainThreadEventPrioritization();
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentChild::RecvFlushInputEventQueue()
{
nsThreadManager::get().FlushInputEventPrioritization();
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentChild::RecvSuspendInputEventQueue()
{
nsThreadManager::get().SuspendInputEventPrioritization();
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentChild::RecvResumeInputEventQueue()
{
nsThreadManager::get().ResumeInputEventPrioritization();
return IPC_OK();
}
already_AddRefed<nsIEventTarget>
ContentChild::GetSpecificMessageEventTarget(const Message& aMsg)
{

View File

@ -621,6 +621,18 @@ public:
virtual mozilla::ipc::IPCResult
RecvResetCodeCoverageCounters() override;
virtual mozilla::ipc::IPCResult
RecvSetInputEventQueueEnabled() override;
virtual mozilla::ipc::IPCResult
RecvFlushInputEventQueue() override;
virtual mozilla::ipc::IPCResult
RecvSuspendInputEventQueue() override;
virtual mozilla::ipc::IPCResult
RecvResumeInputEventQueue() override;
#if defined(XP_WIN) && defined(ACCESSIBILITY)
bool
SendGetA11yContentId();

View File

@ -2094,6 +2094,8 @@ ContentParent::ContentParent(ContentParent* aOpener,
, mCreatedPairedMinidumps(false)
, mShutdownPending(false)
, mIPCOpen(true)
, mIsRemoteInputEventQueueEnabled(false)
, mIsInputPriorityEventEnabled(false)
, mHangMonitorActor(nullptr)
{
// Insert ourselves into the global linked list of ContentParent objects.
@ -2444,6 +2446,7 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
// If this isn't our first content process, just send over cached list.
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
pluginHost->SendPluginsToContent();
MaybeEnableRemoteInputEventQueue();
}
bool
@ -2509,6 +2512,47 @@ ContentParent::OnCompositorDeviceReset()
Unused << SendReinitRenderingForDeviceReset();
}
void
ContentParent::MaybeEnableRemoteInputEventQueue()
{
MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
if (!IsInputEventQueueSupported()) {
return;
}
mIsRemoteInputEventQueueEnabled = true;
Unused << SendSetInputEventQueueEnabled();
SetInputPriorityEventEnabled(true);
}
void
ContentParent::SetInputPriorityEventEnabled(bool aEnabled)
{
if (!IsInputEventQueueSupported() ||
!mIsRemoteInputEventQueueEnabled ||
mIsInputPriorityEventEnabled == aEnabled) {
return;
}
mIsInputPriorityEventEnabled = aEnabled;
// Send IPC messages to flush the pending events in the input event queue and
// the normal event queue. See PContent.ipdl for more details.
Unused << SendSuspendInputEventQueue();
Unused << SendFlushInputEventQueue();
Unused << SendResumeInputEventQueue();
}
/*static*/ bool
ContentParent::IsInputEventQueueSupported()
{
static bool sSupported = false;
static bool sInitialized = false;
if (!sInitialized) {
MOZ_ASSERT(Preferences::IsServiceAvailable());
sSupported = Preferences::GetBool("input_event_queue.supported", false);
sInitialized = true;
}
return sSupported;
}
void
ContentParent::OnVarChanged(const GfxVarUpdate& aVar)
{

View File

@ -638,6 +638,15 @@ public:
void OnCompositorDeviceReset() override;
// Control the priority of the IPC messages for input events.
void SetInputPriorityEventEnabled(bool aEnabled);
bool IsInputPriorityEventEnabled()
{
return mIsInputPriorityEventEnabled;
}
static bool IsInputEventQueueSupported();
protected:
void OnChannelConnected(int32_t pid) override;
@ -1171,6 +1180,11 @@ private:
virtual mozilla::ipc::IPCResult RecvBHRThreadHang(
const HangDetails& aHangDetails) override;
// Notify the ContentChild to enable the input event prioritization when
// initializing.
void MaybeEnableRemoteInputEventQueue();
public:
void SendGetFilesResponseAndForget(const nsID& aID,
const GetFilesResponseResult& aResult);
@ -1233,6 +1247,14 @@ private:
bool mShutdownPending;
bool mIPCOpen;
// True if the input event queue on the main thread of the content process is
// enabled.
bool mIsRemoteInputEventQueueEnabled;
// True if we send input events with input priority. Otherwise, we send input
// events with normal priority.
bool mIsInputPriorityEventEnabled;
RefPtr<nsConsoleService> mConsoleService;
nsConsoleService* GetConsoleService();
nsCOMPtr<nsIContentProcessInfo> mScriptableHelper;

View File

@ -655,6 +655,11 @@ child:
prio(input) async RealMouseMoveEvent(WidgetMouseEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId) compress;
async NormalPriorityRealMouseMoveEvent(WidgetMouseEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId) compress;
/**
* Mouse move events with |reason == eSynthesized| are sent via a separate
* message because they do not generate DOM 'mousemove' events, and the
@ -665,26 +670,59 @@ child:
prio(input) async SynthMouseMoveEvent(WidgetMouseEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
async NormalPrioritySynthMouseMoveEvent(WidgetMouseEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
prio(input) async RealMouseButtonEvent(WidgetMouseEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
async NormalPriorityRealMouseButtonEvent(WidgetMouseEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
prio(input) async RealKeyEvent(WidgetKeyboardEvent event);
async NormalPriorityRealKeyEvent(WidgetKeyboardEvent event);
prio(input) async MouseWheelEvent(WidgetWheelEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
async NormalPriorityMouseWheelEvent(WidgetWheelEvent event,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
prio(input) async RealTouchEvent(WidgetTouchEvent aEvent,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId,
nsEventStatus aApzResponse);
async NormalPriorityRealTouchEvent(WidgetTouchEvent aEvent,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId,
nsEventStatus aApzResponse);
prio(input) async HandleTap(TapType aType, LayoutDevicePoint point,
Modifiers aModifiers, ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
async NormalPriorityHandleTap(TapType aType, LayoutDevicePoint point,
Modifiers aModifiers, ScrollableLayerGuid aGuid,
uint64_t aInputBlockId);
prio(input) async RealTouchMoveEvent(WidgetTouchEvent aEvent,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId,
nsEventStatus aApzResponse);
prio(input) async RealDragEvent(WidgetDragEvent aEvent,
uint32_t aDragAction, uint32_t aDropEffect);
async NormalPriorityRealTouchMoveEvent(WidgetTouchEvent aEvent,
ScrollableLayerGuid aGuid,
uint64_t aInputBlockId,
nsEventStatus aApzResponse);
/*
* We disable the input event queue when there is an active dnd session. We
* don't need support RealDragEvent with input priority.
*/
async RealDragEvent(WidgetDragEvent aEvent, uint32_t aDragAction,
uint32_t aDropEffect);
async PluginEvent(WidgetPluginEvent aEvent);
/**

View File

@ -605,6 +605,65 @@ child:
async DumpCodeCoverageCounters();
async ResetCodeCoverageCounters();
/*
* IPC message to enable the input event queue on the main thread of the
* content process.
*/
async SetInputEventQueueEnabled();
/*
* IPC message to flush the input event queue on the main thread of the
* content process.
*
* When the ContentParent stops sending the input event with input priority,
* there may be some pending events in the input event queue and normal
* event queue. Here is a possible scenario.
* R: Runnables.
* D: Enable the input priority event.
* E: Disable the input priority evnet.
*
* D E
* Normal Queue: R1 R2 R3
* Input Queue: II I2 I3
*
* To avoid the newly added normal events (e.g. R2, which may be an input
* event) preempt the pending input events (e.g. I1), or the newly added
* input events (e.g. I3) preempt the pending normal events (e.g. R2), we
* have to flush all pending events before enabling and disabling the input
* priority event.
*
* To flush the normal event queue and the input event queue, we use three
* IPC messages as the followings.
* FI: Flush the input queue.
* SI: Suspend the input queue.
* RI: Resume the input queue.
*
* Normal Queue: R1 FI RI R2 FI RI R3
* Input Queue: II SI I2 SI I3
*
* When the flush input request is processed before the other two requests,
* we consume all input events until the suspend request. After handling the
* suspend request, we stop consuming the input events until the resume
* request to make sure we consume all pending normal events.
*
* If we process the suspend request before the other two requests, we
* ignore the flush request and consume all pending normal events until the
* resume request.
*/
async FlushInputEventQueue();
/*
* IPC message to resume consuming the pending events in the input event
* queue.
*/
async ResumeInputEventQueue();
/*
* IPC message to suspend consuming the pending events in the input event
* queue.
*/
prio(input) async SuspendInputEventQueue();
parent:
async InitBackground(Endpoint<PBackgroundParent> aEndpoint);

View File

@ -1416,6 +1416,17 @@ TabChild::RecvHandleTap(const GeckoContentController::TapType& aType,
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPriorityHandleTap(
const GeckoContentController::TapType& aType,
const LayoutDevicePoint& aPoint,
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
return RecvHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
}
bool
TabChild::NotifyAPZStateChange(const ViewID& aViewId,
const layers::GeckoContentController::APZStateChange& aChange,
@ -1615,6 +1626,14 @@ TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& aEvent,
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPriorityRealMouseMoveEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId);
}
mozilla::ipc::IPCResult
TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
@ -1626,6 +1645,14 @@ TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& aEvent,
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPrioritySynthMouseMoveEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
return RecvSynthMouseMoveEvent(aEvent, aGuid, aInputBlockId);
}
mozilla::ipc::IPCResult
TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
@ -1669,6 +1696,14 @@ TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPriorityRealMouseButtonEvent(
const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
}
// In case handling repeated mouse wheel takes much time, we skip firing current
// wheel event if it may be coalesced to the next one.
@ -1787,6 +1822,14 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPriorityMouseWheelEvent(const WidgetWheelEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
return RecvMouseWheelEvent(aEvent, aGuid, aInputBlockId);
}
mozilla::ipc::IPCResult
TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
@ -1828,6 +1871,15 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPriorityRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse)
{
return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
}
mozilla::ipc::IPCResult
TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
@ -1840,6 +1892,16 @@ TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPriorityRealTouchMoveEvent(
const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse)
{
return RecvRealTouchMoveEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
}
mozilla::ipc::IPCResult
TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent,
const uint32_t& aDragAction,
@ -2018,6 +2080,12 @@ TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& aEvent)
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvNormalPriorityRealKeyEvent(const WidgetKeyboardEvent& aEvent)
{
return RecvRealKeyEvent(aEvent);
}
mozilla::ipc::IPCResult
TabChild::RecvKeyEvent(const nsString& aType,
const int32_t& aKeyCode,

View File

@ -387,13 +387,26 @@ public:
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult
RecvNormalPriorityRealMouseMoveEvent(const mozilla::WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult RecvSynthMouseMoveEvent(const mozilla::WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult
RecvNormalPrioritySynthMouseMoveEvent(const mozilla::WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult RecvRealMouseButtonEvent(const mozilla::WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult
RecvNormalPriorityRealMouseButtonEvent(const mozilla::WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult RecvRealDragEvent(const WidgetDragEvent& aEvent,
const uint32_t& aDragAction,
@ -402,21 +415,41 @@ public:
virtual mozilla::ipc::IPCResult
RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
virtual mozilla::ipc::IPCResult
RecvNormalPriorityRealKeyEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
virtual mozilla::ipc::IPCResult RecvMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult
RecvNormalPriorityMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual mozilla::ipc::IPCResult RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse) override;
virtual mozilla::ipc::IPCResult
RecvNormalPriorityRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse) override;
virtual mozilla::ipc::IPCResult
RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse) override;
virtual mozilla::ipc::IPCResult
RecvNormalPriorityRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const nsEventStatus& aApzResponse) override;
virtual mozilla::ipc::IPCResult RecvKeyEvent(const nsString& aType,
const int32_t& aKeyCode,
const int32_t& aCharCode,
@ -661,6 +694,14 @@ public:
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
mozilla::ipc::IPCResult
RecvNormalPriorityHandleTap(const layers::GeckoContentController::TapType& aType,
const LayoutDevicePoint& aPoint,
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
void SetAllowedTouchBehavior(uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags>& aFlags) const;

View File

@ -179,8 +179,7 @@ TabParent::TabParent(nsIContentParent* aManager,
MOZ_ASSERT(aManager);
// When the input event queue is disabled, we don't need to handle the case
// that some input events are dispatched before PBrowserConstructor.
mIsReadyToHandleInputEvents =
!Preferences::GetBool("input_event_queue.supported", false);
mIsReadyToHandleInputEvents = !ContentParent::IsInputEventQueueSupported();
}
TabParent::~TabParent()
@ -1154,6 +1153,9 @@ TabParent::SendRealMouseEvent(WidgetMouseEvent& aEvent)
uint64_t blockId;
ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
bool isInputPriorityEventEnabled =
Manager()->AsContentParent()->IsInputPriorityEventEnabled();
if (mIsMouseEnterIntoWidgetEventSuppressed) {
// In the case that the TabParent suppressed the eMouseEnterWidget event due
// to its corresponding TabChild wasn't ready to handle it, we have to
@ -1161,25 +1163,33 @@ TabParent::SendRealMouseEvent(WidgetMouseEvent& aEvent)
mIsMouseEnterIntoWidgetEventSuppressed = false;
WidgetMouseEvent localEvent(aEvent);
localEvent.mMessage = eMouseEnterIntoWidget;
DebugOnly<bool> ret = SendRealMouseButtonEvent(localEvent, guid, blockId);
DebugOnly<bool> ret = isInputPriorityEventEnabled
? SendRealMouseButtonEvent(localEvent, guid, blockId)
: SendNormalPriorityRealMouseButtonEvent(localEvent, guid, blockId);
NS_WARNING_ASSERTION(ret, "SendRealMouseButtonEvent(eMouseEnterIntoWidget) failed");
MOZ_ASSERT(!ret || localEvent.HasBeenPostedToRemoteProcess());
}
if (eMouseMove == aEvent.mMessage) {
if (aEvent.mReason == WidgetMouseEvent::eSynthesized) {
DebugOnly<bool> ret = SendSynthMouseMoveEvent(aEvent, guid, blockId);
DebugOnly<bool> ret = isInputPriorityEventEnabled
? SendSynthMouseMoveEvent(aEvent, guid, blockId)
: SendNormalPrioritySynthMouseMoveEvent(aEvent, guid, blockId);
NS_WARNING_ASSERTION(ret, "SendSynthMouseMoveEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
return;
}
DebugOnly<bool> ret = SendRealMouseMoveEvent(aEvent, guid, blockId);
DebugOnly<bool> ret = isInputPriorityEventEnabled
? SendRealMouseMoveEvent(aEvent, guid, blockId)
: SendNormalPriorityRealMouseMoveEvent(aEvent, guid, blockId);
NS_WARNING_ASSERTION(ret, "SendRealMouseMoveEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
return;
}
DebugOnly<bool> ret = SendRealMouseButtonEvent(aEvent, guid, blockId);
DebugOnly<bool> ret = isInputPriorityEventEnabled
? SendRealMouseButtonEvent(aEvent, guid, blockId)
: SendNormalPriorityRealMouseButtonEvent(aEvent, guid, blockId);
NS_WARNING_ASSERTION(ret, "SendRealMouseButtonEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
}
@ -1275,6 +1285,7 @@ TabParent::SendRealDragEvent(WidgetDragEvent& aEvent, uint32_t aDragAction,
if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
return;
}
MOZ_ASSERT(!Manager()->AsContentParent()->IsInputPriorityEventEnabled());
aEvent.mRefPoint += GetChildProcessOffset();
if (aEvent.mMessage == eDrop) {
if (!QueryDropLinksForVerification()) {
@ -1305,7 +1316,10 @@ TabParent::SendMouseWheelEvent(WidgetWheelEvent& aEvent)
ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
aEvent.mRefPoint += GetChildProcessOffset();
DebugOnly<bool> ret =
PBrowserParent::SendMouseWheelEvent(aEvent, guid, blockId);
Manager()->AsContentParent()->IsInputPriorityEventEnabled()
? PBrowserParent::SendMouseWheelEvent(aEvent, guid, blockId)
: PBrowserParent::SendNormalPriorityMouseWheelEvent(aEvent, guid, blockId);
NS_WARNING_ASSERTION(ret, "PBrowserParent::SendMouseWheelEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
}
@ -1587,8 +1601,11 @@ TabParent::SendRealKeyEvent(WidgetKeyboardEvent& aEvent)
} else {
aEvent.PreventNativeKeyBindings();
}
DebugOnly<bool> ret =
Manager()->AsContentParent()->IsInputPriorityEventEnabled()
? PBrowserParent::SendRealKeyEvent(aEvent)
: PBrowserParent::SendNormalPriorityRealKeyEvent(aEvent);
DebugOnly<bool> ret = PBrowserParent::SendRealKeyEvent(aEvent);
NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealKeyEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
}
@ -1626,18 +1643,27 @@ TabParent::SendRealTouchEvent(WidgetTouchEvent& aEvent)
aEvent.mTouches[i]->mRefPoint += offset;
}
bool inputPriorityEventEnabled =
Manager()->AsContentParent()->IsInputPriorityEventEnabled();
if (aEvent.mMessage == eTouchMove) {
DebugOnly<bool> ret =
PBrowserParent::SendRealTouchMoveEvent(aEvent, guid, blockId,
apzResponse);
DebugOnly<bool> ret = inputPriorityEventEnabled
? PBrowserParent::SendRealTouchMoveEvent(aEvent, guid, blockId,
apzResponse)
: PBrowserParent::SendNormalPriorityRealTouchMoveEvent(aEvent, guid,
blockId,
apzResponse);
NS_WARNING_ASSERTION(ret,
"PBrowserParent::SendRealTouchMoveEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
return;
}
DebugOnly<bool> ret = inputPriorityEventEnabled
? PBrowserParent::SendRealTouchEvent(aEvent, guid, blockId, apzResponse)
: PBrowserParent::SendNormalPriorityRealTouchEvent(aEvent, guid, blockId,
apzResponse);
DebugOnly<bool> ret =
PBrowserParent::SendRealTouchEvent(aEvent, guid, blockId, apzResponse);
NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealTouchEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
}
@ -1665,8 +1691,12 @@ TabParent::SendHandleTap(TapType aType,
GetRenderFrame()->TakeFocusForClickFromTap();
}
LayoutDeviceIntPoint offset = GetChildProcessOffset();
return PBrowserParent::SendHandleTap(aType, aPoint + offset, aModifiers,
aGuid, aInputBlockId);
return Manager()->AsContentParent()->IsInputPriorityEventEnabled()
? PBrowserParent::SendHandleTap(aType, aPoint + offset, aModifiers, aGuid,
aInputBlockId)
: PBrowserParent::SendNormalPriorityHandleTap(aType, aPoint + offset,
aModifiers, aGuid,
aInputBlockId);
}
mozilla::ipc::IPCResult

View File

@ -56,6 +56,9 @@ public:
virtual size_t Count(const MutexAutoLock& aProofOfLock) const = 0;
virtual void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) = 0;
virtual void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) = 0;
virtual void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) = 0;
virtual void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) = 0;
virtual ~AbstractEventQueue() {}
};

View File

@ -31,6 +31,9 @@ public:
already_AddRefed<nsIRunnable> PeekEvent(const MutexAutoLock& aProofOfLock);
void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
private:
mozilla::Queue<nsCOMPtr<nsIRunnable>> mQueue;

View File

@ -46,7 +46,7 @@ PrioritizedEventQueue<InnerQueueT>::PutEvent(already_AddRefed<nsIRunnable>&& aEv
}
}
if (priority == EventPriority::Input && !mWriteToInputQueue) {
if (priority == EventPriority::Input && mInputQueueState == STATE_DISABLED) {
priority = EventPriority::Normal;
}
@ -132,7 +132,7 @@ PrioritizedEventQueue<InnerQueueT>::GetEvent(EventPriority* aPriority,
bool normalPending = mNormalQueue->HasPendingEvent(aProofOfLock);
size_t inputCount = mInputQueue->Count(aProofOfLock);
if (mReadFromInputQueue && mInputHandlingStartTime.IsNull() && inputCount > 0) {
if (mInputQueueState == STATE_ENABLED && mInputHandlingStartTime.IsNull() && inputCount > 0) {
mInputHandlingStartTime =
InputEventStatistics::Get()
.GetInputHandlingStartTime(inputCount);
@ -161,21 +161,27 @@ PrioritizedEventQueue<InnerQueueT>::GetEvent(EventPriority* aPriority,
if (mProcessHighPriorityQueue) {
queue = EventPriority::High;
} else if (inputCount > 0 && TimeStamp::Now() > mInputHandlingStartTime
&& mReadFromInputQueue) {
} else if (inputCount > 0 && (mInputQueueState == STATE_FLUSHING ||
(mInputQueueState == STATE_ENABLED &&
TimeStamp::Now() > mInputHandlingStartTime))) {
queue = EventPriority::Input;
} else if (normalPending) {
MOZ_ASSERT(mInputQueueState != STATE_FLUSHING,
"Shouldn't consume normal event when flusing input events");
queue = EventPriority::Normal;
} else if (highPending) {
queue = EventPriority::High;
} else if (inputCount > 0 && mReadFromInputQueue) {
} else if (inputCount > 0 && mInputQueueState != STATE_SUSPEND) {
MOZ_ASSERT(mInputQueueState != STATE_DISABLED,
"Shouldn't consume input events when the input queue is disabled");
queue = EventPriority::Input;
} else {
// We may not actually return an idle event in this case.
queue = EventPriority::Idle;
}
MOZ_ASSERT_IF(queue == EventPriority::Input, mReadFromInputQueue);
MOZ_ASSERT_IF(queue == EventPriority::Input,
mInputQueueState != STATE_DISABLED && mInputQueueState != STATE_SUSPEND);
mProcessHighPriorityQueue = highPending;
@ -266,46 +272,41 @@ PrioritizedEventQueue<InnerQueueT>::Count(const MutexAutoLock& aProofOfLock) con
MOZ_CRASH("unimplemented");
}
// This is used to flush pending events in nsChainedEventQueue::mNormalQueue
// before starting event prioritization.
template<class InnerQueueT>
class PrioritizedEventQueue<InnerQueueT>::EnablePrioritizationRunnable final
: public mozilla::Runnable
{
public:
explicit EnablePrioritizationRunnable(PrioritizedEventQueue<InnerQueueT>* aQueue)
: Runnable("EnablePrioritizationRunnable")
, mQueue(aQueue)
{}
NS_IMETHOD Run() override
{
// Do don't need to do this with the lock held because mReadFromInputQueue
// is only read from the main thread.
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mQueue->mWriteToInputQueue);
MOZ_ASSERT(!mQueue->mReadFromInputQueue);
mQueue->mReadFromInputQueue = true;
return NS_OK;
}
private:
// This is a weak pointer. It's guaranteed to stay alive until this runnable
// runs since it functions as the event loop in which the runnable is posted.
PrioritizedEventQueue<InnerQueueT>* mQueue;
};
template<class InnerQueueT>
void
PrioritizedEventQueue<InnerQueueT>::EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock)
{
MOZ_ASSERT(!mWriteToInputQueue);
MOZ_ASSERT(!mReadFromInputQueue);
mWriteToInputQueue = true;
MOZ_ASSERT(mInputQueueState == STATE_DISABLED);
mInputQueueState = STATE_ENABLED;
mInputHandlingStartTime = TimeStamp();
}
RefPtr<EnablePrioritizationRunnable> runnable = new EnablePrioritizationRunnable(this);
PutEvent(runnable.forget(), EventPriority::Normal, aProofOfLock);
template<class InnerQueueT>
void
PrioritizedEventQueue<InnerQueueT>::
FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock)
{
MOZ_ASSERT(mInputQueueState == STATE_ENABLED || mInputQueueState == STATE_SUSPEND);
mInputQueueState =
mInputQueueState == STATE_ENABLED ? STATE_FLUSHING : STATE_SUSPEND;
}
template<class InnerQueueT>
void
PrioritizedEventQueue<InnerQueueT>::
SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock)
{
MOZ_ASSERT(mInputQueueState == STATE_ENABLED || mInputQueueState == STATE_FLUSHING);
mInputQueueState = STATE_SUSPEND;
}
template<class InnerQueueT>
void
PrioritizedEventQueue<InnerQueueT>::
ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock)
{
MOZ_ASSERT(mInputQueueState == STATE_SUSPEND);
mInputQueueState = STATE_ENABLED;
}
namespace mozilla {

View File

@ -65,10 +65,11 @@ public:
void SetNextIdleDeadlineRef(TimeStamp& aDeadline) { mNextIdleDeadline = &aDeadline; }
void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
private:
class EnablePrioritizationRunnable;
// Returns a null TimeStamp if we're not in the idle period.
mozilla::TimeStamp GetIdleDeadline();
@ -104,14 +105,14 @@ private:
TimeStamp mInputHandlingStartTime;
// When we enable input event prioritization, we immediately begin adding new
// input events to the input event queue (and set
// mWriteToInputQueue). However, we do not begin processing events from the
// input queue until all events that were in the normal priority queue have
// been processed. That ensures that input events will not jump ahead of
// events that were in the queue before prioritization was enabled.
bool mWriteToInputQueue = false;
bool mReadFromInputQueue = false;
enum InputEventQueueState
{
STATE_DISABLED,
STATE_FLUSHING,
STATE_SUSPEND,
STATE_ENABLED
};
InputEventQueueState mInputQueueState = STATE_DISABLED;
};
class EventQueue;

View File

@ -66,6 +66,9 @@ public:
virtual void SetObserver(nsIThreadObserver* aObserver) = 0;
virtual void EnableInputEventPrioritization() = 0;
virtual void FlushInputEventPrioritization() = 0;
virtual void SuspendInputEventPrioritization() = 0;
virtual void ResumeInputEventPrioritization() = 0;
protected:
virtual ~SynchronizedEventQueue() {}

View File

@ -175,6 +175,30 @@ ThreadEventQueue<InnerQueueT>::EnableInputEventPrioritization()
mBaseQueue->EnableInputEventPrioritization(lock);
}
template<class InnerQueueT>
void
ThreadEventQueue<InnerQueueT>::FlushInputEventPrioritization()
{
MutexAutoLock lock(mLock);
mBaseQueue->FlushInputEventPrioritization(lock);
}
template<class InnerQueueT>
void
ThreadEventQueue<InnerQueueT>::SuspendInputEventPrioritization()
{
MutexAutoLock lock(mLock);
mBaseQueue->SuspendInputEventPrioritization(lock);
}
template<class InnerQueueT>
void
ThreadEventQueue<InnerQueueT>::ResumeInputEventPrioritization()
{
MutexAutoLock lock(mLock);
mBaseQueue->ResumeInputEventPrioritization(lock);
}
template<class InnerQueueT>
already_AddRefed<nsISerialEventTarget>
ThreadEventQueue<InnerQueueT>::PushEventQueue()

View File

@ -49,6 +49,9 @@ public:
void Disconnect(const MutexAutoLock& aProofOfLock) final {}
void EnableInputEventPrioritization() final;
void FlushInputEventPrioritization() final;
void SuspendInputEventPrioritization() final;
void ResumeInputEventPrioritization() final;
/**
* This method causes any events currently enqueued on the thread to be

View File

@ -105,6 +105,21 @@ public:
EventQueue()->EnableInputEventPrioritization();
}
void FlushInputEventPrioritization()
{
EventQueue()->FlushInputEventPrioritization();
}
void SuspendInputEventPrioritization()
{
EventQueue()->SuspendInputEventPrioritization();
}
void ResumeInputEventPrioritization()
{
EventQueue()->ResumeInputEventPrioritization();
}
mozilla::TimeStamp& NextIdleDeadlineRef() { return mNextIdleDeadline; }
mozilla::SynchronizedEventQueue* EventQueue() { return mEvents.get(); }

View File

@ -434,21 +434,32 @@ nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent, uint32_t aPriority)
void
nsThreadManager::EnableMainThreadEventPrioritization()
{
static bool sIsInitialized = false;
if (sIsInitialized) {
return;
}
sIsInitialized = true;
MOZ_ASSERT(Preferences::IsServiceAvailable());
bool supported = Preferences::GetBool("input_event_queue.supported", false);
if (!supported) {
return;
}
MOZ_ASSERT(NS_IsMainThread());
InputEventStatistics::Get().SetEnable(true);
mMainThread->EnableInputEventPrioritization();
}
void
nsThreadManager::FlushInputEventPrioritization()
{
MOZ_ASSERT(NS_IsMainThread());
mMainThread->FlushInputEventPrioritization();
}
void
nsThreadManager::SuspendInputEventPrioritization()
{
MOZ_ASSERT(NS_IsMainThread());
mMainThread->SuspendInputEventPrioritization();
}
void
nsThreadManager::ResumeInputEventPrioritization()
{
MOZ_ASSERT(NS_IsMainThread());
mMainThread->ResumeInputEventPrioritization();
}
NS_IMETHODIMP
nsThreadManager::IdleDispatchToMainThread(nsIRunnable *aEvent, uint32_t aTimeout)
{

View File

@ -55,6 +55,9 @@ public:
}
void EnableMainThreadEventPrioritization();
void FlushInputEventPrioritization();
void SuspendInputEventPrioritization();
void ResumeInputEventPrioritization();
private:
nsThreadManager()