Bug 1407700 Part2: Handle the case to re-entry event loop for mousemove coalescing. r=smaug.

MozReview-Commit-ID: DcHVokf6boL
This commit is contained in:
Stone Shih 2017-10-12 09:05:40 +08:00
parent 69ed33396c
commit 98ed53bd59
4 changed files with 126 additions and 29 deletions

View File

@ -29,6 +29,13 @@ public:
{
}
void RetrieveDataFrom(CoalescedInputData& aSource)
{
mCoalescedInputEvent = Move(aSource.mCoalescedInputEvent);
mGuid = aSource.mGuid;
mInputBlockId = aSource.mInputBlockId;
}
bool IsEmpty()
{
return !mCoalescedInputEvent;

View File

@ -55,8 +55,8 @@ void
CoalescedMouseMoveFlusher::WillRefresh(mozilla::TimeStamp aTime)
{
MOZ_ASSERT(mRefreshDriver);
mTabChild->MaybeDispatchCoalescedMouseMoveEvents();
RemoveObserver();
mTabChild->FlushAllCoalescedMouseData();
mTabChild->ProcessPendingCoalescedMouseDataAndDispatchEvents();
}
void

View File

@ -1059,6 +1059,15 @@ TabChild::DestroyWindow()
mCoalescedMouseEventFlusher->RemoveObserver();
mCoalescedMouseEventFlusher = nullptr;
}
// In case we don't have chance to process all entries, clean all data in
// the queue.
while (mToBeDispatchedMouseData.GetSize() > 0) {
UniquePtr<CoalescedMouseData> data(
static_cast<CoalescedMouseData*>(mToBeDispatchedMouseData.PopFront()));
data.reset();
}
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
if (baseWindow)
baseWindow->Destroy();
@ -1591,27 +1600,62 @@ TabChild::RecvMouseEvent(const nsString& aType,
}
void
TabChild::MaybeDispatchCoalescedMouseMoveEvents()
TabChild::ProcessPendingCoalescedMouseDataAndDispatchEvents()
{
if (!mCoalesceMouseMoveEvents) {
if (!mCoalesceMouseMoveEvents || !mCoalescedMouseEventFlusher) {
// We don't enable mouse coalescing or we are destroying TabChild.
return;
}
// We may reentry the event loop and push more data to
// mToBeDispatchedMouseData while dispatching an event.
// We may have some pending coalesced data while dispatch an event and reentry
// the event loop. In that case we don't have chance to consume the remainding
// pending data until we get new mouse events. Get some helps from
// mCoalescedMouseEventFlusher to trigger it.
mCoalescedMouseEventFlusher->StartObserver();
while (mToBeDispatchedMouseData.GetSize() > 0) {
UniquePtr<CoalescedMouseData> data(
static_cast<CoalescedMouseData*>(mToBeDispatchedMouseData.PopFront()));
UniquePtr<WidgetMouseEvent> event = data->TakeCoalescedEvent();
if (event) {
// Dispatch the pending events. Using HandleRealMouseButtonEvent
// to bypass the coalesce handling in RecvRealMouseMoveEvent. Can't use
// RecvRealMouseButtonEvent because we may also put some mouse events
// other than mousemove.
HandleRealMouseButtonEvent(*event,
data->GetScrollableLayerGuid(),
data->GetInputBlockId());
}
}
// mCoalescedMouseEventFlusher may be destroyed when reentrying the event
// loop.
if (mCoalescedMouseEventFlusher) {
mCoalescedMouseEventFlusher->RemoveObserver();
}
}
void
TabChild::FlushAllCoalescedMouseData()
{
MOZ_ASSERT(mCoalesceMouseMoveEvents);
// Move all entries from mCoalescedMouseData to mToBeDispatchedMouseData.
for (auto iter = mCoalescedMouseData.Iter(); !iter.Done(); iter.Next()) {
CoalescedMouseData* data = iter.UserData();
if (!data || data->IsEmpty()) {
continue;
}
UniquePtr<WidgetMouseEvent> event = data->TakeCoalescedEvent();
MOZ_ASSERT(event);
// Dispatch the coalesced mousemove event. Using RecvRealMouseButtonEvent to
// bypass the coalesce handling in RecvRealMouseMoveEvent.
RecvRealMouseButtonEvent(*event,
data->GetScrollableLayerGuid(),
data->GetInputBlockId());
}
if (mCoalescedMouseEventFlusher) {
mCoalescedMouseEventFlusher->RemoveObserver();
UniquePtr<CoalescedMouseData> dispatchData =
MakeUnique<CoalescedMouseData>();
dispatchData->RetrieveDataFrom(*data);
mToBeDispatchedMouseData.Push(dispatchData.release());
}
mCoalescedMouseData.Clear();
}
mozilla::ipc::IPCResult
@ -1631,10 +1675,23 @@ TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& aEvent,
mCoalescedMouseEventFlusher->StartObserver();
return IPC_OK();
}
// Can't coalesce current mousemove event. Dispatch the coalesced mousemove
// event and coalesce the current one.
MaybeDispatchCoalescedMouseMoveEvents();
data->Coalesce(aEvent, aGuid, aInputBlockId);
// Can't coalesce current mousemove event. Put the coalesced mousemove data
// with the same pointer id to mToBeDispatchedMouseData, coalesce the
// current one, and process all pending data in mToBeDispatchedMouseData.
MOZ_ASSERT(data);
UniquePtr<CoalescedMouseData> dispatchData =
MakeUnique<CoalescedMouseData>();
dispatchData->RetrieveDataFrom(*data);
mToBeDispatchedMouseData.Push(dispatchData.release());
// Put new data to replace the old one in the hash table.
CoalescedMouseData* newData = new CoalescedMouseData();
mCoalescedMouseData.Put(aEvent.pointerId, newData);
newData->Coalesce(aEvent, aGuid, aInputBlockId);
// Dispatch all pending mouse events.
ProcessPendingCoalescedMouseDataAndDispatchEvents();
mCoalescedMouseEventFlusher->StartObserver();
} else if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) {
return IPC_FAIL_NO_REASON(this);
@ -1674,16 +1731,35 @@ TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
if (aEvent.mMessage != eMouseMove) {
// Flush the coalesced mousemove event before dispatching other mouse
// events.
MaybeDispatchCoalescedMouseMoveEvents();
if (aEvent.mMessage == eMouseEnterIntoWidget) {
mCoalescedMouseData.Put(aEvent.pointerId, new CoalescedMouseData());
} else if (aEvent.mMessage == eMouseExitFromWidget) {
mCoalescedMouseData.Remove(aEvent.pointerId);
}
if (mCoalesceMouseMoveEvents && mCoalescedMouseEventFlusher &&
aEvent.mMessage != eMouseMove) {
// When receiving a mouse event other than mousemove, we have to dispatch
// all coalesced events before it. However, we can't dispatch all pending
// coalesced events directly because we may reentry the event loop while
// dispatching. To make sure we won't dispatch disorder events, we move all
// coalesced mousemove events and current event to a deque to dispatch them.
// When reentrying the event loop and dispatching more events, we put new
// events in the end of the nsQueue and dispatch events from the beginning.
FlushAllCoalescedMouseData();
UniquePtr<CoalescedMouseData> dispatchData =
MakeUnique<CoalescedMouseData>();
dispatchData->Coalesce(aEvent, aGuid, aInputBlockId);
mToBeDispatchedMouseData.Push(dispatchData.release());
ProcessPendingCoalescedMouseDataAndDispatchEvents();
return IPC_OK();
}
HandleRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
return IPC_OK();
}
void
TabChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
// Mouse events like eMouseEnterIntoWidget, that are created in the parent
// process EventStateManager code, have an input block id which they get from
// the InputAPZContext in the parent process stack. However, they did not
@ -1713,7 +1789,6 @@ TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
mAPZEventState->ProcessMouseEvent(aEvent, aGuid, aInputBlockId);
}
return IPC_OK();
}
mozilla::ipc::IPCResult

View File

@ -41,6 +41,7 @@
#include "AudioChannelService.h"
#include "PuppetWidget.h"
#include "mozilla/layers/GeckoContentController.h"
#include "nsDeque.h"
#include "nsISHistoryListener.h"
#include "nsIPartialSHistoryListener.h"
@ -761,8 +762,17 @@ public:
return mWidgetNativeData;
}
// Prepare to dispatch all coalesced mousemove events. We'll move all data
// in mCoalescedMouseData to a nsDeque; then we start processing them. We
// can't fetch the coalesced event one by one and dispatch it because we may
// reentry the event loop and access to the same hashtable. It's called when
// dispatching some mouse events other than mousemove.
void FlushAllCoalescedMouseData();
void ProcessPendingCoalescedMouseDataAndDispatchEvents();
void MaybeDispatchCoalescedMouseMoveEvents();
void HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId);
static bool HasActiveTabs()
{
@ -948,7 +958,12 @@ private:
// takes time, some repeated events can be skipped to not flood child process.
mozilla::TimeStamp mLastWheelProcessedTimeFromParent;
mozilla::TimeDuration mLastWheelProcessingDuration;
// Hash table to track coalesced mousemove events for different pointers.
nsClassHashtable<nsUint32HashKey, CoalescedMouseData> mCoalescedMouseData;
nsDeque mToBeDispatchedMouseData;
CoalescedWheelData mCoalescedWheelData;
RefPtr<CoalescedMouseMoveFlusher> mCoalescedMouseEventFlusher;