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