1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "ChromeProcessController.h"
7
8 #include "MainThreadUtils.h" // for NS_IsMainThread()
9 #include "base/message_loop.h" // for MessageLoop
10 #include "mozilla/dom/Element.h"
11 #include "mozilla/layers/CompositorBridgeParent.h"
12 #include "mozilla/layers/APZCCallbackHelper.h"
13 #include "mozilla/layers/APZEventState.h"
14 #include "mozilla/layers/IAPZCTreeManager.h"
15 #include "mozilla/layers/DoubleTapToZoom.h"
16 #include "nsIDocument.h"
17 #include "nsIInterfaceRequestorUtils.h"
18 #include "nsIPresShell.h"
19 #include "nsLayoutUtils.h"
20 #include "nsView.h"
21
22 using namespace mozilla;
23 using namespace mozilla::layers;
24 using namespace mozilla::widget;
25
ChromeProcessController(nsIWidget * aWidget,APZEventState * aAPZEventState,IAPZCTreeManager * aAPZCTreeManager)26 ChromeProcessController::ChromeProcessController(nsIWidget* aWidget,
27 APZEventState* aAPZEventState,
28 IAPZCTreeManager* aAPZCTreeManager)
29 : mWidget(aWidget)
30 , mAPZEventState(aAPZEventState)
31 , mAPZCTreeManager(aAPZCTreeManager)
32 , mUILoop(MessageLoop::current())
33 {
34 // Otherwise we're initializing mUILoop incorrectly.
35 MOZ_ASSERT(NS_IsMainThread());
36 MOZ_ASSERT(aAPZEventState);
37 MOZ_ASSERT(aAPZCTreeManager);
38
39 mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::InitializeRoot));
40 }
41
~ChromeProcessController()42 ChromeProcessController::~ChromeProcessController() {}
43
44 void
InitializeRoot()45 ChromeProcessController::InitializeRoot()
46 {
47 APZCCallbackHelper::InitializeRootDisplayport(GetPresShell());
48 }
49
50 void
RequestContentRepaint(const FrameMetrics & aFrameMetrics)51 ChromeProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
52 {
53 MOZ_ASSERT(IsRepaintThread());
54
55 FrameMetrics metrics = aFrameMetrics;
56 if (metrics.IsRootContent()) {
57 APZCCallbackHelper::UpdateRootFrame(metrics);
58 } else {
59 APZCCallbackHelper::UpdateSubFrame(metrics);
60 }
61 }
62
63 void
PostDelayedTask(already_AddRefed<Runnable> aTask,int aDelayMs)64 ChromeProcessController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
65 {
66 MessageLoop::current()->PostDelayedTask(Move(aTask), aDelayMs);
67 }
68
69 bool
IsRepaintThread()70 ChromeProcessController::IsRepaintThread()
71 {
72 return NS_IsMainThread();
73 }
74
75 void
DispatchToRepaintThread(already_AddRefed<Runnable> aTask)76 ChromeProcessController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
77 {
78 NS_DispatchToMainThread(Move(aTask));
79 }
80
81 void
Destroy()82 ChromeProcessController::Destroy()
83 {
84 if (MessageLoop::current() != mUILoop) {
85 mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::Destroy));
86 return;
87 }
88
89 MOZ_ASSERT(MessageLoop::current() == mUILoop);
90 mWidget = nullptr;
91 mAPZEventState = nullptr;
92 }
93
94 nsIPresShell*
GetPresShell() const95 ChromeProcessController::GetPresShell() const
96 {
97 if (!mWidget) {
98 return nullptr;
99 }
100 if (nsView* view = nsView::GetViewFor(mWidget)) {
101 return view->GetPresShell();
102 }
103 return nullptr;
104 }
105
106 nsIDocument*
GetRootDocument() const107 ChromeProcessController::GetRootDocument() const
108 {
109 if (nsIPresShell* presShell = GetPresShell()) {
110 return presShell->GetDocument();
111 }
112 return nullptr;
113 }
114
115 nsIDocument*
GetRootContentDocument(const FrameMetrics::ViewID & aScrollId) const116 ChromeProcessController::GetRootContentDocument(const FrameMetrics::ViewID& aScrollId) const
117 {
118 nsIContent* content = nsLayoutUtils::FindContentFor(aScrollId);
119 if (!content) {
120 return nullptr;
121 }
122 nsIPresShell* presShell = APZCCallbackHelper::GetRootContentDocumentPresShellForContent(content);
123 if (presShell) {
124 return presShell->GetDocument();
125 }
126 return nullptr;
127 }
128
129 void
HandleDoubleTap(const mozilla::CSSPoint & aPoint,Modifiers aModifiers,const ScrollableLayerGuid & aGuid)130 ChromeProcessController::HandleDoubleTap(const mozilla::CSSPoint& aPoint,
131 Modifiers aModifiers,
132 const ScrollableLayerGuid& aGuid)
133 {
134 MOZ_ASSERT(MessageLoop::current() == mUILoop);
135
136 nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId);
137 if (!document.get()) {
138 return;
139 }
140
141 // CalculateRectToZoomTo performs a hit test on the frame associated with the
142 // Root Content Document. Unfortunately that frame does not know about the
143 // resolution of the document and so we must remove it before calculating
144 // the zoomToRect.
145 nsIPresShell* presShell = document->GetShell();
146 const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f;
147 CSSPoint point(aPoint.x / resolution, aPoint.y / resolution);
148 CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
149
150 uint32_t presShellId;
151 FrameMetrics::ViewID viewId;
152 if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
153 document->GetDocumentElement(), &presShellId, &viewId)) {
154 mAPZCTreeManager->ZoomToRect(
155 ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
156 }
157 }
158
159 void
HandleTap(TapType aType,const mozilla::LayoutDevicePoint & aPoint,Modifiers aModifiers,const ScrollableLayerGuid & aGuid,uint64_t aInputBlockId)160 ChromeProcessController::HandleTap(TapType aType,
161 const mozilla::LayoutDevicePoint& aPoint,
162 Modifiers aModifiers,
163 const ScrollableLayerGuid& aGuid,
164 uint64_t aInputBlockId)
165 {
166 if (MessageLoop::current() != mUILoop) {
167 mUILoop->PostTask(NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers,
168 ScrollableLayerGuid, uint64_t>(this,
169 &ChromeProcessController::HandleTap,
170 aType, aPoint, aModifiers, aGuid, aInputBlockId));
171 return;
172 }
173
174 if (!mAPZEventState) {
175 return;
176 }
177
178 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
179 if (!presShell) {
180 return;
181 }
182 if (!presShell->GetPresContext()) {
183 return;
184 }
185 CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
186 CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
187
188 switch (aType) {
189 case TapType::eSingleTap:
190 mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
191 break;
192 case TapType::eDoubleTap:
193 HandleDoubleTap(point, aModifiers, aGuid);
194 break;
195 case TapType::eSecondTap:
196 mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
197 break;
198 case TapType::eLongTap:
199 mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
200 aInputBlockId);
201 break;
202 case TapType::eLongTapUp:
203 mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
204 break;
205 case TapType::eSentinel:
206 // Should never happen, but we need to handle this case branch for the
207 // compiler to be happy.
208 MOZ_ASSERT(false);
209 break;
210 }
211 }
212
213 void
NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,const ScrollableLayerGuid & aGuid,LayoutDeviceCoord aSpanChange,Modifiers aModifiers)214 ChromeProcessController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
215 const ScrollableLayerGuid& aGuid,
216 LayoutDeviceCoord aSpanChange,
217 Modifiers aModifiers)
218 {
219 if (MessageLoop::current() != mUILoop) {
220 mUILoop->PostTask(NewRunnableMethod
221 <PinchGestureInput::PinchGestureType,
222 ScrollableLayerGuid,
223 LayoutDeviceCoord,
224 Modifiers>(this,
225 &ChromeProcessController::NotifyPinchGesture,
226 aType, aGuid, aSpanChange, aModifiers));
227 return;
228 }
229
230 if (mWidget) {
231 APZCCallbackHelper::NotifyPinchGesture(aType, aSpanChange, aModifiers, mWidget.get());
232 }
233 }
234
235 void
NotifyAPZStateChange(const ScrollableLayerGuid & aGuid,APZStateChange aChange,int aArg)236 ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
237 APZStateChange aChange,
238 int aArg)
239 {
240 if (MessageLoop::current() != mUILoop) {
241 mUILoop->PostTask(NewRunnableMethod
242 <ScrollableLayerGuid,
243 APZStateChange,
244 int>(this, &ChromeProcessController::NotifyAPZStateChange,
245 aGuid, aChange, aArg));
246 return;
247 }
248
249 if (!mAPZEventState) {
250 return;
251 }
252
253 mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg);
254 }
255
256 void
NotifyMozMouseScrollEvent(const FrameMetrics::ViewID & aScrollId,const nsString & aEvent)257 ChromeProcessController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
258 {
259 if (MessageLoop::current() != mUILoop) {
260 mUILoop->PostTask(NewRunnableMethod
261 <FrameMetrics::ViewID,
262 nsString>(this, &ChromeProcessController::NotifyMozMouseScrollEvent,
263 aScrollId, aEvent));
264 return;
265 }
266
267 APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
268 }
269
270 void
NotifyFlushComplete()271 ChromeProcessController::NotifyFlushComplete()
272 {
273 MOZ_ASSERT(IsRepaintThread());
274
275 APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
276 }
277