<!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>[170684] 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/170684">170684</a></dd>
<dt>Author</dt> <dd>benjamin@webkit.org</dd>
<dt>Date</dt> <dd>2014-07-01 17:49:13 -0700 (Tue, 01 Jul 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>[iOS][WK2] Fix a race between the short tap and long tap highlight
https://bugs.webkit.org/show_bug.cgi?id=134530

Reviewed by Enrica Casucci.

There was a potential race of event that can theoretically cause WKContentViewInteraction
to call [WKContentView _showTapHighlight] after all interactions have been cancelled.

The race would be like this:
1) On a short tap, _singleTapRecognized: is called, a tap highlight ID is defined and
   _potentialTapInProgress is set to YES.
2) For some reason, the gesture is cancelled. The method _singleTapDidReset is called, 
   setting _potentialTapInProgress but leaving the tap highlight ID as valid.
3) The UIProcess receives the tap highlight information from the WebProcess, _didGetTapHighlightForRequest:
   has a valid ID, _potentialTapInProgress is false -&gt; the highlight is shown right away as if a long tap
   was in progress.

The missing piece that causes this is _singleTapDidReset: must also invalidate the tap highlight ID. This is done
in the new static function cancelPotentialTapIfNecessary().

Just invalidating the ID would create another race:
1) Short tap gesture recognizer starts.
2) The long press recognizer starts before (1) is commited.
3) The long press recognizers sets up its own tap highlight ID.
4) The short tap gesture recognizer resets, erasing the tap highlight ID defined in (3).

To avoid this, the long press gesture recognizers immediately cancels any potential tap in progress.
If _singleTapDidReset: is called before (3), this does nothing. If the reset is called after (3),
_singleTapDidReset does nothing.

* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _highlightLongPressRecognized:]):
(cancelPotentialTapIfNecessary):
(-[WKContentView _singleTapDidReset:]):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKContentViewInteractionmm">trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (170683 => 170684)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-07-02 00:47:19 UTC (rev 170683)
+++ trunk/Source/WebKit2/ChangeLog        2014-07-02 00:49:13 UTC (rev 170684)
</span><span class="lines">@@ -1,3 +1,40 @@
</span><ins>+2014-07-01  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        [iOS][WK2] Fix a race between the short tap and long tap highlight
+        https://bugs.webkit.org/show_bug.cgi?id=134530
+
+        Reviewed by Enrica Casucci.
+
+        There was a potential race of event that can theoretically cause WKContentViewInteraction
+        to call [WKContentView _showTapHighlight] after all interactions have been cancelled.
+
+        The race would be like this:
+        1) On a short tap, _singleTapRecognized: is called, a tap highlight ID is defined and
+           _potentialTapInProgress is set to YES.
+        2) For some reason, the gesture is cancelled. The method _singleTapDidReset is called, 
+           setting _potentialTapInProgress but leaving the tap highlight ID as valid.
+        3) The UIProcess receives the tap highlight information from the WebProcess, _didGetTapHighlightForRequest:
+           has a valid ID, _potentialTapInProgress is false -&gt; the highlight is shown right away as if a long tap
+           was in progress.
+
+        The missing piece that causes this is _singleTapDidReset: must also invalidate the tap highlight ID. This is done
+        in the new static function cancelPotentialTapIfNecessary().
+
+        Just invalidating the ID would create another race:
+        1) Short tap gesture recognizer starts.
+        2) The long press recognizer starts before (1) is commited.
+        3) The long press recognizers sets up its own tap highlight ID.
+        4) The short tap gesture recognizer resets, erasing the tap highlight ID defined in (3).
+
+        To avoid this, the long press gesture recognizers immediately cancels any potential tap in progress.
+        If _singleTapDidReset: is called before (3), this does nothing. If the reset is called after (3),
+        _singleTapDidReset does nothing.
+
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _highlightLongPressRecognized:]):
+        (cancelPotentialTapIfNecessary):
+        (-[WKContentView _singleTapDidReset:]):
+
</ins><span class="cx"> 2014-07-01  Anders Carlsson  &lt;andersca@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add ABI hacks to allow WKPageRef to use WKSessionStateRef
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKContentViewInteractionmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm (170683 => 170684)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm        2014-07-02 00:47:19 UTC (rev 170683)
+++ trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm        2014-07-02 00:49:13 UTC (rev 170684)
</span><span class="lines">@@ -877,6 +877,7 @@
</span><span class="cx"> 
</span><span class="cx">     switch ([gestureRecognizer state]) {
</span><span class="cx">     case UIGestureRecognizerStateBegan:
</span><ins>+        cancelPotentialTapIfNecessary(self);
</ins><span class="cx">         _page-&gt;tapHighlightAtPosition([gestureRecognizer startPoint], ++_latestTapHighlightID);
</span><span class="cx">         _isTapHighlightIDValid = YES;
</span><span class="cx">         break;
</span><span class="lines">@@ -921,14 +922,20 @@
</span><span class="cx">     _isTapHighlightIDValid = YES;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static void cancelPotentialTapIfNecessary(WKContentView* contentView)
+{
+    if (contentView-&gt;_potentialTapInProgress) {
+        contentView-&gt;_potentialTapInProgress = NO;
+        contentView-&gt;_isTapHighlightIDValid = NO;
+        [contentView _cancelInteraction];
+        contentView-&gt;_page-&gt;cancelPotentialTap();
+    }
+}
+
</ins><span class="cx"> - (void)_singleTapDidReset:(UITapGestureRecognizer *)gestureRecognizer
</span><span class="cx"> {
</span><span class="cx">     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
</span><del>-    if (_potentialTapInProgress) {
-        _potentialTapInProgress = NO;
-        [self _cancelInteraction];
-        _page-&gt;cancelPotentialTap();
-    }
</del><ins>+    cancelPotentialTapIfNecessary(self);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)_commitPotentialTapFailed
</span></span></pre>
</div>
</div>

</body>
</html>