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