iOS: Selection is dismissed even if click is preventDefault()'d
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 May 2019 20:50:22 +0000 (20:50 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 May 2019 20:50:22 +0000 (20:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197686
<rdar://problem/49398824>

Reviewed by Wenson Hsieh.

Source/WebKit:

We currently unconditionally dismiss the selection on any tap; however
if a site preventDefault()s on click, we shouldn't perform the default
action of dismissing the selection.

Instead of clearing the selection in the UI process, clear it in the
Web content process if we don't dispatch a synthetic click; the normal
WebCore machinery will handle it in the case that we do.

* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _singleTapRecognized:]):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::commitPotentialTapFailed):
(WebKit::WebPage::selectWithGesture):
(WebKit::WebPage::clearSelection):
(WebKit::WebPage::selectTextWithGranularityAtPoint):

LayoutTests:

* editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler-expected.txt: Added.
* editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler.html: Added.
* editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler-expected.txt: Added.
* editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler.html: Added.
New tests.

* platform/ios/editing/deleting/smart-delete-paragraph-003-expected.txt:
* platform/ios/editing/pasteboard/smart-paste-paragraph-003-expected.txt:
Rebaseline since we changed the timing of editing callbacks by changing where the selection happens.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245067 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler.html [new file with mode: 0644]
LayoutTests/editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler.html [new file with mode: 0644]
LayoutTests/platform/ios/editing/deleting/smart-delete-paragraph-003-expected.txt
LayoutTests/platform/ios/editing/pasteboard/smart-paste-paragraph-003-expected.txt
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

index f32b5a2..a71418d 100644 (file)
@@ -1,3 +1,21 @@
+2019-05-08  Tim Horton  <timothy_horton@apple.com>
+
+        iOS: Selection is dismissed even if click is preventDefault()'d
+        https://bugs.webkit.org/show_bug.cgi?id=197686
+        <rdar://problem/49398824>
+
+        Reviewed by Wenson Hsieh.
+
+        * editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler-expected.txt: Added.
+        * editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler.html: Added.
+        * editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler-expected.txt: Added.
+        * editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler.html: Added.
+        New tests.
+
+        * platform/ios/editing/deleting/smart-delete-paragraph-003-expected.txt:
+        * platform/ios/editing/pasteboard/smart-paste-paragraph-003-expected.txt:
+        Rebaseline since we changed the timing of editing callbacks by changing where the selection happens.
+
 2019-05-08  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [iOS] Add a quirk to synthesize mouse events when modifying the selection
diff --git a/LayoutTests/editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler-expected.txt b/LayoutTests/editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler-expected.txt
new file mode 100644 (file)
index 0000000..6e639db
--- /dev/null
@@ -0,0 +1,3 @@
+WebKit
+The selected text is: ""
+This test verifies that the DOM selection is dismissed when tapping on an element that does not listen to click events.
diff --git a/LayoutTests/editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler.html b/LayoutTests/editing/selection/ios/clear-selection-after-tapping-on-element-with-no-click-handler.html
new file mode 100644 (file)
index 0000000..2c8197c
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<head>
+    <script src="../../../resources/basic-gestures.js"></script>
+    <script src="../../../resources/ui-helper.js"></script>
+    <style>
+    body {
+        margin: 0;
+    }
+
+    #target {
+        font-size: 100px;
+    }
+
+    #clickTarget {
+        width: 100px;
+        height: 100px;
+    }
+    </style>
+    <script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    async function run()
+    {
+        if (!window.testRunner)
+            return;
+
+        function didChangeSelection()
+        {
+            result.textContent = window.getSelection().toString()
+        }
+
+        document.addEventListener("selectionchange", didChangeSelection);
+
+        var clickTarget = document.getElementById("clickTarget");
+
+        var target = document.getElementById("target");        
+        window.getSelection().setBaseAndExtent(target, 0, target, 6);
+
+        await UIHelper.activateElement(clickTarget);
+
+        setTimeout(async function () {
+            // The test is done, but we need to tap again to ensure we don't
+            // hang the next test with a double tap.
+            document.removeEventListener("selectionchange", didChangeSelection);
+            await UIHelper.tapAt(10, 500);
+
+            testRunner.notifyDone();
+        }, 0);
+    }
+    </script>
+</head>
+<body onload="run()">
+    <div id="target">WebKit</div>
+    <div id="clickTarget"></div>
+    <pre>The selected text is: "<span id="result"></span>"</pre>
+    <p>This test verifies that the DOM selection is dismissed when tapping on an element that does not listen to click events.</p>
+</body>
+</html>
diff --git a/LayoutTests/editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler-expected.txt b/LayoutTests/editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler-expected.txt
new file mode 100644 (file)
index 0000000..e6d97ef
--- /dev/null
@@ -0,0 +1,3 @@
+WebKit
+The selected text is: "WebKit"
+This test verifies that the DOM selection is not dismissed when tapping on an element that preventDefault()s the click event.
diff --git a/LayoutTests/editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler.html b/LayoutTests/editing/selection/ios/persist-selection-after-tapping-on-element-with-click-handler.html
new file mode 100644 (file)
index 0000000..5ba42ac
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<head>
+    <script src="../../../resources/basic-gestures.js"></script>
+    <script src="../../../resources/ui-helper.js"></script>
+    <style>
+    body {
+        margin: 0;
+    }
+
+    #target {
+        font-size: 100px;
+    }
+
+    #clickTarget {
+        width: 100px;
+        height: 100px;
+        background-color: green;
+    }
+    </style>
+    <script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    async function run()
+    {
+        if (!window.testRunner)
+            return;
+
+        function didChangeSelection()
+        {
+            result.textContent = window.getSelection().toString()
+        }
+
+        document.addEventListener("selectionchange", didChangeSelection);
+
+        var clickTarget = document.getElementById("clickTarget");
+
+        clickTarget.addEventListener("click", event => {
+            event.preventDefault();
+
+            setTimeout(async function () {
+                // The test is done, but we need to tap again to ensure we don't
+                // hang the next test with a double tap.
+                document.removeEventListener("selectionchange", didChangeSelection);
+                await UIHelper.tapAt(10, 500);
+
+                testRunner.notifyDone();
+            }, 0);
+        });
+
+        var target = document.getElementById("target");        
+        window.getSelection().setBaseAndExtent(target, 0, target, 6);
+
+        await UIHelper.activateElement(clickTarget);
+    }
+    </script>
+</head>
+<body onload="run()">
+    <div id="target">WebKit</div>
+    <div id="clickTarget"></div>
+    <pre>The selected text is: "<span id="result"></span>"</pre>
+    <p>This test verifies that the DOM selection is not dismissed when tapping on an element that preventDefault()s the click event.</p>
+</body>
+</html>
index 230845a..ed21c52 100644 (file)
@@ -1,10 +1,7 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 3 of BODY > HTML > #document to 3 of BODY > HTML > #document toDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 0 of #text > DIV > #document-fragment to 4 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: shouldDeleteDOMRange:range from 0 of #text > DIV > #document-fragment to 15 of #text > DIV > #document-fragment
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
index 352dbbb..0de3528 100644 (file)
@@ -1,10 +1,7 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 3 of BODY > HTML > #document to 3 of BODY > HTML > #document toDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > #document-fragment to 0 of DIV > #document-fragment toDOMRange:range from 0 of #text > DIV > #document-fragment to 4 of #text > DIV > #document-fragment affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
index eb847aa..47ef3da 100644 (file)
@@ -1,3 +1,28 @@
+2019-05-08  Tim Horton  <timothy_horton@apple.com>
+
+        iOS: Selection is dismissed even if click is preventDefault()'d
+        https://bugs.webkit.org/show_bug.cgi?id=197686
+        <rdar://problem/49398824>
+
+        Reviewed by Wenson Hsieh.
+
+        We currently unconditionally dismiss the selection on any tap; however
+        if a site preventDefault()s on click, we shouldn't perform the default
+        action of dismissing the selection.
+
+        Instead of clearing the selection in the UI process, clear it in the
+        Web content process if we don't dispatch a synthetic click; the normal
+        WebCore machinery will handle it in the case that we do.
+
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _singleTapRecognized:]):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::commitPotentialTapFailed):
+        (WebKit::WebPage::selectWithGesture):
+        (WebKit::WebPage::clearSelection):
+        (WebKit::WebPage::selectTextWithGranularityAtPoint):
+
 2019-05-08  Alexander Mikhaylenko  <exalm7659@gmail.com>
 
         [GTK] Support navigation gesture on touchscreens
index aed5449..9ab06d1 100644 (file)
@@ -2348,12 +2348,6 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
 
     ASSERT(_potentialTapInProgress);
 
-    // We don't want to clear the selection if it is in editable content.
-    // The selection could have been set by autofocusing on page load and not
-    // reflected in the UI process since the user was not interacting with the page.
-    if (!_page->editorState().isContentEditable)
-        _page->clearSelection();
-
     _lastInteractionLocation = gestureRecognizer.location;
 
     [self _endPotentialTapAndEnableDoubleTapGesturesIfNecessary];
index 6eccb3e..4b07cde 100644 (file)
@@ -1818,7 +1818,6 @@ private:
     WebCore::FloatSize m_screenSize;
     WebCore::FloatSize m_availableScreenSize;
     WebCore::FloatSize m_overrideScreenSize;
-    RefPtr<WebCore::Range> m_currentBlockSelection;
     WebCore::IntRect m_blockRectForTextSelection;
 
     RefPtr<WebCore::Range> m_initialSelection;
index 0d05744..8f4047e 100644 (file)
@@ -947,6 +947,9 @@ void WebPage::commitPotentialTap(OptionSet<WebEvent::Modifier> modifiers, uint64
 
 void WebPage::commitPotentialTapFailed()
 {
+    if (!m_page->focusController().focusedOrMainFrame().selection().selection().isContentEditable())
+        clearSelection();
+
     send(Messages::WebPageProxy::CommitPotentialTapFailed());
     send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(m_potentialTapLocation)));
 }
@@ -1367,7 +1370,6 @@ void WebPage::selectWithGesture(const IntPoint& point, uint32_t granularity, uin
         if (wkGestureState == GestureRecognizerState::Began) {
             m_blockSelectionDesiredSize.setWidth(blockSelectionStartWidth);
             m_blockSelectionDesiredSize.setHeight(blockSelectionStartHeight);
-            m_currentBlockSelection = nullptr;
         }
         range = rangeForWebSelectionAtPosition(point, position, flags);
         break;
@@ -1499,7 +1501,6 @@ static IntRect elementRectInRootViewCoordinates(const Element& element)
 void WebPage::clearSelection()
 {
     m_startingGestureRange = nullptr;
-    m_currentBlockSelection = nullptr;
     m_page->focusController().focusedOrMainFrame().selection().clear();
 }
 
@@ -1958,7 +1959,6 @@ void WebPage::selectTextWithGranularityAtPoint(const WebCore::IntPoint& point, u
     if (!isInteractingWithFocusedElement) {
         m_blockSelectionDesiredSize.setWidth(blockSelectionStartWidth);
         m_blockSelectionDesiredSize.setHeight(blockSelectionStartHeight);
-        m_currentBlockSelection = nullptr;
         auto* renderer = range ? range->startContainer().renderer() : nullptr;
         if (renderer && renderer->style().preserveNewline())
             m_blockRectForTextSelection = renderer->absoluteBoundingBoxRect(true);