1 | /* |
2 | * Copyright (C) 2013-2015 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "ViewGestureController.h" |
28 | |
29 | #include "APINavigation.h" |
30 | #include "Logging.h" |
31 | #include "ViewGestureControllerMessages.h" |
32 | #include "WebBackForwardList.h" |
33 | #include "WebFullScreenManagerProxy.h" |
34 | #include "WebPageProxy.h" |
35 | #include "WebProcessProxy.h" |
36 | #include <wtf/MathExtras.h> |
37 | #include <wtf/NeverDestroyed.h> |
38 | #include <wtf/text/StringBuilder.h> |
39 | #include <wtf/text/StringConcatenateNumbers.h> |
40 | |
41 | #if PLATFORM(COCOA) |
42 | #include "RemoteLayerTreeDrawingAreaProxy.h" |
43 | #endif |
44 | |
45 | #if !PLATFORM(IOS_FAMILY) |
46 | #include "ProvisionalPageProxy.h" |
47 | #include "ViewGestureGeometryCollectorMessages.h" |
48 | #endif |
49 | |
50 | namespace WebKit { |
51 | using namespace WebCore; |
52 | |
53 | static const Seconds swipeSnapshotRemovalWatchdogAfterFirstVisuallyNonEmptyLayoutDuration { 3_s }; |
54 | static const Seconds swipeSnapshotRemovalActiveLoadMonitoringInterval { 250_ms }; |
55 | |
56 | #if !PLATFORM(IOS_FAMILY) |
57 | static const Seconds swipeSnapshotRemovalWatchdogDuration = 5_s; |
58 | #else |
59 | static const Seconds swipeSnapshotRemovalWatchdogDuration = 3_s; |
60 | #endif |
61 | |
62 | #if !PLATFORM(IOS_FAMILY) |
63 | static const float minimumHorizontalSwipeDistance = 15; |
64 | static const float minimumScrollEventRatioForSwipe = 0.5; |
65 | |
66 | static const float swipeSnapshotRemovalRenderTreeSizeTargetFraction = 0.5; |
67 | #endif |
68 | |
69 | static HashMap<PageIdentifier, ViewGestureController*>& viewGestureControllersForAllPages() |
70 | { |
71 | // The key in this map is the associated page ID. |
72 | static NeverDestroyed<HashMap<PageIdentifier, ViewGestureController*>> viewGestureControllers; |
73 | return viewGestureControllers.get(); |
74 | } |
75 | |
76 | ViewGestureController::ViewGestureController(WebPageProxy& webPageProxy) |
77 | : m_webPageProxy(webPageProxy) |
78 | , m_swipeActiveLoadMonitoringTimer(RunLoop::main(), this, &ViewGestureController::checkForActiveLoads) |
79 | #if !PLATFORM(IOS_FAMILY) |
80 | , m_pendingSwipeTracker(webPageProxy, *this) |
81 | #endif |
82 | #if PLATFORM(GTK) |
83 | , m_swipeProgressTracker(webPageProxy, *this) |
84 | #endif |
85 | { |
86 | if (webPageProxy.hasRunningProcess()) |
87 | connectToProcess(); |
88 | |
89 | viewGestureControllersForAllPages().add(webPageProxy.pageID(), this); |
90 | } |
91 | |
92 | ViewGestureController::~ViewGestureController() |
93 | { |
94 | platformTeardown(); |
95 | |
96 | viewGestureControllersForAllPages().remove(m_webPageProxy.pageID()); |
97 | |
98 | disconnectFromProcess(); |
99 | } |
100 | |
101 | void ViewGestureController::disconnectFromProcess() |
102 | { |
103 | if (!m_isConnectedToProcess) |
104 | return; |
105 | |
106 | m_webPageProxy.process().removeMessageReceiver(Messages::ViewGestureController::messageReceiverName(), m_webPageProxy.pageID()); |
107 | m_isConnectedToProcess = false; |
108 | } |
109 | |
110 | void ViewGestureController::connectToProcess() |
111 | { |
112 | if (m_isConnectedToProcess) |
113 | return; |
114 | |
115 | m_webPageProxy.process().addMessageReceiver(Messages::ViewGestureController::messageReceiverName(), m_webPageProxy.pageID(), *this); |
116 | m_isConnectedToProcess = true; |
117 | } |
118 | |
119 | ViewGestureController* ViewGestureController::controllerForGesture(PageIdentifier pageID, ViewGestureController::GestureID gestureID) |
120 | { |
121 | auto gestureControllerIter = viewGestureControllersForAllPages().find(pageID); |
122 | if (gestureControllerIter == viewGestureControllersForAllPages().end()) |
123 | return nullptr; |
124 | if (gestureControllerIter->value->m_currentGestureID != gestureID) |
125 | return nullptr; |
126 | return gestureControllerIter->value; |
127 | } |
128 | |
129 | ViewGestureController::GestureID ViewGestureController::takeNextGestureID() |
130 | { |
131 | static GestureID nextGestureID; |
132 | return ++nextGestureID; |
133 | } |
134 | |
135 | void ViewGestureController::willBeginGesture(ViewGestureType type) |
136 | { |
137 | m_activeGestureType = type; |
138 | m_currentGestureID = takeNextGestureID(); |
139 | } |
140 | |
141 | void ViewGestureController::didEndGesture() |
142 | { |
143 | m_activeGestureType = ViewGestureType::None; |
144 | m_currentGestureID = 0; |
145 | } |
146 | |
147 | void ViewGestureController::setAlternateBackForwardListSourcePage(WebPageProxy* page) |
148 | { |
149 | m_alternateBackForwardListSourcePage = makeWeakPtr(page); |
150 | } |
151 | |
152 | bool ViewGestureController::canSwipeInDirection(SwipeDirection direction) const |
153 | { |
154 | if (!m_swipeGestureEnabled) |
155 | return false; |
156 | |
157 | #if ENABLE(FULLSCREEN_API) |
158 | if (m_webPageProxy.fullScreenManager() && m_webPageProxy.fullScreenManager()->isFullScreen()) |
159 | return false; |
160 | #endif |
161 | |
162 | RefPtr<WebPageProxy> alternateBackForwardListSourcePage = m_alternateBackForwardListSourcePage.get(); |
163 | auto& backForwardList = alternateBackForwardListSourcePage ? alternateBackForwardListSourcePage->backForwardList() : m_webPageProxy.backForwardList(); |
164 | if (direction == SwipeDirection::Back) |
165 | return !!backForwardList.backItem(); |
166 | return !!backForwardList.forwardItem(); |
167 | } |
168 | |
169 | void ViewGestureController::didStartProvisionalOrSameDocumentLoadForMainFrame() |
170 | { |
171 | m_snapshotRemovalTracker.resume(); |
172 | #if !PLATFORM(IOS_FAMILY) |
173 | requestRenderTreeSizeNotificationIfNeeded(); |
174 | #endif |
175 | |
176 | if (auto loadCallback = std::exchange(m_loadCallback, nullptr)) |
177 | loadCallback(); |
178 | } |
179 | |
180 | void ViewGestureController::didStartProvisionalLoadForMainFrame() |
181 | { |
182 | didStartProvisionalOrSameDocumentLoadForMainFrame(); |
183 | } |
184 | |
185 | void ViewGestureController::didFirstVisuallyNonEmptyLayoutForMainFrame() |
186 | { |
187 | if (!m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::VisuallyNonEmptyLayout)) |
188 | return; |
189 | |
190 | m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::MainFrameLoad); |
191 | m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::SubresourceLoads); |
192 | m_snapshotRemovalTracker.startWatchdog(swipeSnapshotRemovalWatchdogAfterFirstVisuallyNonEmptyLayoutDuration); |
193 | } |
194 | |
195 | void ViewGestureController::didRepaintAfterNavigation() |
196 | { |
197 | m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::RepaintAfterNavigation); |
198 | } |
199 | |
200 | void ViewGestureController::didHitRenderTreeSizeThreshold() |
201 | { |
202 | m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::RenderTreeSizeThreshold); |
203 | } |
204 | |
205 | void ViewGestureController::didRestoreScrollPosition() |
206 | { |
207 | m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::ScrollPositionRestoration); |
208 | } |
209 | |
210 | void ViewGestureController::didReachMainFrameLoadTerminalState() |
211 | { |
212 | if (m_snapshotRemovalTracker.isPaused() && m_snapshotRemovalTracker.hasRemovalCallback()) { |
213 | removeSwipeSnapshot(); |
214 | return; |
215 | } |
216 | |
217 | if (!m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::MainFrameLoad)) |
218 | return; |
219 | |
220 | // Coming back from the page cache will result in getting a load event, but no first visually non-empty layout. |
221 | // WebCore considers a loaded document enough to be considered visually non-empty, so that's good |
222 | // enough for us too. |
223 | m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::VisuallyNonEmptyLayout); |
224 | |
225 | checkForActiveLoads(); |
226 | } |
227 | |
228 | void ViewGestureController::didSameDocumentNavigationForMainFrame(SameDocumentNavigationType type) |
229 | { |
230 | didStartProvisionalOrSameDocumentLoadForMainFrame(); |
231 | |
232 | bool cancelledOutstandingEvent = false; |
233 | |
234 | // Same-document navigations don't have a main frame load or first visually non-empty layout. |
235 | cancelledOutstandingEvent |= m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::MainFrameLoad); |
236 | cancelledOutstandingEvent |= m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::VisuallyNonEmptyLayout); |
237 | |
238 | if (!cancelledOutstandingEvent) |
239 | return; |
240 | |
241 | if (type != SameDocumentNavigationSessionStateReplace && type != SameDocumentNavigationSessionStatePop) |
242 | return; |
243 | |
244 | checkForActiveLoads(); |
245 | } |
246 | |
247 | void ViewGestureController::checkForActiveLoads() |
248 | { |
249 | if (m_webPageProxy.pageLoadState().isLoading()) { |
250 | if (!m_swipeActiveLoadMonitoringTimer.isActive()) |
251 | m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval); |
252 | return; |
253 | } |
254 | |
255 | m_swipeActiveLoadMonitoringTimer.stop(); |
256 | m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::SubresourceLoads); |
257 | } |
258 | |
259 | ViewGestureController::SnapshotRemovalTracker::SnapshotRemovalTracker() |
260 | : m_watchdogTimer(RunLoop::main(), this, &SnapshotRemovalTracker::watchdogTimerFired) |
261 | { |
262 | } |
263 | |
264 | String ViewGestureController::SnapshotRemovalTracker::eventsDescription(Events event) |
265 | { |
266 | StringBuilder description; |
267 | |
268 | if (event & ViewGestureController::SnapshotRemovalTracker::VisuallyNonEmptyLayout) |
269 | description.append("VisuallyNonEmptyLayout " ); |
270 | |
271 | if (event & ViewGestureController::SnapshotRemovalTracker::RenderTreeSizeThreshold) |
272 | description.append("RenderTreeSizeThreshold " ); |
273 | |
274 | if (event & ViewGestureController::SnapshotRemovalTracker::RepaintAfterNavigation) |
275 | description.append("RepaintAfterNavigation " ); |
276 | |
277 | if (event & ViewGestureController::SnapshotRemovalTracker::MainFrameLoad) |
278 | description.append("MainFrameLoad " ); |
279 | |
280 | if (event & ViewGestureController::SnapshotRemovalTracker::SubresourceLoads) |
281 | description.append("SubresourceLoads " ); |
282 | |
283 | if (event & ViewGestureController::SnapshotRemovalTracker::ScrollPositionRestoration) |
284 | description.append("ScrollPositionRestoration " ); |
285 | |
286 | return description.toString(); |
287 | } |
288 | |
289 | |
290 | void ViewGestureController::SnapshotRemovalTracker::log(const String& log) const |
291 | { |
292 | RELEASE_LOG(ViewGestures, "Swipe Snapshot Removal (%0.2f ms) - %s" , (MonotonicTime::now() - m_startTime).milliseconds(), log.utf8().data()); |
293 | } |
294 | |
295 | void ViewGestureController::SnapshotRemovalTracker::resume() |
296 | { |
297 | if (isPaused() && m_outstandingEvents) |
298 | log("resume" ); |
299 | m_paused = false; |
300 | } |
301 | |
302 | void ViewGestureController::SnapshotRemovalTracker::start(Events desiredEvents, WTF::Function<void()>&& removalCallback) |
303 | { |
304 | m_outstandingEvents = desiredEvents; |
305 | m_removalCallback = WTFMove(removalCallback); |
306 | m_startTime = MonotonicTime::now(); |
307 | |
308 | log("start" ); |
309 | |
310 | startWatchdog(swipeSnapshotRemovalWatchdogDuration); |
311 | |
312 | // Initially start out paused; we'll resume when the load is committed. |
313 | // This avoids processing callbacks from earlier loads. |
314 | pause(); |
315 | } |
316 | |
317 | void ViewGestureController::SnapshotRemovalTracker::reset() |
318 | { |
319 | if (m_outstandingEvents) |
320 | log("reset; had outstanding events: " + eventsDescription(m_outstandingEvents)); |
321 | m_outstandingEvents = 0; |
322 | m_watchdogTimer.stop(); |
323 | m_removalCallback = nullptr; |
324 | } |
325 | |
326 | bool ViewGestureController::SnapshotRemovalTracker::stopWaitingForEvent(Events event, const String& logReason) |
327 | { |
328 | ASSERT(hasOneBitSet(event)); |
329 | |
330 | if (!(m_outstandingEvents & event)) |
331 | return false; |
332 | |
333 | if (isPaused()) { |
334 | log("is paused; ignoring event: " + eventsDescription(event)); |
335 | return false; |
336 | } |
337 | |
338 | log(logReason + eventsDescription(event)); |
339 | |
340 | m_outstandingEvents &= ~event; |
341 | |
342 | fireRemovalCallbackIfPossible(); |
343 | return true; |
344 | } |
345 | |
346 | bool ViewGestureController::SnapshotRemovalTracker::eventOccurred(Events event) |
347 | { |
348 | return stopWaitingForEvent(event, "outstanding event occurred: " ); |
349 | } |
350 | |
351 | bool ViewGestureController::SnapshotRemovalTracker::cancelOutstandingEvent(Events event) |
352 | { |
353 | return stopWaitingForEvent(event, "wait for event cancelled: " ); |
354 | } |
355 | |
356 | bool ViewGestureController::SnapshotRemovalTracker::hasOutstandingEvent(Event event) |
357 | { |
358 | return m_outstandingEvents & event; |
359 | } |
360 | |
361 | void ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackIfPossible() |
362 | { |
363 | if (m_outstandingEvents) { |
364 | log("deferring removal; had outstanding events: " + eventsDescription(m_outstandingEvents)); |
365 | return; |
366 | } |
367 | |
368 | fireRemovalCallbackImmediately(); |
369 | } |
370 | |
371 | void ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackImmediately() |
372 | { |
373 | m_watchdogTimer.stop(); |
374 | |
375 | auto removalCallback = WTFMove(m_removalCallback); |
376 | if (removalCallback) { |
377 | log("removing snapshot" ); |
378 | reset(); |
379 | removalCallback(); |
380 | } |
381 | } |
382 | |
383 | void ViewGestureController::SnapshotRemovalTracker::watchdogTimerFired() |
384 | { |
385 | log("watchdog timer fired" ); |
386 | fireRemovalCallbackImmediately(); |
387 | } |
388 | |
389 | void ViewGestureController::SnapshotRemovalTracker::startWatchdog(Seconds duration) |
390 | { |
391 | log(makeString("(re)started watchdog timer for " , FormattedNumber::fixedWidth(duration.seconds(), 1), " seconds" )); |
392 | m_watchdogTimer.startOneShot(duration); |
393 | } |
394 | |
395 | #if !PLATFORM(IOS_FAMILY) |
396 | static bool deltaShouldCancelSwipe(FloatSize delta) |
397 | { |
398 | return std::abs(delta.height()) >= std::abs(delta.width()) * minimumScrollEventRatioForSwipe; |
399 | } |
400 | |
401 | ViewGestureController::PendingSwipeTracker::PendingSwipeTracker(WebPageProxy& webPageProxy, ViewGestureController& viewGestureController) |
402 | : m_viewGestureController(viewGestureController) |
403 | , m_webPageProxy(webPageProxy) |
404 | { |
405 | } |
406 | |
407 | bool ViewGestureController::PendingSwipeTracker::scrollEventCanBecomeSwipe(PlatformScrollEvent event, ViewGestureController::SwipeDirection& potentialSwipeDirection) |
408 | { |
409 | if (!scrollEventCanStartSwipe(event) || !scrollEventCanInfluenceSwipe(event)) |
410 | return false; |
411 | |
412 | FloatSize size = scrollEventGetScrollingDeltas(event); |
413 | |
414 | if (deltaShouldCancelSwipe(size)) |
415 | return false; |
416 | |
417 | bool isPinnedToLeft = m_shouldIgnorePinnedState || m_webPageProxy.isPinnedToLeftSide(); |
418 | bool isPinnedToRight = m_shouldIgnorePinnedState || m_webPageProxy.isPinnedToRightSide(); |
419 | |
420 | bool tryingToSwipeBack = size.width() > 0 && isPinnedToLeft; |
421 | bool tryingToSwipeForward = size.width() < 0 && isPinnedToRight; |
422 | if (m_webPageProxy.userInterfaceLayoutDirection() != WebCore::UserInterfaceLayoutDirection::LTR) |
423 | std::swap(tryingToSwipeBack, tryingToSwipeForward); |
424 | |
425 | if (!tryingToSwipeBack && !tryingToSwipeForward) |
426 | return false; |
427 | |
428 | potentialSwipeDirection = tryingToSwipeBack ? SwipeDirection::Back : SwipeDirection::Forward; |
429 | return m_viewGestureController.canSwipeInDirection(potentialSwipeDirection); |
430 | } |
431 | |
432 | bool ViewGestureController::PendingSwipeTracker::handleEvent(PlatformScrollEvent event) |
433 | { |
434 | if (scrollEventCanEndSwipe(event)) { |
435 | reset("gesture ended" ); |
436 | return false; |
437 | } |
438 | |
439 | if (m_state == State::None) { |
440 | if (!scrollEventCanBecomeSwipe(event, m_direction)) |
441 | return false; |
442 | |
443 | if (!m_shouldIgnorePinnedState && m_webPageProxy.willHandleHorizontalScrollEvents()) { |
444 | m_state = State::WaitingForWebCore; |
445 | LOG(ViewGestures, "Swipe Start Hysteresis - waiting for WebCore to handle event" ); |
446 | } |
447 | } |
448 | |
449 | if (m_state == State::WaitingForWebCore) |
450 | return false; |
451 | |
452 | return tryToStartSwipe(event); |
453 | } |
454 | |
455 | void ViewGestureController::PendingSwipeTracker::eventWasNotHandledByWebCore(PlatformScrollEvent event) |
456 | { |
457 | if (m_state != State::WaitingForWebCore) |
458 | return; |
459 | |
460 | LOG(ViewGestures, "Swipe Start Hysteresis - WebCore didn't handle event" ); |
461 | m_state = State::None; |
462 | m_cumulativeDelta = FloatSize(); |
463 | tryToStartSwipe(event); |
464 | } |
465 | |
466 | bool ViewGestureController::PendingSwipeTracker::tryToStartSwipe(PlatformScrollEvent event) |
467 | { |
468 | ASSERT(m_state != State::WaitingForWebCore); |
469 | |
470 | if (m_state == State::None) { |
471 | SwipeDirection direction; |
472 | if (!scrollEventCanBecomeSwipe(event, direction)) |
473 | return false; |
474 | } |
475 | |
476 | if (!scrollEventCanInfluenceSwipe(event)) |
477 | return false; |
478 | |
479 | m_cumulativeDelta += scrollEventGetScrollingDeltas(event); |
480 | LOG(ViewGestures, "Swipe Start Hysteresis - consumed event, cumulative delta (%0.2f, %0.2f)" , m_cumulativeDelta.width(), m_cumulativeDelta.height()); |
481 | |
482 | if (deltaShouldCancelSwipe(m_cumulativeDelta)) { |
483 | reset("cumulative delta became too vertical" ); |
484 | return false; |
485 | } |
486 | |
487 | if (std::abs(m_cumulativeDelta.width()) >= minimumHorizontalSwipeDistance) |
488 | m_viewGestureController.startSwipeGesture(event, m_direction); |
489 | else |
490 | m_state = State::InsufficientMagnitude; |
491 | |
492 | return true; |
493 | } |
494 | |
495 | void ViewGestureController::PendingSwipeTracker::reset(const char* resetReasonForLogging) |
496 | { |
497 | if (m_state != State::None) |
498 | LOG(ViewGestures, "Swipe Start Hysteresis - reset; %s" , resetReasonForLogging); |
499 | |
500 | m_state = State::None; |
501 | m_cumulativeDelta = FloatSize(); |
502 | } |
503 | |
504 | void ViewGestureController::startSwipeGesture(PlatformScrollEvent event, SwipeDirection direction) |
505 | { |
506 | ASSERT(m_activeGestureType == ViewGestureType::None); |
507 | |
508 | m_pendingSwipeTracker.reset("starting to track swipe" ); |
509 | |
510 | m_webPageProxy.recordAutomaticNavigationSnapshot(); |
511 | |
512 | RefPtr<WebBackForwardListItem> targetItem = (direction == SwipeDirection::Back) ? m_webPageProxy.backForwardList().backItem() : m_webPageProxy.backForwardList().forwardItem(); |
513 | if (!targetItem) |
514 | return; |
515 | |
516 | trackSwipeGesture(event, direction, targetItem); |
517 | } |
518 | |
519 | bool ViewGestureController::isPhysicallySwipingLeft(SwipeDirection direction) const |
520 | { |
521 | bool isLTR = m_webPageProxy.userInterfaceLayoutDirection() == WebCore::UserInterfaceLayoutDirection::LTR; |
522 | bool isSwipingForward = direction == SwipeDirection::Forward; |
523 | return isLTR != isSwipingForward; |
524 | } |
525 | |
526 | bool ViewGestureController::shouldUseSnapshotForSize(ViewSnapshot& snapshot, FloatSize swipeLayerSize, float topContentInset) |
527 | { |
528 | float deviceScaleFactor = m_webPageProxy.deviceScaleFactor(); |
529 | if (snapshot.deviceScaleFactor() != deviceScaleFactor) |
530 | return false; |
531 | |
532 | FloatSize unobscuredSwipeLayerSizeInDeviceCoordinates = swipeLayerSize - FloatSize(0, topContentInset); |
533 | unobscuredSwipeLayerSizeInDeviceCoordinates.scale(deviceScaleFactor); |
534 | if (snapshot.size() != unobscuredSwipeLayerSizeInDeviceCoordinates) |
535 | return false; |
536 | |
537 | return true; |
538 | } |
539 | |
540 | void ViewGestureController::forceRepaintIfNeeded() |
541 | { |
542 | if (m_activeGestureType != ViewGestureType::Swipe) |
543 | return; |
544 | |
545 | if (m_hasOutstandingRepaintRequest) |
546 | return; |
547 | |
548 | m_hasOutstandingRepaintRequest = true; |
549 | |
550 | auto pageID = m_webPageProxy.pageID(); |
551 | GestureID gestureID = m_currentGestureID; |
552 | m_webPageProxy.forceRepaint(VoidCallback::create([pageID, gestureID] (CallbackBase::Error error) { |
553 | if (auto gestureController = controllerForGesture(pageID, gestureID)) |
554 | gestureController->removeSwipeSnapshot(); |
555 | })); |
556 | } |
557 | |
558 | void ViewGestureController::willEndSwipeGesture(WebBackForwardListItem& targetItem, bool cancelled) |
559 | { |
560 | m_webPageProxy.navigationGestureWillEnd(!cancelled, targetItem); |
561 | } |
562 | |
563 | void ViewGestureController::endSwipeGesture(WebBackForwardListItem* targetItem, bool cancelled) |
564 | { |
565 | ASSERT(m_activeGestureType == ViewGestureType::Swipe); |
566 | ASSERT(targetItem); |
567 | |
568 | #if PLATFORM(MAC) |
569 | m_swipeCancellationTracker = nullptr; |
570 | #endif |
571 | |
572 | if (cancelled) { |
573 | removeSwipeSnapshot(); |
574 | m_webPageProxy.navigationGestureDidEnd(false, *targetItem); |
575 | return; |
576 | } |
577 | |
578 | uint64_t renderTreeSize = 0; |
579 | if (ViewSnapshot* snapshot = targetItem->snapshot()) |
580 | renderTreeSize = snapshot->renderTreeSize(); |
581 | auto renderTreeSizeThreshold = renderTreeSize * swipeSnapshotRemovalRenderTreeSizeTargetFraction; |
582 | |
583 | m_webPageProxy.navigationGestureDidEnd(true, *targetItem); |
584 | m_webPageProxy.goToBackForwardItem(*targetItem); |
585 | |
586 | auto* currentItem = m_webPageProxy.backForwardList().currentItem(); |
587 | // The main frame will not be navigated so hide the snapshot right away. |
588 | if (currentItem && currentItem->itemIsClone(*targetItem)) { |
589 | removeSwipeSnapshot(); |
590 | return; |
591 | } |
592 | |
593 | SnapshotRemovalTracker::Events desiredEvents = SnapshotRemovalTracker::VisuallyNonEmptyLayout |
594 | | SnapshotRemovalTracker::MainFrameLoad |
595 | | SnapshotRemovalTracker::SubresourceLoads |
596 | | SnapshotRemovalTracker::ScrollPositionRestoration; |
597 | |
598 | if (renderTreeSizeThreshold) { |
599 | desiredEvents |= SnapshotRemovalTracker::RenderTreeSizeThreshold; |
600 | m_snapshotRemovalTracker.setRenderTreeSizeThreshold(renderTreeSizeThreshold); |
601 | } |
602 | |
603 | m_snapshotRemovalTracker.start(desiredEvents, [this] { this->forceRepaintIfNeeded(); }); |
604 | |
605 | // FIXME: Like on iOS, we should ensure that even if one of the timeouts fires, |
606 | // we never show the old page content, instead showing the snapshot background color. |
607 | |
608 | if (ViewSnapshot* snapshot = targetItem->snapshot()) |
609 | m_backgroundColorForCurrentSnapshot = snapshot->backgroundColor(); |
610 | } |
611 | |
612 | void ViewGestureController::requestRenderTreeSizeNotificationIfNeeded() |
613 | { |
614 | if (!m_snapshotRemovalTracker.hasOutstandingEvent(SnapshotRemovalTracker::RenderTreeSizeThreshold)) |
615 | return; |
616 | |
617 | auto& process = m_webPageProxy.provisionalPageProxy() ? m_webPageProxy.provisionalPageProxy()->process() : m_webPageProxy.process(); |
618 | auto threshold = m_snapshotRemovalTracker.renderTreeSizeThreshold(); |
619 | |
620 | process.send(Messages::ViewGestureGeometryCollector::SetRenderTreeSizeNotificationThreshold(threshold), m_webPageProxy.pageID()); |
621 | } |
622 | #endif |
623 | |
624 | } // namespace WebKit |
625 | |