/src/mozilla-central/gfx/layers/apz/util/APZCCallbackHelper.cpp
Line | Count | Source (jump to first uncovered line) |
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 "APZCCallbackHelper.h" |
8 | | |
9 | | #include "TouchActionHelper.h" |
10 | | #include "gfxPlatform.h" // For gfxPlatform::UseTiling |
11 | | #include "gfxPrefs.h" |
12 | | #include "LayersLogging.h" // For Stringify |
13 | | #include "mozilla/dom/Element.h" |
14 | | #include "mozilla/dom/MouseEventBinding.h" |
15 | | #include "mozilla/dom/TabParent.h" |
16 | | #include "mozilla/IntegerPrintfMacros.h" |
17 | | #include "mozilla/layers/LayerTransactionChild.h" |
18 | | #include "mozilla/layers/ShadowLayers.h" |
19 | | #include "mozilla/layers/WebRenderLayerManager.h" |
20 | | #include "mozilla/layers/WebRenderBridgeChild.h" |
21 | | #include "mozilla/TouchEvents.h" |
22 | | #include "nsContainerFrame.h" |
23 | | #include "nsContentUtils.h" |
24 | | #include "nsIContent.h" |
25 | | #include "nsIDOMWindow.h" |
26 | | #include "nsIDOMWindowUtils.h" |
27 | | #include "nsIDocument.h" |
28 | | #include "nsIInterfaceRequestorUtils.h" |
29 | | #include "nsIScrollableFrame.h" |
30 | | #include "nsLayoutUtils.h" |
31 | | #include "nsPrintfCString.h" |
32 | | #include "nsRefreshDriver.h" |
33 | | #include "nsString.h" |
34 | | #include "nsView.h" |
35 | | #include "Layers.h" |
36 | | |
37 | | // #define APZCCH_LOGGING 1 |
38 | | #ifdef APZCCH_LOGGING |
39 | | #define APZCCH_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__) |
40 | | #else |
41 | | #define APZCCH_LOG(...) |
42 | | #endif |
43 | | |
44 | | namespace mozilla { |
45 | | namespace layers { |
46 | | |
47 | | using dom::TabParent; |
48 | | |
49 | | uint64_t APZCCallbackHelper::sLastTargetAPZCNotificationInputBlock = uint64_t(-1); |
50 | | |
51 | | void |
52 | | APZCCallbackHelper::AdjustDisplayPortForScrollDelta( |
53 | | mozilla::layers::FrameMetrics& aFrameMetrics, |
54 | | const CSSPoint& aActualScrollOffset) |
55 | 0 | { |
56 | 0 | // Correct the display-port by the difference between the requested scroll |
57 | 0 | // offset and the resulting scroll offset after setting the requested value. |
58 | 0 | ScreenPoint shift = |
59 | 0 | (aFrameMetrics.GetScrollOffset() - aActualScrollOffset) * |
60 | 0 | aFrameMetrics.DisplayportPixelsPerCSSPixel(); |
61 | 0 | ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins(); |
62 | 0 | margins.left -= shift.x; |
63 | 0 | margins.right += shift.x; |
64 | 0 | margins.top -= shift.y; |
65 | 0 | margins.bottom += shift.y; |
66 | 0 | aFrameMetrics.SetDisplayPortMargins(margins); |
67 | 0 | } |
68 | | |
69 | | static void |
70 | | RecenterDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics) |
71 | 0 | { |
72 | 0 | ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins(); |
73 | 0 | margins.right = margins.left = margins.LeftRight() / 2; |
74 | 0 | margins.top = margins.bottom = margins.TopBottom() / 2; |
75 | 0 | aFrameMetrics.SetDisplayPortMargins(margins); |
76 | 0 | } |
77 | | |
78 | | static already_AddRefed<nsIPresShell> |
79 | | GetPresShell(const nsIContent* aContent) |
80 | 0 | { |
81 | 0 | nsCOMPtr<nsIPresShell> result; |
82 | 0 | if (nsIDocument* doc = aContent->GetComposedDoc()) { |
83 | 0 | result = doc->GetShell(); |
84 | 0 | } |
85 | 0 | return result.forget(); |
86 | 0 | } |
87 | | |
88 | | static CSSPoint |
89 | | ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aSuccessOut) |
90 | 0 | { |
91 | 0 | aSuccessOut = false; |
92 | 0 | CSSPoint targetScrollPosition = aMetrics.IsRootContent() |
93 | 0 | ? aMetrics.GetViewport().TopLeft() |
94 | 0 | : aMetrics.GetScrollOffset(); |
95 | 0 |
|
96 | 0 | if (!aFrame) { |
97 | 0 | return targetScrollPosition; |
98 | 0 | } |
99 | 0 | |
100 | 0 | CSSPoint geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition()); |
101 | 0 |
|
102 | 0 | // If the repaint request was triggered due to a previous main-thread scroll |
103 | 0 | // offset update sent to the APZ, then we don't need to do another scroll here |
104 | 0 | // and we can just return. |
105 | 0 | if (!aMetrics.GetScrollOffsetUpdated()) { |
106 | 0 | return geckoScrollPosition; |
107 | 0 | } |
108 | 0 | |
109 | 0 | // If this frame is overflow:hidden, then the expectation is that it was |
110 | 0 | // sized in a way that respects its scrollable boundaries. For the root |
111 | 0 | // frame, this means that it cannot be scrolled in such a way that it moves |
112 | 0 | // the layout viewport. For a non-root frame, this means that it cannot be |
113 | 0 | // scrolled at all. |
114 | 0 | // |
115 | 0 | // In either case, |targetScrollPosition| should be the same as |
116 | 0 | // |geckoScrollPosition| here. |
117 | 0 | // |
118 | 0 | // However, this is slightly racy. We query the overflow property of the |
119 | 0 | // scroll frame at the time the repaint request arrives at the main thread |
120 | 0 | // (i.e., right now), but APZ made the decision of whether or not to allow |
121 | 0 | // scrolling based on the information it had at the time it processed the |
122 | 0 | // scroll event. The overflow property could have changed at some time |
123 | 0 | // between the two events and so APZ may have computed a scrollable region |
124 | 0 | // that is larger than what is actually allowed. |
125 | 0 | // |
126 | 0 | // Currently, we allow the scroll position to change even though the frame is |
127 | 0 | // overflow:hidden (that is, we take |targetScrollPosition|). If this turns |
128 | 0 | // out to be problematic, an alternative solution would be to ignore the |
129 | 0 | // scroll position change (that is, use |geckoScrollPosition|). |
130 | 0 | if (aFrame->GetScrollStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN && |
131 | 0 | targetScrollPosition.y != geckoScrollPosition.y) { |
132 | 0 | NS_WARNING(nsPrintfCString( |
133 | 0 | "APZCCH: targetScrollPosition.y (%f) != geckoScrollPosition.y (%f)", |
134 | 0 | targetScrollPosition.y, geckoScrollPosition.y).get()); |
135 | 0 | } |
136 | 0 | if (aFrame->GetScrollStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && |
137 | 0 | targetScrollPosition.x != geckoScrollPosition.x) { |
138 | 0 | NS_WARNING(nsPrintfCString( |
139 | 0 | "APZCCH: targetScrollPosition.x (%f) != geckoScrollPosition.x (%f)", |
140 | 0 | targetScrollPosition.x, geckoScrollPosition.x).get()); |
141 | 0 | } |
142 | 0 |
|
143 | 0 | // If the scrollable frame is currently in the middle of an async or smooth |
144 | 0 | // scroll then we don't want to interrupt it (see bug 961280). |
145 | 0 | // Also if the scrollable frame got a scroll request from a higher priority origin |
146 | 0 | // since the last layers update, then we don't want to push our scroll request |
147 | 0 | // because we'll clobber that one, which is bad. |
148 | 0 | bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame); |
149 | 0 | if (!scrollInProgress) { |
150 | 0 | aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz); |
151 | 0 | geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition()); |
152 | 0 | aSuccessOut = true; |
153 | 0 | } |
154 | 0 | // Return the final scroll position after setting it so that anything that relies |
155 | 0 | // on it can have an accurate value. Note that even if we set it above re-querying it |
156 | 0 | // is a good idea because it may have gotten clamped or rounded. |
157 | 0 | return geckoScrollPosition; |
158 | 0 | } |
159 | | |
160 | | /** |
161 | | * Scroll the scroll frame associated with |aContent| to the scroll position |
162 | | * requested in |aMetrics|. |
163 | | * The scroll offset in |aMetrics| is updated to reflect the actual scroll |
164 | | * position. |
165 | | * The displayport stored in |aMetrics| and the callback-transform stored on |
166 | | * the content are updated to reflect any difference between the requested |
167 | | * and actual scroll positions. |
168 | | */ |
169 | | static void |
170 | | ScrollFrame(nsIContent* aContent, |
171 | | FrameMetrics& aMetrics) |
172 | 0 | { |
173 | 0 | // Scroll the window to the desired spot |
174 | 0 | nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId()); |
175 | 0 | if (sf) { |
176 | 0 | sf->ResetScrollInfoIfGeneration(aMetrics.GetScrollGeneration()); |
177 | 0 | sf->SetScrollableByAPZ(!aMetrics.IsScrollInfoLayer()); |
178 | 0 | if (sf->IsRootScrollFrameOfDocument()) { |
179 | 0 | if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) { |
180 | 0 | shell->SetVisualViewportOffset(CSSPoint::ToAppUnits(aMetrics.GetScrollOffset())); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | 0 | bool scrollUpdated = false; |
185 | 0 | CSSPoint apzScrollOffset = aMetrics.GetScrollOffset(); |
186 | 0 | CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics, scrollUpdated); |
187 | 0 |
|
188 | 0 | if (scrollUpdated) { |
189 | 0 | if (aMetrics.IsScrollInfoLayer()) { |
190 | 0 | // In cases where the APZ scroll offset is different from the content scroll |
191 | 0 | // offset, we want to interpret the margins as relative to the APZ scroll |
192 | 0 | // offset except when the frame is not scrollable by APZ. Therefore, if the |
193 | 0 | // layer is a scroll info layer, we leave the margins as-is and they will |
194 | 0 | // be interpreted as relative to the content scroll offset. |
195 | 0 | if (nsIFrame* frame = aContent->GetPrimaryFrame()) { |
196 | 0 | frame->SchedulePaint(); |
197 | 0 | } |
198 | 0 | } else { |
199 | 0 | // Correct the display port due to the difference between mScrollOffset and the |
200 | 0 | // actual scroll offset. |
201 | 0 | APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset); |
202 | 0 | } |
203 | 0 | } else if (aMetrics.IsRootContent() && |
204 | 0 | aMetrics.GetScrollOffset() != aMetrics.GetViewport().TopLeft()) { |
205 | 0 | // APZ uses the visual viewport's offset to calculate where to place the |
206 | 0 | // display port, so the display port is misplaced when a pinch zoom occurs. |
207 | 0 | // |
208 | 0 | // We need to force a display port adjustment in the following paint to |
209 | 0 | // account for a difference between mScrollOffset and the actual scroll |
210 | 0 | // offset in repaints requested by AsyncPanZoomController::NotifyLayersUpdated. |
211 | 0 | APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset); |
212 | 0 | } else { |
213 | 0 | // For whatever reason we couldn't update the scroll offset on the scroll frame, |
214 | 0 | // which means the data APZ used for its displayport calculation is stale. Fall |
215 | 0 | // back to a sane default behaviour. Note that we don't tile-align the recentered |
216 | 0 | // displayport because tile-alignment depends on the scroll position, and the |
217 | 0 | // scroll position here is out of our control. See bug 966507 comment 21 for a |
218 | 0 | // more detailed explanation. |
219 | 0 | RecenterDisplayPort(aMetrics); |
220 | 0 | } |
221 | 0 |
|
222 | 0 | aMetrics.SetScrollOffset(actualScrollOffset); |
223 | 0 |
|
224 | 0 | // APZ transforms inputs assuming we applied the exact scroll offset it |
225 | 0 | // requested (|apzScrollOffset|). Since we may not have, record the difference |
226 | 0 | // between what APZ asked for and what we actually applied, and apply it to |
227 | 0 | // input events to compensate. |
228 | 0 | // Note that if the main-thread had a change in its scroll position, we don't |
229 | 0 | // want to record that difference here, because it can be large and throw off |
230 | 0 | // input events by a large amount. It is also going to be transient, because |
231 | 0 | // any main-thread scroll position change will be synced to APZ and we will |
232 | 0 | // get another repaint request when APZ confirms. In the interval while this |
233 | 0 | // is happening we can just leave the callback transform as it was. |
234 | 0 | bool mainThreadScrollChanged = |
235 | 0 | sf && sf->CurrentScrollGeneration() != aMetrics.GetScrollGeneration() && nsLayoutUtils::CanScrollOriginClobberApz(sf->LastScrollOrigin()); |
236 | 0 | if (aContent && !mainThreadScrollChanged) { |
237 | 0 | CSSPoint scrollDelta = apzScrollOffset - actualScrollOffset; |
238 | 0 | aContent->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta), |
239 | 0 | nsINode::DeleteProperty<CSSPoint>); |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | static void |
244 | | SetDisplayPortMargins(nsIPresShell* aPresShell, |
245 | | nsIContent* aContent, |
246 | | const FrameMetrics& aMetrics) |
247 | 0 | { |
248 | 0 | if (!aContent) { |
249 | 0 | return; |
250 | 0 | } |
251 | 0 | |
252 | 0 | bool hadDisplayPort = nsLayoutUtils::HasDisplayPort(aContent); |
253 | 0 | ScreenMargin margins = aMetrics.GetDisplayPortMargins(); |
254 | 0 | nsLayoutUtils::SetDisplayPortMargins(aContent, aPresShell, margins, 0); |
255 | 0 | if (!hadDisplayPort) { |
256 | 0 | nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors( |
257 | 0 | aContent->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::Repaint); |
258 | 0 | } |
259 | 0 |
|
260 | 0 | CSSSize baseSize = aMetrics.CalculateCompositedSizeInCssPixels(); |
261 | 0 | nsRect base(0, 0, |
262 | 0 | baseSize.width * AppUnitsPerCSSPixel(), |
263 | 0 | baseSize.height * AppUnitsPerCSSPixel()); |
264 | 0 | nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base); |
265 | 0 | } |
266 | | |
267 | | static void |
268 | | SetPaintRequestTime(nsIContent* aContent, const TimeStamp& aPaintRequestTime) |
269 | 0 | { |
270 | 0 | aContent->SetProperty(nsGkAtoms::paintRequestTime, |
271 | 0 | new TimeStamp(aPaintRequestTime), |
272 | 0 | nsINode::DeleteProperty<TimeStamp>); |
273 | 0 | } |
274 | | |
275 | | void |
276 | | APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics) |
277 | 0 | { |
278 | 0 | if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { |
279 | 0 | return; |
280 | 0 | } |
281 | 0 | nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); |
282 | 0 | if (!content) { |
283 | 0 | return; |
284 | 0 | } |
285 | 0 | |
286 | 0 | nsCOMPtr<nsIPresShell> shell = GetPresShell(content); |
287 | 0 | if (!shell || aMetrics.GetPresShellId() != shell->GetPresShellId()) { |
288 | 0 | return; |
289 | 0 | } |
290 | 0 | |
291 | 0 | MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); |
292 | 0 |
|
293 | 0 | if (gfxPrefs::APZAllowZooming() && aMetrics.GetScrollOffsetUpdated()) { |
294 | 0 | // If zooming is disabled then we don't really want to let APZ fiddle |
295 | 0 | // with these things. In theory setting the resolution here should be a |
296 | 0 | // no-op, but setting the visual viewport size is bad because it can cause a |
297 | 0 | // stale value to be returned by window.innerWidth/innerHeight (see bug 1187792). |
298 | 0 | // |
299 | 0 | // We also skip this codepath unless the metrics has a scroll offset update |
300 | 0 | // type other eNone, because eNone just means that this repaint request |
301 | 0 | // was triggered by APZ in response to a main-thread update. In this |
302 | 0 | // scenario we don't want to update the main-thread resolution because |
303 | 0 | // it can trigger unnecessary reflows. |
304 | 0 |
|
305 | 0 | float presShellResolution = shell->GetResolution(); |
306 | 0 |
|
307 | 0 | // If the pres shell resolution has changed on the content side side |
308 | 0 | // the time this repaint request was fired, consider this request out of date |
309 | 0 | // and drop it; setting a zoom based on the out-of-date resolution can have |
310 | 0 | // the effect of getting us stuck with the stale resolution. |
311 | 0 | if (!FuzzyEqualsMultiplicative(presShellResolution, aMetrics.GetPresShellResolution())) { |
312 | 0 | return; |
313 | 0 | } |
314 | 0 | |
315 | 0 | // The pres shell resolution is updated by the the async zoom since the |
316 | 0 | // last paint. |
317 | 0 | presShellResolution = aMetrics.GetPresShellResolution() |
318 | 0 | * aMetrics.GetAsyncZoom().scale; |
319 | 0 | shell->SetResolutionAndScaleTo(presShellResolution); |
320 | 0 | } |
321 | 0 |
|
322 | 0 | // Do this as late as possible since scrolling can flush layout. It also |
323 | 0 | // adjusts the display port margins, so do it before we set those. |
324 | 0 | ScrollFrame(content, aMetrics); |
325 | 0 |
|
326 | 0 | SetDisplayPortMargins(shell, content, aMetrics); |
327 | 0 | SetPaintRequestTime(content, aMetrics.GetPaintRequestTime()); |
328 | 0 | } |
329 | | |
330 | | void |
331 | | APZCCallbackHelper::UpdateSubFrame(FrameMetrics& aMetrics) |
332 | 0 | { |
333 | 0 | if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { |
334 | 0 | return; |
335 | 0 | } |
336 | 0 | nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); |
337 | 0 | if (!content) { |
338 | 0 | return; |
339 | 0 | } |
340 | 0 | |
341 | 0 | MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); |
342 | 0 |
|
343 | 0 | // We don't currently support zooming for subframes, so nothing extra |
344 | 0 | // needs to be done beyond the tasks common to this and UpdateRootFrame. |
345 | 0 | ScrollFrame(content, aMetrics); |
346 | 0 | if (nsCOMPtr<nsIPresShell> shell = GetPresShell(content)) { |
347 | 0 | SetDisplayPortMargins(shell, content, aMetrics); |
348 | 0 | } |
349 | 0 | SetPaintRequestTime(content, aMetrics.GetPaintRequestTime()); |
350 | 0 | } |
351 | | |
352 | | bool |
353 | | APZCCallbackHelper::GetOrCreateScrollIdentifiers(nsIContent* aContent, |
354 | | uint32_t* aPresShellIdOut, |
355 | | FrameMetrics::ViewID* aViewIdOut) |
356 | 0 | { |
357 | 0 | if (!aContent) { |
358 | 0 | return false; |
359 | 0 | } |
360 | 0 | *aViewIdOut = nsLayoutUtils::FindOrCreateIDFor(aContent); |
361 | 0 | if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) { |
362 | 0 | *aPresShellIdOut = shell->GetPresShellId(); |
363 | 0 | return true; |
364 | 0 | } |
365 | 0 | return false; |
366 | 0 | } |
367 | | |
368 | | void |
369 | | APZCCallbackHelper::InitializeRootDisplayport(nsIPresShell* aPresShell) |
370 | 0 | { |
371 | 0 | // Create a view-id and set a zero-margin displayport for the root element |
372 | 0 | // of the root document in the chrome process. This ensures that the scroll |
373 | 0 | // frame for this element gets an APZC, which in turn ensures that all content |
374 | 0 | // in the chrome processes is covered by an APZC. |
375 | 0 | // The displayport is zero-margin because this element is generally not |
376 | 0 | // actually scrollable (if it is, APZC will set proper margins when it's |
377 | 0 | // scrolled). |
378 | 0 | if (!aPresShell) { |
379 | 0 | return; |
380 | 0 | } |
381 | 0 | |
382 | 0 | MOZ_ASSERT(aPresShell->GetDocument()); |
383 | 0 | nsIContent* content = aPresShell->GetDocument()->GetDocumentElement(); |
384 | 0 | if (!content) { |
385 | 0 | return; |
386 | 0 | } |
387 | 0 | |
388 | 0 | uint32_t presShellId; |
389 | 0 | FrameMetrics::ViewID viewId; |
390 | 0 | if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId, &viewId)) { |
391 | 0 | nsPresContext* pc = aPresShell->GetPresContext(); |
392 | 0 | // This code is only correct for root content or toplevel documents. |
393 | 0 | MOZ_ASSERT(!pc || pc->IsRootContentDocument() || !pc->GetParentPresContext()); |
394 | 0 | nsIFrame* frame = aPresShell->GetRootScrollFrame(); |
395 | 0 | if (!frame) { |
396 | 0 | frame = aPresShell->GetRootFrame(); |
397 | 0 | } |
398 | 0 | nsRect baseRect; |
399 | 0 | if (frame) { |
400 | 0 | baseRect = |
401 | 0 | nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(frame)); |
402 | 0 | } else if (pc) { |
403 | 0 | baseRect = nsRect(nsPoint(0, 0), pc->GetVisibleArea().Size()); |
404 | 0 | } |
405 | 0 | nsLayoutUtils::SetDisplayPortBaseIfNotSet(content, baseRect); |
406 | 0 | // Note that we also set the base rect that goes with these margins in |
407 | 0 | // nsRootBoxFrame::BuildDisplayList. |
408 | 0 | nsLayoutUtils::SetDisplayPortMargins(content, aPresShell, ScreenMargin(), 0, |
409 | 0 | nsLayoutUtils::RepaintMode::DoNotRepaint); |
410 | 0 | nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors( |
411 | 0 | content->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::DoNotRepaint); |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | | nsPresContext* |
416 | | APZCCallbackHelper::GetPresContextForContent(nsIContent* aContent) |
417 | 0 | { |
418 | 0 | nsIDocument* doc = aContent->GetComposedDoc(); |
419 | 0 | if (!doc) { |
420 | 0 | return nullptr; |
421 | 0 | } |
422 | 0 | nsIPresShell* shell = doc->GetShell(); |
423 | 0 | if (!shell) { |
424 | 0 | return nullptr; |
425 | 0 | } |
426 | 0 | return shell->GetPresContext(); |
427 | 0 | } |
428 | | |
429 | | nsIPresShell* |
430 | | APZCCallbackHelper::GetRootContentDocumentPresShellForContent(nsIContent* aContent) |
431 | 0 | { |
432 | 0 | nsPresContext* context = GetPresContextForContent(aContent); |
433 | 0 | if (!context) { |
434 | 0 | return nullptr; |
435 | 0 | } |
436 | 0 | context = context->GetToplevelContentDocumentPresContext(); |
437 | 0 | if (!context) { |
438 | 0 | return nullptr; |
439 | 0 | } |
440 | 0 | return context->PresShell(); |
441 | 0 | } |
442 | | |
443 | | static nsIPresShell* |
444 | | GetRootDocumentPresShell(nsIContent* aContent) |
445 | 0 | { |
446 | 0 | nsIDocument* doc = aContent->GetComposedDoc(); |
447 | 0 | if (!doc) { |
448 | 0 | return nullptr; |
449 | 0 | } |
450 | 0 | nsIPresShell* shell = doc->GetShell(); |
451 | 0 | if (!shell) { |
452 | 0 | return nullptr; |
453 | 0 | } |
454 | 0 | nsPresContext* context = shell->GetPresContext(); |
455 | 0 | if (!context) { |
456 | 0 | return nullptr; |
457 | 0 | } |
458 | 0 | context = context->GetRootPresContext(); |
459 | 0 | if (!context) { |
460 | 0 | return nullptr; |
461 | 0 | } |
462 | 0 | return context->PresShell(); |
463 | 0 | } |
464 | | |
465 | | CSSPoint |
466 | | APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput, |
467 | | const ScrollableLayerGuid& aGuid) |
468 | 0 | { |
469 | 0 | CSSPoint input = aInput; |
470 | 0 | if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) { |
471 | 0 | return input; |
472 | 0 | } |
473 | 0 | nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId); |
474 | 0 | if (!content) { |
475 | 0 | return input; |
476 | 0 | } |
477 | 0 | |
478 | 0 | // First, scale inversely by the root content document's pres shell |
479 | 0 | // resolution to cancel the scale-to-resolution transform that the |
480 | 0 | // compositor adds to the layer with the pres shell resolution. The points |
481 | 0 | // sent to Gecko by APZ don't have this transform unapplied (unlike other |
482 | 0 | // compositor-side transforms) because APZ doesn't know about it. |
483 | 0 | if (nsIPresShell* shell = GetRootDocumentPresShell(content)) { |
484 | 0 | input = input / shell->GetResolution(); |
485 | 0 | } |
486 | 0 |
|
487 | 0 | // This represents any resolution on the Root Content Document (RCD) |
488 | 0 | // that's not on the Root Document (RD). That is, on platforms where |
489 | 0 | // RCD == RD, it's 1, and on platforms where RCD != RD, it's the RCD |
490 | 0 | // resolution. 'input' has this resolution applied, but the scroll |
491 | 0 | // delta retrieved below do not, so we need to apply them to the |
492 | 0 | // delta before adding the delta to 'input'. (Technically, deltas |
493 | 0 | // from scroll frames outside the RCD would already have this |
494 | 0 | // resolution applied, but we don't have such scroll frames in |
495 | 0 | // practice.) |
496 | 0 | float nonRootResolution = 1.0f; |
497 | 0 | if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) { |
498 | 0 | nonRootResolution = shell->GetCumulativeNonRootScaleResolution(); |
499 | 0 | } |
500 | 0 | // Now apply the callback-transform. This is only approximately correct, |
501 | 0 | // see the comment on GetCumulativeApzCallbackTransform for details. |
502 | 0 | CSSPoint transform = nsLayoutUtils::GetCumulativeApzCallbackTransform(content->GetPrimaryFrame()); |
503 | 0 | return input + transform * nonRootResolution; |
504 | 0 | } |
505 | | |
506 | | LayoutDeviceIntPoint |
507 | | APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint, |
508 | | const ScrollableLayerGuid& aGuid, |
509 | | const CSSToLayoutDeviceScale& aScale) |
510 | 0 | { |
511 | 0 | LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y); |
512 | 0 | point = ApplyCallbackTransform(point / aScale, aGuid) * aScale; |
513 | 0 | return LayoutDeviceIntPoint::Round(point); |
514 | 0 | } |
515 | | |
516 | | void |
517 | | APZCCallbackHelper::ApplyCallbackTransform(WidgetEvent& aEvent, |
518 | | const ScrollableLayerGuid& aGuid, |
519 | | const CSSToLayoutDeviceScale& aScale) |
520 | 0 | { |
521 | 0 | if (aEvent.AsTouchEvent()) { |
522 | 0 | WidgetTouchEvent& event = *(aEvent.AsTouchEvent()); |
523 | 0 | for (size_t i = 0; i < event.mTouches.Length(); i++) { |
524 | 0 | event.mTouches[i]->mRefPoint = ApplyCallbackTransform( |
525 | 0 | event.mTouches[i]->mRefPoint, aGuid, aScale); |
526 | 0 | } |
527 | 0 | } else { |
528 | 0 | aEvent.mRefPoint = ApplyCallbackTransform(aEvent.mRefPoint, aGuid, aScale); |
529 | 0 | } |
530 | 0 | } |
531 | | |
532 | | nsEventStatus |
533 | | APZCCallbackHelper::DispatchWidgetEvent(WidgetGUIEvent& aEvent) |
534 | 0 | { |
535 | 0 | nsEventStatus status = nsEventStatus_eConsumeNoDefault; |
536 | 0 | if (aEvent.mWidget) { |
537 | 0 | aEvent.mWidget->DispatchEvent(&aEvent, status); |
538 | 0 | } |
539 | 0 | return status; |
540 | 0 | } |
541 | | |
542 | | nsEventStatus |
543 | | APZCCallbackHelper::DispatchSynthesizedMouseEvent(EventMessage aMsg, |
544 | | uint64_t aTime, |
545 | | const LayoutDevicePoint& aRefPoint, |
546 | | Modifiers aModifiers, |
547 | | int32_t aClickCount, |
548 | | nsIWidget* aWidget) |
549 | 0 | { |
550 | 0 | MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown || |
551 | 0 | aMsg == eMouseUp || aMsg == eMouseLongTap); |
552 | 0 |
|
553 | 0 | WidgetMouseEvent event(true, aMsg, aWidget, |
554 | 0 | WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); |
555 | 0 | event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y); |
556 | 0 | event.mTime = aTime; |
557 | 0 | event.button = WidgetMouseEvent::eLeftButton; |
558 | 0 | event.inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH; |
559 | 0 | if (aMsg == eMouseLongTap) { |
560 | 0 | event.mFlags.mOnlyChromeDispatch = true; |
561 | 0 | } |
562 | 0 | event.mIgnoreRootScrollFrame = true; |
563 | 0 | if (aMsg != eMouseMove) { |
564 | 0 | event.mClickCount = aClickCount; |
565 | 0 | } |
566 | 0 | event.mModifiers = aModifiers; |
567 | 0 | // Real touch events will generate corresponding pointer events. We set |
568 | 0 | // convertToPointer to false to prevent the synthesized mouse events generate |
569 | 0 | // pointer events again. |
570 | 0 | event.convertToPointer = false; |
571 | 0 | return DispatchWidgetEvent(event); |
572 | 0 | } |
573 | | |
574 | | bool |
575 | | APZCCallbackHelper::DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell, |
576 | | const nsString& aType, |
577 | | const CSSPoint& aPoint, |
578 | | int32_t aButton, |
579 | | int32_t aClickCount, |
580 | | int32_t aModifiers, |
581 | | bool aIgnoreRootScrollFrame, |
582 | | unsigned short aInputSourceArg, |
583 | | uint32_t aPointerId) |
584 | 0 | { |
585 | 0 | NS_ENSURE_TRUE(aPresShell, true); |
586 | 0 |
|
587 | 0 | bool defaultPrevented = false; |
588 | 0 | nsContentUtils::SendMouseEvent(aPresShell, aType, aPoint.x, aPoint.y, |
589 | 0 | aButton, nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED, aClickCount, |
590 | 0 | aModifiers, aIgnoreRootScrollFrame, 0, aInputSourceArg, aPointerId, false, |
591 | 0 | &defaultPrevented, false, /* aIsWidgetEventSynthesized = */ false); |
592 | 0 | return defaultPrevented; |
593 | 0 | } |
594 | | |
595 | | |
596 | | void |
597 | | APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint, |
598 | | Modifiers aModifiers, |
599 | | int32_t aClickCount, |
600 | | nsIWidget* aWidget) |
601 | 0 | { |
602 | 0 | if (aWidget->Destroyed()) { |
603 | 0 | return; |
604 | 0 | } |
605 | 0 | APZCCH_LOG("Dispatching single-tap component events to %s\n", |
606 | 0 | Stringify(aPoint).c_str()); |
607 | 0 | int time = 0; |
608 | 0 | DispatchSynthesizedMouseEvent(eMouseMove, time, aPoint, aModifiers, aClickCount, aWidget); |
609 | 0 | DispatchSynthesizedMouseEvent(eMouseDown, time, aPoint, aModifiers, aClickCount, aWidget); |
610 | 0 | DispatchSynthesizedMouseEvent(eMouseUp, time, aPoint, aModifiers, aClickCount, aWidget); |
611 | 0 | } |
612 | | |
613 | | static dom::Element* |
614 | | GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame) |
615 | 0 | { |
616 | 0 | if (!aScrollableFrame) { |
617 | 0 | return nullptr; |
618 | 0 | } |
619 | 0 | nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame(); |
620 | 0 | if (!scrolledFrame) { |
621 | 0 | return nullptr; |
622 | 0 | } |
623 | 0 | // |scrolledFrame| should at this point be the root content frame of the |
624 | 0 | // nearest ancestor scrollable frame. The element corresponding to this |
625 | 0 | // frame should be the one with the displayport set on it, so find that |
626 | 0 | // element and return it. |
627 | 0 | nsIContent* content = scrolledFrame->GetContent(); |
628 | 0 | MOZ_ASSERT(content->IsElement()); // roc says this must be true |
629 | 0 | return content->AsElement(); |
630 | 0 | } |
631 | | |
632 | | |
633 | | static dom::Element* |
634 | | GetRootDocumentElementFor(nsIWidget* aWidget) |
635 | 0 | { |
636 | 0 | // This returns the root element that ChromeProcessController sets the |
637 | 0 | // displayport on during initialization. |
638 | 0 | if (nsView* view = nsView::GetViewFor(aWidget)) { |
639 | 0 | if (nsIPresShell* shell = view->GetPresShell()) { |
640 | 0 | MOZ_ASSERT(shell->GetDocument()); |
641 | 0 | return shell->GetDocument()->GetDocumentElement(); |
642 | 0 | } |
643 | 0 | } |
644 | 0 | return nullptr; |
645 | 0 | } |
646 | | |
647 | | static nsIFrame* |
648 | | UpdateRootFrameForTouchTargetDocument(nsIFrame* aRootFrame) |
649 | 0 | { |
650 | | #if defined(MOZ_WIDGET_ANDROID) |
651 | | // Re-target so that the hit test is performed relative to the frame for the |
652 | | // Root Content Document instead of the Root Document which are different in |
653 | | // Android. See bug 1229752 comment 16 for an explanation of why this is necessary. |
654 | | if (nsIDocument* doc = aRootFrame->PresShell()->GetPrimaryContentDocument()) { |
655 | | if (nsIPresShell* shell = doc->GetShell()) { |
656 | | if (nsIFrame* frame = shell->GetRootFrame()) { |
657 | | return frame; |
658 | | } |
659 | | } |
660 | | } |
661 | | #endif |
662 | | return aRootFrame; |
663 | 0 | } |
664 | | |
665 | | // Determine the scrollable target frame for the given point and add it to |
666 | | // the target list. If the frame doesn't have a displayport, set one. |
667 | | // Return whether or not a displayport was set. |
668 | | static bool |
669 | | PrepareForSetTargetAPZCNotification(nsIWidget* aWidget, |
670 | | const ScrollableLayerGuid& aGuid, |
671 | | nsIFrame* aRootFrame, |
672 | | const LayoutDeviceIntPoint& aRefPoint, |
673 | | nsTArray<ScrollableLayerGuid>* aTargets) |
674 | 0 | { |
675 | 0 | ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID); |
676 | 0 | nsPoint point = |
677 | 0 | nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame); |
678 | 0 | uint32_t flags = 0; |
679 | | #ifdef MOZ_WIDGET_ANDROID |
680 | | // On Android, we need IGNORE_ROOT_SCROLL_FRAME for correct hit testing |
681 | | // when zoomed out. On desktop, don't use it because it interferes with |
682 | | // hit testing for some purposes such as scrollbar dragging. |
683 | | flags = nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME; |
684 | | #endif |
685 | | nsIFrame* target = |
686 | 0 | nsLayoutUtils::GetFrameForPoint(aRootFrame, point, flags); |
687 | 0 | nsIScrollableFrame* scrollAncestor = target |
688 | 0 | ? nsLayoutUtils::GetAsyncScrollableAncestorFrame(target) |
689 | 0 | : aRootFrame->PresShell()->GetRootScrollFrameAsScrollable(); |
690 | 0 |
|
691 | 0 | // Assuming that if there's no scrollAncestor, there's already a displayPort. |
692 | 0 | nsCOMPtr<dom::Element> dpElement = scrollAncestor |
693 | 0 | ? GetDisplayportElementFor(scrollAncestor) |
694 | 0 | : GetRootDocumentElementFor(aWidget); |
695 | 0 |
|
696 | | #ifdef APZCCH_LOGGING |
697 | | nsAutoString dpElementDesc; |
698 | | if (dpElement) { |
699 | | dpElement->Describe(dpElementDesc); |
700 | | } |
701 | | APZCCH_LOG("For event at %s found scrollable element %p (%s)\n", |
702 | | Stringify(aRefPoint).c_str(), dpElement.get(), |
703 | | NS_LossyConvertUTF16toASCII(dpElementDesc).get()); |
704 | | #endif |
705 | |
|
706 | 0 | bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers( |
707 | 0 | dpElement, &(guid.mPresShellId), &(guid.mScrollId)); |
708 | 0 | aTargets->AppendElement(guid); |
709 | 0 |
|
710 | 0 | if (!guidIsValid || nsLayoutUtils::HasDisplayPort(dpElement)) { |
711 | 0 | return false; |
712 | 0 | } |
713 | 0 | |
714 | 0 | if (!scrollAncestor) { |
715 | 0 | // This can happen if the document element gets swapped out after ChromeProcessController |
716 | 0 | // runs InitializeRootDisplayport. In this case let's try to set a displayport again and |
717 | 0 | // bail out on this operation. |
718 | 0 | APZCCH_LOG("Widget %p's document element %p didn't have a displayport\n", |
719 | 0 | aWidget, dpElement.get()); |
720 | 0 | APZCCallbackHelper::InitializeRootDisplayport(aRootFrame->PresShell()); |
721 | 0 | return false; |
722 | 0 | } |
723 | 0 | |
724 | 0 | APZCCH_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get()); |
725 | 0 | bool activated = nsLayoutUtils::CalculateAndSetDisplayPortMargins( |
726 | 0 | scrollAncestor, nsLayoutUtils::RepaintMode::Repaint); |
727 | 0 | if (!activated) { |
728 | 0 | return false; |
729 | 0 | } |
730 | 0 | |
731 | 0 | nsIFrame* frame = do_QueryFrame(scrollAncestor); |
732 | 0 | nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(frame, |
733 | 0 | nsLayoutUtils::RepaintMode::Repaint); |
734 | 0 |
|
735 | 0 | return true; |
736 | 0 | } |
737 | | |
738 | | static void |
739 | | SendLayersDependentApzcTargetConfirmation(nsIPresShell* aShell, uint64_t aInputBlockId, |
740 | | const nsTArray<ScrollableLayerGuid>& aTargets) |
741 | 0 | { |
742 | 0 | LayerManager* lm = aShell->GetLayerManager(); |
743 | 0 | if (!lm) { |
744 | 0 | return; |
745 | 0 | } |
746 | 0 | |
747 | 0 | if (WebRenderLayerManager* wrlm = lm->AsWebRenderLayerManager()) { |
748 | 0 | if (WebRenderBridgeChild* wrbc = wrlm->WrBridge()) { |
749 | 0 | wrbc->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets); |
750 | 0 | } |
751 | 0 | return; |
752 | 0 | } |
753 | 0 |
|
754 | 0 | LayerTransactionChild* shadow = lm->AsShadowForwarder()->GetShadowManager(); |
755 | 0 | if (!shadow) { |
756 | 0 | return; |
757 | 0 | } |
758 | 0 | |
759 | 0 | shadow->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets); |
760 | 0 | } |
761 | | |
762 | | DisplayportSetListener::DisplayportSetListener(nsIWidget* aWidget, |
763 | | nsIPresShell* aPresShell, |
764 | | const uint64_t& aInputBlockId, |
765 | | const nsTArray<ScrollableLayerGuid>& aTargets) |
766 | | : mWidget(aWidget) |
767 | | , mPresShell(aPresShell) |
768 | | , mInputBlockId(aInputBlockId) |
769 | | , mTargets(aTargets) |
770 | 0 | { |
771 | 0 | } |
772 | | |
773 | | DisplayportSetListener:: ~DisplayportSetListener() |
774 | 0 | { |
775 | 0 | } |
776 | | |
777 | | bool |
778 | | DisplayportSetListener::Register() |
779 | 0 | { |
780 | 0 | if (mPresShell->AddPostRefreshObserver(this)) { |
781 | 0 | APZCCH_LOG("Successfully registered post-refresh observer\n"); |
782 | 0 | return true; |
783 | 0 | } |
784 | 0 | // In case of failure just send the notification right away |
785 | 0 | APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", mInputBlockId); |
786 | 0 | mWidget->SetConfirmedTargetAPZC(mInputBlockId, mTargets); |
787 | 0 | return false; |
788 | 0 | } |
789 | | |
790 | | void |
791 | 0 | DisplayportSetListener::DidRefresh() { |
792 | 0 | if (!mPresShell) { |
793 | 0 | MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it"); |
794 | 0 | return; |
795 | 0 | } |
796 | 0 |
|
797 | 0 | APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId); |
798 | 0 | SendLayersDependentApzcTargetConfirmation(mPresShell, mInputBlockId, std::move(mTargets)); |
799 | 0 |
|
800 | 0 | if (!mPresShell->RemovePostRefreshObserver(this)) { |
801 | 0 | MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered"); |
802 | 0 | // Graceful handling, just in case... |
803 | 0 | mPresShell = nullptr; |
804 | 0 | return; |
805 | 0 | } |
806 | 0 |
|
807 | 0 | delete this; |
808 | 0 | } |
809 | | |
810 | | UniquePtr<DisplayportSetListener> |
811 | | APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget, |
812 | | nsIDocument* aDocument, |
813 | | const WidgetGUIEvent& aEvent, |
814 | | const ScrollableLayerGuid& aGuid, |
815 | | uint64_t aInputBlockId) |
816 | 0 | { |
817 | 0 | if (!aWidget || !aDocument) { |
818 | 0 | return nullptr; |
819 | 0 | } |
820 | 0 | if (aInputBlockId == sLastTargetAPZCNotificationInputBlock) { |
821 | 0 | // We have already confirmed the target APZC for a previous event of this |
822 | 0 | // input block. If we activated a scroll frame for this input block, |
823 | 0 | // sending another target APZC confirmation would be harmful, as it might |
824 | 0 | // race the original confirmation (which needs to go through a layers |
825 | 0 | // transaction). |
826 | 0 | APZCCH_LOG("Not resending target APZC confirmation for input block %" PRIu64 "\n", aInputBlockId); |
827 | 0 | return nullptr; |
828 | 0 | } |
829 | 0 | sLastTargetAPZCNotificationInputBlock = aInputBlockId; |
830 | 0 | if (nsIPresShell* shell = aDocument->GetShell()) { |
831 | 0 | if (nsIFrame* rootFrame = shell->GetRootFrame()) { |
832 | 0 | rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame); |
833 | 0 |
|
834 | 0 | bool waitForRefresh = false; |
835 | 0 | nsTArray<ScrollableLayerGuid> targets; |
836 | 0 |
|
837 | 0 | if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) { |
838 | 0 | for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) { |
839 | 0 | waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid, |
840 | 0 | rootFrame, touchEvent->mTouches[i]->mRefPoint, &targets); |
841 | 0 | } |
842 | 0 | } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) { |
843 | 0 | waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid, |
844 | 0 | rootFrame, wheelEvent->mRefPoint, &targets); |
845 | 0 | } else if (const WidgetMouseEvent* mouseEvent = aEvent.AsMouseEvent()) { |
846 | 0 | waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid, |
847 | 0 | rootFrame, mouseEvent->mRefPoint, &targets); |
848 | 0 | } |
849 | 0 | // TODO: Do other types of events need to be handled? |
850 | 0 |
|
851 | 0 | if (!targets.IsEmpty()) { |
852 | 0 | if (waitForRefresh) { |
853 | 0 | APZCCH_LOG("At least one target got a new displayport, need to wait for refresh\n"); |
854 | 0 | return MakeUnique<DisplayportSetListener>(aWidget, shell, aInputBlockId, std::move(targets)); |
855 | 0 | } |
856 | 0 | APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId); |
857 | 0 | aWidget->SetConfirmedTargetAPZC(aInputBlockId, targets); |
858 | 0 | } |
859 | 0 | } |
860 | 0 | } |
861 | 0 | return nullptr; |
862 | 0 | } |
863 | | |
864 | | void |
865 | | APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification( |
866 | | nsIWidget* aWidget, |
867 | | nsIDocument* aDocument, |
868 | | const WidgetTouchEvent& aEvent, |
869 | | uint64_t aInputBlockId, |
870 | | const SetAllowedTouchBehaviorCallback& aCallback) |
871 | 0 | { |
872 | 0 | if (nsIPresShell* shell = aDocument->GetShell()) { |
873 | 0 | if (nsIFrame* rootFrame = shell->GetRootFrame()) { |
874 | 0 | rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame); |
875 | 0 |
|
876 | 0 | nsTArray<TouchBehaviorFlags> flags; |
877 | 0 | for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) { |
878 | 0 | flags.AppendElement( |
879 | 0 | TouchActionHelper::GetAllowedTouchBehavior(aWidget, |
880 | 0 | rootFrame, aEvent.mTouches[i]->mRefPoint)); |
881 | 0 | } |
882 | 0 | aCallback(aInputBlockId, std::move(flags)); |
883 | 0 | } |
884 | 0 | } |
885 | 0 | } |
886 | | |
887 | | void |
888 | | APZCCallbackHelper::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent) |
889 | 0 | { |
890 | 0 | nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aScrollId); |
891 | 0 | if (!targetContent) { |
892 | 0 | return; |
893 | 0 | } |
894 | 0 | nsCOMPtr<nsIDocument> ownerDoc = targetContent->OwnerDoc(); |
895 | 0 | if (!ownerDoc) { |
896 | 0 | return; |
897 | 0 | } |
898 | 0 | |
899 | 0 | nsContentUtils::DispatchTrustedEvent( |
900 | 0 | ownerDoc, targetContent, |
901 | 0 | aEvent, |
902 | 0 | CanBubble::eYes, |
903 | 0 | Cancelable::eYes); |
904 | 0 | } |
905 | | |
906 | | void |
907 | | APZCCallbackHelper::NotifyFlushComplete(nsIPresShell* aShell) |
908 | 0 | { |
909 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
910 | 0 | // In some cases, flushing the APZ state to the main thread doesn't actually |
911 | 0 | // trigger a flush and repaint (this is an intentional optimization - the stuff |
912 | 0 | // visible to the user is still correct). However, reftests update their |
913 | 0 | // snapshot based on invalidation events that are emitted during paints, |
914 | 0 | // so we ensure that we kick off a paint when an APZ flush is done. Note that |
915 | 0 | // only chrome/testing code can trigger this behaviour. |
916 | 0 | if (aShell && aShell->GetRootFrame()) { |
917 | 0 | aShell->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DEFAULT, false); |
918 | 0 | } |
919 | 0 |
|
920 | 0 | nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); |
921 | 0 | MOZ_ASSERT(observerService); |
922 | 0 | observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr); |
923 | 0 | } |
924 | | |
925 | | /* static */ bool |
926 | | APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame) |
927 | 0 | { |
928 | 0 | return aFrame->IsProcessingAsyncScroll() |
929 | 0 | || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin()) |
930 | 0 | || aFrame->LastSmoothScrollOrigin(); |
931 | 0 | } |
932 | | |
933 | | /* static */ void |
934 | | APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) |
935 | 0 | { |
936 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
937 | 0 | if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aScrollId)) { |
938 | 0 | scrollFrame->AsyncScrollbarDragRejected(); |
939 | 0 | } |
940 | 0 | } |
941 | | |
942 | | /* static */ void |
943 | | APZCCallbackHelper::NotifyAsyncAutoscrollRejected(const FrameMetrics::ViewID& aScrollId) |
944 | 0 | { |
945 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
946 | 0 | nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); |
947 | 0 | MOZ_ASSERT(observerService); |
948 | 0 |
|
949 | 0 | nsAutoString data; |
950 | 0 | data.AppendInt(aScrollId); |
951 | 0 | observerService->NotifyObservers(nullptr, "autoscroll-rejected-by-apz", data.get()); |
952 | 0 | } |
953 | | |
954 | | /* static */ void |
955 | | APZCCallbackHelper::CancelAutoscroll(const FrameMetrics::ViewID& aScrollId) |
956 | 0 | { |
957 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
958 | 0 | nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); |
959 | 0 | MOZ_ASSERT(observerService); |
960 | 0 |
|
961 | 0 | nsAutoString data; |
962 | 0 | data.AppendInt(aScrollId); |
963 | 0 | observerService->NotifyObservers(nullptr, "apz:cancel-autoscroll", data.get()); |
964 | 0 | } |
965 | | |
966 | | /* static */ void |
967 | | APZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType, |
968 | | LayoutDeviceCoord aSpanChange, |
969 | | Modifiers aModifiers, |
970 | | nsIWidget* aWidget) |
971 | 0 | { |
972 | 0 | EventMessage msg; |
973 | 0 | switch (aType) { |
974 | 0 | case PinchGestureInput::PINCHGESTURE_START: |
975 | 0 | msg = eMagnifyGestureStart; |
976 | 0 | break; |
977 | 0 | case PinchGestureInput::PINCHGESTURE_SCALE: |
978 | 0 | msg = eMagnifyGestureUpdate; |
979 | 0 | break; |
980 | 0 | case PinchGestureInput::PINCHGESTURE_END: |
981 | 0 | msg = eMagnifyGesture; |
982 | 0 | break; |
983 | 0 | } |
984 | 0 | |
985 | 0 | WidgetSimpleGestureEvent event(true, msg, aWidget); |
986 | 0 | event.mDelta = aSpanChange; |
987 | 0 | event.mModifiers = aModifiers; |
988 | 0 | DispatchWidgetEvent(event); |
989 | 0 | } |
990 | | |
991 | | } // namespace layers |
992 | | } // namespace mozilla |
993 | | |