283 lines
9.6 KiB
C++
283 lines
9.6 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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/gfx/GPUProcessManager.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/layers/APZThreadUtils.h"
|
|
#include "mozilla/layers/SynchronousTask.h"
|
|
|
|
#include "mozilla/layers/GeckoContentController.h" // for GeckoContentController
|
|
#include "mozilla/layers/DoubleTapToZoom.h" // for DoubleTapToZoomMetrics
|
|
#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession
|
|
#include "mozilla/dom/BrowserParent.h" // for BrowserParent
|
|
#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 = processedEvent;
|
|
break;
|
|
}
|
|
case MOUSE_INPUT: {
|
|
MouseInput& event = aEvent.AsMouseInput();
|
|
MouseInput processedEvent;
|
|
|
|
SendReceiveMouseInputEvent(event, !!aCallback, &res, &processedEvent);
|
|
|
|
event = processedEvent;
|
|
break;
|
|
}
|
|
case PANGESTURE_INPUT: {
|
|
PanGestureInput& event = aEvent.AsPanGestureInput();
|
|
PanGestureInput processedEvent;
|
|
|
|
SendReceivePanGestureInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = processedEvent;
|
|
break;
|
|
}
|
|
case PINCHGESTURE_INPUT: {
|
|
PinchGestureInput& event = aEvent.AsPinchGestureInput();
|
|
PinchGestureInput processedEvent;
|
|
|
|
SendReceivePinchGestureInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = processedEvent;
|
|
break;
|
|
}
|
|
case TAPGESTURE_INPUT: {
|
|
TapGestureInput& event = aEvent.AsTapGestureInput();
|
|
TapGestureInput processedEvent;
|
|
|
|
SendReceiveTapGestureInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = processedEvent;
|
|
break;
|
|
}
|
|
case SCROLLWHEEL_INPUT: {
|
|
ScrollWheelInput& event = aEvent.AsScrollWheelInput();
|
|
ScrollWheelInput processedEvent;
|
|
|
|
SendReceiveScrollWheelInputEvent(event, !!aCallback, &res,
|
|
&processedEvent);
|
|
|
|
event = processedEvent;
|
|
break;
|
|
}
|
|
case KEYBOARD_INPUT: {
|
|
KeyboardInput& event = aEvent.AsKeyboardInput();
|
|
KeyboardInput processedEvent;
|
|
|
|
SendReceiveKeyboardInputEvent(event, !!aCallback, &res, &processedEvent);
|
|
|
|
event = 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;
|
|
}
|
|
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);
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|