<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[188521] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/188521">188521</a></dd>
<dt>Author</dt> <dd>timothy_horton@apple.com</dd>
<dt>Date</dt> <dd>2015-08-16 17:52:50 -0700 (Sun, 16 Aug 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Refactor ViewGestureController swipe snapshot removal to be more platform-independent
https://bugs.webkit.org/show_bug.cgi?id=148062

Reviewed by Dan Bernstein.

* Platform/Logging.h:
Add a ViewGestures logging channel.

* UIProcess/ViewGestureController.cpp: Added.
(viewGestureControllersForAllPages):
(WebKit::ViewGestureController::ViewGestureController):
(WebKit::ViewGestureController::~ViewGestureController):
(WebKit::ViewGestureController::gestureControllerForPage):
(WebKit::ViewGestureController::didFirstVisuallyNonEmptyLayoutForMainFrame):
(WebKit::ViewGestureController::didRepaintAfterNavigation):
(WebKit::ViewGestureController::didHitRenderTreeSizeThreshold):
(WebKit::ViewGestureController::didRestoreScrollPosition):
(WebKit::ViewGestureController::didReachMainFrameLoadTerminalState):
(WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame):
(WebKit::ViewGestureController::checkForActiveLoads):
(WebKit::ViewGestureController::SnapshotRemovalTracker::eventsDescription):
(WebKit::ViewGestureController::SnapshotRemovalTracker::SnapshotRemovalTracker):
(WebKit::ViewGestureController::SnapshotRemovalTracker::log):
(WebKit::ViewGestureController::SnapshotRemovalTracker::start):
(WebKit::ViewGestureController::SnapshotRemovalTracker::reset):
(WebKit::ViewGestureController::SnapshotRemovalTracker::eventOccurred):
(WebKit::ViewGestureController::SnapshotRemovalTracker::cancelOutstandingEvent):
(WebKit::ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackIfPossible):
(WebKit::ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackImmediately):
(WebKit::ViewGestureController::SnapshotRemovalTracker::watchdogTimerFired):
(WebKit::ViewGestureController::SnapshotRemovalTracker::startWatchdog):
Build a platform-independent SnapshotRemovalTracker, which keeps track of
various events that we wait for before removing the snapshot. This
is constructed from the union of ViewGestureController{IOS, Mac}'s snapshot
removal code, and each platform ViewGestureController can specify which
events to wait for (because this currently differs slightly).

Add logging to SnapshotRemovalTracker to make debugging snapshot removal
issues much easier.

* UIProcess/mac/ViewGestureController.h:
(WebKit::ViewGestureController::backgroundColorForCurrentSnapshot):
(WebKit::ViewGestureController::didFinishLoadForMainFrame):
(WebKit::ViewGestureController::didFailLoadForMainFrame):

* UIProcess/ios/ViewGestureControllerIOS.mm:
(WebKit::ViewGestureController::platformTeardown):
(WebKit::ViewGestureController::beginSwipeGesture):
(WebKit::ViewGestureController::endSwipeGesture):
(WebKit::ViewGestureController::setRenderTreeSize):
(WebKit::ViewGestureController::willCommitPostSwipeTransitionLayerTree):
(WebKit::ViewGestureController::removeSwipeSnapshot):
(viewGestureControllersForAllPages): Deleted.
(WebKit::ViewGestureController::ViewGestureController): Deleted.
(WebKit::ViewGestureController::~ViewGestureController): Deleted.
(WebKit::ViewGestureController::didRestoreScrollPosition): Deleted.
(WebKit::ViewGestureController::mainFrameLoadDidReachTerminalState): Deleted.
(WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame): Deleted.
(WebKit::ViewGestureController::activeLoadMonitoringTimerFired): Deleted.
(WebKit::ViewGestureController::swipeSnapshotWatchdogTimerFired): Deleted.
(WebKit::ViewGestureController::removeSwipeSnapshotIfReady): Deleted.
* UIProcess/mac/ViewGestureControllerMac.mm:
(WebKit::ViewGestureController::platformTeardown):
(WebKit::ViewGestureController::endSwipeGesture):
(WebKit::ViewGestureController::forceRepaintIfNeeded):
(WebKit::ViewGestureController::removeSwipeSnapshot):
(WebKit::ViewGestureController::ViewGestureController): Deleted.
(WebKit::ViewGestureController::~ViewGestureController): Deleted.
(WebKit::ViewGestureController::beginSwipeGesture): Deleted.
(WebKit::ViewGestureController::didHitRenderTreeSizeThreshold): Deleted.
(WebKit::ViewGestureController::didFirstVisuallyNonEmptyLayoutForMainFrame): Deleted.
(WebKit::ViewGestureController::mainFrameLoadDidReachTerminalState): Deleted.
(WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame): Deleted.
(WebKit::ViewGestureController::activeLoadMonitoringTimerFired): Deleted.
(WebKit::ViewGestureController::swipeSnapshotWatchdogTimerFired): Deleted.
(WebKit::ViewGestureController::removeSwipeSnapshotAfterRepaint): Deleted.
Move shareable snapshot removal code to a new platform-independent ViewGestureController file.
Move the ViewGestureController constructor/destructor to the platform-independent file.

* UIProcess/mac/ViewGestureController.messages.in:
* WebKit2.xcodeproj/project.pbxproj:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2PlatformLoggingh">trunk/Source/WebKit2/Platform/Logging.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosViewGestureControllerIOSmm">trunk/Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessmacViewGestureControllerh">trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessmacViewGestureControllermessagesin">trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.messages.in</a></li>
<li><a href="#trunkSourceWebKit2UIProcessmacViewGestureControllerMacmm">trunk/Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm</a></li>
<li><a href="#trunkSourceWebKit2WebKit2xcodeprojprojectpbxproj">trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2UIProcessViewGestureControllercpp">trunk/Source/WebKit2/UIProcess/ViewGestureController.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (188520 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-08-16 18:58:48 UTC (rev 188520)
+++ trunk/Source/WebKit2/ChangeLog        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -1,3 +1,87 @@
</span><ins>+2015-08-16  Timothy Horton  &lt;timothy_horton@apple.com&gt;
+
+        Refactor ViewGestureController swipe snapshot removal to be more platform-independent
+        https://bugs.webkit.org/show_bug.cgi?id=148062
+
+        Reviewed by Dan Bernstein.
+
+        * Platform/Logging.h:
+        Add a ViewGestures logging channel.
+
+        * UIProcess/ViewGestureController.cpp: Added.
+        (viewGestureControllersForAllPages):
+        (WebKit::ViewGestureController::ViewGestureController):
+        (WebKit::ViewGestureController::~ViewGestureController):
+        (WebKit::ViewGestureController::gestureControllerForPage):
+        (WebKit::ViewGestureController::didFirstVisuallyNonEmptyLayoutForMainFrame):
+        (WebKit::ViewGestureController::didRepaintAfterNavigation):
+        (WebKit::ViewGestureController::didHitRenderTreeSizeThreshold):
+        (WebKit::ViewGestureController::didRestoreScrollPosition):
+        (WebKit::ViewGestureController::didReachMainFrameLoadTerminalState):
+        (WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame):
+        (WebKit::ViewGestureController::checkForActiveLoads):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::eventsDescription):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::SnapshotRemovalTracker):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::log):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::start):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::reset):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::eventOccurred):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::cancelOutstandingEvent):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackIfPossible):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackImmediately):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::watchdogTimerFired):
+        (WebKit::ViewGestureController::SnapshotRemovalTracker::startWatchdog):
+        Build a platform-independent SnapshotRemovalTracker, which keeps track of
+        various events that we wait for before removing the snapshot. This
+        is constructed from the union of ViewGestureController{IOS, Mac}'s snapshot
+        removal code, and each platform ViewGestureController can specify which
+        events to wait for (because this currently differs slightly).
+
+        Add logging to SnapshotRemovalTracker to make debugging snapshot removal
+        issues much easier.
+
+        * UIProcess/mac/ViewGestureController.h:
+        (WebKit::ViewGestureController::backgroundColorForCurrentSnapshot):
+        (WebKit::ViewGestureController::didFinishLoadForMainFrame):
+        (WebKit::ViewGestureController::didFailLoadForMainFrame):
+
+        * UIProcess/ios/ViewGestureControllerIOS.mm:
+        (WebKit::ViewGestureController::platformTeardown):
+        (WebKit::ViewGestureController::beginSwipeGesture):
+        (WebKit::ViewGestureController::endSwipeGesture):
+        (WebKit::ViewGestureController::setRenderTreeSize):
+        (WebKit::ViewGestureController::willCommitPostSwipeTransitionLayerTree):
+        (WebKit::ViewGestureController::removeSwipeSnapshot):
+        (viewGestureControllersForAllPages): Deleted.
+        (WebKit::ViewGestureController::ViewGestureController): Deleted.
+        (WebKit::ViewGestureController::~ViewGestureController): Deleted.
+        (WebKit::ViewGestureController::didRestoreScrollPosition): Deleted.
+        (WebKit::ViewGestureController::mainFrameLoadDidReachTerminalState): Deleted.
+        (WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame): Deleted.
+        (WebKit::ViewGestureController::activeLoadMonitoringTimerFired): Deleted.
+        (WebKit::ViewGestureController::swipeSnapshotWatchdogTimerFired): Deleted.
+        (WebKit::ViewGestureController::removeSwipeSnapshotIfReady): Deleted.
+        * UIProcess/mac/ViewGestureControllerMac.mm:
+        (WebKit::ViewGestureController::platformTeardown):
+        (WebKit::ViewGestureController::endSwipeGesture):
+        (WebKit::ViewGestureController::forceRepaintIfNeeded):
+        (WebKit::ViewGestureController::removeSwipeSnapshot):
+        (WebKit::ViewGestureController::ViewGestureController): Deleted.
+        (WebKit::ViewGestureController::~ViewGestureController): Deleted.
+        (WebKit::ViewGestureController::beginSwipeGesture): Deleted.
+        (WebKit::ViewGestureController::didHitRenderTreeSizeThreshold): Deleted.
+        (WebKit::ViewGestureController::didFirstVisuallyNonEmptyLayoutForMainFrame): Deleted.
+        (WebKit::ViewGestureController::mainFrameLoadDidReachTerminalState): Deleted.
+        (WebKit::ViewGestureController::didSameDocumentNavigationForMainFrame): Deleted.
+        (WebKit::ViewGestureController::activeLoadMonitoringTimerFired): Deleted.
+        (WebKit::ViewGestureController::swipeSnapshotWatchdogTimerFired): Deleted.
+        (WebKit::ViewGestureController::removeSwipeSnapshotAfterRepaint): Deleted.
+        Move shareable snapshot removal code to a new platform-independent ViewGestureController file.
+        Move the ViewGestureController constructor/destructor to the platform-independent file.
+
+        * UIProcess/mac/ViewGestureController.messages.in:
+        * WebKit2.xcodeproj/project.pbxproj:
+
</ins><span class="cx"> 2015-08-11  Andy Estes  &lt;aestes@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Cocoa] Add redirect support to CustomProtocolManager
</span></span></pre></div>
<a id="trunkSourceWebKit2PlatformLoggingh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Platform/Logging.h (188520 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Platform/Logging.h        2015-08-16 18:58:48 UTC (rev 188520)
+++ trunk/Source/WebKit2/Platform/Logging.h        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx">     M(SessionState) \
</span><span class="cx">     M(StorageAPI) \
</span><span class="cx">     M(TextInput) \
</span><ins>+    M(ViewGestures) \
</ins><span class="cx"> 
</span><span class="cx"> #define DECLARE_LOG_CHANNEL(name) \
</span><span class="cx">     extern WTFLogChannel JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, name);
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessViewGestureControllercpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebKit2/UIProcess/ViewGestureController.cpp (0 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ViewGestureController.cpp                                (rev 0)
+++ trunk/Source/WebKit2/UIProcess/ViewGestureController.cpp        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -0,0 +1,274 @@
</span><ins>+/*
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import &quot;config.h&quot;
+#import &quot;ViewGestureController.h&quot;
+
+#import &quot;Logging.h&quot;
+#import &quot;ViewGestureControllerMessages.h&quot;
+#import &quot;WebPageProxy.h&quot;
+#import &quot;WebProcessProxy.h&quot;
+#import &lt;wtf/MathExtras.h&gt;
+
+using namespace WebCore;
+
+namespace WebKit {
+
+static const std::chrono::seconds swipeSnapshotRemovalWatchdogAfterFirstVisuallyNonEmptyLayoutDuration = 3_s;
+static const std::chrono::milliseconds swipeSnapshotRemovalActiveLoadMonitoringInterval = 250_ms;
+
+#if PLATFORM(MAC)
+static const std::chrono::seconds swipeSnapshotRemovalWatchdogDuration = 5_s;
+#else
+static const std::chrono::seconds swipeSnapshotRemovalWatchdogDuration = 3_s;
+#endif
+
+static HashMap&lt;uint64_t, ViewGestureController*&gt;&amp; viewGestureControllersForAllPages()
+{
+    // The key in this map is the associated page ID.
+    static NeverDestroyed&lt;HashMap&lt;uint64_t, ViewGestureController*&gt;&gt; viewGestureControllers;
+    return viewGestureControllers.get();
+}
+
+
+ViewGestureController::ViewGestureController(WebPageProxy&amp; webPageProxy)
+    : m_webPageProxy(webPageProxy)
+    , m_swipeActiveLoadMonitoringTimer(RunLoop::main(), this, &amp;ViewGestureController::checkForActiveLoads)
+{
+    m_webPageProxy.process().addMessageReceiver(Messages::ViewGestureController::messageReceiverName(), m_webPageProxy.pageID(), *this);
+
+    viewGestureControllersForAllPages().add(webPageProxy.pageID(), this);
+}
+
+ViewGestureController::~ViewGestureController()
+{
+    platformTeardown();
+
+    viewGestureControllersForAllPages().remove(m_webPageProxy.pageID());
+
+    m_webPageProxy.process().removeMessageReceiver(Messages::ViewGestureController::messageReceiverName(), m_webPageProxy.pageID());
+}
+
+ViewGestureController* ViewGestureController::gestureControllerForPage(uint64_t pageID)
+{
+    auto gestureControllerIter = viewGestureControllersForAllPages().find(pageID);
+    if (gestureControllerIter == viewGestureControllersForAllPages().end())
+        return nullptr;
+    return gestureControllerIter-&gt;value;
+}
+
+void ViewGestureController::didFirstVisuallyNonEmptyLayoutForMainFrame()
+{
+    if (!m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::VisuallyNonEmptyLayout))
+        return;
+
+    m_snapshotRemovalTracker.startWatchdog(swipeSnapshotRemovalWatchdogAfterFirstVisuallyNonEmptyLayoutDuration);
+
+    // FIXME: Ideally, this would be sufficient to cancel waiting for the main frame load,
+    // and would make snapshot removal faster, but we need didRestoreScrollPosition
+    // to be usable/accurate on all platforms before doing that, or we get a flash
+    // of the new page in the wrong position.
+}
+
+void ViewGestureController::didRepaintAfterNavigation()
+{
+    m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::RepaintAfterNavigation);
+}
+
+void ViewGestureController::didHitRenderTreeSizeThreshold()
+{
+    m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::RenderTreeSizeThreshold);
+}
+
+void ViewGestureController::didRestoreScrollPosition()
+{
+    m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::ScrollPositionRestoration);
+}
+
+void ViewGestureController::didReachMainFrameLoadTerminalState()
+{
+    if (!m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::MainFrameLoad))
+        return;
+
+    // Coming back from the page cache will result in getting a load event, but no first visually non-empty layout.
+    // WebCore considers a loaded document enough to be considered visually non-empty, so that's good
+    // enough for us too.
+    m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::VisuallyNonEmptyLayout);
+
+    checkForActiveLoads();
+}
+
+void ViewGestureController::didSameDocumentNavigationForMainFrame(SameDocumentNavigationType type)
+{
+    bool cancelledOutstandingEvent = false;
+
+    // Same-document navigations don't have a main frame load or first visually non-empty layout.
+    cancelledOutstandingEvent |= m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::MainFrameLoad);
+    cancelledOutstandingEvent |= m_snapshotRemovalTracker.cancelOutstandingEvent(SnapshotRemovalTracker::VisuallyNonEmptyLayout);
+
+    if (!cancelledOutstandingEvent)
+        return;
+
+    if (type != SameDocumentNavigationSessionStateReplace &amp;&amp; type != SameDocumentNavigationSessionStatePop)
+        return;
+
+    checkForActiveLoads();
+}
+
+void ViewGestureController::checkForActiveLoads()
+{
+    if (m_webPageProxy.pageLoadState().isLoading()) {
+        if (!m_swipeActiveLoadMonitoringTimer.isActive())
+            m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval);
+        return;
+    }
+
+    m_swipeActiveLoadMonitoringTimer.stop();
+    m_snapshotRemovalTracker.eventOccurred(SnapshotRemovalTracker::SubresourceLoads);
+}
+
+ViewGestureController::SnapshotRemovalTracker::SnapshotRemovalTracker()
+    : m_watchdogTimer(RunLoop::main(), this, &amp;SnapshotRemovalTracker::watchdogTimerFired)
+{
+}
+
+String ViewGestureController::SnapshotRemovalTracker::eventsDescription(Events event)
+{
+    StringBuilder description;
+
+    if (event &amp; ViewGestureController::SnapshotRemovalTracker::VisuallyNonEmptyLayout)
+        description.append(&quot;VisuallyNonEmptyLayout &quot;);
+
+    if (event &amp; ViewGestureController::SnapshotRemovalTracker::RenderTreeSizeThreshold)
+        description.append(&quot;RenderTreeSizeThreshold &quot;);
+
+    if (event &amp; ViewGestureController::SnapshotRemovalTracker::RepaintAfterNavigation)
+        description.append(&quot;RepaintAfterNavigation &quot;);
+
+    if (event &amp; ViewGestureController::SnapshotRemovalTracker::MainFrameLoad)
+        description.append(&quot;MainFrameLoad &quot;);
+
+    if (event &amp; ViewGestureController::SnapshotRemovalTracker::SubresourceLoads)
+        description.append(&quot;SubresourceLoads &quot;);
+
+    if (event &amp; ViewGestureController::SnapshotRemovalTracker::ScrollPositionRestoration)
+        description.append(&quot;ScrollPositionRestoration &quot;);
+    
+    return description.toString();
+}
+
+
+void ViewGestureController::SnapshotRemovalTracker::log(const String&amp; log) const
+{
+#if !LOG_DISABLED
+    auto now = std::chrono::steady_clock::now();
+    double millisecondsSinceStart = std::chrono::duration_cast&lt;std::chrono::duration&lt;double, std::milli&gt;&gt;(now - m_startTime).count();
+#endif
+    LOG(ViewGestures, &quot;Swipe Snapshot Removal (%0.2f ms) - %s&quot;, millisecondsSinceStart, log.utf8().data());
+}
+
+void ViewGestureController::SnapshotRemovalTracker::start(Events desiredEvents, std::function&lt;void()&gt; removalCallback)
+{
+    m_outstandingEvents = desiredEvents;
+    m_removalCallback = WTF::move(removalCallback);
+    m_startTime = std::chrono::steady_clock::now();
+
+    log(&quot;start&quot;);
+
+    startWatchdog(swipeSnapshotRemovalWatchdogDuration);
+}
+
+void ViewGestureController::SnapshotRemovalTracker::reset()
+{
+    if (m_outstandingEvents)
+        log(&quot;reset; had outstanding events: &quot; + eventsDescription(m_outstandingEvents));
+    m_outstandingEvents = 0;
+    m_watchdogTimer.stop();
+    m_removalCallback = nullptr;
+}
+
+bool ViewGestureController::SnapshotRemovalTracker::stopWaitingForEvent(Events event, const String&amp; logReason)
+{
+    ASSERT(hasOneBitSet(event));
+
+    if (!(m_outstandingEvents &amp; event))
+        return false;
+
+#if LOG_DISABLED
+    UNUSED_PARAM(logReason);
+#endif
+    log(logReason +  eventsDescription(event));
+
+    m_outstandingEvents &amp;= ~event;
+
+    fireRemovalCallbackIfPossible();
+    return true;
+}
+
+bool ViewGestureController::SnapshotRemovalTracker::eventOccurred(Events event)
+{
+    return stopWaitingForEvent(event, &quot;outstanding event occurred: &quot;);
+}
+
+bool ViewGestureController::SnapshotRemovalTracker::cancelOutstandingEvent(Events event)
+{
+    return stopWaitingForEvent(event, &quot;wait for event cancelled: &quot;);
+}
+
+void ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackIfPossible()
+{
+    if (m_outstandingEvents) {
+        log(&quot;deferring removal; had outstanding events: &quot; + eventsDescription(m_outstandingEvents));
+        return;
+    }
+
+    fireRemovalCallbackImmediately();
+}
+
+void ViewGestureController::SnapshotRemovalTracker::fireRemovalCallbackImmediately()
+{
+    m_watchdogTimer.stop();
+
+    auto removalCallback = WTF::move(m_removalCallback);
+    if (removalCallback) {
+        log(&quot;removing snapshot&quot;);
+        reset();
+        removalCallback();
+    }
+}
+
+void ViewGestureController::SnapshotRemovalTracker::watchdogTimerFired()
+{
+    log(&quot;watchdog timer fired&quot;);
+    fireRemovalCallbackImmediately();
+}
+
+void ViewGestureController::SnapshotRemovalTracker::startWatchdog(std::chrono::seconds duration)
+{
+    log(String::format(&quot;(re)started watchdog timer for %lld seconds&quot;, duration.count()));
+    m_watchdogTimer.startOneShot(duration.count());
+}
+
+} // namespace WebKit
</ins></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosViewGestureControllerIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm (188520 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm        2015-08-16 18:58:48 UTC (rev 188520)
+++ trunk/Source/WebKit2/UIProcess/ios/ViewGestureControllerIOS.mm        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -64,16 +64,7 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static const float swipeSnapshotRemovalRenderTreeSizeTargetFraction = 0.5;
</span><del>-static const std::chrono::seconds swipeSnapshotRemovalWatchdogDuration = 3_s;
-static const std::chrono::milliseconds swipeSnapshotRemovalActiveLoadMonitoringInterval = 250_ms;
</del><span class="cx"> 
</span><del>-// The key in this map is the associated page ID.
-static HashMap&lt;uint64_t, WebKit::ViewGestureController*&gt;&amp; viewGestureControllersForAllPages()
-{
-    static NeverDestroyed&lt;HashMap&lt;uint64_t, WebKit::ViewGestureController*&gt;&gt; viewGestureControllers;
-    return viewGestureControllers.get();
-}
-
</del><span class="cx"> - (instancetype)initWithViewGestureController:(WebKit::ViewGestureController*)gestureController gestureRecognizerView:(UIView *)gestureRecognizerView
</span><span class="cx"> {
</span><span class="cx">     self = [super init];
</span><span class="lines">@@ -146,21 +137,12 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><del>-ViewGestureController::ViewGestureController(WebPageProxy&amp; webPageProxy)
-    : m_webPageProxy(webPageProxy)
-    , m_swipeWatchdogTimer(RunLoop::main(), this, &amp;ViewGestureController::swipeSnapshotWatchdogTimerFired)
-    , m_swipeActiveLoadMonitoringTimer(RunLoop::main(), this, &amp;ViewGestureController::activeLoadMonitoringTimerFired)
</del><ins>+void ViewGestureController::platformTeardown()
</ins><span class="cx"> {
</span><del>-    viewGestureControllersForAllPages().add(webPageProxy.pageID(), this);
-}
-
-ViewGestureController::~ViewGestureController()
-{
</del><span class="cx">     [m_swipeTransitionContext _setTransitionIsInFlight:NO];
</span><span class="cx">     [m_swipeTransitionContext _setInteractor:nil];
</span><span class="cx">     [m_swipeTransitionContext _setAnimator:nil];
</span><span class="cx">     [m_swipeInteractiveTransitionDelegate invalidate];
</span><del>-    viewGestureControllersForAllPages().remove(m_webPageProxy.pageID());
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ViewGestureController::setAlternateBackForwardListSourceView(WKWebView *view)
</span><span class="lines">@@ -249,9 +231,8 @@
</span><span class="cx">     }];
</span><span class="cx">     uint64_t pageID = m_webPageProxy.pageID();
</span><span class="cx">     [m_swipeTransitionContext _setCompletionHandler:^(_UIViewControllerTransitionContext *context, BOOL didComplete) {
</span><del>-        auto gestureControllerIter = viewGestureControllersForAllPages().find(pageID);
-        if (gestureControllerIter != viewGestureControllersForAllPages().end())
-            gestureControllerIter-&gt;value-&gt;endSwipeGesture(targetItem.get(), context, !didComplete);
</del><ins>+        if (auto gestureController = gestureControllerForPage(pageID))
+            gestureController-&gt;endSwipeGesture(targetItem.get(), context, !didComplete);
</ins><span class="cx">     }];
</span><span class="cx">     [m_swipeTransitionContext _setInteractiveUpdateHandler:^(BOOL, CGFloat, BOOL, _UIViewControllerTransitionContext *) { }];
</span><span class="cx"> 
</span><span class="lines">@@ -306,9 +287,10 @@
</span><span class="cx">         uint64_t pageID = m_webPageProxy.pageID();
</span><span class="cx">         uint64_t gesturePendingSnapshotRemoval = m_gesturePendingSnapshotRemoval;
</span><span class="cx">         drawingArea-&gt;dispatchAfterEnsuringDrawing([pageID, gesturePendingSnapshotRemoval] (CallbackBase::Error error) {
</span><del>-            auto gestureControllerIter = viewGestureControllersForAllPages().find(pageID);
-            if (gestureControllerIter != viewGestureControllersForAllPages().end() &amp;&amp; gestureControllerIter-&gt;value-&gt;m_gesturePendingSnapshotRemoval == gesturePendingSnapshotRemoval)
-                gestureControllerIter-&gt;value-&gt;willCommitPostSwipeTransitionLayerTree(error == CallbackBase::Error::None);
</del><ins>+            if (auto gestureController = gestureControllerForPage(pageID)) {
+                if (gestureController-&gt;m_gesturePendingSnapshotRemoval == gesturePendingSnapshotRemoval)
+                    gestureController-&gt;willCommitPostSwipeTransitionLayerTree(error == CallbackBase::Error::None);
+            }
</ins><span class="cx">         });
</span><span class="cx">         drawingArea-&gt;hideContentUntilPendingUpdate();
</span><span class="cx">     } else {
</span><span class="lines">@@ -316,140 +298,52 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    m_swipeWaitingForRenderTreeSizeThreshold = true;
-    m_swipeWaitingForRepaint = true;
-    m_swipeWaitingForTerminalLoadingState = true;
-    m_swipeWaitingForSubresourceLoads = true;
-    m_swipeWaitingForScrollPositionRestoration = true;
</del><ins>+    // FIXME: Should we wait for VisuallyNonEmptyLayout like we do on Mac?
+    m_snapshotRemovalTracker.start(SnapshotRemovalTracker::RenderTreeSizeThreshold
+        | SnapshotRemovalTracker::RepaintAfterNavigation
+        | SnapshotRemovalTracker::MainFrameLoad
+        | SnapshotRemovalTracker::SubresourceLoads
+        | SnapshotRemovalTracker::ScrollPositionRestoration, [this] {
+            this-&gt;removeSwipeSnapshot();
+    });
</ins><span class="cx"> 
</span><del>-    m_swipeWatchdogTimer.startOneShot(swipeSnapshotRemovalWatchdogDuration.count());
-
</del><span class="cx">     if (ViewSnapshot* snapshot = targetItem-&gt;snapshot()) {
</span><span class="cx">         m_backgroundColorForCurrentSnapshot = snapshot-&gt;backgroundColor();
</span><span class="cx">         m_webPageProxy.didChangeBackgroundColor();
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ViewGestureController::willCommitPostSwipeTransitionLayerTree(bool successful)
-{
-    if (m_activeGestureType != ViewGestureType::Swipe)
-        return;
-
-    if (!successful) {
-        removeSwipeSnapshot();
-        return;
-    }
-
-    if (!m_swipeWaitingForRepaint)
-        return;
-
-    m_swipeWaitingForRepaint = false;
-    removeSwipeSnapshotIfReady();
-}
-
</del><span class="cx"> void ViewGestureController::setRenderTreeSize(uint64_t renderTreeSize)
</span><span class="cx"> {
</span><span class="cx">     if (m_activeGestureType != ViewGestureType::Swipe)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    if (!m_swipeWaitingForRenderTreeSizeThreshold)
-        return;
-
-    if (!m_snapshotRemovalTargetRenderTreeSize || renderTreeSize &gt; m_snapshotRemovalTargetRenderTreeSize) {
-        m_swipeWaitingForRenderTreeSizeThreshold = false;
-        removeSwipeSnapshotIfReady();
-    }
</del><ins>+    if (!m_snapshotRemovalTargetRenderTreeSize || renderTreeSize &gt; m_snapshotRemovalTargetRenderTreeSize)
+        didHitRenderTreeSizeThreshold();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ViewGestureController::didRestoreScrollPosition()
</del><ins>+void ViewGestureController::willCommitPostSwipeTransitionLayerTree(bool successful)
</ins><span class="cx"> {
</span><span class="cx">     if (m_activeGestureType != ViewGestureType::Swipe)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    if (!m_swipeWaitingForScrollPositionRestoration)
</del><ins>+    if (!successful) {
+        removeSwipeSnapshot();
</ins><span class="cx">         return;
</span><del>-
-    m_swipeWaitingForScrollPositionRestoration = false;
-    removeSwipeSnapshotIfReady();
-}
-
-void ViewGestureController::mainFrameLoadDidReachTerminalState()
-{
-    if (m_activeGestureType != ViewGestureType::Swipe)
-        return;
-
-    if (!m_swipeWaitingForTerminalLoadingState)
-        return;
-
-    m_swipeWaitingForTerminalLoadingState = false;
-
-    if (m_webPageProxy.pageLoadState().isLoading()) {
-        m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval);
-        return;
</del><span class="cx">     }
</span><span class="cx"> 
</span><del>-    m_swipeWaitingForSubresourceLoads = false;
-    removeSwipeSnapshotIfReady();
</del><ins>+    didRepaintAfterNavigation();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ViewGestureController::didSameDocumentNavigationForMainFrame(SameDocumentNavigationType type)
-{
-    if (m_activeGestureType != ViewGestureType::Swipe)
-        return;
-
-    // This is nearly equivalent to didFinishLoad in the same document navigation case.
-    if (!m_swipeWaitingForTerminalLoadingState)
-        return;
-
-    m_swipeWaitingForTerminalLoadingState = false;
-
-    if (type != SameDocumentNavigationSessionStateReplace &amp;&amp; type != SameDocumentNavigationSessionStatePop)
-        return;
-
-    m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval);
-}
-
-void ViewGestureController::activeLoadMonitoringTimerFired()
-{
-    if (m_webPageProxy.pageLoadState().isLoading())
-        return;
-
-    if (!m_swipeWaitingForSubresourceLoads)
-        return;
-
-    m_swipeWaitingForSubresourceLoads = false;
-    removeSwipeSnapshotIfReady();
-}
-
-void ViewGestureController::swipeSnapshotWatchdogTimerFired()
-{
-    removeSwipeSnapshot();
-}
-
-void ViewGestureController::removeSwipeSnapshotIfReady()
-{
-    if (m_swipeWaitingForRenderTreeSizeThreshold || m_swipeWaitingForRepaint || m_swipeWaitingForTerminalLoadingState || m_swipeWaitingForSubresourceLoads || m_swipeWaitingForScrollPositionRestoration)
-        return;
-
-    removeSwipeSnapshot();
-}
-
</del><span class="cx"> void ViewGestureController::removeSwipeSnapshot()
</span><span class="cx"> {
</span><del>-    m_swipeWaitingForRenderTreeSizeThreshold = false;
-    m_swipeWaitingForRepaint = false;
-    m_swipeWaitingForTerminalLoadingState = false;
-    m_swipeWaitingForSubresourceLoads = false;
-    m_swipeWaitingForScrollPositionRestoration = false;
</del><ins>+    m_snapshotRemovalTracker.reset();
</ins><span class="cx"> 
</span><del>-    m_swipeWatchdogTimer.stop();
-    m_swipeActiveLoadMonitoringTimer.stop();
-
</del><span class="cx">     if (m_activeGestureType != ViewGestureType::Swipe)
</span><span class="cx">         return;
</span><del>-    
</del><ins>+
</ins><span class="cx">     ++m_gesturePendingSnapshotRemoval;
</span><del>-    
</del><ins>+
</ins><span class="cx">     [m_snapshotView removeFromSuperview];
</span><span class="cx">     m_snapshotView = nullptr;
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacViewGestureControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.h (188520 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.h        2015-08-16 18:58:48 UTC (rev 188520)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.h        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -31,9 +31,12 @@
</span><span class="cx"> #include &quot;WeakObjCPtr.h&quot;
</span><span class="cx"> #include &lt;WebCore/Color.h&gt;
</span><span class="cx"> #include &lt;WebCore/FloatRect.h&gt;
</span><ins>+#include &lt;chrono&gt;
</ins><span class="cx"> #include &lt;wtf/RetainPtr.h&gt;
</span><span class="cx"> #include &lt;wtf/RunLoop.h&gt;
</span><span class="cx"> 
</span><ins>+// FIXME: Move this file out of the mac/ subdirectory.
+
</ins><span class="cx"> OBJC_CLASS CALayer;
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="lines">@@ -65,6 +68,7 @@
</span><span class="cx"> public:
</span><span class="cx">     ViewGestureController(WebPageProxy&amp;);
</span><span class="cx">     ~ViewGestureController();
</span><ins>+    void platformTeardown();
</ins><span class="cx">     
</span><span class="cx">     enum class ViewGestureType {
</span><span class="cx">         None,
</span><span class="lines">@@ -109,8 +113,6 @@
</span><span class="cx"> 
</span><span class="cx">     bool shouldIgnorePinnedState() { return m_shouldIgnorePinnedState; }
</span><span class="cx">     void setShouldIgnorePinnedState(bool ignore) { m_shouldIgnorePinnedState = ignore; }
</span><del>-
-    void didFirstVisuallyNonEmptyLayoutForMainFrame();
</del><span class="cx"> #else
</span><span class="cx">     void installSwipeHandler(UIView *gestureRecognizerView, UIView *swipingView);
</span><span class="cx">     void setAlternateBackForwardListSourceView(WKWebView *);
</span><span class="lines">@@ -119,30 +121,72 @@
</span><span class="cx">     void endSwipeGesture(WebBackForwardListItem* targetItem, _UIViewControllerTransitionContext *, bool cancelled);
</span><span class="cx">     void willCommitPostSwipeTransitionLayerTree(bool);
</span><span class="cx">     void setRenderTreeSize(uint64_t);
</span><del>-    void didRestoreScrollPosition();
</del><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    void didFinishLoadForMainFrame() { mainFrameLoadDidReachTerminalState(); }
-    void didFailLoadForMainFrame() { mainFrameLoadDidReachTerminalState(); }
-    void mainFrameLoadDidReachTerminalState();
-    void removeSwipeSnapshot();
</del><ins>+    WebCore::Color backgroundColorForCurrentSnapshot() const { return m_backgroundColorForCurrentSnapshot; }
+
+    void didFinishLoadForMainFrame() { didReachMainFrameLoadTerminalState(); }
+    void didFailLoadForMainFrame() { didReachMainFrameLoadTerminalState(); }
+    void didFirstVisuallyNonEmptyLayoutForMainFrame();
+    void didRepaintAfterNavigation();
+    void didHitRenderTreeSizeThreshold();
+    void didRestoreScrollPosition();
+    void didReachMainFrameLoadTerminalState();
</ins><span class="cx">     void didSameDocumentNavigationForMainFrame(SameDocumentNavigationType);
</span><span class="cx"> 
</span><del>-    WebCore::Color backgroundColorForCurrentSnapshot() const { return m_backgroundColorForCurrentSnapshot; }
</del><ins>+    void checkForActiveLoads();
</ins><span class="cx"> 
</span><ins>+    void removeSwipeSnapshot();
+
</ins><span class="cx"> private:
</span><span class="cx">     // IPC::MessageReceiver.
</span><span class="cx">     virtual void didReceiveMessage(IPC::Connection&amp;, IPC::MessageDecoder&amp;) override;
</span><span class="cx"> 
</span><del>-    void swipeSnapshotWatchdogTimerFired();
-    void activeLoadMonitoringTimerFired();
</del><ins>+    static ViewGestureController* gestureControllerForPage(uint64_t);
</ins><span class="cx"> 
</span><ins>+    class SnapshotRemovalTracker {
+    public:
+        enum Event : uint8_t {
+            VisuallyNonEmptyLayout = 1 &lt;&lt; 0,
+            RenderTreeSizeThreshold = 1 &lt;&lt; 1,
+            RepaintAfterNavigation = 1 &lt;&lt; 2,
+            MainFrameLoad = 1 &lt;&lt; 3,
+            SubresourceLoads = 1 &lt;&lt; 4,
+            ScrollPositionRestoration = 1 &lt;&lt; 5
+        };
+        typedef uint8_t Events;
+
+        SnapshotRemovalTracker();
+
+        void start(Events, std::function&lt;void()&gt;);
+        void reset();
+
+        bool eventOccurred(Events);
+        bool cancelOutstandingEvent(Events);
+
+        void startWatchdog(std::chrono::seconds);
+
+    private:
+        static String eventsDescription(Events);
+        void log(const String&amp;) const;
+
+        void fireRemovalCallbackImmediately();
+        void fireRemovalCallbackIfPossible();
+        void watchdogTimerFired();
+
+        bool stopWaitingForEvent(Events, const String&amp; logReason);
+
+        Events m_outstandingEvents { 0 };
+        std::function&lt;void()&gt; m_removalCallback;
+        std::chrono::steady_clock::time_point m_startTime;
+
+        RunLoop::Timer&lt;SnapshotRemovalTracker&gt; m_watchdogTimer;
+    };
+
</ins><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     // Message handlers.
</span><span class="cx">     void didCollectGeometryForMagnificationGesture(WebCore::FloatRect visibleContentBounds, bool frameHandlesMagnificationGesture);
</span><span class="cx">     void didCollectGeometryForSmartMagnificationGesture(WebCore::FloatPoint origin, WebCore::FloatRect renderRect, WebCore::FloatRect visibleContentBounds, bool isReplacedElement, double viewportMinimumScale, double viewportMaximumScale);
</span><del>-    void didHitRenderTreeSizeThreshold();
-    void removeSwipeSnapshotAfterRepaint();
</del><span class="cx"> 
</span><span class="cx">     WebCore::FloatPoint scaledMagnificationOrigin(WebCore::FloatPoint origin, double scale);
</span><span class="cx"> 
</span><span class="lines">@@ -159,14 +203,13 @@
</span><span class="cx">     CALayer *determineLayerAdjacentToSnapshotForParent(SwipeDirection, CALayer *snapshotLayerParent) const;
</span><span class="cx">     void applyDebuggingPropertiesToSwipeViews();
</span><span class="cx">     void didMoveSwipeSnapshotLayer();
</span><del>-#else
-    void removeSwipeSnapshotIfReady();
</del><ins>+
+    void forceRepaintIfNeeded();
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     WebPageProxy&amp; m_webPageProxy;
</span><span class="cx">     ViewGestureType m_activeGestureType { ViewGestureType::None };
</span><span class="cx"> 
</span><del>-    RunLoop::Timer&lt;ViewGestureController&gt; m_swipeWatchdogTimer;
</del><span class="cx">     RunLoop::Timer&lt;ViewGestureController&gt; m_swipeActiveLoadMonitoringTimer;
</span><span class="cx"> 
</span><span class="cx">     WebCore::Color m_backgroundColorForCurrentSnapshot;
</span><span class="lines">@@ -174,8 +217,6 @@
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     RefPtr&lt;ViewSnapshot&gt; m_currentSwipeSnapshot;
</span><span class="cx"> 
</span><del>-    RunLoop::Timer&lt;ViewGestureController&gt; m_swipeWatchdogAfterFirstVisuallyNonEmptyLayoutTimer;
-
</del><span class="cx">     double m_magnification;
</span><span class="cx">     WebCore::FloatPoint m_magnificationOrigin;
</span><span class="cx"> 
</span><span class="lines">@@ -208,7 +249,8 @@
</span><span class="cx">     void (^m_didMoveSwipeSnapshotCallback)(CGRect) { nullptr };
</span><span class="cx"> 
</span><span class="cx">     bool m_shouldIgnorePinnedState { false };
</span><del>-    bool m_swipeInProgress { false };
</del><ins>+
+    bool m_hasOutstandingRepaintRequest { false };
</ins><span class="cx"> #else    
</span><span class="cx">     UIView *m_liveSwipeView { nullptr };
</span><span class="cx">     RetainPtr&lt;UIView&gt; m_liveSwipeViewClippingView;
</span><span class="lines">@@ -222,12 +264,7 @@
</span><span class="cx">     uint64_t m_gesturePendingSnapshotRemoval { 0 };
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    bool m_swipeWaitingForVisuallyNonEmptyLayout { false };
-    bool m_swipeWaitingForRenderTreeSizeThreshold { false };
-    bool m_swipeWaitingForRepaint { false };
-    bool m_swipeWaitingForTerminalLoadingState { false };
-    bool m_swipeWaitingForSubresourceLoads { false };
-    bool m_swipeWaitingForScrollPositionRestoration { false };
</del><ins>+    SnapshotRemovalTracker m_snapshotRemovalTracker;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacViewGestureControllermessagesin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.messages.in (188520 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.messages.in        2015-08-16 18:58:48 UTC (rev 188520)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewGestureController.messages.in        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -21,7 +21,7 @@
</span><span class="cx"> # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</span><span class="cx"> 
</span><span class="cx"> messages -&gt; ViewGestureController {
</span><del>-#if !PLATFORM(IOS)
</del><ins>+#if PLATFORM(MAC)
</ins><span class="cx">     DidCollectGeometryForMagnificationGesture(WebCore::FloatRect visibleContentBounds, bool frameHandlesMagnificationGesture)
</span><span class="cx">     DidCollectGeometryForSmartMagnificationGesture(WebCore::FloatPoint origin, WebCore::FloatRect renderRect, WebCore::FloatRect visibleContentBounds, bool isReplacedElement, double viewportMinimumScale, double viewportMaximumScale)
</span><span class="cx">     DidHitRenderTreeSizeThreshold()
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacViewGestureControllerMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm (188520 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm        2015-08-16 18:58:48 UTC (rev 188520)
+++ trunk/Source/WebKit2/UIProcess/mac/ViewGestureControllerMac.mm        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -47,7 +47,7 @@
</span><span class="cx"> 
</span><span class="cx"> using namespace WebCore;
</span><span class="cx"> 
</span><del>-#if PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &lt; 101000
</del><ins>+#if __MAC_OS_X_VERSION_MIN_REQUIRED &lt; 101000
</ins><span class="cx"> #define ENABLE_LEGACY_SWIPE_SHADOW_STYLE 1
</span><span class="cx"> #else
</span><span class="cx"> #define ENABLE_LEGACY_SWIPE_SHADOW_STYLE 0
</span><span class="lines">@@ -78,9 +78,6 @@
</span><span class="cx"> static const float minimumScrollEventRatioForSwipe = 0.5;
</span><span class="cx"> 
</span><span class="cx"> static const float swipeSnapshotRemovalRenderTreeSizeTargetFraction = 0.5;
</span><del>-static const std::chrono::seconds swipeSnapshotRemovalWatchdogDuration = 5_s;
-static const std::chrono::seconds swipeSnapshotRemovalWatchdogAfterFirstVisuallyNonEmptyLayoutDuration = 3_s;
-static const std::chrono::milliseconds swipeSnapshotRemovalActiveLoadMonitoringInterval = 250_ms;
</del><span class="cx"> 
</span><span class="cx"> @interface WKSwipeCancellationTracker : NSObject {
</span><span class="cx"> @private
</span><span class="lines">@@ -97,17 +94,8 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><del>-ViewGestureController::ViewGestureController(WebPageProxy&amp; webPageProxy)
-    : m_webPageProxy(webPageProxy)
-    , m_swipeWatchdogTimer(RunLoop::main(), this, &amp;ViewGestureController::swipeSnapshotWatchdogTimerFired)
-    , m_swipeActiveLoadMonitoringTimer(RunLoop::main(), this, &amp;ViewGestureController::activeLoadMonitoringTimerFired)
-    , m_swipeWatchdogAfterFirstVisuallyNonEmptyLayoutTimer(RunLoop::main(), this, &amp;ViewGestureController::swipeSnapshotWatchdogTimerFired)
</del><ins>+void ViewGestureController::platformTeardown()
</ins><span class="cx"> {
</span><del>-    m_webPageProxy.process().addMessageReceiver(Messages::ViewGestureController::messageReceiverName(), m_webPageProxy.pageID(), *this);
-}
-
-ViewGestureController::~ViewGestureController()
-{
</del><span class="cx">     if (m_swipeCancellationTracker)
</span><span class="cx">         [m_swipeCancellationTracker setIsCancelled:YES];
</span><span class="cx"> 
</span><span class="lines">@@ -118,8 +106,6 @@
</span><span class="cx">         Block_release(m_didMoveSwipeSnapshotCallback);
</span><span class="cx">         m_didMoveSwipeSnapshotCallback = nullptr;
</span><span class="cx">     }
</span><del>-
-    m_webPageProxy.process().removeMessageReceiver(Messages::ViewGestureController::messageReceiverName(), m_webPageProxy.pageID());
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static double resistanceForDelta(double deltaScale, double currentScale)
</span><span class="lines">@@ -535,7 +521,6 @@
</span><span class="cx">     m_webPageProxy.navigationGestureDidBegin();
</span><span class="cx"> 
</span><span class="cx">     m_activeGestureType = ViewGestureType::Swipe;
</span><del>-    m_swipeInProgress = true;
</del><span class="cx"> 
</span><span class="cx">     CALayer *rootContentLayer = m_webPageProxy.acceleratedCompositingRootLayer();
</span><span class="cx"> 
</span><span class="lines">@@ -752,8 +737,6 @@
</span><span class="cx"> 
</span><span class="cx">     m_swipeCancellationTracker = nullptr;
</span><span class="cx"> 
</span><del>-    m_swipeInProgress = false;
-
</del><span class="cx">     CALayer *rootLayer = m_webPageProxy.acceleratedCompositingRootLayer();
</span><span class="cx"> 
</span><span class="cx">     [rootLayer setShadowOpacity:0];
</span><span class="lines">@@ -771,110 +754,47 @@
</span><span class="cx"> 
</span><span class="cx">     m_webPageProxy.process().send(Messages::ViewGestureGeometryCollector::SetRenderTreeSizeNotificationThreshold(renderTreeSize * swipeSnapshotRemovalRenderTreeSizeTargetFraction), m_webPageProxy.pageID());
</span><span class="cx"> 
</span><del>-    m_swipeWaitingForVisuallyNonEmptyLayout = true;
-    m_swipeWaitingForRenderTreeSizeThreshold = true;
-
</del><span class="cx">     m_webPageProxy.navigationGestureDidEnd(true, *targetItem);
</span><span class="cx">     m_webPageProxy.goToBackForwardItem(targetItem);
</span><span class="cx"> 
</span><ins>+    // FIXME: We should be able to include scroll position restoration here,
+    // and then can address the FIXME in didFirstVisuallyNonEmptyLayoutForMainFrame.
+    m_snapshotRemovalTracker.start(SnapshotRemovalTracker::VisuallyNonEmptyLayout
+        | SnapshotRemovalTracker::RenderTreeSizeThreshold
+        | SnapshotRemovalTracker::MainFrameLoad
+        | SnapshotRemovalTracker::SubresourceLoads, [this] {
+            this-&gt;forceRepaintIfNeeded();
+        });
+
</ins><span class="cx">     // FIXME: Like on iOS, we should ensure that even if one of the timeouts fires,
</span><del>-    // we never show the old page content, instead showing white (or the snapshot background color).
-    m_swipeWatchdogTimer.startOneShot(swipeSnapshotRemovalWatchdogDuration.count());
</del><ins>+    // we never show the old page content, instead showing the snapshot background color.
</ins><span class="cx"> 
</span><span class="cx">     if (ViewSnapshot* snapshot = targetItem-&gt;snapshot())
</span><span class="cx">         m_backgroundColorForCurrentSnapshot = snapshot-&gt;backgroundColor();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ViewGestureController::didHitRenderTreeSizeThreshold()
</del><ins>+void ViewGestureController::forceRepaintIfNeeded()
</ins><span class="cx"> {
</span><del>-    if (m_activeGestureType != ViewGestureType::Swipe || m_swipeInProgress)
</del><ins>+    if (m_activeGestureType != ViewGestureType::Swipe)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_swipeWaitingForRenderTreeSizeThreshold = false;
-
-    if (!m_swipeWaitingForVisuallyNonEmptyLayout) {
-        // FIXME: Ideally we would call removeSwipeSnapshotAfterRepaint() here, but sometimes
-        // scroll position isn't done restoring until didFinishLoadForFrame, so we flash the wrong content.
-    }
-}
-
-void ViewGestureController::didFirstVisuallyNonEmptyLayoutForMainFrame()
-{
-    if (m_activeGestureType != ViewGestureType::Swipe || m_swipeInProgress)
</del><ins>+    if (m_hasOutstandingRepaintRequest)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_swipeWaitingForVisuallyNonEmptyLayout = false;
</del><ins>+    m_hasOutstandingRepaintRequest = true;
</ins><span class="cx"> 
</span><del>-    if (!m_swipeWaitingForRenderTreeSizeThreshold) {
-        // FIXME: Ideally we would call removeSwipeSnapshotAfterRepaint() here, but sometimes
-        // scroll position isn't done restoring until didFinishLoadForFrame, so we flash the wrong content.
-    } else {
-        m_swipeWatchdogAfterFirstVisuallyNonEmptyLayoutTimer.startOneShot(swipeSnapshotRemovalWatchdogAfterFirstVisuallyNonEmptyLayoutDuration.count());
-        m_swipeWatchdogTimer.stop();
-    }
-}
-
-void ViewGestureController::mainFrameLoadDidReachTerminalState()
-{
-    if (m_activeGestureType != ViewGestureType::Swipe || m_swipeInProgress)
-        return;
-
-    if (m_webPageProxy.pageLoadState().isLoading()) {
-        m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval);
-        return;
-    }
-
-    removeSwipeSnapshotAfterRepaint();
-}
-
-void ViewGestureController::didSameDocumentNavigationForMainFrame(SameDocumentNavigationType type)
-{
-    if (m_activeGestureType != ViewGestureType::Swipe || m_swipeInProgress)
-        return;
-
-    if (type != SameDocumentNavigationSessionStateReplace &amp;&amp; type != SameDocumentNavigationSessionStatePop)
-        return;
-
-    m_swipeActiveLoadMonitoringTimer.startRepeating(swipeSnapshotRemovalActiveLoadMonitoringInterval);
-}
-
-void ViewGestureController::activeLoadMonitoringTimerFired()
-{
-    if (m_webPageProxy.pageLoadState().isLoading())
-        return;
-
-    removeSwipeSnapshotAfterRepaint();
-}
-
-void ViewGestureController::swipeSnapshotWatchdogTimerFired()
-{
-    removeSwipeSnapshotAfterRepaint();
-}
-
-void ViewGestureController::removeSwipeSnapshotAfterRepaint()
-{
-    m_swipeActiveLoadMonitoringTimer.stop();
-
-    if (m_activeGestureType != ViewGestureType::Swipe || m_swipeInProgress)
-        return;
-
-    if (m_swipeWaitingForRepaint)
-        return;
-
-    m_swipeWaitingForRepaint = true;
-
-    WebPageProxy* webPageProxy = &amp;m_webPageProxy;
-    m_webPageProxy.forceRepaint(VoidCallback::create([webPageProxy] (CallbackBase::Error error) {
-        webPageProxy-&gt;removeNavigationGestureSnapshot();
</del><ins>+    uint64_t pageID = m_webPageProxy.pageID();
+    m_webPageProxy.forceRepaint(VoidCallback::create([this, pageID] (CallbackBase::Error error) {
+        if (auto gestureController = gestureControllerForPage(pageID))
+            gestureController-&gt;removeSwipeSnapshot();
</ins><span class="cx">     }));
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ViewGestureController::removeSwipeSnapshot()
</span><span class="cx"> {
</span><del>-    m_swipeWaitingForRepaint = false;
</del><ins>+    m_snapshotRemovalTracker.reset();
</ins><span class="cx"> 
</span><del>-    m_swipeWatchdogTimer.stop();
-    m_swipeWatchdogAfterFirstVisuallyNonEmptyLayoutTimer.stop();
</del><ins>+    m_hasOutstandingRepaintRequest = false;
</ins><span class="cx"> 
</span><span class="cx">     if (m_activeGestureType != ViewGestureType::Swipe)
</span><span class="cx">         return;
</span><span class="lines">@@ -921,4 +841,4 @@
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span><span class="cx"> 
</span><del>-#endif // !PLATFORM(IOS)
</del><ins>+#endif // PLATFORM(MAC)
</ins></span></pre></div>
<a id="trunkSourceWebKit2WebKit2xcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj (188520 => 188521)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj        2015-08-16 18:58:48 UTC (rev 188520)
+++ trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj        2015-08-17 00:52:50 UTC (rev 188521)
</span><span class="lines">@@ -661,6 +661,7 @@
</span><span class="cx">                 2DACE64E18ADBFF000E4CA76 /* _WKThumbnailViewInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DACE64D18ADBFF000E4CA76 /* _WKThumbnailViewInternal.h */; };
</span><span class="cx">                 2DAF06D618BD1A470081CEB1 /* SmartMagnificationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DAF06D418BD1A470081CEB1 /* SmartMagnificationController.h */; };
</span><span class="cx">                 2DAF06D718BD1A470081CEB1 /* SmartMagnificationController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DAF06D518BD1A470081CEB1 /* SmartMagnificationController.mm */; };
</span><ins>+                2DAF4FFB1B636181006013D6 /* ViewGestureController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DAF4FFA1B636181006013D6 /* ViewGestureController.cpp */; };
</ins><span class="cx">                 2DB9C4AC1B3251BD0070F27F /* SafariServicesSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DB9C4AB1B3251BD0070F27F /* SafariServicesSPI.h */; };
</span><span class="cx">                 2DC6D9C318C44A610043BAD4 /* WKWebViewContentProviderRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC6D9C118C44A610043BAD4 /* WKWebViewContentProviderRegistry.h */; };
</span><span class="cx">                 2DC6D9C418C44A610043BAD4 /* WKWebViewContentProviderRegistry.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DC6D9C218C44A610043BAD4 /* WKWebViewContentProviderRegistry.mm */; };
</span><span class="lines">@@ -2806,6 +2807,7 @@
</span><span class="cx">                 2DAF06D418BD1A470081CEB1 /* SmartMagnificationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SmartMagnificationController.h; path = ios/SmartMagnificationController.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2DAF06D518BD1A470081CEB1 /* SmartMagnificationController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SmartMagnificationController.mm; path = ios/SmartMagnificationController.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2DAF06D818BD23BA0081CEB1 /* SmartMagnificationController.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; name = SmartMagnificationController.messages.in; path = ios/SmartMagnificationController.messages.in; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                2DAF4FFA1B636181006013D6 /* ViewGestureController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ViewGestureController.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 2DB9C4AB1B3251BD0070F27F /* SafariServicesSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SafariServicesSPI.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2DC6D9C118C44A610043BAD4 /* WKWebViewContentProviderRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKWebViewContentProviderRegistry.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2DC6D9C218C44A610043BAD4 /* WKWebViewContentProviderRegistry.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebViewContentProviderRegistry.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -6107,6 +6109,7 @@
</span><span class="cx">                                 4A410F3A19AF7B04002EBAB5 /* UserMediaPermissionRequestManagerProxy.h */,
</span><span class="cx">                                 4A410F3B19AF7B04002EBAB5 /* UserMediaPermissionRequestProxy.cpp */,
</span><span class="cx">                                 4A410F3C19AF7B04002EBAB5 /* UserMediaPermissionRequestProxy.h */,
</span><ins>+                                2DAF4FFA1B636181006013D6 /* ViewGestureController.cpp */,
</ins><span class="cx">                                 1A0F29E1120B44420053D1B9 /* VisitedLinkProvider.cpp */,
</span><span class="cx">                                 1A0F29E2120B44420053D1B9 /* VisitedLinkProvider.h */,
</span><span class="cx">                                 1A60224918C16B0800C3E8C9 /* VisitedLinkProvider.messages.in */,
</span><span class="lines">@@ -9442,6 +9445,7 @@
</span><span class="cx">                                 1A64230812DD09EB00CAAE2C /* DrawingAreaProxyMessageReceiver.cpp in Sources */,
</span><span class="cx">                                 5DA6ED0B1490606900B41D12 /* DynamicLinkerEnvironmentExtractor.mm in Sources */,
</span><span class="cx">                                 8CFECE941490F140002AAA32 /* EditorState.cpp in Sources */,
</span><ins>+                                2DAF4FFB1B636181006013D6 /* ViewGestureController.cpp in Sources */,
</ins><span class="cx">                                 51B15A8413843A3900321AD8 /* EnvironmentUtilities.cpp in Sources */,
</span><span class="cx">                                 1A7C6CDA1378950800B9C04D /* EnvironmentVariables.cpp in Sources */,
</span><span class="cx">                                 1AA575FA1496B52600A4EE06 /* EventDispatcher.cpp in Sources */,
</span></span></pre>
</div>
</div>

</body>
</html>