APZInputBridgeChild.cpp (9820B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/layers/APZInputBridgeChild.h" 8 9 #include "InputData.h" // for InputData, etc 10 #include "mozilla/gfx/GPUProcessManager.h" 11 #include "mozilla/ipc/Endpoint.h" 12 #include "mozilla/layers/APZThreadUtils.h" 13 #include "mozilla/layers/SynchronousTask.h" 14 15 #include "mozilla/layers/GeckoContentController.h" // for GeckoContentController 16 #include "mozilla/layers/DoubleTapToZoom.h" // for DoubleTapToZoomMetrics 17 #include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession 18 #include "mozilla/dom/BrowserParent.h" // for BrowserParent 19 #ifdef MOZ_WIDGET_ANDROID 20 # include "mozilla/jni/Utils.h" // for DispatchToGeckoPriorityQueue 21 #endif 22 23 namespace mozilla { 24 namespace layers { 25 26 /* static */ 27 RefPtr<APZInputBridgeChild> APZInputBridgeChild::Create( 28 const uint64_t& aProcessToken, Endpoint<PAPZInputBridgeChild>&& aEndpoint) { 29 RefPtr<APZInputBridgeChild> child = new APZInputBridgeChild(aProcessToken); 30 31 MOZ_ASSERT(APZThreadUtils::IsControllerThreadAlive()); 32 33 APZThreadUtils::RunOnControllerThread( 34 NewRunnableMethod<Endpoint<PAPZInputBridgeChild>&&>( 35 "layers::APZInputBridgeChild::Open", child, 36 &APZInputBridgeChild::Open, std::move(aEndpoint))); 37 38 return child; 39 } 40 41 APZInputBridgeChild::APZInputBridgeChild(const uint64_t& aProcessToken) 42 : mIsOpen(false), 43 mProcessToken(aProcessToken), 44 mCompositorSession(nullptr) { 45 MOZ_ASSERT(XRE_IsParentProcess()); 46 MOZ_ASSERT(NS_IsMainThread()); 47 } 48 49 APZInputBridgeChild::~APZInputBridgeChild() = default; 50 51 void APZInputBridgeChild::SetCompositorSession( 52 RemoteCompositorSession* aSession) { 53 mCompositorSession = aSession; 54 } 55 56 void APZInputBridgeChild::Open(Endpoint<PAPZInputBridgeChild>&& aEndpoint) { 57 APZThreadUtils::AssertOnControllerThread(); 58 59 mIsOpen = aEndpoint.Bind(this); 60 61 if (!mIsOpen) { 62 // The GPU Process Manager might be gone if we receive ActorDestroy very 63 // late in shutdown. 64 if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) { 65 gpm->NotifyRemoteActorDestroyed(mProcessToken); 66 } 67 return; 68 } 69 } 70 71 void APZInputBridgeChild::Destroy() { 72 MOZ_ASSERT(XRE_IsParentProcess()); 73 MOZ_ASSERT(NS_IsMainThread()); 74 75 // Destroy will get called from the main thread, so we must synchronously 76 // dispatch to the controller thread to close the bridge. 77 layers::SynchronousTask task("layers::APZInputBridgeChild::Destroy"); 78 APZThreadUtils::RunOnControllerThread( 79 NS_NewRunnableFunction("layers::APZInputBridgeChild::Destroy", [&]() { 80 APZThreadUtils::AssertOnControllerThread(); 81 AutoCompleteTask complete(&task); 82 83 // Clear the process token so that we don't notify the GPUProcessManager 84 // about an abnormal shutdown, thereby tearing down the GPU process. 85 mProcessToken = 0; 86 87 if (mIsOpen) { 88 PAPZInputBridgeChild::Close(); 89 mIsOpen = false; 90 } 91 })); 92 93 task.Wait(); 94 } 95 96 void APZInputBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { 97 mIsOpen = false; 98 99 if (mProcessToken) { 100 gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); 101 mProcessToken = 0; 102 } 103 } 104 105 APZEventResult APZInputBridgeChild::ReceiveInputEvent( 106 InputData& aEvent, InputBlockCallback&& aCallback) { 107 MOZ_ASSERT(mIsOpen); 108 APZThreadUtils::AssertOnControllerThread(); 109 110 APZEventResult res; 111 switch (aEvent.mInputType) { 112 case MULTITOUCH_INPUT: { 113 MultiTouchInput& event = aEvent.AsMultiTouchInput(); 114 MultiTouchInput processedEvent; 115 116 SendReceiveMultiTouchInputEvent(event, !!aCallback, &res, 117 &processedEvent); 118 119 event = processedEvent; 120 break; 121 } 122 case MOUSE_INPUT: { 123 MouseInput& event = aEvent.AsMouseInput(); 124 MouseInput processedEvent; 125 126 SendReceiveMouseInputEvent(event, !!aCallback, &res, &processedEvent); 127 128 event = processedEvent; 129 break; 130 } 131 case PANGESTURE_INPUT: { 132 PanGestureInput& event = aEvent.AsPanGestureInput(); 133 PanGestureInput processedEvent; 134 135 SendReceivePanGestureInputEvent(event, !!aCallback, &res, 136 &processedEvent); 137 138 event = processedEvent; 139 break; 140 } 141 case PINCHGESTURE_INPUT: { 142 PinchGestureInput& event = aEvent.AsPinchGestureInput(); 143 PinchGestureInput processedEvent; 144 145 SendReceivePinchGestureInputEvent(event, !!aCallback, &res, 146 &processedEvent); 147 148 event = processedEvent; 149 break; 150 } 151 case TAPGESTURE_INPUT: { 152 TapGestureInput& event = aEvent.AsTapGestureInput(); 153 TapGestureInput processedEvent; 154 155 SendReceiveTapGestureInputEvent(event, !!aCallback, &res, 156 &processedEvent); 157 158 event = processedEvent; 159 break; 160 } 161 case SCROLLWHEEL_INPUT: { 162 ScrollWheelInput& event = aEvent.AsScrollWheelInput(); 163 ScrollWheelInput processedEvent; 164 165 SendReceiveScrollWheelInputEvent(event, !!aCallback, &res, 166 &processedEvent); 167 168 event = processedEvent; 169 break; 170 } 171 case KEYBOARD_INPUT: { 172 KeyboardInput& event = aEvent.AsKeyboardInput(); 173 KeyboardInput processedEvent; 174 175 SendReceiveKeyboardInputEvent(event, !!aCallback, &res, &processedEvent); 176 177 event = processedEvent; 178 break; 179 } 180 default: { 181 MOZ_ASSERT_UNREACHABLE("Invalid InputData type."); 182 res.SetStatusAsConsumeNoDefault(); 183 break; 184 } 185 } 186 187 if (aCallback && res.WillHaveDelayedResult()) { 188 mInputBlockCallbacks.emplace(res.mInputBlockId, std::move(aCallback)); 189 } 190 191 return res; 192 } 193 194 void APZInputBridgeChild::HandleTapOnMainThread( 195 const TapType& aType, const LayoutDevicePoint& aPoint, 196 const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, 197 const uint64_t& aInputBlockId, 198 const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) { 199 if (mCompositorSession && 200 mCompositorSession->RootLayerTreeId() == aGuid.mLayersId && 201 mCompositorSession->GetContentController()) { 202 RefPtr<GeckoContentController> controller = 203 mCompositorSession->GetContentController(); 204 controller->HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId, 205 aDoubleTapToZoomMetrics); 206 return; 207 } 208 dom::BrowserParent* tab = 209 dom::BrowserParent::GetBrowserParentFromLayersId(aGuid.mLayersId); 210 if (tab) { 211 #ifdef MOZ_WIDGET_ANDROID 212 // On Android, touch events are dispatched from the UI thread to the main 213 // thread using the Android priority queue. It is possible that this tap has 214 // made it to the GPU process and back before they have been processed. We 215 // must therefore dispatch this message to the same queue, otherwise the tab 216 // may receive the tap event before the touch events that synthesized it. 217 mozilla::jni::DispatchToGeckoPriorityQueue( 218 NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, 219 ScrollableLayerGuid, uint64_t, 220 Maybe<DoubleTapToZoomMetrics>>( 221 "dom::BrowserParent::SendHandleTap", tab, 222 &dom::BrowserParent::SendHandleTap, aType, aPoint, aModifiers, 223 aGuid, aInputBlockId, aDoubleTapToZoomMetrics)); 224 #else 225 tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId, 226 aDoubleTapToZoomMetrics); 227 #endif 228 } 229 } 230 231 mozilla::ipc::IPCResult APZInputBridgeChild::RecvHandleTap( 232 const TapType& aType, const LayoutDevicePoint& aPoint, 233 const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, 234 const uint64_t& aInputBlockId, 235 const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) { 236 if (NS_IsMainThread()) { 237 HandleTapOnMainThread(aType, aPoint, aModifiers, aGuid, aInputBlockId, 238 aDoubleTapToZoomMetrics); 239 } else { 240 NS_DispatchToMainThread( 241 NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, 242 ScrollableLayerGuid, uint64_t, 243 Maybe<DoubleTapToZoomMetrics>>( 244 "layers::APZInputBridgeChild::HandleTapOnMainThread", this, 245 &APZInputBridgeChild::HandleTapOnMainThread, aType, aPoint, 246 aModifiers, aGuid, aInputBlockId, aDoubleTapToZoomMetrics)); 247 } 248 return IPC_OK(); 249 } 250 251 mozilla::ipc::IPCResult APZInputBridgeChild::RecvCallInputBlockCallback( 252 uint64_t aInputBlockId, const APZHandledResult& aHandledResult) { 253 auto it = mInputBlockCallbacks.find(aInputBlockId); 254 if (it != mInputBlockCallbacks.end()) { 255 it->second(aInputBlockId, aHandledResult); 256 // The callback is one-shot; discard it after calling it. 257 mInputBlockCallbacks.erase(it); 258 } 259 260 return IPC_OK(); 261 } 262 263 void APZInputBridgeChild::ProcessUnhandledEvent( 264 LayoutDeviceIntPoint* aRefPoint, ScrollableLayerGuid* aOutTargetGuid, 265 uint64_t* aOutFocusSequenceNumber, LayersId* aOutLayersId) { 266 MOZ_ASSERT(mIsOpen); 267 APZThreadUtils::AssertOnControllerThread(); 268 269 SendProcessUnhandledEvent(*aRefPoint, aRefPoint, aOutTargetGuid, 270 aOutFocusSequenceNumber, aOutLayersId); 271 } 272 273 void APZInputBridgeChild::UpdateWheelTransaction( 274 LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage, 275 const Maybe<ScrollableLayerGuid>& aTargetGuid) { 276 MOZ_ASSERT(mIsOpen); 277 APZThreadUtils::AssertOnControllerThread(); 278 279 SendUpdateWheelTransaction(aRefPoint, aEventMessage, aTargetGuid); 280 } 281 282 } // namespace layers 283 } // namespace mozilla