[iOS] Callout menu overlaps in-page controls when editing a comment in github.com...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Feb 2019 00:48:16 +0000 (00:48 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Feb 2019 00:48:16 +0000 (00:48 +0000)
commit97da7c1062d556d2f4e642721922cf056e745a8b
treeb03c690921b980e383e0377775aa06492ab55e17
parentb5669f3ec2c2194ac69acbac59179493b8a6ae7b
[iOS] Callout menu overlaps in-page controls when editing a comment in github.com's issue tracker
https://bugs.webkit.org/show_bug.cgi?id=194873
<rdar://problem/46701974>

Reviewed by Tim Horton.

Source/WebKit:

On the topic of supporting web-based rich text editors on iOS, one problematic area has always been handling
conflicts between platform UI (i.e., the system callout menu) and in-page text editing controls. This issue
comes up in websites that don't use the "hidden contenteditable" approach to rich text editing, but also show
additional controls in a toolbar or contextual menu above the selection. In these cases, what often happens is
that system controls overlap controls in the page.

Luckily, the iOS callout menu (i.e. the private UICalloutBar) is capable of presenting with a list of "evasion
rects" to avoid; if the callout bar would normally intersect with one of these rects, then a different
orientation that does not intersect with one of these rects is chosen instead. Currently, the only rect added
here by UIKit when presenting the callout menu is the bounding rect of the on-screen keyboard, but after
<rdar://problem/48128337>, we now have a generalized mechanism for offering additional evasion rects before
UIKit presents the callout menu.

This patch adopts the mechanism introduced in <rdar://problem/48128337>, and introduces a heuristic for
determining the approximate location of controls in the page which might overlap the callout menu. This
heuristic works by hit-testing for clickable (but non-editable) nodes above the bounds of the selection, which
are additionally not hit-tested by advancing outwards from any of the other edges of the selection bounds.
Additionally, any hit-tested nodes whose bounding rects are very large (relative to the content view size) are
ignored (this deals with scenarios where the body or a large container element has a click handler). We then add
the bounding rects of each of the nodes that fit this criteria to the list of rects for UIKit to avoid when
presenting the system callout menu.

The result is that WebKit will, by default, avoid overlapping anything that looks like controls in the page when
showing a callout menu in editable content. In practice, this fixes overlapping controls on most websites that
roll their own context menu or toolbar in their rich text editor.

Test: editing/selection/ios/avoid-showing-callout-menu-over-controls.html

* Platform/spi/ios/UIKitSPI.h:
* UIProcess/WebPageProxy.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView requestAutocorrectionRectsForString:withCompletionHandler:]):
(-[WKContentView requestRectsToEvadeForSelectionCommandsWithCompletionHandler:]):
(-[WKContentView requestAutocorrectionContextWithCompletionHandler:]):

Drive-by: handle null completion handler arguments more gracefully, by raising an NSException and bailing before
attempting to invoke a nil block.

* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::requestEvasionRectsAboveSelection):

See above for more detail.

* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::requestEvasionRectsAboveSelection):

Tools:

Add a couple of UIScriptController methods to make callout menu testing on iOS easier (see below).

* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::menuRect const):
(WTR::UIScriptController::isShowingMenu const):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::menuRect const):

Add a function to query the bounds of the callout menu in content coordinates.

(WTR::UIScriptController::isShowingMenu const):

Add a function to query whether the callout menu is shown (i.e., has finished its appearance animation).

* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::rectForMenuAction const):
(WTR::UIScriptController::menuRect const):
(WTR::UIScriptController::isShowingMenu const):
(WTR::findViewInHierarchyOfType): Deleted.

LayoutTests:

Add a test to ensure that the we dodge clickable elements when showing the callout bar.

* editing/selection/ios/avoid-showing-callout-menu-over-controls-expected.txt: Added.
* editing/selection/ios/avoid-showing-callout-menu-over-controls.html: Added.
* resources/ui-helper.js:
(window.UIHelper.waitForMenuToShow.return.new.Promise):
(window.UIHelper.waitForMenuToShow):
(window.UIHelper.menuRect):
(window.UIHelper):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@241971 268f45cc-cd09-0410-ab3c-d52691b4dbfc
19 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/selection/ios/avoid-showing-callout-menu-over-controls-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/ios/avoid-showing-callout-menu-over-controls.html [new file with mode: 0644]
LayoutTests/resources/ui-helper.js
Source/WebKit/ChangeLog
Source/WebKit/Platform/spi/ios/UIKitSPI.h
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Tools/ChangeLog
Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h
Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm