Visual viewports: carets and selection UI are incorrectly positioned when editing...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Dec 2016 20:24:37 +0000 (20:24 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Dec 2016 20:24:37 +0000 (20:24 +0000)
commit63089ee2c1c77558ad06563366b3f41f9820d78c
tree349923c5f5f255febe6b82926dd3c01ba5cc83cc
parent5fa71f7629306a35adea66425ef0efa948aaa160
Visual viewports: carets and selection UI are incorrectly positioned when editing fixed elements
https://bugs.webkit.org/show_bug.cgi?id=165767
<rdar://problem/29602382>

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
"selectionView.selection.caretRect".

(-[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 "selectionView.selection.selectionRects" for
the text selection assistant and @"selectionView.selectionRects" 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 "move" 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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@209931 268f45cc-cd09-0410-ab3c-d52691b4dbfc
35 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll-expected.txt [new file with mode: 0644]
LayoutTests/editing/caret/ios/absolute-caret-position-after-scroll.html [new file with mode: 0644]
LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll-expected.txt [new file with mode: 0644]
LayoutTests/editing/caret/ios/fixed-caret-position-after-scroll.html [new file with mode: 0644]
LayoutTests/editing/selection/ios/absolute-selection-after-scroll-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/ios/absolute-selection-after-scroll.html [new file with mode: 0644]
LayoutTests/editing/selection/ios/fixed-selection-after-scroll-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/ios/fixed-selection-after-scroll.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/FrameView.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/Platform/spi/ios/UIKitSPI.h
Source/WebKit2/Shared/EditorState.cpp
Source/WebKit2/Shared/EditorState.h
Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.h
Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit2/UIProcess/WebPageProxy.h
Source/WebKit2/UIProcess/ios/WKContentView.mm
Source/WebKit2/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm
Tools/ChangeLog
Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm
Tools/DumpRenderTree/mac/UIScriptControllerMac.mm
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm
Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm
Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm