From ec4f0b765257b8b44d9a1d85fe44b3a4c95ff5aa Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 11 Sep 2012 13:05:52 -0700 Subject: [PATCH] Bug 788943: Allow TabParents to capture event series for faster dispatch to subprocesses. Implements capturing of touch-event series. r=smaug sr=roc --- content/events/src/nsEventStateManager.cpp | 63 ++++++++++++--------- content/events/src/nsEventStateManager.h | 3 + dom/ipc/TabParent.cpp | 65 +++++++++++++++++++++- dom/ipc/TabParent.h | 27 +++++++++ widget/gonk/nsAppShell.cpp | 10 +++- widget/gonk/nsWindow.cpp | 22 +++++++- widget/gonk/nsWindow.h | 3 +- 7 files changed, 157 insertions(+), 36 deletions(-) diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index bc77e9c8ef5a..eec6d4a821df 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -1576,6 +1576,42 @@ nsEventStateManager::IsRemoteTarget(nsIContent* target) { return false; } +/*static*/ void +nsEventStateManager::MapEventCoordinatesForChildProcess(nsFrameLoader* aFrameLoader, + nsEvent* aEvent) +{ + // The "toplevel widget" in child processes is always at position + // 0,0. Map the event coordinates to match that. + nsIFrame* targetFrame = aFrameLoader->GetPrimaryFrameOfOwningContent(); + if (!targetFrame) { + return; + } + nsPresContext* presContext = targetFrame->PresContext(); + + if (aEvent->eventStructType != NS_TOUCH_EVENT) { + nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, + targetFrame); + aEvent->refPoint = pt.ToNearestPixels(presContext->AppUnitsPerDevPixel()); + } else { + aEvent->refPoint = nsIntPoint(); + // Find out how far we're offset from the nearest widget. + nsPoint offset = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, targetFrame); + nsIntPoint intOffset = + offset.ToNearestPixels(presContext->AppUnitsPerDevPixel()); + nsTouchEvent* touchEvent = static_cast(aEvent); + // Then offset all the touch points by that distance, to put them + // in the space where top-left is 0,0. + const nsTArray >& touches = touchEvent->touches; + for (uint32_t i = 0; i < touches.Length(); ++i) { + nsIDOMTouch* touch = touches[i]; + if (touch) { + touch->mRefPoint += intOffset; + } + } + } +} + bool CrossProcessSafeEvent(const nsEvent& aEvent) { @@ -1689,32 +1725,7 @@ nsEventStateManager::HandleCrossProcessEvent(nsEvent *aEvent, continue; } - // The "toplevel widget" in content processes is always at position - // 0,0. Map the event coordinates to match that. - if (aEvent->eventStructType != NS_TOUCH_EVENT) { - nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, - aTargetFrame); - aEvent->refPoint = - pt.ToNearestPixels(mPresContext->AppUnitsPerDevPixel()); - } else { - nsIFrame* targetFrame = frameLoader->GetPrimaryFrameOfOwningContent(); - aEvent->refPoint = nsIntPoint(); - // Find out how far we're offset from the nearest widget. - nsPoint offset = - nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, targetFrame); - nsIntPoint intOffset = - offset.ToNearestPixels(mPresContext->AppUnitsPerDevPixel()); - nsTouchEvent* touchEvent = static_cast(aEvent); - // Then offset all the touch points by that distance, to put them - // in the space where top-left is 0,0. - const nsTArray >& touches = touchEvent->touches; - for (uint32_t i = 0; i < touches.Length(); ++i) { - nsIDOMTouch* touch = touches[i]; - if (touch) { - touch->mRefPoint += intOffset; - } - } - } + MapEventCoordinatesForChildProcess(frameLoader, aEvent); dispatched |= DispatchCrossProcessEvent(aEvent, frameLoader, aStatus); } diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index d8e36920c612..5482fb07ce58 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -204,6 +204,9 @@ public: static bool IsRemoteTarget(nsIContent* aTarget); + static void MapEventCoordinatesForChildProcess(nsFrameLoader* aFrameLoader, + nsEvent* aEvent); + // Holds the point in screen coords that a mouse event was dispatched to, // before we went into pointer lock mode. This is constantly updated while // the pointer is not locked, but we don't update it while the pointer is diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index a1b96bb41efc..48489552a030 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -25,6 +25,7 @@ #include "nsContentUtils.h" #include "nsDebug.h" #include "nsEventDispatcher.h" +#include "nsEventStateManager.h" #include "nsFocusManager.h" #include "nsFrameLoader.h" #include "nsIContent.h" @@ -66,6 +67,8 @@ using namespace mozilla::dom::indexedDB; namespace mozilla { namespace dom { +TabParent* sEventCapturer; + TabParent *TabParent::mIMETabParent = nullptr; NS_IMPL_ISUPPORTS3(TabParent, nsITabParent, nsIAuthPromptProvider, nsISecureBrowserUI) @@ -79,6 +82,7 @@ TabParent::TabParent(mozIApplication* aApp, bool aIsBrowserElement) , mIMECompositionEnding(false) , mIMECompositionStart(0) , mIMESeqno(0) + , mEventCaptureDepth(0) , mDPI(0) , mActive(false) , mIsBrowserElement(aIsBrowserElement) @@ -121,8 +125,12 @@ TabParent::Recv__delete__() void TabParent::ActorDestroy(ActorDestroyReason why) { - if (mIMETabParent == this) + if (sEventCapturer == this) { + sEventCapturer = nullptr; + } + if (mIMETabParent == this) { mIMETabParent = nullptr; + } nsRefPtr frameLoader = GetFrameLoader(); if (frameLoader) { frameLoader->DestroyChild(); @@ -361,21 +369,72 @@ bool TabParent::SendRealKeyEvent(nsKeyEvent& event) bool TabParent::SendRealTouchEvent(nsTouchEvent& event) { + if (event.message == NS_TOUCH_START) { + MOZ_ASSERT((!sEventCapturer && mEventCaptureDepth == 0) || + (sEventCapturer == this && mEventCaptureDepth > 0)); + // We want to capture all remaining touch events in this series + // for fast-path dispatch. + sEventCapturer = this; + ++mEventCaptureDepth; + } + nsTouchEvent e(event); - // PresShell::HandleEventInternal adds touches on touch end/cancel. + // PresShell::HandleEventInternal adds touches on touch end/cancel, + // when we're not capturing raw events from the widget backend. // This hack filters those out. Bug 785554 - if (event.message == NS_TOUCH_END || event.message == NS_TOUCH_CANCEL) { + if (sEventCapturer != this && + (event.message == NS_TOUCH_END || event.message == NS_TOUCH_CANCEL)) { for (int i = e.touches.Length() - 1; i >= 0; i--) { if (!e.touches[i]->mChanged) e.touches.RemoveElementAt(i); } } + MaybeForwardEventToRenderFrame(event, &e); return (e.message == NS_TOUCH_MOVE) ? PBrowserParent::SendRealTouchMoveEvent(e) : PBrowserParent::SendRealTouchEvent(e); } +/*static*/ TabParent* +TabParent::GetEventCapturer() +{ + return sEventCapturer; +} + +bool +TabParent::TryCapture(const nsGUIEvent& aEvent) +{ + MOZ_ASSERT(sEventCapturer == this && mEventCaptureDepth > 0); + + if (aEvent.eventStructType != NS_TOUCH_EVENT) { + // Only capture of touch events is implemented, for now. + return false; + } + + nsTouchEvent event(static_cast(aEvent)); + + bool isTouchPointUp = (event.message == NS_TOUCH_END || + event.message == NS_TOUCH_CANCEL); + if (event.message == NS_TOUCH_START || isTouchPointUp) { + // Let the DOM see touch start/end events so that its touch-point + // state stays consistent. + if (isTouchPointUp && 0 == --mEventCaptureDepth) { + // All event series are un-captured, don't try to catch any + // more. + sEventCapturer = nullptr; + } + return false; + } + + // Adjust the widget coordinates to be relative to our frame. + nsRefPtr frameLoader = GetFrameLoader(); + nsEventStateManager::MapEventCoordinatesForChildProcess(frameLoader, &event); + + SendRealTouchEvent(event); + return true; +} + bool TabParent::RecvSyncMessage(const nsString& aMessage, const ClonedMessageData& aData, diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index cc205d269ea7..22ebd186a5aa 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -66,6 +66,30 @@ public: mozIApplication* GetApp() { return mApp; } bool IsBrowserElement() { return mIsBrowserElement; } + /** + * Return the TabParent that has decided it wants to capture an + * event series for fast-path dispatch to its subprocess, if one + * has. + * + * DOM event dispatch and widget are free to ignore capture + * requests from TabParents; the end result wrt remote content is + * (must be) always the same, albeit usually slower without + * subprocess capturing. This allows frontends/widget backends to + * "opt in" to faster cross-process dispatch. + */ + static TabParent* GetEventCapturer(); + /** + * If this is the current event capturer, give this a chance to + * capture the event. If it was captured, return true, false + * otherwise. Un-captured events should follow normal DOM + * dispatch; captured events should result in no further + * processing from the caller of TryCapture(). + * + * It's an error to call TryCapture() if this isn't the event + * capturer. + */ + bool TryCapture(const nsGUIEvent& aEvent); + void Destroy(); virtual bool RecvMoveFocus(const bool& aForward); @@ -246,6 +270,9 @@ protected: uint32_t mIMECompositionStart; uint32_t mIMESeqno; + // The number of event series we're currently capturing. + int32_t mEventCaptureDepth; + float mDPI; bool mActive; bool mIsBrowserElement; diff --git a/widget/gonk/nsAppShell.cpp b/widget/gonk/nsAppShell.cpp index a8e3fc1a1200..66a436c56399 100644 --- a/widget/gonk/nsAppShell.cpp +++ b/widget/gonk/nsAppShell.cpp @@ -154,7 +154,7 @@ addDOMTouch(UserInputData& data, nsTouchEvent& event, int i) } static nsEventStatus -sendTouchEvent(UserInputData& data) +sendTouchEvent(UserInputData& data, bool* captured) { uint32_t msg; int32_t action = data.action & AMOTION_EVENT_ACTION_MASK; @@ -190,7 +190,7 @@ sendTouchEvent(UserInputData& data) addDOMTouch(data, event, i); } - return nsWindow::DispatchInputEvent(event); + return nsWindow::DispatchInputEvent(event, captured); } static nsEventStatus @@ -435,7 +435,11 @@ GeckoInputDispatcher::dispatchOnce() nsEventStatus status = nsEventStatus_eIgnore; if ((data.action & AMOTION_EVENT_ACTION_MASK) != AMOTION_EVENT_ACTION_HOVER_MOVE) { - status = sendTouchEvent(data); + bool captured; + status = sendTouchEvent(data, &captured); + if (captured) { + return; + } } uint32_t msg; diff --git a/widget/gonk/nsWindow.cpp b/widget/gonk/nsWindow.cpp index b42552bac179..feca64e6ec75 100644 --- a/widget/gonk/nsWindow.cpp +++ b/widget/gonk/nsWindow.cpp @@ -20,6 +20,7 @@ #include "android/log.h" #include "ui/FramebufferNativeWindow.h" +#include "mozilla/dom/TabParent.h" #include "mozilla/Hal.h" #include "mozilla/Preferences.h" #include "mozilla/FileUtils.h" @@ -283,15 +284,30 @@ nsWindow::DoDraw(void) } nsEventStatus -nsWindow::DispatchInputEvent(nsGUIEvent &aEvent) +nsWindow::DispatchInputEvent(nsGUIEvent &aEvent, bool* aWasCaptured) { - if (!gFocusedWindow) + if (aWasCaptured) { + *aWasCaptured = false; + } + if (!gFocusedWindow) { return nsEventStatus_eIgnore; + } gFocusedWindow->UserActivity(); - nsEventStatus status; aEvent.widget = gFocusedWindow; + + if (TabParent* capturer = TabParent::GetEventCapturer()) { + bool captured = capturer->TryCapture(aEvent); + if (aWasCaptured) { + *aWasCaptured = captured; + } + if (captured) { + return nsEventStatus_eConsumeNoDefault; + } + } + + nsEventStatus status; gFocusedWindow->DispatchEvent(&aEvent, status); return status; } diff --git a/widget/gonk/nsWindow.h b/widget/gonk/nsWindow.h index 16af033d6b61..7366b48b540f 100644 --- a/widget/gonk/nsWindow.h +++ b/widget/gonk/nsWindow.h @@ -46,7 +46,8 @@ public: virtual ~nsWindow(); static void DoDraw(void); - static nsEventStatus DispatchInputEvent(nsGUIEvent &aEvent); + static nsEventStatus DispatchInputEvent(nsGUIEvent &aEvent, + bool* aWasCaptured = nullptr); NS_IMETHOD Create(nsIWidget *aParent, void *aNativeParent,