ChromeProcessController.cpp (13458B)
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 "ChromeProcessController.h" 8 9 #include "MainThreadUtils.h" // for NS_IsMainThread() 10 #include "base/task.h" 11 #include "mozilla/PresShell.h" 12 #include "mozilla/dom/Document.h" 13 #include "mozilla/dom/Element.h" 14 #include "mozilla/dom/PointerEventHandler.h" 15 #include "mozilla/layers/CompositorBridgeParent.h" 16 #include "mozilla/layers/APZCCallbackHelper.h" 17 #include "mozilla/layers/APZEventState.h" 18 #include "mozilla/layers/APZThreadUtils.h" 19 #include "mozilla/layers/IAPZCTreeManager.h" 20 #include "mozilla/layers/InputAPZContext.h" 21 #include "mozilla/layers/DoubleTapToZoom.h" 22 #include "mozilla/layers/RepaintRequest.h" 23 #include "nsIInterfaceRequestorUtils.h" 24 #include "nsLayoutUtils.h" 25 26 static mozilla::LazyLogModule sApzChromeLog("apz.cc.chrome"); 27 28 using namespace mozilla; 29 using namespace mozilla::layers; 30 using namespace mozilla::widget; 31 32 ChromeProcessController::ChromeProcessController( 33 nsIWidget* aWidget, APZEventState* aAPZEventState, 34 IAPZCTreeManager* aAPZCTreeManager) 35 : mWidget(aWidget), 36 mAPZEventState(aAPZEventState), 37 mAPZCTreeManager(aAPZCTreeManager), 38 mUIThread(NS_GetCurrentThread()) { 39 // Otherwise we're initializing mUIThread incorrectly. 40 MOZ_ASSERT(NS_IsMainThread()); 41 MOZ_ASSERT(aAPZEventState); 42 MOZ_ASSERT(aAPZCTreeManager); 43 44 mUIThread->Dispatch( 45 NewRunnableMethod("layers::ChromeProcessController::InitializeRoot", this, 46 &ChromeProcessController::InitializeRoot)); 47 } 48 49 ChromeProcessController::~ChromeProcessController() = default; 50 51 void ChromeProcessController::InitializeRoot() { 52 nsIFrame* widgetFrame = GetWidgetFrame(); 53 if (widgetFrame && widgetFrame->IsMenuPopupFrame()) { 54 // For popup window, the menu frame should be the root and have 55 // the display port. 56 APZCCallbackHelper::InitializeRootDisplayport(widgetFrame); 57 return; 58 } 59 60 APZCCallbackHelper::InitializeRootDisplayport(GetPresShell()); 61 } 62 63 void ChromeProcessController::NotifyLayerTransforms( 64 nsTArray<MatrixMessage>&& aTransforms) { 65 if (!mUIThread->IsOnCurrentThread()) { 66 mUIThread->Dispatch( 67 NewRunnableMethod<StoreCopyPassByRRef<nsTArray<MatrixMessage>>>( 68 "layers::ChromeProcessController::NotifyLayerTransforms", this, 69 &ChromeProcessController::NotifyLayerTransforms, 70 std::move(aTransforms))); 71 return; 72 } 73 74 APZCCallbackHelper::NotifyLayerTransforms(aTransforms); 75 } 76 77 void ChromeProcessController::RequestContentRepaint( 78 const RepaintRequest& aRequest) { 79 MOZ_ASSERT(IsRepaintThread()); 80 81 if (aRequest.IsRootContent()) { 82 APZCCallbackHelper::UpdateRootFrame(aRequest); 83 } else { 84 APZCCallbackHelper::UpdateSubFrame(aRequest); 85 } 86 } 87 88 bool ChromeProcessController::IsRepaintThread() { return NS_IsMainThread(); } 89 90 void ChromeProcessController::DispatchToRepaintThread( 91 already_AddRefed<Runnable> aTask) { 92 NS_DispatchToMainThread(std::move(aTask)); 93 } 94 95 void ChromeProcessController::Destroy() { 96 if (!mUIThread->IsOnCurrentThread()) { 97 mUIThread->Dispatch( 98 NewRunnableMethod("layers::ChromeProcessController::Destroy", this, 99 &ChromeProcessController::Destroy)); 100 return; 101 } 102 103 MOZ_ASSERT(mUIThread->IsOnCurrentThread()); 104 mWidget = nullptr; 105 if (mAPZEventState) { 106 mAPZEventState->Destroy(); 107 } 108 mAPZEventState = nullptr; 109 } 110 111 PresShell* ChromeProcessController::GetPresShell() const { 112 if (!mWidget) { 113 return nullptr; 114 } 115 auto* frame = mWidget->GetFrame(); 116 return frame ? frame->PresShell() : nullptr; 117 } 118 119 dom::Document* ChromeProcessController::GetRootDocument() const { 120 if (PresShell* presShell = GetPresShell()) { 121 return presShell->GetDocument(); 122 } 123 return nullptr; 124 } 125 126 dom::Document* ChromeProcessController::GetRootContentDocument( 127 const ScrollableLayerGuid::ViewID& aScrollId) const { 128 nsIContent* content = nsLayoutUtils::FindContentFor(aScrollId); 129 if (!content) { 130 return nullptr; 131 } 132 if (PresShell* presShell = 133 APZCCallbackHelper::GetRootContentDocumentPresShellForContent( 134 content)) { 135 return presShell->GetDocument(); 136 } 137 return nullptr; 138 } 139 140 void ChromeProcessController::HandleDoubleTap( 141 const mozilla::CSSPoint& aPoint, Modifiers aModifiers, 142 const ScrollableLayerGuid& aGuid, 143 const DoubleTapToZoomMetrics& aDoubleTapToZoomMetrics) { 144 MOZ_LOG(sApzChromeLog, LogLevel::Debug, ("HandleDoubleTap\n")); 145 MOZ_ASSERT(mUIThread->IsOnCurrentThread()); 146 147 RefPtr<dom::Document> document = GetRootContentDocument(aGuid.mScrollId); 148 if (!document) { 149 return; 150 } 151 152 ZoomTarget zoomTarget = 153 CalculateRectToZoomTo(document, aPoint, aDoubleTapToZoomMetrics); 154 155 mAPZCTreeManager->ZoomToRect(aGuid, zoomTarget, 156 ZoomToRectBehavior::DEFAULT_BEHAVIOR); 157 } 158 159 void ChromeProcessController::HandleTap( 160 TapType aType, const mozilla::LayoutDevicePoint& aPoint, 161 Modifiers aModifiers, const ScrollableLayerGuid& aGuid, 162 uint64_t aInputBlockId, 163 const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) { 164 MOZ_LOG(sApzChromeLog, LogLevel::Debug, 165 ("HandleTap called with %d\n", (int)aType)); 166 if (!mUIThread->IsOnCurrentThread()) { 167 MOZ_LOG(sApzChromeLog, LogLevel::Debug, ("HandleTap redispatching\n")); 168 mUIThread->Dispatch( 169 NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers, 170 ScrollableLayerGuid, uint64_t, 171 Maybe<DoubleTapToZoomMetrics>>( 172 "layers::ChromeProcessController::HandleTap", this, 173 &ChromeProcessController::HandleTap, aType, aPoint, aModifiers, 174 aGuid, aInputBlockId, aDoubleTapToZoomMetrics)); 175 return; 176 } 177 178 if (!mAPZEventState) { 179 return; 180 } 181 182 RefPtr<PresShell> presShell = GetPresShell(); 183 if (!presShell) { 184 return; 185 } 186 if (!presShell->GetPresContext()) { 187 return; 188 } 189 CSSToLayoutDeviceScale scale( 190 presShell->GetPresContext()->CSSToDevPixelScale()); 191 192 CSSPoint point = aPoint / scale; 193 194 // Stash the guid in InputAPZContext so that when the visual-to-layout 195 // transform is applied to the event's coordinates, we use the right transform 196 // based on the scroll frame being targeted. 197 // The other values don't really matter. 198 InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel); 199 200 switch (aType) { 201 case TapType::eSingleTap: { 202 RefPtr<APZEventState> eventState(mAPZEventState); 203 eventState->ProcessSingleTap(point, scale, aModifiers, 1, aInputBlockId); 204 break; 205 } 206 case TapType::eDoubleTap: 207 MOZ_ASSERT(aDoubleTapToZoomMetrics); 208 HandleDoubleTap(point, aModifiers, aGuid, *aDoubleTapToZoomMetrics); 209 break; 210 case TapType::eSecondTap: { 211 RefPtr<APZEventState> eventState(mAPZEventState); 212 eventState->ProcessSingleTap(point, scale, aModifiers, 2, aInputBlockId); 213 break; 214 } 215 case TapType::eLongTap: { 216 RefPtr<APZEventState> eventState(mAPZEventState); 217 eventState->ProcessLongTap(presShell, point, scale, aModifiers, 218 aInputBlockId); 219 break; 220 } 221 case TapType::eLongTapUp: { 222 RefPtr<APZEventState> eventState(mAPZEventState); 223 eventState->ProcessLongTapUp(presShell, point, scale, aModifiers); 224 break; 225 } 226 } 227 228 // mAPZEventState may not dispatch the compatibility mouse events. Therefore, 229 // we should release the pointer capturing element at the last ePointerUp 230 // here. 231 PointerEventHandler::ReleasePointerCapturingElementAtLastPointerUp(); 232 } 233 234 void ChromeProcessController::NotifyPinchGesture( 235 PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid, 236 const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange, 237 Modifiers aModifiers) { 238 if (!mUIThread->IsOnCurrentThread()) { 239 mUIThread->Dispatch( 240 NewRunnableMethod<PinchGestureInput::PinchGestureType, 241 ScrollableLayerGuid, LayoutDevicePoint, 242 LayoutDeviceCoord, Modifiers>( 243 "layers::ChromeProcessController::NotifyPinchGesture", this, 244 &ChromeProcessController::NotifyPinchGesture, aType, aGuid, 245 aFocusPoint, aSpanChange, aModifiers)); 246 return; 247 } 248 249 if (mWidget) { 250 // Dispatch the call to APZCCallbackHelper::NotifyPinchGesture to the main 251 // thread so that it runs asynchronously from the current call. This is 252 // because the call can run arbitrary JS code, which can also spin the event 253 // loop and cause undesirable re-entrancy in APZ. 254 mUIThread->Dispatch(NewRunnableFunction( 255 "layers::ChromeProcessController::NotifyPinchGestureAsync", 256 &APZCCallbackHelper::NotifyPinchGesture, aType, aFocusPoint, 257 aSpanChange, aModifiers, mWidget)); 258 } 259 } 260 261 void ChromeProcessController::NotifyAPZStateChange( 262 const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg, 263 Maybe<uint64_t> aInputBlockId) { 264 if (!mUIThread->IsOnCurrentThread()) { 265 mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, APZStateChange, 266 int, Maybe<uint64_t>>( 267 "layers::ChromeProcessController::NotifyAPZStateChange", this, 268 &ChromeProcessController::NotifyAPZStateChange, aGuid, aChange, aArg, 269 aInputBlockId)); 270 return; 271 } 272 273 if (!mAPZEventState) { 274 return; 275 } 276 277 mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg, 278 aInputBlockId); 279 } 280 281 void ChromeProcessController::NotifyMozMouseScrollEvent( 282 const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) { 283 if (!mUIThread->IsOnCurrentThread()) { 284 mUIThread->Dispatch( 285 NewRunnableMethod<ScrollableLayerGuid::ViewID, nsString>( 286 "layers::ChromeProcessController::NotifyMozMouseScrollEvent", this, 287 &ChromeProcessController::NotifyMozMouseScrollEvent, aScrollId, 288 aEvent)); 289 return; 290 } 291 292 APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent); 293 } 294 295 void ChromeProcessController::NotifyFlushComplete() { 296 MOZ_ASSERT(IsRepaintThread()); 297 298 APZCCallbackHelper::NotifyFlushComplete(GetPresShell()); 299 } 300 301 void ChromeProcessController::NotifyAsyncScrollbarDragInitiated( 302 uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId, 303 ScrollDirection aDirection) { 304 if (!mUIThread->IsOnCurrentThread()) { 305 mUIThread->Dispatch(NewRunnableMethod<uint64_t, ScrollableLayerGuid::ViewID, 306 ScrollDirection>( 307 "layers::ChromeProcessController::NotifyAsyncScrollbarDragInitiated", 308 this, &ChromeProcessController::NotifyAsyncScrollbarDragInitiated, 309 aDragBlockId, aScrollId, aDirection)); 310 return; 311 } 312 313 APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, 314 aDirection); 315 } 316 317 void ChromeProcessController::NotifyAsyncScrollbarDragRejected( 318 const ScrollableLayerGuid::ViewID& aScrollId) { 319 if (!mUIThread->IsOnCurrentThread()) { 320 mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>( 321 "layers::ChromeProcessController::NotifyAsyncScrollbarDragRejected", 322 this, &ChromeProcessController::NotifyAsyncScrollbarDragRejected, 323 aScrollId)); 324 return; 325 } 326 327 APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId); 328 } 329 330 void ChromeProcessController::NotifyAsyncAutoscrollRejected( 331 const ScrollableLayerGuid::ViewID& aScrollId) { 332 if (!mUIThread->IsOnCurrentThread()) { 333 mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>( 334 "layers::ChromeProcessController::NotifyAsyncAutoscrollRejected", this, 335 &ChromeProcessController::NotifyAsyncAutoscrollRejected, aScrollId)); 336 return; 337 } 338 339 APZCCallbackHelper::NotifyAsyncAutoscrollRejected(aScrollId); 340 } 341 342 void ChromeProcessController::CancelAutoscroll( 343 const ScrollableLayerGuid& aGuid) { 344 mUIThread->Dispatch(NewRunnableFunction("layers::CancelAutoscroll", 345 &APZCCallbackHelper::CancelAutoscroll, 346 aGuid.mScrollId)); 347 } 348 349 void ChromeProcessController::NotifyScaleGestureComplete( 350 const ScrollableLayerGuid& aGuid, float aScale) { 351 if (!mUIThread->IsOnCurrentThread()) { 352 mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, float>( 353 "layers::ChromeProcessController::NotifyScaleGestureComplete", this, 354 &ChromeProcessController::NotifyScaleGestureComplete, aGuid, aScale)); 355 return; 356 } 357 358 if (mWidget) { 359 // Dispatch the call to APZCCallbackHelper::NotifyScaleGestureComplete 360 // to the main thread so that it runs asynchronously from the current call. 361 // This is because the call can run arbitrary JS code, which can also spin 362 // the event loop and cause undesirable re-entrancy in APZ. 363 mUIThread->Dispatch(NewRunnableFunction( 364 "layers::ChromeProcessController::NotifyScaleGestureComplete", 365 &APZCCallbackHelper::NotifyScaleGestureComplete, mWidget, aScale)); 366 } 367 } 368 369 nsIFrame* ChromeProcessController::GetWidgetFrame() const { 370 return mWidget ? mWidget->GetFrame() : nullptr; 371 }