SetKeyboardMap, SetDPI, SetBrowserGestureResponse, StartAutoscroll, StopAutoscroll and SetLongTapEnabled are only ever sent from the UI process to the GPU process, so move them to PAPZInputBridge, a protocol which doesn't have an endpoint in the content process at all. Notable details: - PAPZInputBridge's child endpoint is bound to the APZ controller thread rather than the main thread, so APZInputBridgeChild hops to it before sending. Note that on Android, these calls can initially arrive on the Java UI thread. - Originally, four of those methods are executed via APZUpdater queue by RunOnUpdaterThread in APZCTreeManagerParent. But this patch doesn't use the queue anymore, because the message doesn't need to be ordered with respect to WR frame generation first of all. Differential Revision: https://phabricator.services.mozilla.com/D319238
392 lines
13 KiB
C++
392 lines
13 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/layers/APZInputBridgeChild.h"
|
|
|
|
#include "InputData.h" // for InputData, etc
|
|
#include "mozilla/dom/BrowserParent.h" // for BrowserParent
|
|
#include "mozilla/gfx/GPUProcessManager.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/layers/APZThreadUtils.h"
|
|
#include "mozilla/layers/DoubleTapToZoom.h" // for DoubleTapToZoomMetrics
|
|
#include "mozilla/layers/GeckoContentController.h" // for GeckoContentController
|
|
#include "mozilla/layers/KeyboardMap.h" // for KeyboardMap
|
|
#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession
|
|
#include "mozilla/layers/SynchronousTask.h"
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
# include "mozilla/jni/Utils.h" // for DispatchToGeckoPriorityQueue
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
/* static */
|
|
RefPtr<APZInputBridgeChild> APZInputBridgeChild::Create(
|
|
const uint64_t& aProcessToken, Endpoint<PAPZInputBridgeChild>&& aEndpoint) {
|
|
RefPtr<APZInputBridgeChild> child = new APZInputBridgeChild(aProcessToken);
|
|
|
|
MOZ_ASSERT(APZThreadUtils::IsControllerThreadAlive());
|
|
|
|
APZThreadUtils::RunOnControllerThread(
|
|
NewRunnableMethod<Endpoint<PAPZInputBridgeChild>&&>(
|
|
"layers::APZInputBridgeChild::Open", child,
|
|
&APZInputBridgeChild::Open, std::move(aEndpoint)));
|
|
|
|
return child;
|
|
}
|
|
|
|
APZInputBridgeChild::APZInputBridgeChild(const uint64_t& aProcessToken)
|
|
: mIsOpen(false),
|
|
mProcessToken(aProcessToken),
|
|
mCompositorSession(nullptr) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
APZInputBridgeChild::~APZInputBridgeChild() = default;
|
|
|
|
void APZInputBridgeChild::SetCompositorSession(
|
|
RemoteCompositorSession* aSession) {
|
|
mCompositorSession = aSession;
|
|
}
|
|
|
|
void APZInputBridgeChild::Open(Endpoint<PAPZInputBridgeChild>&& aEndpoint) {
|
|
APZThreadUtils::AssertOnControllerThread();
|
|
|
|
mIsOpen = aEndpoint.Bind(this);
|
|
|
|
if (!mIsOpen) {
|
|
// The GPU Process Manager might be gone if we receive ActorDestroy very
|
|
// late in shutdown.
|
|
if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) {
|
|
gpm->NotifyRemoteActorDestroyed(mProcessToken);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
void APZInputBridgeChild::Destroy() {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Destroy will get called from the main thread, so we must synchronously
|
|
// dispatch to the controller thread to close the bridge.
|
|
layers::SynchronousTask task("layers::APZInputBridgeChild::Destroy");
|
|
APZThreadUtils::RunOnControllerThread(
|
|
NS_NewRunnableFunction("layers::APZInputBridgeChild::Destroy", [&]() {
|
|
APZThreadUtils::AssertOnControllerThread();
|
|
AutoCompleteTask complete(&task);
|
|
|
|
// Clear the process token so that we don't notify the GPUProcessManager
|
|
// about an abnormal shutdown, thereby tearing down the GPU process.
|
|
mProcessToken = 0;
|
|
|
|
if (mIsOpen) {
|
|
PAPZInputBridgeChild::Close();
|
|
mIsOpen = false;
|
|
}
|
|
}));
|
|
|
|
task.Wait();
|
|
}
|
|
|
|
void APZInputBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
|
|
mIsOpen = false;
|
|
|
|
if (mProcessToken) {
|
|
gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
|
|
mProcessToken = 0;
|
|
}
|
|
}
|
|
|
|
APZEventResult APZInputBridgeChild::ReceiveInputEvent(
|
|
InputData& aEvent, InputBlockCallback&& aCallback) {
|
|
MOZ_ASSERT(mIsOpen);
|
|
APZThreadUtils::AssertOnControllerThread();
|
|
|
|
APZEventResult res;
|
|
switch (aEvent.mInputType) {
|
|
case MULTITOUCH_INPUT: {
|
|
MultiTouchInput& event = aEvent.AsMultiTouchInput();
|
|
MultiTouchInput processedEvent;
|
|
|
|
SendReceiveMultiTouchInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = std::move(processedEvent);
|
|
break;
|
|
}
|
|
case MOUSE_INPUT: {
|
|
MouseInput& event = aEvent.AsMouseInput();
|
|
MouseInput processedEvent;
|
|
|
|
SendReceiveMouseInputEvent(event, !!aCallback, &res, &processedEvent);
|
|
|
|
event = std::move(processedEvent);
|
|
break;
|
|
}
|
|
case PANGESTURE_INPUT: {
|
|
PanGestureInput& event = aEvent.AsPanGestureInput();
|
|
PanGestureInput processedEvent;
|
|
|
|
SendReceivePanGestureInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = std::move(processedEvent);
|
|
break;
|
|
}
|
|
case PINCHGESTURE_INPUT: {
|
|
PinchGestureInput& event = aEvent.AsPinchGestureInput();
|
|
PinchGestureInput processedEvent;
|
|
|
|
SendReceivePinchGestureInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = std::move(processedEvent);
|
|
break;
|
|
}
|
|
case TAPGESTURE_INPUT: {
|
|
TapGestureInput& event = aEvent.AsTapGestureInput();
|
|
TapGestureInput processedEvent;
|
|
|
|
SendReceiveTapGestureInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = std::move(processedEvent);
|
|
break;
|
|
}
|
|
case SCROLLWHEEL_INPUT: {
|
|
ScrollWheelInput& event = aEvent.AsScrollWheelInput();
|
|
ScrollWheelInput processedEvent;
|
|
|
|
SendReceiveScrollWheelInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = std::move(processedEvent);
|
|
break;
|
|
}
|
|
case KEYBOARD_INPUT: {
|
|
KeyboardInput& event = aEvent.AsKeyboardInput();
|
|
KeyboardInput processedEvent;
|
|
|
|
SendReceiveKeyboardInputEvent(event, !!aCallback, &res, &processedEvent);
|
|
|
|
event = std::move(processedEvent);
|
|
break;
|
|
}
|
|
default: {
|
|
MOZ_ASSERT_UNREACHABLE("Invalid InputData type.");
|
|
res.SetStatusAsConsumeNoDefault();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (aCallback && res.WillHaveDelayedResult()) {
|
|
mInputBlockCallbacks.emplace(res.mInputBlockId, std::move(aCallback));
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void APZInputBridgeChild::HandleTapOnMainThread(
|
|
const TapType& aType, const LayoutDevicePoint& aPoint,
|
|
const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid,
|
|
const uint64_t& aInputBlockId,
|
|
const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
|
|
if (mCompositorSession &&
|
|
mCompositorSession->RootLayerTreeId() == aGuid.mLayersId &&
|
|
mCompositorSession->GetContentController()) {
|
|
RefPtr<GeckoContentController> controller =
|
|
mCompositorSession->GetContentController();
|
|
controller->HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId,
|
|
aDoubleTapToZoomMetrics);
|
|
return;
|
|
}
|
|
// Hold strong reference to BrowserParent because SendHandleTap
|
|
// can run script via SetFocus.
|
|
RefPtr<dom::BrowserParent> tab =
|
|
dom::BrowserParent::GetBrowserParentFromLayersId(aGuid.mLayersId);
|
|
if (tab) {
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
// On Android, touch events are dispatched from the UI thread to the main
|
|
// thread using the Android priority queue. It is possible that this tap has
|
|
// made it to the GPU process and back before they have been processed. We
|
|
// must therefore dispatch this message to the same queue, otherwise the tab
|
|
// may receive the tap event before the touch events that synthesized it.
|
|
mozilla::jni::DispatchToGeckoPriorityQueue(
|
|
NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers,
|
|
ScrollableLayerGuid, uint64_t,
|
|
Maybe<DoubleTapToZoomMetrics>>(
|
|
"dom::BrowserParent::SendHandleTap", tab,
|
|
&dom::BrowserParent::SendHandleTap, aType, aPoint, aModifiers,
|
|
aGuid, aInputBlockId, aDoubleTapToZoomMetrics));
|
|
#else
|
|
tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId,
|
|
aDoubleTapToZoomMetrics);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult APZInputBridgeChild::RecvHandleTap(
|
|
const TapType& aType, const LayoutDevicePoint& aPoint,
|
|
const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid,
|
|
const uint64_t& aInputBlockId,
|
|
const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
|
|
if (NS_IsMainThread()) {
|
|
HandleTapOnMainThread(aType, aPoint, aModifiers, aGuid, aInputBlockId,
|
|
aDoubleTapToZoomMetrics);
|
|
} else {
|
|
NS_DispatchToMainThread(
|
|
NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers,
|
|
ScrollableLayerGuid, uint64_t,
|
|
Maybe<DoubleTapToZoomMetrics>>(
|
|
"layers::APZInputBridgeChild::HandleTapOnMainThread", this,
|
|
&APZInputBridgeChild::HandleTapOnMainThread, aType, aPoint,
|
|
aModifiers, aGuid, aInputBlockId, aDoubleTapToZoomMetrics));
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult APZInputBridgeChild::RecvCallInputBlockCallback(
|
|
uint64_t aInputBlockId, const APZHandledResult& aHandledResult) {
|
|
auto it = mInputBlockCallbacks.find(aInputBlockId);
|
|
if (it != mInputBlockCallbacks.end()) {
|
|
it->second(aInputBlockId, aHandledResult);
|
|
// The callback is one-shot; discard it after calling it.
|
|
mInputBlockCallbacks.erase(it);
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
void APZInputBridgeChild::ProcessUnhandledEvent(
|
|
LayoutDeviceIntPoint* aRefPoint, ScrollableLayerGuid* aOutTargetGuid,
|
|
uint64_t* aOutFocusSequenceNumber, LayersId* aOutLayersId) {
|
|
MOZ_ASSERT(mIsOpen);
|
|
APZThreadUtils::AssertOnControllerThread();
|
|
|
|
SendProcessUnhandledEvent(*aRefPoint, aRefPoint, aOutTargetGuid,
|
|
aOutFocusSequenceNumber, aOutLayersId);
|
|
}
|
|
|
|
void APZInputBridgeChild::UpdateWheelTransaction(
|
|
LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage,
|
|
const Maybe<ScrollableLayerGuid>& aTargetGuid) {
|
|
MOZ_ASSERT(mIsOpen);
|
|
APZThreadUtils::AssertOnControllerThread();
|
|
|
|
SendUpdateWheelTransaction(aRefPoint, aEventMessage, aTargetGuid);
|
|
}
|
|
|
|
// This actor is bound to the controller thread (see
|
|
// APZInputBridgeChild::Open()), so they must hop to it before sending.
|
|
void APZInputBridgeChild::SetKeyboardMap(const KeyboardMap& aKeyboardMap) {
|
|
if (!APZThreadUtils::IsControllerThread()) {
|
|
APZThreadUtils::RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
|
|
"layers::APZInputBridgeChild::SetKeyboardMap", this,
|
|
&APZInputBridgeChild::SetKeyboardMap, aKeyboardMap));
|
|
return;
|
|
}
|
|
|
|
if (!mIsOpen) {
|
|
return;
|
|
}
|
|
|
|
SendSetKeyboardMap(aKeyboardMap);
|
|
}
|
|
|
|
// This actor is bound to the controller thread (see
|
|
// APZInputBridgeChild::Open()), so they must hop to it before sending.
|
|
void APZInputBridgeChild::SetDPI(float aDpiValue) {
|
|
if (!APZThreadUtils::IsControllerThread()) {
|
|
APZThreadUtils::RunOnControllerThread(
|
|
NewRunnableMethod<float>("layers::APZInputBridgeChild::SetDPI", this,
|
|
&APZInputBridgeChild::SetDPI, aDpiValue));
|
|
return;
|
|
}
|
|
|
|
if (!mIsOpen) {
|
|
return;
|
|
}
|
|
|
|
SendSetDPI(aDpiValue);
|
|
}
|
|
|
|
// This actor is bound to the controller thread (see
|
|
// APZInputBridgeChild::Open()), so they must hop to it before sending.
|
|
void APZInputBridgeChild::SetBrowserGestureResponse(
|
|
uint64_t aInputBlockId, BrowserGestureResponse aResponse) {
|
|
if (!APZThreadUtils::IsControllerThread()) {
|
|
APZThreadUtils::RunOnControllerThread(
|
|
NewRunnableMethod<uint64_t, BrowserGestureResponse>(
|
|
"layers::APZInputBridgeChild::SetBrowserGestureResponse", this,
|
|
&APZInputBridgeChild::SetBrowserGestureResponse, aInputBlockId,
|
|
aResponse));
|
|
return;
|
|
}
|
|
|
|
if (!mIsOpen) {
|
|
return;
|
|
}
|
|
|
|
SendSetBrowserGestureResponse(aInputBlockId, aResponse);
|
|
}
|
|
|
|
// This actor is bound to the controller thread (see
|
|
// APZInputBridgeChild::Open()), so they must hop to it before sending.
|
|
void APZInputBridgeChild::StartAutoscroll(const ScrollableLayerGuid& aGuid,
|
|
const ScreenPoint& aAnchorLocation) {
|
|
if (!APZThreadUtils::IsControllerThread()) {
|
|
APZThreadUtils::RunOnControllerThread(
|
|
NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
|
|
"layers::APZInputBridgeChild::StartAutoscroll", this,
|
|
&APZInputBridgeChild::StartAutoscroll, aGuid, aAnchorLocation));
|
|
return;
|
|
}
|
|
|
|
if (!mIsOpen) {
|
|
return;
|
|
}
|
|
|
|
SendStartAutoscroll(aGuid, aAnchorLocation);
|
|
}
|
|
|
|
// This actor is bound to the controller thread (see
|
|
// APZInputBridgeChild::Open()), so they must hop to it before sending.
|
|
void APZInputBridgeChild::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
|
|
if (!APZThreadUtils::IsControllerThread()) {
|
|
APZThreadUtils::RunOnControllerThread(
|
|
NewRunnableMethod<ScrollableLayerGuid>(
|
|
"layers::APZInputBridgeChild::StopAutoscroll", this,
|
|
&APZInputBridgeChild::StopAutoscroll, aGuid));
|
|
return;
|
|
}
|
|
|
|
if (!mIsOpen) {
|
|
return;
|
|
}
|
|
|
|
SendStopAutoscroll(aGuid);
|
|
}
|
|
|
|
// This actor is bound to the controller thread (see
|
|
// APZInputBridgeChild::Open()), so they must hop to it before sending.
|
|
void APZInputBridgeChild::SetLongTapEnabled(bool aTapGestureEnabled) {
|
|
if (!APZThreadUtils::IsControllerThread()) {
|
|
APZThreadUtils::RunOnControllerThread(NewRunnableMethod<bool>(
|
|
"layers::APZInputBridgeChild::SetLongTapEnabled", this,
|
|
&APZInputBridgeChild::SetLongTapEnabled, aTapGestureEnabled));
|
|
return;
|
|
}
|
|
|
|
if (!mIsOpen) {
|
|
return;
|
|
}
|
|
|
|
SendSetLongTapEnabled(aTapGestureEnabled);
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|