Changeset 239543 in webkit


Ignore:
Timestamp:
Dec 22, 2018 10:38:24 PM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOS] Suppress native selection behaviors when focusing a very small editable element
https://bugs.webkit.org/show_bug.cgi?id=193005
<rdar://problem/46583527>

Reviewed by Tim Horton.

Source/WebKit:

In r238146, I added a mechanism to detect when the selection is hidden within transparent editable elements, and
used this to suppress native selection on iOS (such as selection handles, highlight, callout bar, etc.) to avoid
conflicts between the page's editing UI and the platform.

However, one additional technique observed on some websites involves hiding the selection by moving it into a
tiny (1x1) editable element. Here, we currently still present a callout bar with editing actions, as well as
show a selection caret or handles on iOS. To fix this, we extend the mechanism added in r238146 by also
suppressing the selection assistant in the case where the editable element's area is beneath a tiny minimum
threshold.

Test: editing/selection/ios/hide-selection-in-tiny-contenteditable.html

  • Shared/EditorState.cpp:

(WebKit::EditorState::PostLayoutData::encode const):
(WebKit::EditorState::PostLayoutData::decode):
(WebKit::operator<<):

  • Shared/EditorState.h:

Rename selectionClipRect to focusedElementRect. We currently propagate the bounds of the focused element to the
UI process through EditorState updates, but only for the purpose of returning it in the computed selection clip
rect; instead, rename this member to something more general-purpose, so we can also use it when determining
whether to suppress the selection assistant.

  • UIProcess/API/Cocoa/WKWebView.mm:

(-[WKWebView _candidateRect]):

  • UIProcess/Cocoa/WebViewImpl.mm:

(WebKit::WebViewImpl::handleRequestedCandidates):

  • UIProcess/ios/WKContentViewInteraction.h:

Add a new SuppressSelectionAssistantReason that corresponds to focusing tiny editable elements.

  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView _zoomToRevealFocusedElement]):
(-[WKContentView _selectionClipRect]):
(-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:]):
(-[WKContentView _updateChangedSelection:]):

Check the size of the focused element, and begin or stop suppressing the selection assistant accordingly.

  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::platformEditorState const):

  • WebProcess/WebPage/mac/WebPageMac.mm:

(WebKit::WebPage::platformEditorState const):

LayoutTests:

Add a new layout test to verify that native selection UI is suppressed when focusing a tiny (1px by 1px)
editable element.

  • editing/selection/ios/hide-selection-in-tiny-contenteditable-expected.txt: Added.
  • editing/selection/ios/hide-selection-in-tiny-contenteditable.html: Added.
  • resources/ui-helper.js:

(window.UIHelper.zoomToScale):

Location:
trunk
Files:
2 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r239537 r239543  
     12018-12-22  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Suppress native selection behaviors when focusing a very small editable element
     4        https://bugs.webkit.org/show_bug.cgi?id=193005
     5        <rdar://problem/46583527>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add a new layout test to verify that native selection UI is suppressed when focusing a tiny (1px by 1px)
     10        editable element.
     11
     12        * editing/selection/ios/hide-selection-in-tiny-contenteditable-expected.txt: Added.
     13        * editing/selection/ios/hide-selection-in-tiny-contenteditable.html: Added.
     14        * resources/ui-helper.js:
     15        (window.UIHelper.zoomToScale):
     16
    1172018-12-20  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
    218
  • trunk/LayoutTests/resources/ui-helper.js

    r239400 r239543  
    347347    }
    348348
     349    static zoomToScale(scale)
     350    {
     351        const uiScript = `uiController.zoomToScale(${scale}, () => uiController.uiScriptComplete())`;
     352        return new Promise(resolve => testRunner.runUIScript(uiScript, resolve));
     353    }
     354
    349355    static typeCharacter(characterString)
    350356    {
  • trunk/Source/WebKit/ChangeLog

    r239535 r239543  
     12018-12-22  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Suppress native selection behaviors when focusing a very small editable element
     4        https://bugs.webkit.org/show_bug.cgi?id=193005
     5        <rdar://problem/46583527>
     6
     7        Reviewed by Tim Horton.
     8
     9        In r238146, I added a mechanism to detect when the selection is hidden within transparent editable elements, and
     10        used this to suppress native selection on iOS (such as selection handles, highlight, callout bar, etc.) to avoid
     11        conflicts between the page's editing UI and the platform.
     12
     13        However, one additional technique observed on some websites involves hiding the selection by moving it into a
     14        tiny (1x1) editable element. Here, we currently still present a callout bar with editing actions, as well as
     15        show a selection caret or handles on iOS. To fix this, we extend the mechanism added in r238146 by also
     16        suppressing the selection assistant in the case where the editable element's area is beneath a tiny minimum
     17        threshold.
     18
     19        Test: editing/selection/ios/hide-selection-in-tiny-contenteditable.html
     20
     21        * Shared/EditorState.cpp:
     22        (WebKit::EditorState::PostLayoutData::encode const):
     23        (WebKit::EditorState::PostLayoutData::decode):
     24        (WebKit::operator<<):
     25        * Shared/EditorState.h:
     26
     27        Rename selectionClipRect to focusedElementRect. We currently propagate the bounds of the focused element to the
     28        UI process through EditorState updates, but only for the purpose of returning it in the computed selection clip
     29        rect; instead, rename this member to something more general-purpose, so we can also use it when determining
     30        whether to suppress the selection assistant.
     31
     32        * UIProcess/API/Cocoa/WKWebView.mm:
     33        (-[WKWebView _candidateRect]):
     34        * UIProcess/Cocoa/WebViewImpl.mm:
     35        (WebKit::WebViewImpl::handleRequestedCandidates):
     36        * UIProcess/ios/WKContentViewInteraction.h:
     37
     38        Add a new SuppressSelectionAssistantReason that corresponds to focusing tiny editable elements.
     39
     40        * UIProcess/ios/WKContentViewInteraction.mm:
     41        (-[WKContentView _zoomToRevealFocusedElement]):
     42        (-[WKContentView _selectionClipRect]):
     43        (-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:]):
     44        (-[WKContentView _updateChangedSelection:]):
     45
     46        Check the size of the focused element, and begin or stop suppressing the selection assistant accordingly.
     47
     48        * WebProcess/WebPage/ios/WebPageIOS.mm:
     49        (WebKit::WebPage::platformEditorState const):
     50        * WebProcess/WebPage/mac/WebPageMac.mm:
     51        (WebKit::WebPage::platformEditorState const):
     52
    1532018-12-20  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
    254
  • trunk/Source/WebKit/Shared/EditorState.cpp

    r239427 r239543  
    113113#endif
    114114#if PLATFORM(IOS_FAMILY) || PLATFORM(MAC)
    115     encoder << selectionClipRect;
     115    encoder << focusedElementRect;
    116116    encoder << selectedTextLength;
    117117    encoder << textAlignment;
     
    154154#endif
    155155#if PLATFORM(IOS_FAMILY) || PLATFORM(MAC)
    156     if (!decoder.decode(result.selectionClipRect))
     156    if (!decoder.decode(result.focusedElementRect))
    157157        return false;
    158158    if (!decoder.decode(result.selectedTextLength))
     
    263263#endif
    264264#if PLATFORM(IOS_FAMILY) || PLATFORM(MAC)
    265     if (editorState.postLayoutData().selectionClipRect != IntRect())
    266         ts.dumpProperty("selectionClipRect", editorState.postLayoutData().selectionClipRect);
     265    if (editorState.postLayoutData().focusedElementRect != IntRect())
     266        ts.dumpProperty("focusedElementRect", editorState.postLayoutData().focusedElementRect);
    267267    if (editorState.postLayoutData().selectedTextLength)
    268268        ts.dumpProperty("selectedTextLength", editorState.postLayoutData().selectedTextLength);
  • trunk/Source/WebKit/Shared/EditorState.h

    r239427 r239543  
    9090#endif
    9191#if PLATFORM(IOS_FAMILY) || PLATFORM(MAC)
    92         WebCore::IntRect selectionClipRect;
     92        WebCore::IntRect focusedElementRect;
    9393        uint64_t selectedTextLength { 0 };
    9494        uint32_t textAlignment { NoAlignment };
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm

    r239535 r239543  
    67406740- (NSRect)_candidateRect
    67416741{
    6742     return _page->editorState().postLayoutData().selectionClipRect;
     6742    return _page->editorState().postLayoutData().focusedElementRect;
    67436743}
    67446744
  • trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm

    r239535 r239543  
    32983298#if HAVE(TOUCH_BAR)
    32993299    NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
    3300     WebCore::IntRect offsetSelectionRect = postLayoutData.selectionClipRect;
     3300    WebCore::IntRect offsetSelectionRect = postLayoutData.focusedElementRect;
    33013301    offsetSelectionRect.move(0, offsetSelectionRect.height());
    33023302
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h

    r239454 r239543  
    164164enum SuppressSelectionAssistantReason : uint8_t {
    165165    FocusedElementIsTransparent = 1 << 0,
    166     DropAnimationIsRunning = 1 << 1
     166    FocusedElementIsTooSmall = 1 << 1,
     167    DropAnimationIsRunning = 1 << 2
    167168};
    168169
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r239454 r239543  
    13991399- (void)_zoomToRevealFocusedElement
    14001400{
    1401     if (_suppressSelectionAssistantReasons.contains(WebKit::FocusedElementIsTransparent))
     1401    if (_suppressSelectionAssistantReasons.contains(WebKit::FocusedElementIsTransparent) || _suppressSelectionAssistantReasons.contains(WebKit::FocusedElementIsTooSmall))
    14021402        return;
    14031403
     
    14661466    if (!hasFocusedElement(_focusedElementInformation))
    14671467        return CGRectNull;
    1468     return _page->editorState().postLayoutData().selectionClipRect;
     1468    return _page->editorState().postLayoutData().focusedElementRect;
    14691469}
    14701470
     
    44474447}
    44484448
     4449static const double minimumFocusedElementAreaForSuppressingSelectionAssistant = 4;
     4450
    44494451- (void)_elementDidFocus:(const WebKit::FocusedElementInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode changingActivityState:(BOOL)changingActivityState userObject:(NSObject <NSSecureCoding> *)userObject
    44504452{
     
    44744476    else
    44754477        [self _stopSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTransparent];
     4478
     4479    if (information.elementRect.area() < minimumFocusedElementAreaForSuppressingSelectionAssistant)
     4480        [self _beginSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTooSmall];
     4481    else
     4482        [self _stopSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTooSmall];
    44764483
    44774484    switch (startInputSessionPolicy) {
     
    50015008
    50025009    auto& postLayoutData = state.postLayoutData();
    5003     if (hasFocusedElement(_focusedElementInformation)) {
     5010    if (!state.selectionIsNone && hasFocusedElement(_focusedElementInformation)) {
    50045011        if (postLayoutData.elementIsTransparent)
    50055012            [self _beginSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTransparent];
    50065013        else
    50075014            [self _stopSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTransparent];
     5015
     5016        if (postLayoutData.focusedElementRect.area() < minimumFocusedElementAreaForSuppressingSelectionAssistant)
     5017            [self _beginSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTooSmall];
     5018        else
     5019            [self _stopSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTooSmall];
    50085020    }
    50095021
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r239535 r239543  
    242242    if (!selection.isNone()) {
    243243        if (m_focusedElement && m_focusedElement->renderer()) {
    244             postLayoutData.selectionClipRect = view->contentsToRootView(m_focusedElement->renderer()->absoluteBoundingBoxRect());
     244            postLayoutData.focusedElementRect = view->contentsToRootView(m_focusedElement->renderer()->absoluteBoundingBoxRect());
    245245            postLayoutData.caretColor = m_focusedElement->renderer()->style().caretColor();
    246246            postLayoutData.elementIsTransparent = m_focusedElement->renderer()->isTransparentRespectingParentFrames();
  • trunk/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm

    r238697 r239543  
    152152    selectedRange->absoluteTextQuads(quads);
    153153    if (!quads.isEmpty())
    154         postLayoutData.selectionClipRect = frame.view()->contentsToWindow(quads[0].enclosingBoundingBox());
     154        postLayoutData.focusedElementRect = frame.view()->contentsToWindow(quads[0].enclosingBoundingBox());
    155155    else {
    156156        // Range::absoluteTextQuads() will be empty at the start of a paragraph.
    157157        if (selection.isCaret())
    158             postLayoutData.selectionClipRect = frame.view()->contentsToWindow(frame.selection().absoluteCaretBounds());
     158            postLayoutData.focusedElementRect = frame.view()->contentsToWindow(frame.selection().absoluteCaretBounds());
    159159    }
    160160}
Note: See TracChangeset for help on using the changeset viewer.