Swipe snapshot removed too early (jumps around) on arstechnica and NYT
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Mar 2015 19:02:15 +0000 (19:02 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Mar 2015 19:02:15 +0000 (19:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143199
<rdar://problem/18420467>

Reviewed by Dan Bernstein.

Make ViewGestureControllerIOS's snapshot removal timing behave more like the Mac version.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didCommitLayerTree:]):
Let ViewGestureController know when the page has finished state restoration.

(-[WKWebView _didFinishLoadForMainFrame]):
(-[WKWebView _didSameDocumentNavigationForMainFrame:]):
Forward these to ViewGestureController.

* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::didFinishLoadForMainFrame):
Forward didFinishLoadForMainFrame to WKWebView.

* UIProcess/ios/ViewGestureControllerIOS.mm:
(WebKit::ViewGestureController::ViewGestureController):
(WebKit::ViewGestureController::endSwipeGesture):
Split the swipe snapshot removal state into a bunch of bools.

(WebKit::ViewGestureController::willCommitPostSwipeTransitionLayerTree):
(WebKit::ViewGestureController::setRenderTreeSize):
(WebKit::ViewGestureController::removeSwipeSnapshotIfReady):
(WebKit::ViewGestureController::removeSwipeSnapshot):
Unify the snapshot removal logic into one function; removeSwipeSnapshotIfReady
completely owns the decision. For now, we're waiting for everything, but
we can do better in the future (using firstVisuallyNonEmptyLayout like Mac used to).

(WebKit::ViewGestureController::didRestoreScrollPosition):
(WebKit::ViewGestureController::didFinishLoadForMainFrame):
(WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame):
(WebKit::ViewGestureController::activeLoadMonitoringTimerFired):
Add more conditions for swipe snapshot removal. We'll now wait for
didFinishLoadForMainFrame (or didSameDocumentNavigationForMainFrame),
for active loads to finish, and for the scroll position to be restored.
This brings the iOS implementation in line with the slightly better Mac
implementation, and also sets the stage for deduplication of all of this code.

* UIProcess/mac/ViewGestureController.h:
* UIProcess/mac/ViewGestureControllerMac.mm:
(WebKit::ViewGestureController::ViewGestureController):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@182150 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewInternal.h
Source/WebKit2/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm
Source/WebKit2/UIProcess/mac/ViewGestureController.h
Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm

index 836eee7..cbda0b6 100644 (file)
@@ -1,3 +1,53 @@
+2015-03-30  Tim Horton  <timothy_horton@apple.com>
+
+        Swipe snapshot removed too early (jumps around) on arstechnica and NYT
+        https://bugs.webkit.org/show_bug.cgi?id=143199
+        <rdar://problem/18420467>
+
+        Reviewed by Dan Bernstein.
+
+        Make ViewGestureControllerIOS's snapshot removal timing behave more like the Mac version.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didCommitLayerTree:]):
+        Let ViewGestureController know when the page has finished state restoration.
+
+        (-[WKWebView _didFinishLoadForMainFrame]):
+        (-[WKWebView _didSameDocumentNavigationForMainFrame:]):
+        Forward these to ViewGestureController.
+
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::didFinishLoadForMainFrame):
+        Forward didFinishLoadForMainFrame to WKWebView.
+
+        * UIProcess/ios/ViewGestureControllerIOS.mm:
+        (WebKit::ViewGestureController::ViewGestureController):
+        (WebKit::ViewGestureController::endSwipeGesture):
+        Split the swipe snapshot removal state into a bunch of bools.
+
+        (WebKit::ViewGestureController::willCommitPostSwipeTransitionLayerTree):
+        (WebKit::ViewGestureController::setRenderTreeSize):
+        (WebKit::ViewGestureController::removeSwipeSnapshotIfReady):
+        (WebKit::ViewGestureController::removeSwipeSnapshot):
+        Unify the snapshot removal logic into one function; removeSwipeSnapshotIfReady
+        completely owns the decision. For now, we're waiting for everything, but
+        we can do better in the future (using firstVisuallyNonEmptyLayout like Mac used to).
+
+        (WebKit::ViewGestureController::didRestoreScrollPosition):
+        (WebKit::ViewGestureController::didFinishLoadForMainFrame):
+        (WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame):
+        (WebKit::ViewGestureController::activeLoadMonitoringTimerFired):
+        Add more conditions for swipe snapshot removal. We'll now wait for
+        didFinishLoadForMainFrame (or didSameDocumentNavigationForMainFrame),
+        for active loads to finish, and for the scroll position to be restored.
+        This brings the iOS implementation in line with the slightly better Mac
+        implementation, and also sets the stage for deduplication of all of this code.
+        
+        * UIProcess/mac/ViewGestureController.h:
+        * UIProcess/mac/ViewGestureControllerMac.mm:
+        (WebKit::ViewGestureController::ViewGestureController):
+
 2015-03-30  Chris Dumez  <cdumez@apple.com>
 
         [WK2][NetworkCache] Add support for "Cache-Control: max-stale" request header
index 364f220..3d6174d 100644 (file)
@@ -907,7 +907,9 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
         [self _updateVisibleContentRects];
     }
 
-    if (_needsToRestoreExposedRect && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
+    bool isTransactionAfterPageRestore = layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore;
+
+    if (_needsToRestoreExposedRect && isTransactionAfterPageRestore) {
         _needsToRestoreExposedRect = NO;
 
         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
@@ -915,11 +917,12 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
             exposedPosition.scale(_scaleToRestore, _scaleToRestore);
 
             changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
+            _gestureController->didRestoreScrollPosition();
         }
         [self _updateVisibleContentRects];
     }
 
-    if (_needsToRestoreUnobscuredCenter && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
+    if (_needsToRestoreUnobscuredCenter && isTransactionAfterPageRestore) {
         _needsToRestoreUnobscuredCenter = NO;
 
         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
@@ -931,6 +934,7 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
             topLeftInDocumentCoordinate.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
 
             changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinate);
+            _gestureController->didRestoreScrollPosition();
         }
         [self _updateVisibleContentRects];
     }
@@ -1496,9 +1500,18 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
         inStableState:isStableState isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
 }
 
+- (void)_didFinishLoadForMainFrame
+{
+    if (_gestureController)
+        _gestureController->didFinishLoadForMainFrame();
+}
+
 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
 {
     [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
+
+    if (_gestureController)
+        _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
 }
 
 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
index 9fccc64..509e8eb 100644 (file)
@@ -93,6 +93,7 @@ struct PrintInfo;
 
 - (void)_updateVisibleContentRects;
 
+- (void)_didFinishLoadForMainFrame;
 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType;
 
 - (BOOL)_isShowingVideoOptimized;
index 9b56aa4..2809179 100644 (file)
@@ -705,6 +705,7 @@ void PageClientImpl::didFirstVisuallyNonEmptyLayoutForMainFrame()
 
 void PageClientImpl::didFinishLoadForMainFrame()
 {
+    [m_webView _didFinishLoadForMainFrame];
 }
 
 void PageClientImpl::didSameDocumentNavigationForMainFrame(SameDocumentNavigationType navigationType)
index 435b01a..0dee931 100644 (file)
@@ -64,6 +64,7 @@ using namespace WebCore;
 
 static const float swipeSnapshotRemovalRenderTreeSizeTargetFraction = 0.5;
 static const std::chrono::seconds swipeSnapshotRemovalWatchdogDuration = 3_s;
+static const std::chrono::milliseconds swipeSnapshotRemovalActiveLoadMonitoringInterval = 250_ms;
 
 // The key in this map is the associated page ID.
 static HashMap<uint64_t, WebKit::ViewGestureController*>& viewGestureControllersForAllPages()
@@ -138,11 +139,8 @@ namespace WebKit {
 
 ViewGestureController::ViewGestureController(WebPageProxy& webPageProxy)
     : m_webPageProxy(webPageProxy)
-    , m_activeGestureType(ViewGestureType::None)
     , m_swipeWatchdogTimer(RunLoop::main(), this, &ViewGestureController::swipeSnapshotWatchdogTimerFired)
-    , m_snapshotRemovalTargetRenderTreeSize(0)
-    , m_shouldRemoveSnapshotWhenTargetRenderTreeSizeHit(false)
-    , m_gesturePendingSnapshotRemoval(0)
+    , m_swipeActiveLoadMonitoringTimer(RunLoop::main(), this, &ViewGestureController::activeLoadMonitoringTimerFired)
 {
     viewGestureControllersForAllPages().add(webPageProxy.pageID(), this);
 }
@@ -301,6 +299,12 @@ void ViewGestureController::endSwipeGesture(WebBackForwardListItem* targetItem,
         return;
     }
 
+    m_swipeWaitingForRenderTreeSizeThreshold = true;
+    m_swipeWaitingForRepaint = true;
+    m_swipeWaitingForDidFinishLoad = true;
+    m_swipeWaitingForSubresourceLoads = true;
+    m_swipeWaitingForScrollPositionRestoration = true;
+
     m_swipeWatchdogTimer.startOneShot(swipeSnapshotRemovalWatchdogDuration.count());
 }
 
@@ -314,7 +318,11 @@ void ViewGestureController::willCommitPostSwipeTransitionLayerTree(bool successf
         return;
     }
 
-    m_shouldRemoveSnapshotWhenTargetRenderTreeSizeHit = true;
+    if (!m_swipeWaitingForRepaint)
+        return;
+
+    m_swipeWaitingForRepaint = false;
+    removeSwipeSnapshotIfReady();
 }
 
 void ViewGestureController::setRenderTreeSize(uint64_t renderTreeSize)
@@ -322,11 +330,67 @@ void ViewGestureController::setRenderTreeSize(uint64_t renderTreeSize)
     if (m_activeGestureType != ViewGestureType::Swipe)
         return;
 
-    if (!m_shouldRemoveSnapshotWhenTargetRenderTreeSizeHit)
+    if (!m_swipeWaitingForRenderTreeSizeThreshold)
         return;
 
-    if (!m_snapshotRemovalTargetRenderTreeSize || renderTreeSize > m_snapshotRemovalTargetRenderTreeSize)
-        removeSwipeSnapshot();
+    if (!m_snapshotRemovalTargetRenderTreeSize || renderTreeSize > m_snapshotRemovalTargetRenderTreeSize) {
+        m_swipeWaitingForRenderTreeSizeThreshold = false;
+        removeSwipeSnapshotIfReady();
+    }
+}
+
+void ViewGestureController::didRestoreScrollPosition()
+{
+    if (m_activeGestureType != ViewGestureType::Swipe)
+        return;
+
+    if (!m_swipeWaitingForScrollPositionRestoration)
+        return;
+
+    m_swipeWaitingForScrollPositionRestoration = false;
+    removeSwipeSnapshotIfReady();
+}
+
+void ViewGestureController::didFinishLoadForMainFrame()
+{
+    if (m_activeGestureType != ViewGestureType::Swipe)
+        return;
+
+    if (!m_swipeWaitingForDidFinishLoad)
+        return;
+
+    m_swipeWaitingForDidFinishLoad = false;
+
+    if (m_webPageProxy.pageLoadState().isLoading()) {
+        m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval);
+        return;
+    }
+
+    m_swipeWaitingForSubresourceLoads = false;
+    removeSwipeSnapshotIfReady();
+}
+
+void ViewGestureController::didSameDocumentNavigationForMainFrame(SameDocumentNavigationType type)
+{
+    if (m_activeGestureType != ViewGestureType::Swipe)
+        return;
+
+    // This is nearly equivalent to didFinishLoad in the same document navigation case.
+    m_swipeWaitingForDidFinishLoad = false;
+
+    if (type != SameDocumentNavigationSessionStateReplace && type != SameDocumentNavigationSessionStatePop)
+        return;
+
+    m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval);
+}
+
+void ViewGestureController::activeLoadMonitoringTimerFired()
+{
+    if (m_webPageProxy.pageLoadState().isLoading())
+        return;
+
+    m_swipeWaitingForSubresourceLoads = false;
+    removeSwipeSnapshotIfReady();
 }
 
 void ViewGestureController::swipeSnapshotWatchdogTimerFired()
@@ -334,11 +398,24 @@ void ViewGestureController::swipeSnapshotWatchdogTimerFired()
     removeSwipeSnapshot();
 }
 
+void ViewGestureController::removeSwipeSnapshotIfReady()
+{
+    if (m_swipeWaitingForRenderTreeSizeThreshold || m_swipeWaitingForRepaint || m_swipeWaitingForDidFinishLoad || m_swipeWaitingForSubresourceLoads || m_swipeWaitingForScrollPositionRestoration)
+        return;
+
+    removeSwipeSnapshot();
+}
+
 void ViewGestureController::removeSwipeSnapshot()
 {
-    m_shouldRemoveSnapshotWhenTargetRenderTreeSizeHit = false;
+    m_swipeWaitingForRenderTreeSizeThreshold = false;
+    m_swipeWaitingForRepaint = false;
+    m_swipeWaitingForDidFinishLoad = false;
+    m_swipeWaitingForSubresourceLoads = false;
+    m_swipeWaitingForScrollPositionRestoration = false;
 
     m_swipeWatchdogTimer.stop();
+    m_swipeActiveLoadMonitoringTimer.stop();
 
     if (m_activeGestureType != ViewGestureType::Swipe)
         return;
index 7f379c7..f484862 100644 (file)
@@ -109,8 +109,6 @@ public:
     void setShouldIgnorePinnedState(bool ignore) { m_shouldIgnorePinnedState = ignore; }
 
     void didFirstVisuallyNonEmptyLayoutForMainFrame();
-    void didFinishLoadForMainFrame();
-    void didSameDocumentNavigationForMainFrame(SameDocumentNavigationType);
 #else
     void installSwipeHandler(UIView *gestureRecognizerView, UIView *swipingView);
     void setAlternateBackForwardListSourceView(WKWebView *);
@@ -119,15 +117,19 @@ public:
     void endSwipeGesture(WebBackForwardListItem* targetItem, _UIViewControllerTransitionContext *, bool cancelled);
     void willCommitPostSwipeTransitionLayerTree(bool);
     void setRenderTreeSize(uint64_t);
+    void didRestoreScrollPosition();
 #endif
 
+    void didFinishLoadForMainFrame();
     void removeSwipeSnapshot();
+    void didSameDocumentNavigationForMainFrame(SameDocumentNavigationType);
 
 private:
     // IPC::MessageReceiver.
     virtual void didReceiveMessage(IPC::Connection&, IPC::MessageDecoder&) override;
 
     void swipeSnapshotWatchdogTimerFired();
+    void activeLoadMonitoringTimerFired();
 
 #if PLATFORM(MAC)
     // Message handlers.
@@ -135,7 +137,6 @@ private:
     void didCollectGeometryForSmartMagnificationGesture(WebCore::FloatPoint origin, WebCore::FloatRect renderRect, WebCore::FloatRect visibleContentBounds, bool isReplacedElement, double viewportMinimumScale, double viewportMaximumScale);
     void didHitRenderTreeSizeThreshold();
     void removeSwipeSnapshotAfterRepaint();
-    void activeLoadMonitoringTimerFired();
 
     void endMagnificationGesture();
     WebCore::FloatPoint scaledMagnificationOrigin(WebCore::FloatPoint origin, double scale);
@@ -152,67 +153,71 @@ private:
     CALayer *determineLayerAdjacentToSnapshotForParent(SwipeDirection, CALayer *snapshotLayerParent) const;
     void applyDebuggingPropertiesToSwipeViews();
     void didMoveSwipeSnapshotLayer();
+#else
+    void removeSwipeSnapshotIfReady();
 #endif
 
     WebPageProxy& m_webPageProxy;
-    ViewGestureType m_activeGestureType;
+    ViewGestureType m_activeGestureType { ViewGestureType::None };
 
     RunLoop::Timer<ViewGestureController> m_swipeWatchdogTimer;
+    RunLoop::Timer<ViewGestureController> m_swipeActiveLoadMonitoringTimer;
 
 #if PLATFORM(MAC)
     RefPtr<ViewSnapshot> m_currentSwipeSnapshot;
 
     RunLoop::Timer<ViewGestureController> m_swipeWatchdogAfterFirstVisuallyNonEmptyLayoutTimer;
-    RunLoop::Timer<ViewGestureController> m_swipeActiveLoadMonitoringTimer;
 
     double m_magnification;
     WebCore::FloatPoint m_magnificationOrigin;
 
     WebCore::FloatRect m_lastSmartMagnificationUnscaledTargetRect;
-    bool m_lastMagnificationGestureWasSmartMagnification;
+    bool m_lastMagnificationGestureWasSmartMagnification { false };
     WebCore::FloatPoint m_lastSmartMagnificationOrigin;
 
     WebCore::FloatRect m_visibleContentRect;
-    bool m_visibleContentRectIsValid;
-    bool m_frameHandlesMagnificationGesture;
+    bool m_visibleContentRectIsValid { false };
+    bool m_frameHandlesMagnificationGesture { false };
 
     RetainPtr<WKSwipeCancellationTracker> m_swipeCancellationTracker;
     RetainPtr<CALayer> m_swipeLayer;
     RetainPtr<CALayer> m_swipeSnapshotLayer;
     Vector<RetainPtr<CALayer>> m_currentSwipeLiveLayers;
 
-    SwipeTransitionStyle m_swipeTransitionStyle;
+    SwipeTransitionStyle m_swipeTransitionStyle { SwipeTransitionStyle::Overlap };
     Vector<RetainPtr<NSView>> m_customSwipeViews;
-    float m_customSwipeViewsTopContentInset;
+    float m_customSwipeViewsTopContentInset { 0 };
     WebCore::FloatRect m_currentSwipeCustomViewBounds;
 
     // If we need to wait for content to decide if it is going to consume
     // the scroll event that would have started a swipe, we'll fill these in.
-    PendingSwipeReason m_pendingSwipeReason;
+    PendingSwipeReason m_pendingSwipeReason { PendingSwipeReason::None };
     SwipeDirection m_pendingSwipeDirection;
     WebCore::FloatSize m_cumulativeDeltaForPendingSwipe;
 
-    void (^m_didMoveSwipeSnapshotCallback)(CGRect);
+    void (^m_didMoveSwipeSnapshotCallback)(CGRect) { nullptr };
 
-    bool m_shouldIgnorePinnedState;
-
-    bool m_swipeWaitingForVisuallyNonEmptyLayout;
-    bool m_swipeWaitingForRenderTreeSizeThreshold;
-    bool m_swipeWaitingForRepaint;
-    bool m_swipeInProgress;
+    bool m_shouldIgnorePinnedState { false };
+    bool m_swipeInProgress { false };
 #else    
-    UIView *m_liveSwipeView;
+    UIView *m_liveSwipeView { nullptr };
     RetainPtr<UIView> m_liveSwipeViewClippingView;
     RetainPtr<UIView> m_snapshotView;
     RetainPtr<UIView> m_transitionContainerView;
     RetainPtr<WKSwipeTransitionController> m_swipeInteractiveTransitionDelegate;
     RetainPtr<_UIViewControllerOneToOneTransitionContext> m_swipeTransitionContext;
-    uint64_t m_snapshotRemovalTargetRenderTreeSize;
-    bool m_shouldRemoveSnapshotWhenTargetRenderTreeSizeHit;
+    uint64_t m_snapshotRemovalTargetRenderTreeSize { 0 };
     WeakObjCPtr<WKWebView> m_alternateBackForwardListSourceView;
     RefPtr<WebPageProxy> m_webPageProxyForBackForwardListForCurrentSwipe;
-    uint64_t m_gesturePendingSnapshotRemoval;
+    uint64_t m_gesturePendingSnapshotRemoval { 0 };
 #endif
+
+    bool m_swipeWaitingForVisuallyNonEmptyLayout { false };
+    bool m_swipeWaitingForRenderTreeSizeThreshold { false };
+    bool m_swipeWaitingForRepaint { false };
+    bool m_swipeWaitingForDidFinishLoad { false };
+    bool m_swipeWaitingForSubresourceLoads { false };
+    bool m_swipeWaitingForScrollPositionRestoration { false };
 };
 
 } // namespace WebKit
index b92d932..b5945ee 100644 (file)
@@ -86,22 +86,9 @@ namespace WebKit {
 
 ViewGestureController::ViewGestureController(WebPageProxy& webPageProxy)
     : m_webPageProxy(webPageProxy)
-    , m_activeGestureType(ViewGestureType::None)
     , m_swipeWatchdogTimer(RunLoop::main(), this, &ViewGestureController::swipeSnapshotWatchdogTimerFired)
-    , m_swipeWatchdogAfterFirstVisuallyNonEmptyLayoutTimer(RunLoop::main(), this, &ViewGestureController::swipeSnapshotWatchdogTimerFired)
     , m_swipeActiveLoadMonitoringTimer(RunLoop::main(), this, &ViewGestureController::activeLoadMonitoringTimerFired)
-    , m_lastMagnificationGestureWasSmartMagnification(false)
-    , m_visibleContentRectIsValid(false)
-    , m_frameHandlesMagnificationGesture(false)
-    , m_swipeTransitionStyle(SwipeTransitionStyle::Overlap)
-    , m_customSwipeViewsTopContentInset(0)
-    , m_pendingSwipeReason(PendingSwipeReason::None)
-    , m_didMoveSwipeSnapshotCallback(nullptr)
-    , m_shouldIgnorePinnedState(false)
-    , m_swipeWaitingForVisuallyNonEmptyLayout(false)
-    , m_swipeWaitingForRenderTreeSizeThreshold(false)
-    , m_swipeWaitingForRepaint(false)
-    , m_swipeInProgress(false)
+    , m_swipeWatchdogAfterFirstVisuallyNonEmptyLayoutTimer(RunLoop::main(), this, &ViewGestureController::swipeSnapshotWatchdogTimerFired)
 {
     m_webPageProxy.process().addMessageReceiver(Messages::ViewGestureController::messageReceiverName(), m_webPageProxy.pageID(), *this);
 }