<!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>[209931] trunk</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/209931">209931</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2016-12-16 12:24:37 -0800 (Fri, 16 Dec 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Visual viewports: carets and selection UI are incorrectly positioned when editing fixed elements
https://bugs.webkit.org/show_bug.cgi?id=165767
&lt;rdar://problem/29602382&gt;

Reviewed by Simon Fraser.

Source/WebCore:

When changing the layout viewport override, mark viewport-constrained objects as needing layout. If only the
width and height of the old and new layout viewports are compared, EditorState info (namely selection and caret
rects) that depends on the document location of fixed elements may be stale immediately after the layout
viewport override changes and before layout occurs.

This caused one of the tests (fixed-caret-position-after-scroll.html) to occasionally fail.

Tests: editing/caret/ios/absolute-caret-position-after-scroll.html
       editing/caret/ios/fixed-caret-position-after-scroll.html
       editing/selection/ios/absolute-selection-after-scroll.html
       editing/selection/ios/fixed-selection-after-scroll.html

* page/FrameView.cpp:
(WebCore::FrameView::setLayoutViewportOverrideRect):

Source/WebKit2:

When focusing an input, the position of the caret on iOS is determined by the overridden layout viewport rect in
the web process. However, this rect is not updated until the end the scroll gesture. Whereas this is fine for
non-fixed inputs since their document location does not change, fixed inputs effectively change their position
in the document as the user scrolls. This causes the caret to be 'left behind' in the document position it was
in at the start of the scroll. To fix this, we deactivate the selection when exiting stable state if the
assisted node is in a fixed position container, and reenable it upon receiving the next stable state EditorState
update (as indicated by postLayoutData().isStableStateUpdate). Additionally, we apply similar treatment to the
web selection assistant -- this time, we need to force the selection view to hide (analagous to deactivating
the text selection assistant) and show it again upon receiving the next selection change update when the WebPage
(in the web process) is stable.

Furthermore, adds test support for querying text caret and selection rects, as well as perform a callback after
the WebPage has indicated that it is stable, both as SPI on the WKWebView.

Covered by 4 new layout tests in fast/editing/caret/ios and fast/editing/selection/ios.

* Platform/spi/ios/UIKitSPI.h:
* Shared/EditorState.cpp:
(WebKit::EditorState::PostLayoutData::encode):
(WebKit::EditorState::PostLayoutData::decode):
* Shared/EditorState.h:

Introduce isStableStateUpdate, which is true when the WebPage is known to be in stable state, as well as
insideFixedPosition, which is true when the current selection is inside a fixed position container.

* Shared/mac/RemoteLayerTreeTransaction.h:
(WebKit::RemoteLayerTreeTransaction::isInStableState):
(WebKit::RemoteLayerTreeTransaction::setIsInStableState):
* Shared/mac/RemoteLayerTreeTransaction.mm:
(WebKit::RemoteLayerTreeTransaction::encode):
(WebKit::RemoteLayerTreeTransaction::decode):
(WebKit::RemoteLayerTreeTransaction::description):
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didCommitLayerTree:]):
(-[WKWebView _uiTextCaretRect]):

Introduced a new SPI method for fetching the current rect of the text assistant's caret view, at keyPath
&quot;selectionView.selection.caretRect&quot;.

(-[WKWebView _uiTextSelectionRects]):

Renamed (and refactored) from _uiTextSelectionRectViews, which was previously fetching an array of UIViews. I
found this to cause character-granularity-rect.html to fail due to the array of UIViews here being empty, so I
refactored this to simply return an array of rects from the keyPath &quot;selectionView.selection.selectionRects&quot; for
the text selection assistant and @&quot;selectionView.selectionRects&quot; for the web selection assistant.

(-[WKWebView _doAfterNextStablePresentationUpdate:]):

Runs the given block after both the UI process and web processes agree that the visible content rect state is
stable. To do this, we fire presentation updates until the UI process (via RemoteLayerTreeTransactions)
discovers that the web page is in stable state. This is used solely for testing purposes.

(-[WKWebView _firePresentationUpdateForPendingStableStatePresentationCallbacks]):
(-[WKWebView _uiTextSelectionRectViews]): Deleted.
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/WebPageProxy.h:
(WebKit::WebPageProxy::inStableState):
* UIProcess/ios/WKContentView.mm:
(-[WKContentView _didExitStableState]):

Deactivate the text selection if the assisted node is inside a fixed container.

(-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInset:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]):
(-[WKContentView cleanupInteraction]):
(-[WKContentView shouldHideSelectionWhenScrolling]):
(-[WKContentView _uiTextSelectionRects]):
(-[WKContentView _didEndScrollingOrZooming]):
(-[WKContentView _updateChangedSelection:]):

If the EditorState was created after a stable state update, reactivate the text selection assistant if it exists.
Additionally, if we are deferring the end scrolling selection update until after the first stable editor state
update arrives from the web process, we need to also call [_textSelectionAssistant didEndScrollingOverflow]
and [_webSelectionAssistant didEndScrollingOrZoomingPage] here instead of doing so immediately after scrolling
finishes. This ensures that selection UI (the callout and selection highlights) do not flicker from their old
position to the new position when scrolling finishes.

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::willCommitLayerTree):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::platformEditorState):
(WebKit::WebPage::updateVisibleContentRects):

When updating the layout viewport override rect, also recompute the caret if needed and send an updated
EditorState over to the UI process.

Tools:

Introduces two new UIScriptController methods: doAfterWebPageIsInStableState and textSelectionCaretRect. See
WebKit2 ChangeLog for more details.

* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::doAfterNextStablePresentationUpdate):
(WTR::UIScriptController::textSelectionCaretRect):
* DumpRenderTree/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::doAfterNextStablePresentationUpdate):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::doAfterNextStablePresentationUpdate):
(WTR::UIScriptController::textSelectionCaretRect):
* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
(-[TestRunnerWKWebView _setStableStateOverride:]):

Force the WKWebView to update its visible content rects when changing the stable state override.

* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::toNSDictionary):
(WTR::UIScriptController::doAfterNextStablePresentationUpdate):
(WTR::UIScriptController::selectionRangeViewRects):
(WTR::UIScriptController::textSelectionCaretRect):
* WebKitTestRunner/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::doAfterNextStablePresentationUpdate):

LayoutTests:

Adds new layout tests verifying that scrolling selected text (non-editable) and a text caret (in editable
content) results in the selection/caret rects having the correct location relative to the document, in both
cases where the selected/focused element has fixed position or absolute position. For fixed position elements,
this means that the rects must &quot;move&quot; down in the document as the document is scrolled, but for absolute
elements, these rects must remain in place.

* TestExpectations:
* editing/caret/ios/absolute-caret-position-after-scroll-expected.txt: Added.
* editing/caret/ios/absolute-caret-position-after-scroll.html: Added.
* editing/caret/ios/fixed-caret-position-after-scroll-expected.txt: Added.
* editing/caret/ios/fixed-caret-position-after-scroll.html: Added.
* editing/selection/ios/absolute-selection-after-scroll-expected.txt: Added.
* editing/selection/ios/absolute-selection-after-scroll.html: Added.
* editing/selection/ios/fixed-selection-after-scroll-expected.txt: Added.
* editing/selection/ios/fixed-selection-after-scroll.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsTestExpectations">trunk/LayoutTests/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorepageFrameViewcpp">trunk/Source/WebCore/page/FrameView.cpp</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2PlatformspiiosUIKitSPIh">trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h</a></li>
<li><a href="#trunkSourceWebKit2SharedEditorStatecpp">trunk/Source/WebKit2/Shared/EditorState.cpp</a></li>
<li><a href="#trunkSourceWebKit2SharedEditorStateh">trunk/Source/WebKit2/Shared/EditorState.h</a></li>
<li><a href="#trunkSourceWebKit2SharedmacRemoteLayerTreeTransactionh">trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.h</a></li>
<li><a href="#trunkSourceWebKit2SharedmacRemoteLayerTreeTransactionmm">trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKWebViewPrivateh">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebPageProxyh">trunk/Source/WebKit2/UIProcess/WebPageProxy.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKContentViewmm">trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKContentViewInteractionh">trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKContentViewInteractionmm">trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageWebPagecpp">trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageiosWebPageIOSmm">trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsDumpRenderTreeiosUIScriptControllerIOSmm">trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm</a></li>
<li><a href="#trunkToolsDumpRenderTreemacUIScriptControllerMacmm">trunk/Tools/DumpRenderTree/mac/UIScriptControllerMac.mm</a></li>
<li><a href="#trunkToolsTestRunnerSharedUIScriptContextBindingsUIScriptControlleridl">trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl</a></li>
<li><a href="#trunkToolsTestRunnerSharedUIScriptContextUIScriptControllercpp">trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp</a></li>
<li><a href="#trunkToolsTestRunnerSharedUIScriptContextUIScriptControllerh">trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h</a></li>
<li><a href="#trunkToolsWebKitTestRunnercocoaTestRunnerWKWebViewmm">trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm</a></li>
<li><a href="#trunkToolsWebKitTestRunneriosUIScriptControllerIOSmm">trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm</a></li>
<li><a href="#trunkToolsWebKitTestRunnermacUIScriptControllerMacmm">trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/LayoutTests/editing/caret/ios/</li>
<li><a href="#trunkLayoutTestseditingcaretiosabsolutecaretpositionafterscrollexpectedtxt">trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll-expected.txt</a></li>
<li><a href="#trunkLayoutTestseditingcaretiosabsolutecaretpositionafterscrollhtml">trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll.html</a></li>
<li><a href="#trunkLayoutTestseditingcaretiosfixedcaretpositionafterscrollexpectedtxt">trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll-expected.txt</a></li>
<li><a href="#trunkLayoutTestseditingcaretiosfixedcaretpositionafterscrollhtml">trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll.html</a></li>
<li>trunk/LayoutTests/editing/selection/ios/</li>
<li><a href="#trunkLayoutTestseditingselectioniosabsoluteselectionafterscrollexpectedtxt">trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll-expected.txt</a></li>
<li><a href="#trunkLayoutTestseditingselectioniosabsoluteselectionafterscrollhtml">trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll.html</a></li>
<li><a href="#trunkLayoutTestseditingselectioniosfixedselectionafterscrollexpectedtxt">trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll-expected.txt</a></li>
<li><a href="#trunkLayoutTestseditingselectioniosfixedselectionafterscrollhtml">trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/LayoutTests/ChangeLog        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -1,3 +1,27 @@
</span><ins>+2016-12-16  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Visual viewports: carets and selection UI are incorrectly positioned when editing fixed elements
+        https://bugs.webkit.org/show_bug.cgi?id=165767
+        &lt;rdar://problem/29602382&gt;
+
+        Reviewed by Simon Fraser.
+
+        Adds new layout tests verifying that scrolling selected text (non-editable) and a text caret (in editable
+        content) results in the selection/caret rects having the correct location relative to the document, in both
+        cases where the selected/focused element has fixed position or absolute position. For fixed position elements,
+        this means that the rects must &quot;move&quot; down in the document as the document is scrolled, but for absolute
+        elements, these rects must remain in place.
+
+        * TestExpectations:
+        * editing/caret/ios/absolute-caret-position-after-scroll-expected.txt: Added.
+        * editing/caret/ios/absolute-caret-position-after-scroll.html: Added.
+        * editing/caret/ios/fixed-caret-position-after-scroll-expected.txt: Added.
+        * editing/caret/ios/fixed-caret-position-after-scroll.html: Added.
+        * editing/selection/ios/absolute-selection-after-scroll-expected.txt: Added.
+        * editing/selection/ios/absolute-selection-after-scroll.html: Added.
+        * editing/selection/ios/fixed-selection-after-scroll-expected.txt: Added.
+        * editing/selection/ios/fixed-selection-after-scroll.html: Added.
+
</ins><span class="cx"> 2016-12-16  Zalan Bujtas  &lt;zalan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Defer certain accessibility callbacks until after layout is finished.
</span></span></pre></div>
<a id="trunkLayoutTestsTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/TestExpectations (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/TestExpectations        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/LayoutTests/TestExpectations        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -13,7 +13,9 @@
</span><span class="cx"> accessibility/win [ Skip ]
</span><span class="cx"> displaylists [ Skip ]
</span><span class="cx"> editing/mac [ Skip ]
</span><ins>+editing/caret/ios [ Skip ]
</ins><span class="cx"> editing/pasteboard/gtk [ Skip ]
</span><ins>+editing/selection/ios [ Skip ]
</ins><span class="cx"> tiled-drawing [ Skip ]
</span><span class="cx"> fast/visual-viewport/tiled-drawing [ Skip ]
</span><span class="cx"> swipe [ Skip ]
</span></span></pre></div>
<a id="trunkLayoutTestseditingcaretiosabsolutecaretpositionafterscrollexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll-expected.txt (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll-expected.txt                                (rev 0)
+++ trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll-expected.txt        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+PASS successfullyParsed is true
+
+TEST COMPLETE
+The initial caret rect is: [6 17 ; 3 15]
+The caret rect after scrolling 1000px down is: [6 17 ; 3 15]
+PASS finalCaretRect.top is initialCaretRect.top
+PASS finalCaretRect.left is initialCaretRect.left
+PASS finalCaretRect.width is initialCaretRect.width
+PASS finalCaretRect.height is initialCaretRect.height
+
</ins></span></pre></div>
<a id="trunkLayoutTestseditingcaretiosabsolutecaretpositionafterscrollhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll.html (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll.html                                (rev 0)
+++ trunk/LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll.html        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,88 @@
</span><ins>+&lt;html&gt;
+&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, user-scalable=no&quot;&gt;
+&lt;head&gt;
+    &lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+    &lt;style&gt;
+        body {
+            margin: 0;
+        }
+
+        input {
+            width: 100%;
+            height: 50px;
+            position: absolute;
+            left: 0;
+            top: 0;
+        }
+
+        div {
+            background-image: linear-gradient(0deg, blue, red);
+            height: 4000px;
+        }
+    &lt;/style&gt;
+    &lt;script&gt;
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    function tapInInputScript(tapX, tapY)
+    {
+        return `(function() {
+            uiController.didShowKeyboardCallback = function() {
+                uiController.doAfterNextStablePresentationUpdate(function() {
+                    uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionCaretRect));
+                });
+            };
+            uiController.singleTapAtPoint(${tapX}, ${tapY}, function() { });
+        })()`;
+    }
+
+    function simulateScrollingScript(distance)
+    {
+        return `(function() {
+            uiController.stableStateOverride = false;
+            uiController.immediateScrollToOffset(0, ${distance});
+            uiController.stableStateOverride = true;
+            uiController.doAfterNextStablePresentationUpdate(function() {
+                uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionCaretRect));
+            });
+        })()`;
+    }
+
+    function toString(rect)
+    {
+        return `[${rect.left} ${rect.top} ; ${rect.width} ${rect.height}]`;
+    }
+
+    function run()
+    {
+        if (!window.testRunner || !testRunner.runUIScript) {
+            description(&quot;To manually test, tap this input field and scroll up. The text caret should not end up outside of the input.&quot;);
+            return;
+        }
+
+        testRunner.runUIScript(tapInInputScript(window.innerWidth / 2, 30), initialCaretRect =&gt; {
+            initialCaretRect = JSON.parse(initialCaretRect);
+            window.initialCaretRect = initialCaretRect;
+            debug(`The initial caret rect is: ${toString(initialCaretRect)}`);
+            testRunner.runUIScript(simulateScrollingScript(1000), finalCaretRect =&gt; {
+                finalCaretRect = JSON.parse(finalCaretRect);
+                window.finalCaretRect = finalCaretRect;
+                debug(`The caret rect after scrolling 1000px down is: ${toString(finalCaretRect)}`);
+                shouldBe(&quot;finalCaretRect.top&quot;, &quot;initialCaretRect.top&quot;);
+                shouldBe(&quot;finalCaretRect.left&quot;, &quot;initialCaretRect.left&quot;);
+                shouldBe(&quot;finalCaretRect.width&quot;, &quot;initialCaretRect.width&quot;);
+                shouldBe(&quot;finalCaretRect.height&quot;, &quot;initialCaretRect.height&quot;);
+                testRunner.notifyDone();
+            });
+        });
+    }
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=run()&gt;
+    &lt;input&gt;&lt;/input&gt;
+    &lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestseditingcaretiosfixedcaretpositionafterscrollexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll-expected.txt (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll-expected.txt                                (rev 0)
+++ trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll-expected.txt        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+PASS successfullyParsed is true
+
+TEST COMPLETE
+The initial caret rect is: [6 17 ; 3 15]
+The caret rect after scrolling 1000px down is: [6 1017 ; 3 15]
+PASS finalCaretRect.top - initialCaretRect.top is 1000
+
</ins></span></pre></div>
<a id="trunkLayoutTestseditingcaretiosfixedcaretpositionafterscrollhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll.html (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll.html                                (rev 0)
+++ trunk/LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll.html        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,85 @@
</span><ins>+&lt;html&gt;
+&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, user-scalable=no&quot;&gt;
+&lt;head&gt;
+    &lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+    &lt;style&gt;
+        body {
+            margin: 0;
+        }
+
+        input {
+            width: 100%;
+            height: 50px;
+            position: fixed;
+            left: 0;
+            top: 0;
+        }
+
+        div {
+            background-image: linear-gradient(blue, red);
+            height: 4000px;
+        }
+    &lt;/style&gt;
+    &lt;script&gt;
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    function tapInInputScript(tapX, tapY)
+    {
+        return `(function() {
+            uiController.didShowKeyboardCallback = function() {
+                uiController.doAfterNextStablePresentationUpdate(function() {
+                    uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionCaretRect));
+                });
+            };
+            uiController.singleTapAtPoint(${tapX}, ${tapY}, function() { });
+        })()`;
+    }
+
+    function simulateScrollingScript(distance)
+    {
+        return `(function() {
+            uiController.stableStateOverride = false;
+            uiController.immediateScrollToOffset(0, ${distance});
+            uiController.stableStateOverride = true;
+            uiController.doAfterNextStablePresentationUpdate(function() {
+                uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionCaretRect));
+            });
+        })()`;
+    }
+
+    function toString(rect)
+    {
+        return `[${rect.left} ${rect.top} ; ${rect.width} ${rect.height}]`;
+    }
+
+    function run()
+    {
+        if (!window.testRunner || !testRunner.runUIScript) {
+            description(&quot;To manually test, tap this input field and scroll up. The text caret should not end up outside of the input.&quot;);
+            return;
+        }
+
+        testRunner.runUIScript(tapInInputScript(window.innerWidth / 2, 30), initialCaretRect =&gt; {
+            initialCaretRect = JSON.parse(initialCaretRect);
+            window.initialCaretRect = initialCaretRect;
+            debug(`The initial caret rect is: ${toString(initialCaretRect)}`);
+            testRunner.runUIScript(simulateScrollingScript(1000), finalCaretRect =&gt; {
+                finalCaretRect = JSON.parse(finalCaretRect);
+                window.finalCaretRect = finalCaretRect;
+                debug(`The caret rect after scrolling 1000px down is: ${toString(finalCaretRect)}`);
+                shouldBe(&quot;finalCaretRect.top - initialCaretRect.top&quot;, &quot;1000&quot;);
+                testRunner.notifyDone();
+            });
+        });
+    }
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=run()&gt;
+    &lt;input&gt;&lt;/input&gt;
+    &lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestseditingselectioniosabsoluteselectionafterscrollexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll-expected.txt (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll-expected.txt                                (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll-expected.txt        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,11 @@
</span><ins>+PASS successfullyParsed is true
+
+TEST COMPLETE
+After long pressing, the selection rects are: [0 0 ; 309 114]
+After scrolling 1000px down, the selection rects are: [0 0 ; 309 114]
+PASS finalSelectionRects[0].top is initialSelectionRects[0].top
+PASS finalSelectionRects[0].left is initialSelectionRects[0].left
+PASS finalSelectionRects[0].width is initialSelectionRects[0].width
+PASS finalSelectionRects[0].height is initialSelectionRects[0].height
+WebKit
+
</ins></span></pre></div>
<a id="trunkLayoutTestseditingselectioniosabsoluteselectionafterscrollhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll.html (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll.html                                (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/absolute-selection-after-scroll.html        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,94 @@
</span><ins>+&lt;html&gt;
+&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, user-scalable=no&quot;&gt;
+&lt;head&gt;
+    &lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+    &lt;style&gt;
+        body {
+            margin: 0;
+        }
+
+        #fixed {
+            width: 100vw;
+            height: 50px;
+            position: absolute;
+            left: 0;
+            top: 0;
+            font-size: 100px;
+        }
+
+        #content {
+            background-image: linear-gradient(blue, red);
+            height: 4000px;
+        }
+    &lt;/style&gt;
+    &lt;script&gt;
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    function toString(rect)
+    {
+        return `[${rect.left} ${rect.top} ; ${rect.width} ${rect.height}]`;
+    }
+
+    function selectFixedTextScript(tapX, tapY)
+    {
+        return `
+        (function() {
+            uiController.longPressAtPoint(${tapX}, ${tapY}, function() {
+                uiController.doAfterNextStablePresentationUpdate(function() {
+                    uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects));
+                });
+            });
+        })();`;
+    }
+
+    function simulateScrollingScript(distance)
+    {
+        return `(function() {
+            uiController.stableStateOverride = false;
+            uiController.immediateScrollToOffset(0, ${distance});
+            uiController.stableStateOverride = true;
+            uiController.doAfterNextStablePresentationUpdate(function() {
+                uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects));
+            });
+        })()`;
+    }
+
+    function toString(rect)
+    {
+        return `[${rect.left} ${rect.top} ; ${rect.width} ${rect.height}]`;
+    }
+
+    function run()
+    {
+        if (!window.testRunner || !testRunner.runUIScript) {
+            description(&quot;To manually test, long press this text and scroll up. The selection rect should be in the expected place (at the very top of the page) after scrolling.&quot;);
+            return;
+        }
+
+        testRunner.runUIScript(selectFixedTextScript(50, 50), initialSelectionRects =&gt; {
+            initialSelectionRects = JSON.parse(initialSelectionRects);
+            window.initialSelectionRects = initialSelectionRects;
+            debug(`After long pressing, the selection rects are: ${initialSelectionRects.map(toString)}`);
+            testRunner.runUIScript(simulateScrollingScript(1000), finalSelectionRects =&gt; {
+                finalSelectionRects = JSON.parse(finalSelectionRects);
+                window.finalSelectionRects = finalSelectionRects;
+                debug(`After scrolling 1000px down, the selection rects are: ${finalSelectionRects.map(toString)}`);
+                shouldBe(&quot;finalSelectionRects[0].top&quot;, &quot;initialSelectionRects[0].top&quot;);
+                shouldBe(&quot;finalSelectionRects[0].left&quot;, &quot;initialSelectionRects[0].left&quot;);
+                shouldBe(&quot;finalSelectionRects[0].width&quot;, &quot;initialSelectionRects[0].width&quot;);
+                shouldBe(&quot;finalSelectionRects[0].height&quot;, &quot;initialSelectionRects[0].height&quot;);
+                testRunner.notifyDone();
+            });
+        });
+    }
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=run()&gt;
+    &lt;div id=&quot;fixed&quot;&gt;WebKit&lt;/div&gt;
+    &lt;div id=&quot;content&quot;&gt;&lt;/div&gt;
+    &lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkLayoutTestseditingselectioniosfixedselectionafterscrollexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll-expected.txt (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll-expected.txt                                (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll-expected.txt        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+PASS successfullyParsed is true
+
+TEST COMPLETE
+After long pressing, the selection rects are: [0 0 ; 309 114]
+After scrolling 1000px down, the selection rects are: [0 1000 ; 309 114]
+PASS initialSelectionRects.length is 1
+PASS finalSelectionRects.length is 1
+PASS finalSelectionRects[0].top - initialSelectionRects[0].top is 1000
+WebKit
+
</ins></span></pre></div>
<a id="trunkLayoutTestseditingselectioniosfixedselectionafterscrollhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll.html (0 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll.html                                (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/fixed-selection-after-scroll.html        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -0,0 +1,93 @@
</span><ins>+&lt;html&gt;
+&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, user-scalable=no&quot;&gt;
+&lt;head&gt;
+    &lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+    &lt;style&gt;
+        body {
+            margin: 0;
+        }
+
+        #fixed {
+            width: 100vw;
+            height: 50px;
+            position: fixed;
+            left: 0;
+            top: 0;
+            font-size: 100px;
+        }
+
+        #content {
+            background-image: linear-gradient(blue, red);
+            height: 4000px;
+        }
+    &lt;/style&gt;
+    &lt;script&gt;
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    function toString(rect)
+    {
+        return `[${rect.left} ${rect.top} ; ${rect.width} ${rect.height}]`;
+    }
+
+    function selectFixedTextScript(tapX, tapY)
+    {
+        return `
+        (function() {
+            uiController.longPressAtPoint(${tapX}, ${tapY}, function() {
+                uiController.doAfterNextStablePresentationUpdate(function() {
+                    uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects));
+                });
+            });
+        })();`;
+    }
+
+    function simulateScrollingScript(distance)
+    {
+        return `(function() {
+            uiController.stableStateOverride = false;
+            uiController.immediateScrollToOffset(0, ${distance});
+            uiController.stableStateOverride = true;
+            uiController.doAfterNextStablePresentationUpdate(function() {
+                uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects));
+            });
+        })()`;
+    }
+
+    function toString(rect)
+    {
+        return `[${rect.left} ${rect.top} ; ${rect.width} ${rect.height}]`;
+    }
+
+    function run()
+    {
+        if (!window.testRunner || !testRunner.runUIScript) {
+            description(&quot;To manually test, long press this text and scroll up. The selection rect should be in the expected place (in the fixed div) after scrolling.&quot;);
+            return;
+        }
+
+        testRunner.runUIScript(selectFixedTextScript(50, 50), initialSelectionRects =&gt; {
+            initialSelectionRects = JSON.parse(initialSelectionRects);
+            window.initialSelectionRects = initialSelectionRects;
+            debug(`After long pressing, the selection rects are: ${initialSelectionRects.map(toString)}`);
+            testRunner.runUIScript(simulateScrollingScript(1000), finalSelectionRects =&gt; {
+                finalSelectionRects = JSON.parse(finalSelectionRects);
+                window.finalSelectionRects = finalSelectionRects;
+                debug(`After scrolling 1000px down, the selection rects are: ${finalSelectionRects.map(toString)}`);
+                shouldBe(&quot;initialSelectionRects.length&quot;, &quot;1&quot;);
+                shouldBe(&quot;finalSelectionRects.length&quot;, &quot;1&quot;);
+                shouldBe(&quot;finalSelectionRects[0].top - initialSelectionRects[0].top&quot;, &quot;1000&quot;);
+                testRunner.notifyDone();
+            });
+        });
+    }
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=run()&gt;
+    &lt;div id=&quot;fixed&quot;&gt;WebKit&lt;/div&gt;
+    &lt;div id=&quot;content&quot;&gt;&lt;/div&gt;
+    &lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebCore/ChangeLog        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -1,3 +1,26 @@
</span><ins>+2016-12-16  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Visual viewports: carets and selection UI are incorrectly positioned when editing fixed elements
+        https://bugs.webkit.org/show_bug.cgi?id=165767
+        &lt;rdar://problem/29602382&gt;
+
+        Reviewed by Simon Fraser.
+
+        When changing the layout viewport override, mark viewport-constrained objects as needing layout. If only the
+        width and height of the old and new layout viewports are compared, EditorState info (namely selection and caret
+        rects) that depends on the document location of fixed elements may be stale immediately after the layout
+        viewport override changes and before layout occurs.
+
+        This caused one of the tests (fixed-caret-position-after-scroll.html) to occasionally fail.
+
+        Tests: editing/caret/ios/absolute-caret-position-after-scroll.html
+               editing/caret/ios/fixed-caret-position-after-scroll.html
+               editing/selection/ios/absolute-selection-after-scroll.html
+               editing/selection/ios/fixed-selection-after-scroll.html
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::setLayoutViewportOverrideRect):
+
</ins><span class="cx"> 2016-12-14  Sam Weinig  &lt;sam@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         [ApplePay] Remove remaining custom bindings from the ApplePay code
</span></span></pre></div>
<a id="trunkSourceWebCorepageFrameViewcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/FrameView.cpp (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/FrameView.cpp        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebCore/page/FrameView.cpp        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -1849,8 +1849,7 @@
</span><span class="cx"> 
</span><span class="cx">     LOG_WITH_STREAM(Scrolling, stream &lt;&lt; &quot;\nFrameView &quot; &lt;&lt; this &lt;&lt; &quot; setLayoutViewportOverrideRect() - changing layout viewport from &quot; &lt;&lt; oldRect &lt;&lt; &quot; to &quot; &lt;&lt; m_layoutViewportOverrideRect.value());
</span><span class="cx"> 
</span><del>-    // FIXME: do we need to also do this if the origin changes?
-    if (oldRect.size() != layoutViewportRect().size())
</del><ins>+    if (oldRect != layoutViewportRect())
</ins><span class="cx">         setViewportConstrainedObjectsNeedLayout();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/ChangeLog        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -1,3 +1,99 @@
</span><ins>+2016-12-16  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Visual viewports: carets and selection UI are incorrectly positioned when editing fixed elements
+        https://bugs.webkit.org/show_bug.cgi?id=165767
+        &lt;rdar://problem/29602382&gt;
+
+        Reviewed by Simon Fraser.
+
+        When focusing an input, the position of the caret on iOS is determined by the overridden layout viewport rect in
+        the web process. However, this rect is not updated until the end the scroll gesture. Whereas this is fine for
+        non-fixed inputs since their document location does not change, fixed inputs effectively change their position
+        in the document as the user scrolls. This causes the caret to be 'left behind' in the document position it was
+        in at the start of the scroll. To fix this, we deactivate the selection when exiting stable state if the
+        assisted node is in a fixed position container, and reenable it upon receiving the next stable state EditorState
+        update (as indicated by postLayoutData().isStableStateUpdate). Additionally, we apply similar treatment to the
+        web selection assistant -- this time, we need to force the selection view to hide (analagous to deactivating
+        the text selection assistant) and show it again upon receiving the next selection change update when the WebPage
+        (in the web process) is stable.
+
+        Furthermore, adds test support for querying text caret and selection rects, as well as perform a callback after
+        the WebPage has indicated that it is stable, both as SPI on the WKWebView.
+
+        Covered by 4 new layout tests in fast/editing/caret/ios and fast/editing/selection/ios.
+
+        * Platform/spi/ios/UIKitSPI.h:
+        * Shared/EditorState.cpp:
+        (WebKit::EditorState::PostLayoutData::encode):
+        (WebKit::EditorState::PostLayoutData::decode):
+        * Shared/EditorState.h:
+
+        Introduce isStableStateUpdate, which is true when the WebPage is known to be in stable state, as well as
+        insideFixedPosition, which is true when the current selection is inside a fixed position container.
+
+        * Shared/mac/RemoteLayerTreeTransaction.h:
+        (WebKit::RemoteLayerTreeTransaction::isInStableState):
+        (WebKit::RemoteLayerTreeTransaction::setIsInStableState):
+        * Shared/mac/RemoteLayerTreeTransaction.mm:
+        (WebKit::RemoteLayerTreeTransaction::encode):
+        (WebKit::RemoteLayerTreeTransaction::decode):
+        (WebKit::RemoteLayerTreeTransaction::description):
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didCommitLayerTree:]):
+        (-[WKWebView _uiTextCaretRect]):
+
+        Introduced a new SPI method for fetching the current rect of the text assistant's caret view, at keyPath
+        &quot;selectionView.selection.caretRect&quot;.
+
+        (-[WKWebView _uiTextSelectionRects]):
+
+        Renamed (and refactored) from _uiTextSelectionRectViews, which was previously fetching an array of UIViews. I
+        found this to cause character-granularity-rect.html to fail due to the array of UIViews here being empty, so I
+        refactored this to simply return an array of rects from the keyPath &quot;selectionView.selection.selectionRects&quot; for
+        the text selection assistant and @&quot;selectionView.selectionRects&quot; for the web selection assistant.
+
+        (-[WKWebView _doAfterNextStablePresentationUpdate:]):
+
+        Runs the given block after both the UI process and web processes agree that the visible content rect state is 
+        stable. To do this, we fire presentation updates until the UI process (via RemoteLayerTreeTransactions)
+        discovers that the web page is in stable state. This is used solely for testing purposes.
+
+        (-[WKWebView _firePresentationUpdateForPendingStableStatePresentationCallbacks]):
+        (-[WKWebView _uiTextSelectionRectViews]): Deleted.
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+        * UIProcess/WebPageProxy.h:
+        (WebKit::WebPageProxy::inStableState):
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView _didExitStableState]):
+
+        Deactivate the text selection if the assisted node is inside a fixed container.
+
+        (-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInset:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setupInteraction]):
+        (-[WKContentView cleanupInteraction]):
+        (-[WKContentView shouldHideSelectionWhenScrolling]):
+        (-[WKContentView _uiTextSelectionRects]):
+        (-[WKContentView _didEndScrollingOrZooming]):
+        (-[WKContentView _updateChangedSelection:]):
+
+        If the EditorState was created after a stable state update, reactivate the text selection assistant if it exists.
+        Additionally, if we are deferring the end scrolling selection update until after the first stable editor state
+        update arrives from the web process, we need to also call [_textSelectionAssistant didEndScrollingOverflow]
+        and [_webSelectionAssistant didEndScrollingOrZoomingPage] here instead of doing so immediately after scrolling
+        finishes. This ensures that selection UI (the callout and selection highlights) do not flicker from their old
+        position to the new position when scrolling finishes.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::willCommitLayerTree):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::platformEditorState):
+        (WebKit::WebPage::updateVisibleContentRects):
+
+        When updating the layout viewport override rect, also recompute the caret if needed and send an updated
+        EditorState over to the UI process.
+
</ins><span class="cx"> 2016-12-16  Claudio Saavedra  &lt;csaavedra@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [WK2] SharedMemory: include cleanups
</span></span></pre></div>
<a id="trunkSourceWebKit2PlatformspiiosUIKitSPIh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/Platform/spi/ios/UIKitSPI.h        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -450,9 +450,16 @@
</span><span class="cx"> @property (nonatomic, assign, setter=_setBackdropMaskViewFlags:) NSInteger _backdropMaskViewFlags;
</span><span class="cx"> @end
</span><span class="cx"> 
</span><ins>+@interface UIWebSelectionView : UIView
+@end
+
</ins><span class="cx"> @interface UIWebSelectionAssistant : NSObject &lt;UIGestureRecognizerDelegate&gt;
</span><span class="cx"> @end
</span><span class="cx"> 
</span><ins>+@protocol UISelectionInteractionAssistant
+- (void)showSelectionCommands;
+@end
+
</ins><span class="cx"> @interface UIWebSelectionAssistant ()
</span><span class="cx"> - (BOOL)isSelectionGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
</span><span class="cx"> - (id)initWithView:(UIView *)view;
</span><span class="lines">@@ -464,6 +471,7 @@
</span><span class="cx"> - (void)setGestureRecognizers;
</span><span class="cx"> - (void)willStartScrollingOrZoomingPage;
</span><span class="cx"> - (void)willStartScrollingOverflow;
</span><ins>+@property (nonatomic, retain) UIWebSelectionView *selectionView;
</ins><span class="cx"> @property (nonatomic, readonly) CGRect selectionFrame;
</span><span class="cx"> @end
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedEditorStatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/EditorState.cpp (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/EditorState.cpp        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/Shared/EditorState.cpp        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -125,6 +125,8 @@
</span><span class="cx">     encoder &lt;&lt; twoCharacterBeforeSelection;
</span><span class="cx">     encoder &lt;&lt; isReplaceAllowed;
</span><span class="cx">     encoder &lt;&lt; hasContent;
</span><ins>+    encoder &lt;&lt; isStableStateUpdate;
+    encoder &lt;&lt; insideFixedPosition;
</ins><span class="cx"> #endif
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     encoder &lt;&lt; candidateRequestStartPosition;
</span><span class="lines">@@ -170,6 +172,10 @@
</span><span class="cx">         return false;
</span><span class="cx">     if (!decoder.decode(result.hasContent))
</span><span class="cx">         return false;
</span><ins>+    if (!decoder.decode(result.isStableStateUpdate))
+        return false;
+    if (!decoder.decode(result.insideFixedPosition))
+        return false;
</ins><span class="cx"> #endif
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     if (!decoder.decode(result.candidateRequestStartPosition))
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedEditorStateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/EditorState.h (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/EditorState.h        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/Shared/EditorState.h        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -99,6 +99,8 @@
</span><span class="cx">         UChar32 twoCharacterBeforeSelection { 0 };
</span><span class="cx">         bool isReplaceAllowed { false };
</span><span class="cx">         bool hasContent { false };
</span><ins>+        bool isStableStateUpdate { false };
+        bool insideFixedPosition { false };
</ins><span class="cx"> #endif
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">         uint64_t candidateRequestStartPosition { 0 };
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedmacRemoteLayerTreeTransactionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.h (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.h        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.h        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -249,6 +249,9 @@
</span><span class="cx">     bool viewportMetaTagCameFromImageDocument() const { return m_viewportMetaTagCameFromImageDocument; }
</span><span class="cx">     void setViewportMetaTagCameFromImageDocument(bool cameFromImageDocument) { m_viewportMetaTagCameFromImageDocument = cameFromImageDocument; }
</span><span class="cx"> 
</span><ins>+    bool isInStableState() const { return m_isInStableState; }
+    void setIsInStableState(bool isInStableState) { m_isInStableState = isInStableState; }
+
</ins><span class="cx">     bool allowsUserScaling() const { return m_allowsUserScaling; }
</span><span class="cx">     void setAllowsUserScaling(bool allowsUserScaling) { m_allowsUserScaling = allowsUserScaling; }
</span><span class="cx"> 
</span><span class="lines">@@ -295,6 +298,7 @@
</span><span class="cx">     bool m_allowsUserScaling { false };
</span><span class="cx">     bool m_viewportMetaTagWidthWasExplicit { false };
</span><span class="cx">     bool m_viewportMetaTagCameFromImageDocument { false };
</span><ins>+    bool m_isInStableState { false };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedmacRemoteLayerTreeTransactionmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -553,6 +553,8 @@
</span><span class="cx">     encoder &lt;&lt; m_viewportMetaTagWidthWasExplicit;
</span><span class="cx">     encoder &lt;&lt; m_viewportMetaTagCameFromImageDocument;
</span><span class="cx"> 
</span><ins>+    encoder &lt;&lt; m_isInStableState;
+
</ins><span class="cx">     encoder &lt;&lt; m_callbackIDs;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -660,6 +662,9 @@
</span><span class="cx">     if (!decoder.decode(result.m_viewportMetaTagCameFromImageDocument))
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+    if (!decoder.decode(result.m_isInStableState))
+        return false;
+
</ins><span class="cx">     if (!decoder.decode(result.m_callbackIDs))
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="lines">@@ -873,6 +878,7 @@
</span><span class="cx">     ts.dumpProperty(&quot;viewportMetaTagWidth&quot;, m_viewportMetaTagWidth);
</span><span class="cx">     ts.dumpProperty(&quot;viewportMetaTagWidthWasExplicit&quot;, m_viewportMetaTagWidthWasExplicit);
</span><span class="cx">     ts.dumpProperty(&quot;viewportMetaTagCameFromImageDocument&quot;, m_viewportMetaTagCameFromImageDocument);
</span><ins>+    ts.dumpProperty(&quot;isInStableState&quot;, m_isInStableState);
</ins><span class="cx">     ts.dumpProperty(&quot;renderTreeSize&quot;, m_renderTreeSize);
</span><span class="cx"> 
</span><span class="cx">     ts &lt;&lt; &quot;root-layer &quot; &lt;&lt; m_rootLayerID &lt;&lt; &quot;)&quot;;
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -272,6 +272,7 @@
</span><span class="cx">     BOOL _hadDelayedUpdateVisibleContentRects;
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;std::function&lt;void ()&gt;&gt; _snapshotsDeferredDuringResize;
</span><ins>+    RetainPtr&lt;NSMutableArray&gt; _stableStatePresentationUpdateCallbacks;
</ins><span class="cx"> #endif
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     std::unique_ptr&lt;WebKit::WebViewImpl&gt; _impl;
</span><span class="lines">@@ -1296,7 +1297,14 @@
</span><span class="cx">     _viewportMetaTagWidthWasExplicit = layerTreeTransaction.viewportMetaTagWidthWasExplicit();
</span><span class="cx">     _viewportMetaTagCameFromImageDocument = layerTreeTransaction.viewportMetaTagCameFromImageDocument();
</span><span class="cx">     _initialScaleFactor = layerTreeTransaction.initialScaleFactor();
</span><ins>+    if (_page-&gt;inStableState() &amp;&amp; layerTreeTransaction.isInStableState() &amp;&amp; [_stableStatePresentationUpdateCallbacks count]) {
+        for (dispatch_block_t action in _stableStatePresentationUpdateCallbacks.get())
+            action();
</ins><span class="cx"> 
</span><ins>+        [_stableStatePresentationUpdateCallbacks removeAllObjects];
+        _stableStatePresentationUpdateCallbacks = nil;
+    }
+
</ins><span class="cx">     BOOL needUpdateVisbleContentRects = _page-&gt;updateLayoutViewportParameters(layerTreeTransaction);
</span><span class="cx"> 
</span><span class="cx">     if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
</span><span class="lines">@@ -4736,11 +4744,22 @@
</span><span class="cx">     // For subclasses to override.
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (NSArray&lt;UIView *&gt; *)_uiTextSelectionRectViews
</del><ins>+- (CGRect)_uiTextCaretRect
</ins><span class="cx"> {
</span><del>-    return [_contentView valueForKeyPath:@&quot;interactionAssistant.selectionView.rangeView.m_rectViews&quot;];
</del><ins>+    // Force the selection view to update if needed.
+    [_contentView _updateChangedSelection];
+
+    return [[_contentView valueForKeyPath:@&quot;interactionAssistant.selectionView.selection.caretRect&quot;] CGRectValue];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (NSArray&lt;NSValue *&gt; *)_uiTextSelectionRects
+{
+    // Force the selection view to update if needed.
+    [_contentView _updateChangedSelection];
+
+    return [_contentView _uiTextSelectionRects];
+}
+
</ins><span class="cx"> - (NSString *)_scrollingTreeAsText
</span><span class="cx"> {
</span><span class="cx">     WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page-&gt;scrollingCoordinatorProxy();
</span><span class="lines">@@ -4756,6 +4775,27 @@
</span><span class="cx">     return nil;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_doAfterNextStablePresentationUpdate:(dispatch_block_t)updateBlock
+{
+    updateBlock = Block_copy(updateBlock);
+    if (_stableStatePresentationUpdateCallbacks)
+        [_stableStatePresentationUpdateCallbacks addObject:updateBlock];
+    else {
+        _stableStatePresentationUpdateCallbacks = adoptNS([[NSMutableArray alloc] initWithObjects:Block_copy(updateBlock), nil]);
+        [self _firePresentationUpdateForPendingStableStatePresentationCallbacks];
+    }
+    Block_release(updateBlock);
+}
+
+- (void)_firePresentationUpdateForPendingStableStatePresentationCallbacks
+{
+    RetainPtr&lt;WKWebView&gt; strongSelf = self;
+    [self _doAfterNextPresentationUpdate:^() {
+        if ([strongSelf-&gt;_stableStatePresentationUpdateCallbacks count])
+            [strongSelf _firePresentationUpdateForPendingStableStatePresentationCallbacks];
+    }];
+}
+
</ins><span class="cx"> #endif // PLATFORM(IOS)
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(MAC)
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKWebViewPrivateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -287,8 +287,10 @@
</span><span class="cx"> 
</span><span class="cx"> - (void)_didShowForcePressPreview WK_API_AVAILABLE(ios(WK_IOS_TBA));
</span><span class="cx"> - (void)_didDismissForcePressPreview WK_API_AVAILABLE(ios(WK_IOS_TBA));
</span><ins>+- (void)_doAfterNextStablePresentationUpdate:(dispatch_block_t)updateBlock WK_API_AVAILABLE(ios(WK_IOS_TBA));
</ins><span class="cx"> 
</span><del>-@property (nonatomic, readonly) NSArray&lt;UIView *&gt; *_uiTextSelectionRectViews WK_API_AVAILABLE(ios(WK_IOS_TBA));
</del><ins>+@property (nonatomic, readonly) NSArray&lt;NSValue *&gt; *_uiTextSelectionRects WK_API_AVAILABLE(ios(WK_IOS_TBA));
+@property (nonatomic, readonly) CGRect _uiTextCaretRect WK_API_AVAILABLE(ios(WK_IOS_TBA));
</ins><span class="cx"> 
</span><span class="cx"> @property (nonatomic, readonly) NSString *_scrollingTreeAsText WK_API_AVAILABLE(ios(WK_IOS_TBA));
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebPageProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.h (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/WebPageProxy.h        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.h        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -467,6 +467,7 @@
</span><span class="cx">     double displayedContentScale() const { return m_lastVisibleContentRectUpdate.scale(); }
</span><span class="cx">     const WebCore::FloatRect&amp; exposedContentRect() const { return m_lastVisibleContentRectUpdate.exposedContentRect(); }
</span><span class="cx">     const WebCore::FloatRect&amp; unobscuredContentRect() const { return m_lastVisibleContentRectUpdate.unobscuredContentRect(); }
</span><ins>+    bool inStableState() const { return m_lastVisibleContentRectUpdate.inStableState(); }
</ins><span class="cx">     // When visual viewports are enabled, this is the layout viewport rect.
</span><span class="cx">     const WebCore::FloatRect&amp; customFixedPositionRect() const { return m_lastVisibleContentRectUpdate.customFixedPositionRect(); }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKContentViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -362,6 +362,16 @@
</span><span class="cx">     [_fixedClippingView setBounds:clippingBounds];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)_didExitStableState
+{
+    _needsDeferredEndScrollingSelectionUpdate = self.shouldHideSelectionWhenScrolling;
+    if (!_needsDeferredEndScrollingSelectionUpdate)
+        return;
+
+    [_textSelectionAssistant deactivateSelection];
+    [[_webSelectionAssistant selectionView] setHidden:YES];
+}
+
</ins><span class="cx"> - (void)didUpdateVisibleRect:(CGRect)visibleContentRect
</span><span class="cx">     unobscuredRect:(CGRect)unobscuredContentRect
</span><span class="cx">     unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
</span><span class="lines">@@ -405,6 +415,7 @@
</span><span class="cx"> 
</span><span class="cx">     LOG_WITH_STREAM(VisibleRects, stream &lt;&lt; &quot;-[WKContentView didUpdateVisibleRect]&quot; &lt;&lt; visibleContentRectUpdateInfo.dump());
</span><span class="cx"> 
</span><ins>+    bool wasStableState = _page-&gt;inStableState();
</ins><span class="cx">     _page-&gt;updateVisibleContentRects(visibleContentRectUpdateInfo);
</span><span class="cx"> 
</span><span class="cx">     _sizeChangedSinceLastVisibleContentRectUpdate = NO;
</span><span class="lines">@@ -415,6 +426,9 @@
</span><span class="cx">     drawingArea-&gt;updateDebugIndicator();
</span><span class="cx">     
</span><span class="cx">     [self updateFixedClippingView:fixedPositionRect];
</span><ins>+
+    if (wasStableState &amp;&amp; !isStableState)
+        [self _didExitStableState];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)didFinishScrolling
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKContentViewInteractionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.h (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.h        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.h        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -178,6 +178,7 @@
</span><span class="cx">     BOOL _showDebugTapHighlightsForFastClicking;
</span><span class="cx"> 
</span><span class="cx">     BOOL _resigningFirstResponder;
</span><ins>+    BOOL _needsDeferredEndScrollingSelectionUpdate;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> @end
</span><span class="lines">@@ -186,6 +187,7 @@
</span><span class="cx"> 
</span><span class="cx"> @property (nonatomic, readonly) CGPoint lastInteractionLocation;
</span><span class="cx"> @property (nonatomic, readonly) BOOL isEditable;
</span><ins>+@property (nonatomic, readonly) BOOL shouldHideSelectionWhenScrolling;
</ins><span class="cx"> @property (nonatomic, readonly) const WebKit::InteractionInformationAtPosition&amp; positionInformation;
</span><span class="cx"> @property (nonatomic, readonly) const WebKit::WKAutoCorrectionData&amp; autocorrectionData;
</span><span class="cx"> @property (nonatomic, readonly) const WebKit::AssistedNodeInformation&amp; assistedNodeInformation;
</span><span class="lines">@@ -228,6 +230,7 @@
</span><span class="cx"> - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler;
</span><span class="cx"> - (void)_setDoubleTapGesturesEnabled:(BOOL)enabled;
</span><span class="cx"> - (NSArray *)_dataDetectionResults;
</span><ins>+- (NSArray&lt;NSValue *&gt; *)_uiTextSelectionRects;
</ins><span class="cx"> @end
</span><span class="cx"> 
</span><span class="cx"> @interface WKContentView (WKTesting)
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKContentViewInteractionmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -589,6 +589,7 @@
</span><span class="cx">     _potentialTapInProgress = NO;
</span><span class="cx">     _isDoubleTapPending = NO;
</span><span class="cx">     _showDebugTapHighlightsForFastClicking = [[NSUserDefaults standardUserDefaults] boolForKey:@&quot;WebKitShowFastClickDebugTapHighlights&quot;];
</span><ins>+    _needsDeferredEndScrollingSelectionUpdate = NO;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)cleanupInteraction
</span><span class="lines">@@ -599,6 +600,7 @@
</span><span class="cx">     _smartMagnificationController = nil;
</span><span class="cx">     _didAccessoryTabInitiateFocus = NO;
</span><span class="cx">     _isExpectingFastSingleTapCommit = NO;
</span><ins>+    _needsDeferredEndScrollingSelectionUpdate = NO;
</ins><span class="cx">     [_formInputSession invalidate];
</span><span class="cx">     _formInputSession = nil;
</span><span class="cx">     [_highlightView removeFromSuperview];
</span><span class="lines">@@ -765,6 +767,15 @@
</span><span class="cx">     return _lastInteractionLocation;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (BOOL)shouldHideSelectionWhenScrolling
+{
+    if (_isEditable)
+        return _assistedNodeInformation.insideFixedPosition;
+
+    auto&amp; editorState = _page-&gt;editorState();
+    return !editorState.isMissingPostLayoutData &amp;&amp; editorState.postLayoutData().insideFixedPosition;
+}
+
</ins><span class="cx"> - (BOOL)isEditable
</span><span class="cx"> {
</span><span class="cx">     return _isEditable;
</span><span class="lines">@@ -1261,6 +1272,21 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+- (NSArray&lt;NSValue *&gt; *)_uiTextSelectionRects
+{
+    NSMutableArray *textSelectionRects = [NSMutableArray array];
+
+    if (_textSelectionAssistant) {
+        for (WKTextSelectionRect *selectionRect in [_textSelectionAssistant valueForKeyPath:@&quot;selectionView.selection.selectionRects&quot;])
+            [textSelectionRects addObject:[NSValue valueWithCGRect:selectionRect.webRect.rect]];
+    } else if (_webSelectionAssistant) {
+        for (WebSelectionRect *selectionRect in [_webSelectionAssistant valueForKeyPath:@&quot;selectionView.selectionRects&quot;])
+            [textSelectionRects addObject:[NSValue valueWithCGRect:selectionRect.rect]];
+    }
+
+    return textSelectionRects;
+}
+
</ins><span class="cx"> - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
</span><span class="cx"> {
</span><span class="cx">     CGPoint point = [gestureRecognizer locationInView:self];
</span><span class="lines">@@ -1642,8 +1668,10 @@
</span><span class="cx"> 
</span><span class="cx"> - (void)_didEndScrollingOrZooming
</span><span class="cx"> {
</span><del>-    [_webSelectionAssistant didEndScrollingOrZoomingPage];
-    [_textSelectionAssistant didEndScrollingOverflow];
</del><ins>+    if (!_needsDeferredEndScrollingSelectionUpdate) {
+        [_webSelectionAssistant didEndScrollingOrZoomingPage];
+        [_textSelectionAssistant didEndScrollingOverflow];
+    }
</ins><span class="cx">     _page-&gt;setIsScrollingOrZooming(false);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -3735,25 +3763,37 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     WKSelectionDrawingInfo selectionDrawingInfo(_page-&gt;editorState());
</span><del>-    if (!force &amp;&amp; selectionDrawingInfo == _lastSelectionDrawingInfo)
-        return;
</del><ins>+    if (force || selectionDrawingInfo != _lastSelectionDrawingInfo) {
+        LOG_WITH_STREAM(Selection, stream &lt;&lt; &quot;_updateChangedSelection &quot; &lt;&lt; selectionDrawingInfo);
</ins><span class="cx"> 
</span><del>-    LOG_WITH_STREAM(Selection, stream &lt;&lt; &quot;_updateChangedSelection &quot; &lt;&lt; selectionDrawingInfo);
</del><ins>+        _lastSelectionDrawingInfo = selectionDrawingInfo;
</ins><span class="cx"> 
</span><del>-    _lastSelectionDrawingInfo = selectionDrawingInfo;
</del><ins>+        // FIXME: We need to figure out what to do if the selection is changed by Javascript.
+        if (_textSelectionAssistant) {
+            _markedText = (_page-&gt;editorState().hasComposition) ? _page-&gt;editorState().markedText : String();
+            if (!_showingTextStyleOptions)
+                [_textSelectionAssistant selectionChanged];
+        } else if (!_page-&gt;editorState().isContentEditable)
+            [_webSelectionAssistant selectionChanged];
</ins><span class="cx"> 
</span><del>-    // FIXME: We need to figure out what to do if the selection is changed by Javascript.
-    if (_textSelectionAssistant) {
-        _markedText = (_page-&gt;editorState().hasComposition) ? _page-&gt;editorState().markedText : String();
-        if (!_showingTextStyleOptions)
-            [_textSelectionAssistant selectionChanged];
-    } else if (!_page-&gt;editorState().isContentEditable)
-        [_webSelectionAssistant selectionChanged];
-    _selectionNeedsUpdate = NO;
-    if (_shouldRestoreSelection) {
-        [_webSelectionAssistant didEndScrollingOverflow];
</del><ins>+        _selectionNeedsUpdate = NO;
+        if (_shouldRestoreSelection) {
+            [_webSelectionAssistant didEndScrollingOverflow];
+            [_textSelectionAssistant didEndScrollingOverflow];
+            _shouldRestoreSelection = NO;
+        }
+    }
+
+    auto&amp; state = _page-&gt;editorState();
+    if (!state.isMissingPostLayoutData &amp;&amp; state.postLayoutData().isStableStateUpdate &amp;&amp; _needsDeferredEndScrollingSelectionUpdate &amp;&amp; _page-&gt;inStableState()) {
+        [[self selectionInteractionAssistant] showSelectionCommands];
+        [_webSelectionAssistant didEndScrollingOrZoomingPage];
+        [[_webSelectionAssistant selectionView] setHidden:NO];
+
+        [_textSelectionAssistant activateSelection];
</ins><span class="cx">         [_textSelectionAssistant didEndScrollingOverflow];
</span><del>-        _shouldRestoreSelection = NO;
</del><ins>+
+        _needsDeferredEndScrollingSelectionUpdate = NO;
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageWebPagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -3273,6 +3273,7 @@
</span><span class="cx">     layerTransaction.setViewportMetaTagWidth(m_viewportConfiguration.viewportArguments().width);
</span><span class="cx">     layerTransaction.setViewportMetaTagWidthWasExplicit(m_viewportConfiguration.viewportArguments().widthWasExplicit);
</span><span class="cx">     layerTransaction.setViewportMetaTagCameFromImageDocument(m_viewportConfiguration.viewportArguments().type == ViewportArguments::ImageDocument);
</span><ins>+    layerTransaction.setIsInStableState(m_isInStableState);
</ins><span class="cx">     layerTransaction.setAllowsUserScaling(allowsUserScaling());
</span><span class="cx"> #endif
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageiosWebPageIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -166,8 +166,12 @@
</span><span class="cx">     auto&amp; postLayoutData = result.postLayoutData();
</span><span class="cx">     FrameView* view = frame.view();
</span><span class="cx">     const VisibleSelection&amp; selection = frame.selection().selection();
</span><ins>+    postLayoutData.isStableStateUpdate = m_isInStableState;
+    bool startNodeIsInsideFixedPosition = false;
+    bool endNodeIsInsideFixedPosition = false;
</ins><span class="cx">     if (selection.isCaret()) {
</span><del>-        postLayoutData.caretRectAtStart = view-&gt;contentsToRootView(frame.selection().absoluteCaretBounds());
</del><ins>+        postLayoutData.caretRectAtStart = view-&gt;contentsToRootView(frame.selection().absoluteCaretBounds(&amp;startNodeIsInsideFixedPosition));
+        endNodeIsInsideFixedPosition = startNodeIsInsideFixedPosition;
</ins><span class="cx">         postLayoutData.caretRectAtEnd = postLayoutData.caretRectAtStart;
</span><span class="cx">         // FIXME: The following check should take into account writing direction.
</span><span class="cx">         postLayoutData.isReplaceAllowed = result.isContentEditable &amp;&amp; atBoundaryOfGranularity(selection.start(), WordGranularity, DirectionForward);
</span><span class="lines">@@ -178,8 +182,8 @@
</span><span class="cx">             postLayoutData.hasContent = root &amp;&amp; root-&gt;hasChildNodes() &amp;&amp; !isEndOfEditableOrNonEditableContent(firstPositionInNode(root));
</span><span class="cx">         }
</span><span class="cx">     } else if (selection.isRange()) {
</span><del>-        postLayoutData.caretRectAtStart = view-&gt;contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds());
-        postLayoutData.caretRectAtEnd = view-&gt;contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds());
</del><ins>+        postLayoutData.caretRectAtStart = view-&gt;contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds(&amp;startNodeIsInsideFixedPosition));
+        postLayoutData.caretRectAtEnd = view-&gt;contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds(&amp;endNodeIsInsideFixedPosition));
</ins><span class="cx">         RefPtr&lt;Range&gt; selectedRange = selection.toNormalizedRange();
</span><span class="cx">         String selectedText;
</span><span class="cx">         if (selectedRange) {
</span><span class="lines">@@ -194,6 +198,7 @@
</span><span class="cx">         // FIXME: We should disallow replace when the string contains only CJ characters.
</span><span class="cx">         postLayoutData.isReplaceAllowed = result.isContentEditable &amp;&amp; !result.isInPasswordField &amp;&amp; !selectedText.containsOnlyWhitespace();
</span><span class="cx">     }
</span><ins>+    postLayoutData.insideFixedPosition = startNodeIsInsideFixedPosition || endNodeIsInsideFixedPosition;
</ins><span class="cx">     if (!selection.isNone()) {
</span><span class="cx">         if (m_assistedNode &amp;&amp; m_assistedNode-&gt;renderer())
</span><span class="cx">             postLayoutData.selectionClipRect = view-&gt;contentsToRootView(m_assistedNode-&gt;renderer()-&gt;absoluteBoundingBoxRect());
</span><span class="lines">@@ -3081,9 +3086,14 @@
</span><span class="cx">     frameView.setScrollVelocity(horizontalVelocity, verticalVelocity, scaleChangeRate, visibleContentRectUpdateInfo.timestamp());
</span><span class="cx"> 
</span><span class="cx">     if (m_isInStableState) {
</span><del>-        if (frameView.frame().settings().visualViewportEnabled())
</del><ins>+        if (frameView.frame().settings().visualViewportEnabled()) {
</ins><span class="cx">             frameView.setLayoutViewportOverrideRect(LayoutRect(visibleContentRectUpdateInfo.customFixedPositionRect()));
</span><del>-        else
</del><ins>+            const auto&amp; state = editorState();
+            if (!state.isMissingPostLayoutData &amp;&amp; state.postLayoutData().insideFixedPosition) {
+                frameView.frame().selection().setCaretRectNeedsUpdate();
+                send(Messages::WebPageProxy::EditorStateChanged(state));
+            }
+        } else
</ins><span class="cx">             frameView.setCustomFixedPositionLayoutRect(enclosingIntRect(visibleContentRectUpdateInfo.customFixedPositionRect()));
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/ChangeLog        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2016-12-16  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Visual viewports: carets and selection UI are incorrectly positioned when editing fixed elements
+        https://bugs.webkit.org/show_bug.cgi?id=165767
+        &lt;rdar://problem/29602382&gt;
+
+        Reviewed by Simon Fraser.
+
+        Introduces two new UIScriptController methods: doAfterWebPageIsInStableState and textSelectionCaretRect. See
+        WebKit2 ChangeLog for more details.
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::doAfterNextStablePresentationUpdate):
+        (WTR::UIScriptController::textSelectionCaretRect):
+        * DumpRenderTree/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::doAfterNextStablePresentationUpdate):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::doAfterNextStablePresentationUpdate):
+        (WTR::UIScriptController::textSelectionCaretRect):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+        (-[TestRunnerWKWebView _setStableStateOverride:]):
+
+        Force the WKWebView to update its visible content rects when changing the stable state override.
+
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::toNSDictionary):
+        (WTR::UIScriptController::doAfterNextStablePresentationUpdate):
+        (WTR::UIScriptController::selectionRangeViewRects):
+        (WTR::UIScriptController::textSelectionCaretRect):
+        * WebKitTestRunner/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::doAfterNextStablePresentationUpdate):
+
</ins><span class="cx"> 2016-12-15  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Arguments called in wrong order
</span></span></pre></div>
<a id="trunkToolsDumpRenderTreeiosUIScriptControllerIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -54,6 +54,11 @@
</span><span class="cx">     return doAsyncTask(callback);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef callback)
+{
+    doAsyncTask(callback);
+}
+
</ins><span class="cx"> void UIScriptController::zoomToScale(double scale, JSValueRef callback)
</span><span class="cx"> {
</span><span class="cx">     RefPtr&lt;UIScriptController&gt; protectedThis(this);
</span><span class="lines">@@ -261,6 +266,11 @@
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+JSObjectRef UIScriptController::textSelectionCaretRect() const
+{
+    return nullptr;
+}
+
</ins><span class="cx"> void UIScriptController::removeAllDynamicDictionaries()
</span><span class="cx"> {
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkToolsDumpRenderTreemacUIScriptControllerMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/DumpRenderTree/mac/UIScriptControllerMac.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/DumpRenderTree/mac/UIScriptControllerMac.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/DumpRenderTree/mac/UIScriptControllerMac.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -54,6 +54,11 @@
</span><span class="cx">     return doAsyncTask(callback);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef callback)
+{
+    doAsyncTask(callback);
+}
+
</ins><span class="cx"> void UIScriptController::insertText(JSStringRef, int, int)
</span><span class="cx"> {
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkToolsTestRunnerSharedUIScriptContextBindingsUIScriptControlleridl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> 
</span><span class="cx">     void doAsyncTask(object callback); // Used to test the harness.
</span><span class="cx">     void doAfterPresentationUpdate(object callback); // Call the callback after sending a message to the WebProcess and receiving a subsequent update.
</span><ins>+    void doAfterNextStablePresentationUpdate(object callback);
</ins><span class="cx"> 
</span><span class="cx">     void simulateAccessibilitySettingsChangeNotification(object callback);
</span><span class="cx"> 
</span><span class="lines">@@ -204,6 +205,7 @@
</span><span class="cx">     readonly attribute object contentVisibleRect; // Returned object has 'left', 'top', 'width', 'height' properties.
</span><span class="cx"> 
</span><span class="cx">     readonly attribute object selectionRangeViewRects; // An array of objects with 'left', 'top', 'width', and 'height' properties.
</span><ins>+    readonly attribute object textSelectionCaretRect; // An object with 'left', 'top', 'width', 'height' properties.
</ins><span class="cx"> 
</span><span class="cx">     void insertText(DOMString text, long location, long length);
</span><span class="cx">     void removeAllDynamicDictionaries();
</span></span></pre></div>
<a id="trunkToolsTestRunnerSharedUIScriptContextUIScriptControllercpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -64,6 +64,10 @@
</span><span class="cx"> void UIScriptController::doAfterPresentationUpdate(JSValueRef)
</span><span class="cx"> {
</span><span class="cx"> }
</span><ins>+
+void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef)
+{
+}
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> void UIScriptController::setDidStartFormControlInteractionCallback(JSValueRef callback)
</span><span class="lines">@@ -307,6 +311,11 @@
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+JSObjectRef UIScriptController::textSelectionCaretRect() const
+{
+    return nullptr;
+}
+
</ins><span class="cx"> void UIScriptController::removeAllDynamicDictionaries()
</span><span class="cx"> {
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkToolsTestRunnerSharedUIScriptContextUIScriptControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -52,6 +52,7 @@
</span><span class="cx">     
</span><span class="cx">     void doAsyncTask(JSValueRef callback);
</span><span class="cx">     void doAfterPresentationUpdate(JSValueRef callback);
</span><ins>+    void doAfterNextStablePresentationUpdate(JSValueRef callback);
</ins><span class="cx"> 
</span><span class="cx">     void zoomToScale(double scale, JSValueRef callback);
</span><span class="cx"> 
</span><span class="lines">@@ -128,6 +129,7 @@
</span><span class="cx">     JSObjectRef contentVisibleRect() const;
</span><span class="cx">     
</span><span class="cx">     JSObjectRef selectionRangeViewRects() const;
</span><ins>+    JSObjectRef textSelectionCaretRect() const;
</ins><span class="cx"> 
</span><span class="cx">     void insertText(JSStringRef, int location, int length);
</span><span class="cx">     void removeAllDynamicDictionaries();
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunnercocoaTestRunnerWKWebViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view;
</span><span class="cx"> - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale;
</span><span class="cx"> - (void)_didFinishScrolling;
</span><ins>+- (void)_updateVisibleContentRects;
</ins><span class="cx"> 
</span><span class="cx"> @end
</span><span class="cx"> #endif
</span><span class="lines">@@ -183,6 +184,7 @@
</span><span class="cx"> - (void)_setStableStateOverride:(NSNumber *)overrideBoolean
</span><span class="cx"> {
</span><span class="cx">     m_stableStateOverride = overrideBoolean;
</span><ins>+    [self _updateVisibleContentRects];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunneriosUIScriptControllerIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -44,6 +44,16 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WTR {
</span><span class="cx"> 
</span><ins>+static NSDictionary *toNSDictionary(CGRect rect)
+{
+    return @{
+        @&quot;left&quot;: @(rect.origin.x),
+        @&quot;top&quot;: @(rect.origin.y),
+        @&quot;width&quot;: @(rect.size.width),
+        @&quot;height&quot;: @(rect.size.height)
+    };
+}
+
</ins><span class="cx"> void UIScriptController::doAsyncTask(JSValueRef callback)
</span><span class="cx"> {
</span><span class="cx">     unsigned callbackID = m_context-&gt;prepareForAsyncTask(callback, CallbackTypeNonPersistent);
</span><span class="lines">@@ -67,6 +77,17 @@
</span><span class="cx">     }];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef callback)
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()-&gt;platformView();
+
+    unsigned callbackID = m_context-&gt;prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+    [webView _doAfterNextStablePresentationUpdate:^() {
+        if (m_context)
+            m_context-&gt;asyncTaskComplete(callbackID);
+    }];
+}
+
</ins><span class="cx"> void UIScriptController::zoomToScale(double scale, JSValueRef callback)
</span><span class="cx"> {
</span><span class="cx">     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()-&gt;platformView();
</span><span class="lines">@@ -462,21 +483,18 @@
</span><span class="cx"> JSObjectRef UIScriptController::selectionRangeViewRects() const
</span><span class="cx"> {
</span><span class="cx">     NSMutableArray *selectionRects = [[NSMutableArray alloc] init];
</span><del>-    for (UIView *rectView in TestController::singleton().mainWebView()-&gt;platformView()._uiTextSelectionRectViews) {
-        if (rectView.hidden)
-            continue;
</del><ins>+    NSArray *rects = TestController::singleton().mainWebView()-&gt;platformView()._uiTextSelectionRects;
+    for (NSValue *rect in rects)
+        [selectionRects addObject:toNSDictionary([rect CGRectValue])];
</ins><span class="cx"> 
</span><del>-        CGRect frame = rectView.frame;
-        [selectionRects addObject:@{
-            @&quot;left&quot;: @(frame.origin.x),
-            @&quot;top&quot;: @(frame.origin.y),
-            @&quot;width&quot;: @(frame.size.width),
-            @&quot;height&quot;: @(frame.size.height),
-        }];
-    }
</del><span class="cx">     return JSValueToObject(m_context-&gt;jsContext(), [JSValue valueWithObject:selectionRects inContext:[JSContext contextWithJSGlobalContextRef:m_context-&gt;jsContext()]].JSValueRef, nullptr);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+JSObjectRef UIScriptController::textSelectionCaretRect() const
+{
+    return JSValueToObject(m_context-&gt;jsContext(), [JSValue valueWithObject:toNSDictionary(TestController::singleton().mainWebView()-&gt;platformView()._uiTextCaretRect) inContext:[JSContext contextWithJSGlobalContextRef:m_context-&gt;jsContext()]].JSValueRef, nullptr);
+}
+
</ins><span class="cx"> void UIScriptController::removeAllDynamicDictionaries()
</span><span class="cx"> {
</span><span class="cx">     [UIKeyboard removeAllDynamicDictionaries];
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunnermacUIScriptControllerMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm (209930 => 209931)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm        2016-12-16 20:01:01 UTC (rev 209930)
+++ trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm        2016-12-16 20:24:37 UTC (rev 209931)
</span><span class="lines">@@ -61,6 +61,11 @@
</span><span class="cx">     return doAsyncTask(callback);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef callback)
+{
+    doAsyncTask(callback);
+}
+
</ins><span class="cx"> void UIScriptController::insertText(JSStringRef text, int location, int length)
</span><span class="cx"> {
</span><span class="cx"> #if WK_API_ENABLED
</span></span></pre>
</div>
</div>

</body>
</html>