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/Element.h"
13 #include "mozilla/layers/CompositorBridgeParent.h"
14 #include "mozilla/layers/APZCCallbackHelper.h"
15 #include "mozilla/layers/APZEventState.h"
16 #include "mozilla/layers/APZThreadUtils.h"
17 #include "mozilla/layers/IAPZCTreeManager.h"
18 #include "mozilla/layers/InputAPZContext.h"
19 #include "mozilla/layers/DoubleTapToZoom.h"
20 #include "mozilla/layers/RepaintRequest.h"
21 #include "mozilla/dom/Document.h"
22 #include "nsIInterfaceRequestorUtils.h"
23 #include "nsLayoutUtils.h"
24 #include "nsView.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 
ChromeProcessController(nsIWidget * aWidget,APZEventState * aAPZEventState,IAPZCTreeManager * aAPZCTreeManager)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 
InitializeRoot()51 void ChromeProcessController::InitializeRoot() {
52   APZCCallbackHelper::InitializeRootDisplayport(GetPresShell());
53 }
54 
NotifyLayerTransforms(nsTArray<MatrixMessage> && aTransforms)55 void ChromeProcessController::NotifyLayerTransforms(
56     nsTArray<MatrixMessage>&& aTransforms) {
57   if (!mUIThread->IsOnCurrentThread()) {
58     mUIThread->Dispatch(
59         NewRunnableMethod<StoreCopyPassByRRef<nsTArray<MatrixMessage>>>(
60             "layers::ChromeProcessController::NotifyLayerTransforms", this,
61             &ChromeProcessController::NotifyLayerTransforms,
62             std::move(aTransforms)));
63     return;
64   }
65 
66   APZCCallbackHelper::NotifyLayerTransforms(aTransforms);
67 }
68 
RequestContentRepaint(const RepaintRequest & aRequest)69 void ChromeProcessController::RequestContentRepaint(
70     const RepaintRequest& aRequest) {
71   MOZ_ASSERT(IsRepaintThread());
72 
73   if (aRequest.IsRootContent()) {
74     APZCCallbackHelper::UpdateRootFrame(aRequest);
75   } else {
76     APZCCallbackHelper::UpdateSubFrame(aRequest);
77   }
78 }
79 
IsRepaintThread()80 bool ChromeProcessController::IsRepaintThread() { return NS_IsMainThread(); }
81 
DispatchToRepaintThread(already_AddRefed<Runnable> aTask)82 void ChromeProcessController::DispatchToRepaintThread(
83     already_AddRefed<Runnable> aTask) {
84   NS_DispatchToMainThread(std::move(aTask));
85 }
86 
Destroy()87 void ChromeProcessController::Destroy() {
88   if (!mUIThread->IsOnCurrentThread()) {
89     mUIThread->Dispatch(
90         NewRunnableMethod("layers::ChromeProcessController::Destroy", this,
91                           &ChromeProcessController::Destroy));
92     return;
93   }
94 
95   MOZ_ASSERT(mUIThread->IsOnCurrentThread());
96   mWidget = nullptr;
97   mAPZEventState = nullptr;
98 }
99 
GetPresShell() const100 PresShell* ChromeProcessController::GetPresShell() const {
101   if (!mWidget) {
102     return nullptr;
103   }
104   if (nsView* view = nsView::GetViewFor(mWidget)) {
105     return view->GetPresShell();
106   }
107   return nullptr;
108 }
109 
GetRootDocument() const110 dom::Document* ChromeProcessController::GetRootDocument() const {
111   if (PresShell* presShell = GetPresShell()) {
112     return presShell->GetDocument();
113   }
114   return nullptr;
115 }
116 
GetRootContentDocument(const ScrollableLayerGuid::ViewID & aScrollId) const117 dom::Document* ChromeProcessController::GetRootContentDocument(
118     const ScrollableLayerGuid::ViewID& aScrollId) const {
119   nsIContent* content = nsLayoutUtils::FindContentFor(aScrollId);
120   if (!content) {
121     return nullptr;
122   }
123   if (PresShell* presShell =
124           APZCCallbackHelper::GetRootContentDocumentPresShellForContent(
125               content)) {
126     return presShell->GetDocument();
127   }
128   return nullptr;
129 }
130 
HandleDoubleTap(const mozilla::CSSPoint & aPoint,Modifiers aModifiers,const ScrollableLayerGuid & aGuid)131 void ChromeProcessController::HandleDoubleTap(
132     const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
133     const ScrollableLayerGuid& aGuid) {
134   MOZ_ASSERT(mUIThread->IsOnCurrentThread());
135 
136   RefPtr<dom::Document> document = GetRootContentDocument(aGuid.mScrollId);
137   if (!document.get()) {
138     return;
139   }
140 
141   ZoomTarget zoomTarget = CalculateRectToZoomTo(document, aPoint);
142 
143   uint32_t presShellId;
144   ScrollableLayerGuid::ViewID viewId;
145   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
146           document->GetDocumentElement(), &presShellId, &viewId)) {
147     mAPZCTreeManager->ZoomToRect(
148         ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomTarget,
149         ZoomToRectBehavior::DEFAULT_BEHAVIOR);
150   }
151 }
152 
HandleTap(TapType aType,const mozilla::LayoutDevicePoint & aPoint,Modifiers aModifiers,const ScrollableLayerGuid & aGuid,uint64_t aInputBlockId)153 void ChromeProcessController::HandleTap(
154     TapType aType, const mozilla::LayoutDevicePoint& aPoint,
155     Modifiers aModifiers, const ScrollableLayerGuid& aGuid,
156     uint64_t aInputBlockId) {
157   MOZ_LOG(sApzChromeLog, LogLevel::Debug,
158           ("HandleTap called with %d\n", (int)aType));
159   if (!mUIThread->IsOnCurrentThread()) {
160     MOZ_LOG(sApzChromeLog, LogLevel::Debug, ("HandleTap redispatching\n"));
161     mUIThread->Dispatch(
162         NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers,
163                           ScrollableLayerGuid, uint64_t>(
164             "layers::ChromeProcessController::HandleTap", this,
165             &ChromeProcessController::HandleTap, aType, aPoint, aModifiers,
166             aGuid, aInputBlockId));
167     return;
168   }
169 
170   if (!mAPZEventState) {
171     return;
172   }
173 
174   RefPtr<PresShell> presShell = GetPresShell();
175   if (!presShell) {
176     return;
177   }
178   if (!presShell->GetPresContext()) {
179     return;
180   }
181   CSSToLayoutDeviceScale scale(
182       presShell->GetPresContext()->CSSToDevPixelScale());
183 
184   CSSPoint point = aPoint / scale;
185 
186   // Stash the guid in InputAPZContext so that when the visual-to-layout
187   // transform is applied to the event's coordinates, we use the right transform
188   // based on the scroll frame being targeted.
189   // The other values don't really matter.
190   InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel);
191 
192   switch (aType) {
193     case TapType::eSingleTap:
194       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1);
195       break;
196     case TapType::eDoubleTap:
197       HandleDoubleTap(point, aModifiers, aGuid);
198       break;
199     case TapType::eSecondTap:
200       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2);
201       break;
202     case TapType::eLongTap: {
203       RefPtr<APZEventState> eventState(mAPZEventState);
204       eventState->ProcessLongTap(presShell, point, scale, aModifiers,
205                                  aInputBlockId);
206       break;
207     }
208     case TapType::eLongTapUp: {
209       RefPtr<APZEventState> eventState(mAPZEventState);
210       eventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
211       break;
212     }
213   }
214 }
215 
NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,const ScrollableLayerGuid & aGuid,const LayoutDevicePoint & aFocusPoint,LayoutDeviceCoord aSpanChange,Modifiers aModifiers)216 void ChromeProcessController::NotifyPinchGesture(
217     PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid,
218     const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange,
219     Modifiers aModifiers) {
220   if (!mUIThread->IsOnCurrentThread()) {
221     mUIThread->Dispatch(
222         NewRunnableMethod<PinchGestureInput::PinchGestureType,
223                           ScrollableLayerGuid, LayoutDevicePoint,
224                           LayoutDeviceCoord, Modifiers>(
225             "layers::ChromeProcessController::NotifyPinchGesture", this,
226             &ChromeProcessController::NotifyPinchGesture, aType, aGuid,
227             aFocusPoint, aSpanChange, aModifiers));
228     return;
229   }
230 
231   if (mWidget) {
232     // Dispatch the call to APZCCallbackHelper::NotifyPinchGesture to the main
233     // thread so that it runs asynchronously from the current call. This is
234     // because the call can run arbitrary JS code, which can also spin the event
235     // loop and cause undesirable re-entrancy in APZ.
236     mUIThread->Dispatch(NewRunnableFunction(
237         "layers::ChromeProcessController::NotifyPinchGestureAsync",
238         &APZCCallbackHelper::NotifyPinchGesture, aType, aFocusPoint,
239         aSpanChange, aModifiers, mWidget));
240   }
241 }
242 
NotifyAPZStateChange(const ScrollableLayerGuid & aGuid,APZStateChange aChange,int aArg)243 void ChromeProcessController::NotifyAPZStateChange(
244     const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) {
245   if (!mUIThread->IsOnCurrentThread()) {
246     mUIThread->Dispatch(
247         NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int>(
248             "layers::ChromeProcessController::NotifyAPZStateChange", this,
249             &ChromeProcessController::NotifyAPZStateChange, aGuid, aChange,
250             aArg));
251     return;
252   }
253 
254   if (!mAPZEventState) {
255     return;
256   }
257 
258   mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg);
259 }
260 
NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID & aScrollId,const nsString & aEvent)261 void ChromeProcessController::NotifyMozMouseScrollEvent(
262     const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) {
263   if (!mUIThread->IsOnCurrentThread()) {
264     mUIThread->Dispatch(
265         NewRunnableMethod<ScrollableLayerGuid::ViewID, nsString>(
266             "layers::ChromeProcessController::NotifyMozMouseScrollEvent", this,
267             &ChromeProcessController::NotifyMozMouseScrollEvent, aScrollId,
268             aEvent));
269     return;
270   }
271 
272   APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
273 }
274 
NotifyFlushComplete()275 void ChromeProcessController::NotifyFlushComplete() {
276   MOZ_ASSERT(IsRepaintThread());
277 
278   APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
279 }
280 
NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId,const ScrollableLayerGuid::ViewID & aScrollId,ScrollDirection aDirection)281 void ChromeProcessController::NotifyAsyncScrollbarDragInitiated(
282     uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
283     ScrollDirection aDirection) {
284   if (!mUIThread->IsOnCurrentThread()) {
285     mUIThread->Dispatch(NewRunnableMethod<uint64_t, ScrollableLayerGuid::ViewID,
286                                           ScrollDirection>(
287         "layers::ChromeProcessController::NotifyAsyncScrollbarDragInitiated",
288         this, &ChromeProcessController::NotifyAsyncScrollbarDragInitiated,
289         aDragBlockId, aScrollId, aDirection));
290     return;
291   }
292 
293   APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId,
294                                                         aDirection);
295 }
296 
NotifyAsyncScrollbarDragRejected(const ScrollableLayerGuid::ViewID & aScrollId)297 void ChromeProcessController::NotifyAsyncScrollbarDragRejected(
298     const ScrollableLayerGuid::ViewID& aScrollId) {
299   if (!mUIThread->IsOnCurrentThread()) {
300     mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
301         "layers::ChromeProcessController::NotifyAsyncScrollbarDragRejected",
302         this, &ChromeProcessController::NotifyAsyncScrollbarDragRejected,
303         aScrollId));
304     return;
305   }
306 
307   APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
308 }
309 
NotifyAsyncAutoscrollRejected(const ScrollableLayerGuid::ViewID & aScrollId)310 void ChromeProcessController::NotifyAsyncAutoscrollRejected(
311     const ScrollableLayerGuid::ViewID& aScrollId) {
312   if (!mUIThread->IsOnCurrentThread()) {
313     mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
314         "layers::ChromeProcessController::NotifyAsyncAutoscrollRejected", this,
315         &ChromeProcessController::NotifyAsyncAutoscrollRejected, aScrollId));
316     return;
317   }
318 
319   APZCCallbackHelper::NotifyAsyncAutoscrollRejected(aScrollId);
320 }
321 
CancelAutoscroll(const ScrollableLayerGuid & aGuid)322 void ChromeProcessController::CancelAutoscroll(
323     const ScrollableLayerGuid& aGuid) {
324   if (!mUIThread->IsOnCurrentThread()) {
325     mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid>(
326         "layers::ChromeProcessController::CancelAutoscroll", this,
327         &ChromeProcessController::CancelAutoscroll, aGuid));
328     return;
329   }
330 
331   APZCCallbackHelper::CancelAutoscroll(aGuid.mScrollId);
332 }
333