<!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>[180539] 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/180539">180539</a></dd>
<dt>Author</dt> <dd>benjamin@webkit.org</dd>
<dt>Date</dt> <dd>2015-02-23 18:07:33 -0800 (Mon, 23 Feb 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[iOS WK2] The WebProcess keep scrolling pages based on the last velocity after scrolling has been interrupted
https://bugs.webkit.org/show_bug.cgi?id=141933
rdar://problem/18746673
rdar://problem/19711490

Patch by Benjamin Poulain &lt;bpoulain@apple.com&gt; on 2015-02-23
Reviewed by Simon Fraser.

The bug happened like this:
1) The user scroll the page. WKContentView tracks the velocity and send
   the update rect + velocity to the WebProcess.
2) The user interupts the scrolling but does not commit to either scrolling
   again or cancelling the scrolling.
   Since we were not notified of this state, the WebProcess still believed
   the velocity is stable.
3) With any paint update, the WebProcess would account for the last velocity
   and try to guess the best repaint area. This would drift endlessly out
   of the view since the view is not really moving.

This patch fixes the issue by adding special handling for interrupted scrolling.

Kudos to Kurt Revis for providing us the required APIs.

* Shared/VisibleContentRectUpdateInfo.h:
(WebKit::operator==):
We can no longer filter VisibleContentRectUpdateInfo ignoring the velocity.

Typically, UIScrollView would call -scrollViewDidScroll: before being interrupted.
If we filter based on the VisibleContentRectUpdateInfo, we have two identical
states differing only by the velocity. If we filter the second update, the WebProcess
would never know the velocity should be zero.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _scrollViewDidInterruptDecelerating:]):
We get this callback when scrolling is interrupted. We just need to clear
the velocity and re-send a new update for the current state.

(-[WKWebView _updateVisibleContentRects]):
Do not consider an interrupted scroll as a stable state. We don't know if scrolling
will resume or will stop.

* UIProcess/ios/WKContentView.h:
* UIProcess/ios/WKContentView.mm:
(-[WKContentView didInterruptScrolling]):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2SharedVisibleContentRectUpdateInfoh">trunk/Source/WebKit2/Shared/VisibleContentRectUpdateInfo.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKContentViewh">trunk/Source/WebKit2/UIProcess/ios/WKContentView.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKContentViewmm">trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (180538 => 180539)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-02-24 01:43:36 UTC (rev 180538)
+++ trunk/Source/WebKit2/ChangeLog        2015-02-24 02:07:33 UTC (rev 180539)
</span><span class="lines">@@ -1,3 +1,49 @@
</span><ins>+2015-02-23  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        [iOS WK2] The WebProcess keep scrolling pages based on the last velocity after scrolling has been interrupted
+        https://bugs.webkit.org/show_bug.cgi?id=141933
+        rdar://problem/18746673
+        rdar://problem/19711490
+
+        Reviewed by Simon Fraser.
+
+        The bug happened like this:
+        1) The user scroll the page. WKContentView tracks the velocity and send
+           the update rect + velocity to the WebProcess.
+        2) The user interupts the scrolling but does not commit to either scrolling
+           again or cancelling the scrolling.
+           Since we were not notified of this state, the WebProcess still believed
+           the velocity is stable.
+        3) With any paint update, the WebProcess would account for the last velocity
+           and try to guess the best repaint area. This would drift endlessly out
+           of the view since the view is not really moving.
+
+        This patch fixes the issue by adding special handling for interrupted scrolling.
+
+        Kudos to Kurt Revis for providing us the required APIs.
+
+        * Shared/VisibleContentRectUpdateInfo.h:
+        (WebKit::operator==):
+        We can no longer filter VisibleContentRectUpdateInfo ignoring the velocity.
+
+        Typically, UIScrollView would call -scrollViewDidScroll: before being interrupted.
+        If we filter based on the VisibleContentRectUpdateInfo, we have two identical
+        states differing only by the velocity. If we filter the second update, the WebProcess
+        would never know the velocity should be zero.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _scrollViewDidInterruptDecelerating:]):
+        We get this callback when scrolling is interrupted. We just need to clear
+        the velocity and re-send a new update for the current state.
+
+        (-[WKWebView _updateVisibleContentRects]):
+        Do not consider an interrupted scroll as a stable state. We don't know if scrolling
+        will resume or will stop.
+
+        * UIProcess/ios/WKContentView.h:
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView didInterruptScrolling]):
+
</ins><span class="cx"> 2015-02-23  Anders Carlsson  &lt;andersca@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add API for fetching website data records to _WKWebsiteDataStore
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedVisibleContentRectUpdateInfoh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/VisibleContentRectUpdateInfo.h (180538 => 180539)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/VisibleContentRectUpdateInfo.h        2015-02-24 01:43:36 UTC (rev 180538)
+++ trunk/Source/WebKit2/Shared/VisibleContentRectUpdateInfo.h        2015-02-24 02:07:33 UTC (rev 180539)
</span><span class="lines">@@ -101,6 +101,9 @@
</span><span class="cx">         &amp;&amp; a.exposedRect() == b.exposedRect()
</span><span class="cx">         &amp;&amp; a.unobscuredRect() == b.unobscuredRect()
</span><span class="cx">         &amp;&amp; a.customFixedPositionRect() == b.customFixedPositionRect()
</span><ins>+        &amp;&amp; a.horizontalVelocity() == b.horizontalVelocity()
+        &amp;&amp; a.verticalVelocity() == b.verticalVelocity()
+        &amp;&amp; a.scaleChangeRate() == b.scaleChangeRate()
</ins><span class="cx">         &amp;&amp; a.inStableState() == b.inStableState();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm (180538 => 180539)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2015-02-24 01:43:36 UTC (rev 180538)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2015-02-24 02:07:33 UTC (rev 180539)
</span><span class="lines">@@ -103,6 +103,7 @@
</span><span class="cx"> @interface UIScrollView (UIScrollViewInternal)
</span><span class="cx"> - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
</span><span class="cx"> - (BOOL)_isScrollingToTop;
</span><ins>+- (BOOL)_isInterruptingDeceleration;
</ins><span class="cx"> @end
</span><span class="cx"> 
</span><span class="cx"> @interface UIPeripheralHost(UIKitInternal)
</span><span class="lines">@@ -1411,6 +1412,15 @@
</span><span class="cx">     [self _didFinishScrolling];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
+{
+    if (![self usesStandardContentView])
+        return;
+
+    [_contentView didInterruptScrolling];
+    [self _updateVisibleContentRects];
+}
+
</ins><span class="cx"> - (void)_frameOrBoundsChanged
</span><span class="cx"> {
</span><span class="cx">     CGRect bounds = self.bounds;
</span><span class="lines">@@ -1466,6 +1476,11 @@
</span><span class="cx">     CGFloat scaleFactor = contentZoomScale(self);
</span><span class="cx"> 
</span><span class="cx">     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop]);
</span><ins>+
+    // FIXME: this can be made static after we stop supporting iOS 8.x.
+    if (isStableState &amp;&amp; [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
+        isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
+
</ins><span class="cx">     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
</span><span class="cx">         unobscuredRect:unobscuredRectInContentCoordinates
</span><span class="cx">         unobscuredRectInScrollViewCoordinates:unobscuredRect
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKContentViewh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKContentView.h (180538 => 180539)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKContentView.h        2015-02-24 01:43:36 UTC (rev 180538)
+++ trunk/Source/WebKit2/UIProcess/ios/WKContentView.h        2015-02-24 02:07:33 UTC (rev 180539)
</span><span class="lines">@@ -71,6 +71,7 @@
</span><span class="cx">     inStableState:(BOOL)isStableState isChangingObscuredInsetsInteractively:(BOOL)isChangingObscuredInsetsInteractively;
</span><span class="cx"> 
</span><span class="cx"> - (void)didFinishScrolling;
</span><ins>+- (void)didInterruptScrolling;
</ins><span class="cx"> - (void)didZoomToScale:(CGFloat)scale;
</span><span class="cx"> - (void)willStartZoomOrScroll;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKContentViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm (180538 => 180539)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm        2015-02-24 01:43:36 UTC (rev 180538)
+++ trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm        2015-02-24 02:07:33 UTC (rev 180539)
</span><span class="lines">@@ -381,6 +381,11 @@
</span><span class="cx">     [self _didEndScrollingOrZooming];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)didInterruptScrolling
+{
+    _historicalKinematicData.clear();
+}
+
</ins><span class="cx"> - (void)willStartZoomOrScroll
</span><span class="cx"> {
</span><span class="cx">     [self _willStartScrollingOrZooming];
</span></span></pre>
</div>
</div>

</body>
</html>