Changeset 238146 in webkit


Ignore:
Timestamp:
Nov 13, 2018 2:30:27 PM (6 years ago)
Author:
Wenson Hsieh
Message:

[iOS] Do not show selection UI for editable elements with opacity near zero
https://bugs.webkit.org/show_bug.cgi?id=191442
<rdar://problem/45958625>

Reviewed by Simon Fraser.

Source/WebCore:

Tests: editing/selection/ios/do-not-zoom-to-focused-hidden-contenteditable.html

editing/selection/ios/hide-selection-after-hiding-contenteditable.html
editing/selection/ios/hide-selection-in-contenteditable-nested-transparency.html
editing/selection/ios/hide-selection-in-hidden-contenteditable-frame.html
editing/selection/ios/hide-selection-in-hidden-contenteditable.html

  • rendering/RenderObject.cpp:

(WebCore::RenderObject::isTransparentRespectingParentFrames const):

Add a helper function to determine whether a RenderObject is contained within a transparent layer, taking parent
frames into account. A layer is considered transparent if its opacity is less than a small threshold (i.e. 0.01).
Opacity on ancestor elements is applied multiplicatively.

  • rendering/RenderObject.h:

Source/WebKit:

Add support for suppressing native selection UI (for instance, selection highlight views, selection handles, and
selection-related gestures) when the selection is inside a transparent editable element. This helps maintain
compatibility with text editors that work by capturing key events and input events hidden contenteditable
elements, and reflect these changes in different document or different part of the document.

Since selection UI is rendered in the UI process on iOS using element geometry propagated from the web process,
selection rendering is entirely decoupled from the process of painting in the web process. This means that if
the editable root has an opacity of 0, we would correctly hide the caret and selection on macOS, but draw over
the transparent element on iOS. When these hidden editable elements are focused, this often results in unwanted
behaviors, such as double caret painting, native and custom selection UI from the page being drawn on top of one
another, and the ability to change selection via tap and loupe gestures within hidden text.

To fix this, we compute whether the focused element is transparent when an element is focused, or when the
selection changes, and send this information over to the UI process via AssistedNodeInformation and
EditorState. In the UI process, we then respect this information by suppressing the selection assistant if the
focused element is transparent; this disables showing and laying out selection views, as well as gestures
associated with selection overlays. However, this still allows for contextual autocorrection and spell checking.

  • Shared/AssistedNodeInformation.cpp:

(WebKit::AssistedNodeInformation::encode const):
(WebKit::AssistedNodeInformation::decode):

  • Shared/AssistedNodeInformation.h:
  • Shared/EditorState.cpp:

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

  • Shared/EditorState.h:

Add elementIsTransparent flags, and also add boilerplate IPC code.

  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView _displayFormNodeInputView]):

Prevent zooming to the focused element if the focused element is hidden.

(-[WKContentView hasSelectablePositionAtPoint:]):
(-[WKContentView pointIsNearMarkedText:]):
(-[WKContentView textInteractionGesture:shouldBeginAtPoint:]):

Don't allow these text interaction gestures to begin while suppressing the selection assistant.

(-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:]):

When an element is focused, begin suppressing the selection assistant if the element is fully transparent.

(-[WKContentView _stopAssistingNode]):

When the focused element is blurred, reset state by ending selection assistant suppression (additionally
reactivating the selection assistant if needed). This ensures that selection in non-editable text isn't broken
after focusing a hidden editable element.

(-[WKContentView _updateChangedSelection:]):

If needed, suppress or un-suppress the selection assistant when the selection changes. On certain rich text
editors, a combination of custom selection UI and native selection UI is used. For instance, on Microsoft Office
365, caret selections are rendered using the native caret view, but as soon as the selection becomes ranged, the
editable root becomes fully transparent, and Office's selection UI takes over.

(-[WKContentView _shouldSuppressSelectionCommands]):

Override this UIKit SPI hook to suppress selection commands (e.g. the callout bar) when suppressing the
selection assistant.

  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::platformEditorState const):
(WebKit::WebPage::getAssistedNodeInformation):

Compute and set elementIsTransparent using the assisted node.

Tools:

Add a couple of new testing helpers to UIScriptController.

  • TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
  • TestRunnerShared/UIScriptContext/UIScriptController.cpp:

(WTR::UIScriptController::textSelectionRangeRects const):
(WTR::UIScriptController::selectionCaretViewRect const):
(WTR::UIScriptController::selectionRangeViewRects const):

  • TestRunnerShared/UIScriptContext/UIScriptController.h:
  • WebKitTestRunner/ios/UIScriptControllerIOS.mm:

(WTR::UIScriptController::textSelectionRangeRects const):

Rename selectionRangeViewRects to textSelectionRangeRects. This allows us to draw a distinction between
textSelectionRangeRects/textSelectionCaretRect, which retrieve information about selection rects known
to the text interaction assistant, and selectionCaretViewRect/selectionRangeViewRects, which retrieve the
actual frames of the selection views used to draw overlaid selection UI. This difference is important in the
new layout tests added in this patch, which only suppress caret rendering (i.e. selection views remain hidden).

Also, drive-by fix a leaked NSMutableArray.

(WTR::UIScriptController::selectionStartGrabberViewRect const):
(WTR::UIScriptController::selectionEndGrabberViewRect const):
(WTR::UIScriptController::selectionCaretViewRect const):
(WTR::UIScriptController::selectionRangeViewRects const):

Testing helpers to grab the frames of caret and selection views, in WKContentView's coordinate space. These
rects are also clamped to WKContentView bounds.

LayoutTests:

Add 5 new layout tests. See below for more details.

  • editing/selection/character-granularity-rect.html:

Adjust for a renamed UIScriptController function.

  • editing/selection/ios/do-not-zoom-to-focused-hidden-contenteditable-expected.txt: Added.
  • editing/selection/ios/do-not-zoom-to-focused-hidden-contenteditable.html: Added.

Add a test to verify that we don't zoom to fit the focused element, if the focused element is completely
transparent.

  • editing/selection/ios/hide-selection-after-hiding-contenteditable-expected.txt: Added.
  • editing/selection/ios/hide-selection-after-hiding-contenteditable.html: Added.

Add a test to verify that selection UI is hidden after making an editable root transparent, and shown again when
the editable root becomes opaque.

  • editing/selection/ios/hide-selection-in-contenteditable-nested-transparency-expected.txt: Added.
  • editing/selection/ios/hide-selection-in-contenteditable-nested-transparency.html: Added.

Add a test to verify that transparency applied on an editable root via nested transparent containers causes
selection UI to be suppressed.

  • editing/selection/ios/hide-selection-in-hidden-contenteditable-expected.txt: Added.
  • editing/selection/ios/hide-selection-in-hidden-contenteditable-frame-expected.txt: Added.
  • editing/selection/ios/hide-selection-in-hidden-contenteditable-frame.html: Added.

Add a test to verify that selection UI is suppressed when an editable element inside a subframe is focused. This
test checks that the caret, selection rects and selection handle views are not shown, and additionally verifies
that the selection in a hidden contenteditable area cannot be changed via tap gesture.

  • editing/selection/ios/hide-selection-in-hidden-contenteditable.html: Added.

Same test as above, but in a regular editable element in the main document instead of a subframe.

  • resources/ui-helper.js:

(window.UIHelper.getUISelectionRects.return.new.Promise.):
(window.UIHelper.getUISelectionRects.return.new.Promise):
(window.UIHelper.getUISelectionRects):
(window.UIHelper.getUICaretViewRect.return.new.Promise.):
(window.UIHelper.getUICaretViewRect.return.new.Promise):
(window.UIHelper.getUICaretViewRect):

Add new UIHelper wrapper methods. See Tools/ChangeLog for more detail.

Location:
trunk
Files:
10 added
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r238140 r238146  
     12018-11-13  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Do not show selection UI for editable elements with opacity near zero
     4        https://bugs.webkit.org/show_bug.cgi?id=191442
     5        <rdar://problem/45958625>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Add 5 new layout tests. See below for more details.
     10
     11        * editing/selection/character-granularity-rect.html:
     12
     13        Adjust for a renamed UIScriptController function.
     14
     15        * editing/selection/ios/do-not-zoom-to-focused-hidden-contenteditable-expected.txt: Added.
     16        * editing/selection/ios/do-not-zoom-to-focused-hidden-contenteditable.html: Added.
     17
     18        Add a test to verify that we don't zoom to fit the focused element, if the focused element is completely
     19        transparent.
     20
     21        * editing/selection/ios/hide-selection-after-hiding-contenteditable-expected.txt: Added.
     22        * editing/selection/ios/hide-selection-after-hiding-contenteditable.html: Added.
     23
     24        Add a test to verify that selection UI is hidden after making an editable root transparent, and shown again when
     25        the editable root becomes opaque.
     26
     27        * editing/selection/ios/hide-selection-in-contenteditable-nested-transparency-expected.txt: Added.
     28        * editing/selection/ios/hide-selection-in-contenteditable-nested-transparency.html: Added.
     29
     30        Add a test to verify that transparency applied on an editable root via nested transparent containers causes
     31        selection UI to be suppressed.
     32
     33        * editing/selection/ios/hide-selection-in-hidden-contenteditable-expected.txt: Added.
     34        * editing/selection/ios/hide-selection-in-hidden-contenteditable-frame-expected.txt: Added.
     35        * editing/selection/ios/hide-selection-in-hidden-contenteditable-frame.html: Added.
     36
     37        Add a test to verify that selection UI is suppressed when an editable element inside a subframe is focused. This
     38        test checks that the caret, selection rects and selection handle views are not shown, and additionally verifies
     39        that the selection in a hidden contenteditable area cannot be changed via tap gesture.
     40
     41        * editing/selection/ios/hide-selection-in-hidden-contenteditable.html: Added.
     42
     43        Same test as above, but in a regular editable element in the main document instead of a subframe.
     44
     45        * resources/ui-helper.js:
     46        (window.UIHelper.getUISelectionRects.return.new.Promise.):
     47        (window.UIHelper.getUISelectionRects.return.new.Promise):
     48        (window.UIHelper.getUISelectionRects):
     49        (window.UIHelper.getUICaretViewRect.return.new.Promise.):
     50        (window.UIHelper.getUICaretViewRect.return.new.Promise):
     51        (window.UIHelper.getUICaretViewRect):
     52
     53        Add new UIHelper wrapper methods. See Tools/ChangeLog for more detail.
     54
    1552018-11-13  Matt Baker  <mattbaker@apple.com>
    256
  • trunk/LayoutTests/editing/selection/character-granularity-rect.html

    r206449 r238146  
    2323        (function() {
    2424            uiController.longPressAtPoint(30, 20, function() {
    25                 uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects));
     25                uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionRangeRects));
    2626            });
    2727        })();`
     
    3636        if (testRunner.runUIScript) {
    3737            testRunner.runUIScript(getUIScript(), function(result) {
    38                 var selectionRangeViewRects = JSON.parse(result);
     38                var textSelectionRangeRects = JSON.parse(result);
    3939                var output;
    40                 if (selectionRangeViewRects.length !== 1)
     40                if (textSelectionRangeRects.length !== 1)
    4141                    output = 'FAIL: Unexpected number of selection range views: ' + result;
    4242                else {
    43                     var rect = selectionRangeViewRects[0];
     43                    var rect = textSelectionRangeRects[0];
    4444                    if (rect.left != 8 || rect.top != 8 || rect.width != 112 || rect.height != 17 )
    4545                        output = 'FAIL: Unexpected selection range view frame: ' + result;
  • trunk/LayoutTests/resources/ui-helper.js

    r238108 r238146  
    171171            testRunner.runUIScript(`(function() {
    172172                uiController.doAfterNextStablePresentationUpdate(function() {
     173                    uiController.uiScriptComplete(JSON.stringify(uiController.textSelectionRangeRects));
     174                });
     175            })()`, jsonString => {
     176                resolve(JSON.parse(jsonString));
     177            });
     178        });
     179    }
     180
     181    static getUICaretViewRect()
     182    {
     183        if (!this.isWebKit2() || !this.isIOS())
     184            return Promise.resolve();
     185
     186        return new Promise(resolve => {
     187            testRunner.runUIScript(`(function() {
     188                uiController.doAfterNextStablePresentationUpdate(function() {
     189                    uiController.uiScriptComplete(JSON.stringify(uiController.selectionCaretViewRect));
     190                });
     191            })()`, jsonString => {
     192                resolve(JSON.parse(jsonString));
     193            });
     194        });
     195    }
     196
     197    static getUISelectionViewRects()
     198    {
     199        if (!this.isWebKit2() || !this.isIOS())
     200            return Promise.resolve();
     201
     202        return new Promise(resolve => {
     203            testRunner.runUIScript(`(function() {
     204                uiController.doAfterNextStablePresentationUpdate(function() {
    173205                    uiController.uiScriptComplete(JSON.stringify(uiController.selectionRangeViewRects));
    174206                });
  • trunk/Source/WebCore/ChangeLog

    r238145 r238146  
     12018-11-13  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Do not show selection UI for editable elements with opacity near zero
     4        https://bugs.webkit.org/show_bug.cgi?id=191442
     5        <rdar://problem/45958625>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Tests: editing/selection/ios/do-not-zoom-to-focused-hidden-contenteditable.html
     10               editing/selection/ios/hide-selection-after-hiding-contenteditable.html
     11               editing/selection/ios/hide-selection-in-contenteditable-nested-transparency.html
     12               editing/selection/ios/hide-selection-in-hidden-contenteditable-frame.html
     13               editing/selection/ios/hide-selection-in-hidden-contenteditable.html
     14
     15        * rendering/RenderObject.cpp:
     16        (WebCore::RenderObject::isTransparentRespectingParentFrames const):
     17
     18        Add a helper function to determine whether a RenderObject is contained within a transparent layer, taking parent
     19        frames into account. A layer is considered transparent if its opacity is less than a small threshold (i.e. 0.01).
     20        Opacity on ancestor elements is applied multiplicatively.
     21
     22        * rendering/RenderObject.h:
     23
    1242018-11-13  Eric Carlson  <eric.carlson@apple.com>
    225
  • trunk/Source/WebCore/rendering/RenderObject.cpp

    r238001 r238146  
    15151515}
    15161516
     1517bool RenderObject::isTransparentRespectingParentFrames() const
     1518{
     1519    static const double minimumVisibleOpacity = 0.01;
     1520
     1521    float currentOpacity = 1;
     1522    auto* layer = enclosingLayer();
     1523    while (layer) {
     1524        auto& layerRenderer = layer->renderer();
     1525        currentOpacity *= layerRenderer.style().opacity();
     1526        if (currentOpacity < minimumVisibleOpacity)
     1527            return true;
     1528
     1529        auto* parentLayer = layer->parent();
     1530        if (!parentLayer) {
     1531            if (!is<RenderView>(layerRenderer))
     1532                return false;
     1533
     1534            auto& enclosingFrame = downcast<RenderView>(layerRenderer).view().frame();
     1535            if (enclosingFrame.isMainFrame())
     1536                return false;
     1537
     1538            if (auto *frameOwnerRenderer = enclosingFrame.ownerElement()->renderer())
     1539                parentLayer = frameOwnerRenderer->enclosingLayer();
     1540        }
     1541        layer = parentLayer;
     1542    }
     1543    return false;
     1544}
     1545
    15171546Position RenderObject::positionForPoint(const LayoutPoint& point)
    15181547{
  • trunk/Source/WebCore/rendering/RenderObject.h

    r238001 r238146  
    797797    void initializeFragmentedFlowStateOnInsertion();
    798798    virtual void insertedIntoTree();
     799
     800    WEBCORE_EXPORT bool isTransparentRespectingParentFrames() const;
    799801
    800802protected:
  • trunk/Source/WebKit/ChangeLog

    r238142 r238146  
     12018-11-13  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Do not show selection UI for editable elements with opacity near zero
     4        https://bugs.webkit.org/show_bug.cgi?id=191442
     5        <rdar://problem/45958625>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Add support for suppressing native selection UI (for instance, selection highlight views, selection handles, and
     10        selection-related gestures) when the selection is inside a transparent editable element. This helps maintain
     11        compatibility with text editors that work by capturing key events and input events hidden contenteditable
     12        elements, and reflect these changes in different document or different part of the document.
     13
     14        Since selection UI is rendered in the UI process on iOS using element geometry propagated from the web process,
     15        selection rendering is entirely decoupled from the process of painting in the web process. This means that if
     16        the editable root has an opacity of 0, we would correctly hide the caret and selection on macOS, but draw over
     17        the transparent element on iOS. When these hidden editable elements are focused, this often results in unwanted
     18        behaviors, such as double caret painting, native and custom selection UI from the page being drawn on top of one
     19        another, and the ability to change selection via tap and loupe gestures within hidden text.
     20
     21        To fix this, we compute whether the focused element is transparent when an element is focused, or when the
     22        selection changes, and send this information over to the UI process via `AssistedNodeInformation` and
     23        `EditorState`. In the UI process, we then respect this information by suppressing the selection assistant if the
     24        focused element is transparent; this disables showing and laying out selection views, as well as gestures
     25        associated with selection overlays. However, this still allows for contextual autocorrection and spell checking.
     26
     27        * Shared/AssistedNodeInformation.cpp:
     28        (WebKit::AssistedNodeInformation::encode const):
     29        (WebKit::AssistedNodeInformation::decode):
     30        * Shared/AssistedNodeInformation.h:
     31        * Shared/EditorState.cpp:
     32        (WebKit::EditorState::PostLayoutData::encode const):
     33        (WebKit::EditorState::PostLayoutData::decode):
     34        * Shared/EditorState.h:
     35
     36        Add `elementIsTransparent` flags, and also add boilerplate IPC code.
     37
     38        * UIProcess/ios/WKContentViewInteraction.mm:
     39        (-[WKContentView _displayFormNodeInputView]):
     40
     41        Prevent zooming to the focused element if the focused element is hidden.
     42
     43        (-[WKContentView hasSelectablePositionAtPoint:]):
     44        (-[WKContentView pointIsNearMarkedText:]):
     45        (-[WKContentView textInteractionGesture:shouldBeginAtPoint:]):
     46
     47        Don't allow these text interaction gestures to begin while suppressing the selection assistant.
     48
     49        (-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:]):
     50
     51        When an element is focused, begin suppressing the selection assistant if the element is fully transparent.
     52
     53        (-[WKContentView _stopAssistingNode]):
     54
     55        When the focused element is blurred, reset state by ending selection assistant suppression (additionally
     56        reactivating the selection assistant if needed). This ensures that selection in non-editable text isn't broken
     57        after focusing a hidden editable element.
     58
     59        (-[WKContentView _updateChangedSelection:]):
     60
     61        If needed, suppress or un-suppress the selection assistant when the selection changes. On certain rich text
     62        editors, a combination of custom selection UI and native selection UI is used. For instance, on Microsoft Office
     63        365, caret selections are rendered using the native caret view, but as soon as the selection becomes ranged, the
     64        editable root becomes fully transparent, and Office's selection UI takes over.
     65
     66        (-[WKContentView _shouldSuppressSelectionCommands]):
     67
     68        Override this UIKit SPI hook to suppress selection commands (e.g. the callout bar) when suppressing the
     69        selection assistant.
     70
     71        * WebProcess/WebPage/ios/WebPageIOS.mm:
     72        (WebKit::WebPage::platformEditorState const):
     73        (WebKit::WebPage::getAssistedNodeInformation):
     74
     75        Compute and set `elementIsTransparent` using the assisted node.
     76
    1772018-11-13  Ryan Haddad  <ryanhaddad@apple.com>
    278
  • trunk/Source/WebKit/Shared/AssistedNodeInformation.cpp

    r237266 r238146  
    9292    encoder << acceptsAutofilledLoginCredentials;
    9393    encoder << isAutofillableUsernameField;
     94    encoder << elementIsTransparent;
    9495    encoder << representingPageURL;
    9596    encoder.encodeEnum(autofillFieldName);
     
    192193        return false;
    193194
     195    if (!decoder.decode(result.elementIsTransparent))
     196        return false;
     197
    194198    if (!decoder.decode(result.representingPageURL))
    195199        return false;
  • trunk/Source/WebKit/Shared/AssistedNodeInformation.h

    r237266 r238146  
    121121    bool acceptsAutofilledLoginCredentials { false };
    122122    bool isAutofillableUsernameField { false };
     123    bool elementIsTransparent { false };
    123124    WebCore::URL representingPageURL;
    124125    WebCore::AutofillFieldName autofillFieldName { WebCore::AutofillFieldName::None };
  • trunk/Source/WebKit/Shared/EditorState.cpp

    r237266 r238146  
    132132    encoder << insideFixedPosition;
    133133    encoder << hasPlainText;
     134    encoder << elementIsTransparent;
    134135    encoder << caretColor;
    135136#endif
     
    187188        return false;
    188189    if (!decoder.decode(result.hasPlainText))
     190        return false;
     191    if (!decoder.decode(result.elementIsTransparent))
    189192        return false;
    190193    if (!decoder.decode(result.caretColor))
  • trunk/Source/WebKit/Shared/EditorState.h

    r237266 r238146  
    108108        bool insideFixedPosition { false };
    109109        bool hasPlainText { false };
     110        bool elementIsTransparent { false };
    110111        WebCore::Color caretColor;
    111112#endif
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r238078 r238146  
    13431343- (void)_displayFormNodeInputView
    13441344{
    1345     // In case user scaling is force enabled, do not use that scaling when zooming in with an input field.
    1346     // Zooming above the page's default scale factor should only happen when the user performs it.
    1347     [self _zoomToFocusRect:_assistedNodeInformation.elementRect
    1348         selectionRect:_didAccessoryTabInitiateFocus ? IntRect() : _assistedNodeInformation.selectionRect
    1349         insideFixed:_assistedNodeInformation.insideFixedPosition
    1350         fontSize:_assistedNodeInformation.nodeFontSize
    1351         minimumScale:_assistedNodeInformation.minimumScaleFactor
    1352         maximumScale:_assistedNodeInformation.maximumScaleFactorIgnoringAlwaysScalable
    1353         allowScaling:_assistedNodeInformation.allowsUserScalingIgnoringAlwaysScalable && !currentUserInterfaceIdiomIsPad()
    1354         forceScroll:(_assistedNodeInformation.inputMode == InputMode::None) ? !currentUserInterfaceIdiomIsPad() : [self requiresAccessoryView]];
     1345    if (!self.suppressAssistantSelectionView) {
     1346        // In case user scaling is force enabled, do not use that scaling when zooming in with an input field.
     1347        // Zooming above the page's default scale factor should only happen when the user performs it.
     1348        [self _zoomToFocusRect:_assistedNodeInformation.elementRect
     1349            selectionRect:_didAccessoryTabInitiateFocus ? IntRect() : _assistedNodeInformation.selectionRect
     1350            insideFixed:_assistedNodeInformation.insideFixedPosition
     1351            fontSize:_assistedNodeInformation.nodeFontSize
     1352            minimumScale:_assistedNodeInformation.minimumScaleFactor
     1353            maximumScale:_assistedNodeInformation.maximumScaleFactorIgnoringAlwaysScalable
     1354            allowScaling:_assistedNodeInformation.allowsUserScalingIgnoringAlwaysScalable && !currentUserInterfaceIdiomIsPad()
     1355            forceScroll:(_assistedNodeInformation.inputMode == InputMode::None) ? !currentUserInterfaceIdiomIsPad() : [self requiresAccessoryView]];
     1356    }
    13551357
    13561358    _didAccessoryTabInitiateFocus = NO;
     
    17471749        return NO;
    17481750
     1751    if (self.suppressAssistantSelectionView)
     1752        return NO;
     1753
    17491754    if (_inspectorNodeSearchEnabled)
    17501755        return NO;
     
    17701775        return NO;
    17711776
     1777    if (self.suppressAssistantSelectionView)
     1778        return NO;
     1779
    17721780    InteractionInformationRequest request(roundedIntPoint(point));
    17731781    if (![self ensurePositionInformationIsUpToDate:request])
     
    17791787{
    17801788    if (!_webView.configuration._textInteractionGesturesEnabled)
     1789        return NO;
     1790
     1791    if (self.suppressAssistantSelectionView)
    17811792        return NO;
    17821793
     
    42684279        startInputSessionPolicy = [inputDelegate _webView:_webView decidePolicyForFocusedElement:focusedElementInfo.get()];
    42694280
     4281    self.suppressAssistantSelectionView = information.elementIsTransparent;
     4282
    42704283    switch (startInputSessionPolicy) {
    42714284    case _WKFocusStartsInputSessionPolicyAuto:
     
    44104423
    44114424    [_webView didEndFormControlInteraction];
     4425
     4426    self.suppressAssistantSelectionView = NO;
    44124427}
    44134428
     
    47424757- (void)_updateChangedSelection:(BOOL)force
    47434758{
    4744     if (!_selectionNeedsUpdate || _page->editorState().isMissingPostLayoutData)
    4745         return;
     4759    auto& state = _page->editorState();
     4760    if (state.isMissingPostLayoutData)
     4761        return;
     4762
     4763    auto& postLayoutData = state.postLayoutData();
     4764    if (hasAssistedNode(_assistedNodeInformation))
     4765        self.suppressAssistantSelectionView = postLayoutData.elementIsTransparent;
    47464766
    47474767    WKSelectionDrawingInfo selectionDrawingInfo(_page->editorState());
     
    47654785    }
    47664786
    4767     auto& state = _page->editorState();
    4768     if (!state.isMissingPostLayoutData && state.postLayoutData().isStableStateUpdate && _needsDeferredEndScrollingSelectionUpdate && _page->inStableState()) {
     4787    if (postLayoutData.isStableStateUpdate && _needsDeferredEndScrollingSelectionUpdate && _page->inStableState()) {
    47694788        [[self selectionInteractionAssistant] showSelectionCommands];
    47704789
     
    47764795        _needsDeferredEndScrollingSelectionUpdate = NO;
    47774796    }
     4797}
     4798
     4799- (BOOL)_shouldSuppressSelectionCommands
     4800{
     4801    return _suppressAssistantSelectionView;
    47784802}
    47794803
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r238078 r238146  
    241241            postLayoutData.selectionClipRect = view->contentsToRootView(m_assistedNode->renderer()->absoluteBoundingBoxRect());
    242242            postLayoutData.caretColor = m_assistedNode->renderer()->style().caretColor();
     243            postLayoutData.elementIsTransparent = m_assistedNode->renderer()->isTransparentRespectingParentFrames();
    243244        }
    244245        computeEditableRootHasContentAndPlainText(selection, postLayoutData);
     
    23382339        information.elementRect = elementRectInRootViewCoordinates(*m_assistedNode, elementFrame);
    23392340        information.nodeFontSize = renderer->style().fontDescription().computedSize();
     2341        information.elementIsTransparent = renderer->isTransparentRespectingParentFrames();
    23402342
    23412343        bool inFixed = false;
  • trunk/Tools/ChangeLog

    r238134 r238146  
     12018-11-13  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Do not show selection UI for editable elements with opacity near zero
     4        https://bugs.webkit.org/show_bug.cgi?id=191442
     5        <rdar://problem/45958625>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Add a couple of new testing helpers to UIScriptController.
     10
     11        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
     12        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
     13        (WTR::UIScriptController::textSelectionRangeRects const):
     14        (WTR::UIScriptController::selectionCaretViewRect const):
     15        (WTR::UIScriptController::selectionRangeViewRects const):
     16        * TestRunnerShared/UIScriptContext/UIScriptController.h:
     17        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
     18        (WTR::UIScriptController::textSelectionRangeRects const):
     19
     20        Rename `selectionRangeViewRects` to `textSelectionRangeRects`. This allows us to draw a distinction between
     21        `textSelectionRangeRects`/`textSelectionCaretRect`, which retrieve information about selection rects known
     22        to the text interaction assistant, and `selectionCaretViewRect`/`selectionRangeViewRects`, which retrieve the
     23        actual frames of the selection views used to draw overlaid selection UI. This difference is important in the
     24        new layout tests added in this patch, which only suppress caret rendering (i.e. selection views remain hidden).
     25
     26        Also, drive-by fix a leaked `NSMutableArray`.
     27
     28        (WTR::UIScriptController::selectionStartGrabberViewRect const):
     29        (WTR::UIScriptController::selectionEndGrabberViewRect const):
     30        (WTR::UIScriptController::selectionCaretViewRect const):
     31        (WTR::UIScriptController::selectionRangeViewRects const):
     32
     33        Testing helpers to grab the frames of caret and selection views, in WKContentView's coordinate space. These
     34        rects are also clamped to WKContentView bounds.
     35
    1362018-11-13  Daniel Bates  <dabates@apple.com>
    237
  • trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm

    r238108 r238146  
    310310}
    311311
     312JSObjectRef UIScriptController::textSelectionRangeRects() const
     313{
     314    return nullptr;
     315}
     316
     317JSObjectRef UIScriptController::textSelectionCaretRect() const
     318{
     319    return nullptr;
     320}
     321
     322JSObjectRef UIScriptController::inputViewBounds() const
     323{
     324    return nullptr;
     325}
     326
     327void UIScriptController::removeAllDynamicDictionaries()
     328{
     329}
     330
     331JSRetainPtr<JSStringRef> UIScriptController::scrollingTreeAsText() const
     332{
     333    return nullptr;
     334}
     335
     336JSObjectRef UIScriptController::propertiesOfLayerWithID(uint64_t layerID) const
     337{
     338    return nullptr;
     339}
     340
     341void UIScriptController::retrieveSpeakSelectionContent(JSValueRef)
     342{
     343}
     344
     345JSRetainPtr<JSStringRef> UIScriptController::accessibilitySpeakSelectionContent() const
     346{
     347    return nullptr;
     348}
     349
     350void UIScriptController::simulateRotation(DeviceOrientation*, JSValueRef)
     351{
     352}
     353
     354void UIScriptController::simulateRotationLikeSafari(DeviceOrientation*, JSValueRef)
     355{
     356}
     357
     358void UIScriptController::findString(JSStringRef, unsigned long options, unsigned long maxCount)
     359{
     360}
     361
     362void UIScriptController::removeViewFromWindow(JSValueRef)
     363{
     364}
     365
     366void UIScriptController::addViewToWindow(JSValueRef)
     367{
     368}
     369
     370void UIScriptController::setSafeAreaInsets(double, double, double, double)
     371{
     372}
     373
     374void UIScriptController::beginBackSwipe(JSValueRef callback)
     375{
     376}
     377
     378void UIScriptController::completeBackSwipe(JSValueRef callback)
     379{
     380}
     381
     382JSObjectRef UIScriptController::selectionStartGrabberViewRect() const
     383{
     384    return nullptr;
     385}
     386
     387JSObjectRef UIScriptController::selectionEndGrabberViewRect() const
     388{
     389    return nullptr;
     390}
     391
     392JSObjectRef UIScriptController::selectionCaretViewRect() const
     393{
     394    return nullptr;
     395}
     396
    312397JSObjectRef UIScriptController::selectionRangeViewRects() const
    313398{
     
    315400}
    316401
    317 JSObjectRef UIScriptController::textSelectionCaretRect() const
    318 {
    319     return nullptr;
    320 }
    321 
    322 JSObjectRef UIScriptController::inputViewBounds() const
    323 {
    324     return nullptr;
    325 }
    326 
    327 void UIScriptController::removeAllDynamicDictionaries()
    328 {
    329 }
    330 
    331 JSRetainPtr<JSStringRef> UIScriptController::scrollingTreeAsText() const
    332 {
    333     return nullptr;
    334 }
    335 
    336 JSObjectRef UIScriptController::propertiesOfLayerWithID(uint64_t layerID) const
    337 {
    338     return nullptr;
    339 }
    340 
    341 void UIScriptController::retrieveSpeakSelectionContent(JSValueRef)
    342 {
    343 }
    344 
    345 JSRetainPtr<JSStringRef> UIScriptController::accessibilitySpeakSelectionContent() const
    346 {
    347     return nullptr;
    348 }
    349 
    350 void UIScriptController::simulateRotation(DeviceOrientation*, JSValueRef)
    351 {
    352 }
    353 
    354 void UIScriptController::simulateRotationLikeSafari(DeviceOrientation*, JSValueRef)
    355 {
    356 }
    357 
    358 void UIScriptController::findString(JSStringRef, unsigned long options, unsigned long maxCount)
    359 {
    360 }
    361 
    362 void UIScriptController::removeViewFromWindow(JSValueRef)
    363 {
    364 }
    365 
    366 void UIScriptController::addViewToWindow(JSValueRef)
    367 {
    368 }
    369 
    370 void UIScriptController::setSafeAreaInsets(double, double, double, double)
    371 {
    372 }
    373 
    374 void UIScriptController::beginBackSwipe(JSValueRef callback)
    375 {
    376 }
    377 
    378 void UIScriptController::completeBackSwipe(JSValueRef callback)
    379 {
    380 }
    381 
    382 JSObjectRef UIScriptController::selectionStartGrabberViewRect() const
    383 {
    384     return nullptr;
    385 }
    386 
    387 JSObjectRef UIScriptController::selectionEndGrabberViewRect() const
    388 {
    389     return nullptr;
    390 }
    391 
    392402bool UIScriptController::isShowingDataListSuggestions() const
    393403{
  • trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl

    r238108 r238146  
    239239    readonly attribute object contentVisibleRect; // Returned object has 'left', 'top', 'width', 'height' properties.
    240240
    241     readonly attribute object selectionRangeViewRects; // An array of objects with 'left', 'top', 'width', and 'height' properties.
     241    readonly attribute object textSelectionRangeRects; // An array of objects with 'left', 'top', 'width', and 'height' properties.
    242242    readonly attribute object textSelectionCaretRect; // An object with 'left', 'top', 'width', 'height' properties.
    243243    readonly attribute object selectionStartGrabberViewRect;
    244244    readonly attribute object selectionEndGrabberViewRect;
     245    readonly attribute object selectionCaretViewRect;
     246    readonly attribute object selectionRangeViewRects;
    245247    readonly attribute object calendarType;
    246248    void setDefaultCalendarType(DOMString calendarIdentifier);
  • trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp

    r238108 r238146  
    390390}
    391391
     392JSObjectRef UIScriptController::textSelectionRangeRects() const
     393{
     394    return nullptr;
     395}
     396
     397JSObjectRef UIScriptController::textSelectionCaretRect() const
     398{
     399    return nullptr;
     400}
     401
     402JSObjectRef UIScriptController::selectionStartGrabberViewRect() const
     403{
     404    return nullptr;
     405}
     406
     407JSObjectRef UIScriptController::selectionCaretViewRect() const
     408{
     409    return nullptr;
     410}
     411
    392412JSObjectRef UIScriptController::selectionRangeViewRects() const
    393 {
    394     return nullptr;
    395 }
    396 
    397 JSObjectRef UIScriptController::textSelectionCaretRect() const
    398 {
    399     return nullptr;
    400 }
    401 
    402 JSObjectRef UIScriptController::selectionStartGrabberViewRect() const
    403413{
    404414    return nullptr;
  • trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h

    r238108 r238146  
    160160    JSObjectRef contentVisibleRect() const;
    161161   
    162     JSObjectRef selectionRangeViewRects() const;
     162    JSObjectRef textSelectionRangeRects() const;
    163163    JSObjectRef textSelectionCaretRect() const;
    164164    JSObjectRef selectionStartGrabberViewRect() const;
    165165    JSObjectRef selectionEndGrabberViewRect() const;
     166    JSObjectRef selectionCaretViewRect() const;
     167    JSObjectRef selectionRangeViewRects() const;
    166168    JSObjectRef calendarType() const;
    167169    void setDefaultCalendarType(JSStringRef calendarIdentifier);
  • trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm

    r238134 r238146  
    508508}
    509509
    510 JSObjectRef UIScriptController::selectionRangeViewRects() const
    511 {
    512     NSMutableArray *selectionRects = [[NSMutableArray alloc] init];
     510JSObjectRef UIScriptController::textSelectionRangeRects() const
     511{
     512    auto selectionRects = adoptNS([[NSMutableArray alloc] init]);
    513513    NSArray *rects = TestController::singleton().mainWebView()->platformView()._uiTextSelectionRects;
    514514    for (NSValue *rect in rects)
    515         [selectionRects addObject:toNSDictionary([rect CGRectValue])];
    516 
    517     return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:selectionRects inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
     515        [selectionRects addObject:toNSDictionary(rect.CGRectValue)];
     516
     517    return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:selectionRects.get() inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
    518518}
    519519
     
    529529    UIView *selectionRangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"];
    530530    auto frameInContentCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"startGrabber"] frame] toView:contentView];
     531    frameInContentCoordinates = CGRectIntersection(contentView.bounds, frameInContentCoordinates);
    531532    auto jsContext = m_context->jsContext();
    532533    return JSValueToObject(jsContext, [JSValue valueWithObject:toNSDictionary(frameInContentCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:jsContext]].JSValueRef, nullptr);
     
    539540    UIView *selectionRangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"];
    540541    auto frameInContentCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"endGrabber"] frame] toView:contentView];
     542    frameInContentCoordinates = CGRectIntersection(contentView.bounds, frameInContentCoordinates);
    541543    auto jsContext = m_context->jsContext();
    542544    return JSValueToObject(jsContext, [JSValue valueWithObject:toNSDictionary(frameInContentCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:jsContext]].JSValueRef, nullptr);
     545}
     546
     547JSObjectRef UIScriptController::selectionCaretViewRect() const
     548{
     549    WKWebView *webView = TestController::singleton().mainWebView()->platformView();
     550    UIView *contentView = [webView valueForKeyPath:@"_currentContentView"];
     551    UIView *caretView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.caretView"];
     552    auto rectInContentViewCoordinates = CGRectIntersection([caretView convertRect:caretView.bounds toView:contentView], contentView.bounds);
     553    return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:toNSDictionary(rectInContentViewCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
     554}
     555
     556JSObjectRef UIScriptController::selectionRangeViewRects() const
     557{
     558    WKWebView *webView = TestController::singleton().mainWebView()->platformView();
     559    UIView *contentView = [webView valueForKeyPath:@"_currentContentView"];
     560    UIView *rangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"];
     561    auto rectsAsDictionaries = adoptNS([[NSMutableArray alloc] init]);
     562    NSArray *textRectInfoArray = [rangeView valueForKeyPath:@"rects"];
     563    for (id textRectInfo in textRectInfoArray) {
     564        NSValue *rectValue = [textRectInfo valueForKeyPath:@"rect"];
     565        auto rangeRectInContentViewCoordinates = [rangeView convertRect:rectValue.CGRectValue toView:contentView];
     566        [rectsAsDictionaries addObject:toNSDictionary(CGRectIntersection(rangeRectInContentViewCoordinates, contentView.bounds))];
     567    }
     568    return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:rectsAsDictionaries.get() inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
    543569}
    544570
Note: See TracChangeset for help on using the changeset viewer.