Title: [251337] branches/safari-608-branch/Source
Revision
251337
Author
bshaf...@apple.com
Date
2019-10-20 10:32:48 -0700 (Sun, 20 Oct 2019)

Log Message

Cherry-pick r250642. rdar://problem/56280704

    [iOS] When hit testing for a context menu interaction, do not consider whether the element is contenteditable
    https://bugs.webkit.org/show_bug.cgi?id=202498
    <rdar://problem/54723131>

    Reviewed by Tim Horton.

    Source/WebCore:

    When the user selects a context menu action, WebKit performs a hit test in order to find the
    acted-on element on the page. This is separate from the hit test performed to generate the
    context menu's targeted preview. Since an arbitrary amount of time can elapse between
    preview generation and action selection, this second hit-tests might return a different
    element.

    One case where we know a different element can be returned is in apps that dynamically
    enable and disable editing. If editing is disabled when the first hit test occurs but is
    enabled when the second one occurs, different elements will be returned due to
    Frame::qualifyingNodeAtViewportLocation preferring to return the root editable element when
    the approximate node is contenteditable.

    While the appropriate long-term fix is to only hit-test once and use that element for both
    preview generation and action selection, this patch implements a short-term fix to address
    the specific problem in rdar://problem/54723131 by disabling the contenteditable behavior
    described above for context menu interaction hit testing.

    The long-term fix is tracked by <https://webkit.org/b/202499>.

    * page/Frame.h:
    * page/ios/FrameIOS.mm:
    (WebCore::Frame::qualifyingNodeAtViewportLocation):
    (WebCore::Frame::approximateNodeAtViewportLocationLegacy):
    (WebCore::ancestorRespondingToClickEventsNodeQualifier):
    (WebCore::Frame::nodeRespondingToClickEvents):
    (WebCore::Frame::nodeRespondingToDoubleClickEvent):
    (WebCore::Frame::nodeRespondingToInteraction):
    (WebCore::Frame::nodeRespondingToScrollWheelEvents):

    Source/WebKit:

    * WebProcess/WebPage/ios/WebPageIOS.mm:
    (WebKit::WebPage::startInteractionWithElementAtPosition): Changed to call
    WebCore::Frame::nodeRespondingToInteraction.

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

Modified Paths

Diff

Modified: branches/safari-608-branch/Source/WebCore/ChangeLog (251336 => 251337)


--- branches/safari-608-branch/Source/WebCore/ChangeLog	2019-10-20 17:32:45 UTC (rev 251336)
+++ branches/safari-608-branch/Source/WebCore/ChangeLog	2019-10-20 17:32:48 UTC (rev 251337)
@@ -1,5 +1,92 @@
 2019-10-15  Kocsen Chung  <kocsen_ch...@apple.com>
 
+        Cherry-pick r250642. rdar://problem/56280704
+
+    [iOS] When hit testing for a context menu interaction, do not consider whether the element is contenteditable
+    https://bugs.webkit.org/show_bug.cgi?id=202498
+    <rdar://problem/54723131>
+    
+    Reviewed by Tim Horton.
+    
+    Source/WebCore:
+    
+    When the user selects a context menu action, WebKit performs a hit test in order to find the
+    acted-on element on the page. This is separate from the hit test performed to generate the
+    context menu's targeted preview. Since an arbitrary amount of time can elapse between
+    preview generation and action selection, this second hit-tests might return a different
+    element.
+    
+    One case where we know a different element can be returned is in apps that dynamically
+    enable and disable editing. If editing is disabled when the first hit test occurs but is
+    enabled when the second one occurs, different elements will be returned due to
+    Frame::qualifyingNodeAtViewportLocation preferring to return the root editable element when
+    the approximate node is contenteditable.
+    
+    While the appropriate long-term fix is to only hit-test once and use that element for both
+    preview generation and action selection, this patch implements a short-term fix to address
+    the specific problem in rdar://problem/54723131 by disabling the contenteditable behavior
+    described above for context menu interaction hit testing.
+    
+    The long-term fix is tracked by <https://webkit.org/b/202499>.
+    
+    * page/Frame.h:
+    * page/ios/FrameIOS.mm:
+    (WebCore::Frame::qualifyingNodeAtViewportLocation):
+    (WebCore::Frame::approximateNodeAtViewportLocationLegacy):
+    (WebCore::ancestorRespondingToClickEventsNodeQualifier):
+    (WebCore::Frame::nodeRespondingToClickEvents):
+    (WebCore::Frame::nodeRespondingToDoubleClickEvent):
+    (WebCore::Frame::nodeRespondingToInteraction):
+    (WebCore::Frame::nodeRespondingToScrollWheelEvents):
+    
+    Source/WebKit:
+    
+    * WebProcess/WebPage/ios/WebPageIOS.mm:
+    (WebKit::WebPage::startInteractionWithElementAtPosition): Changed to call
+    WebCore::Frame::nodeRespondingToInteraction.
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250642 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2019-10-02  Andy Estes  <aes...@apple.com>
+
+            [iOS] When hit testing for a context menu interaction, do not consider whether the element is contenteditable
+            https://bugs.webkit.org/show_bug.cgi?id=202498
+            <rdar://problem/54723131>
+
+            Reviewed by Tim Horton.
+
+            When the user selects a context menu action, WebKit performs a hit test in order to find the
+            acted-on element on the page. This is separate from the hit test performed to generate the
+            context menu's targeted preview. Since an arbitrary amount of time can elapse between
+            preview generation and action selection, this second hit-tests might return a different
+            element.
+
+            One case where we know a different element can be returned is in apps that dynamically
+            enable and disable editing. If editing is disabled when the first hit test occurs but is
+            enabled when the second one occurs, different elements will be returned due to
+            Frame::qualifyingNodeAtViewportLocation preferring to return the root editable element when
+            the approximate node is contenteditable.
+
+            While the appropriate long-term fix is to only hit-test once and use that element for both
+            preview generation and action selection, this patch implements a short-term fix to address
+            the specific problem in rdar://problem/54723131 by disabling the contenteditable behavior
+            described above for context menu interaction hit testing.
+
+            The long-term fix is tracked by <https://webkit.org/b/202499>.
+
+            * page/Frame.h:
+            * page/ios/FrameIOS.mm:
+            (WebCore::Frame::qualifyingNodeAtViewportLocation):
+            (WebCore::Frame::approximateNodeAtViewportLocationLegacy):
+            (WebCore::ancestorRespondingToClickEventsNodeQualifier):
+            (WebCore::Frame::nodeRespondingToClickEvents):
+            (WebCore::Frame::nodeRespondingToDoubleClickEvent):
+            (WebCore::Frame::nodeRespondingToInteraction):
+            (WebCore::Frame::nodeRespondingToScrollWheelEvents):
+
+2019-10-15  Kocsen Chung  <kocsen_ch...@apple.com>
+
         Cherry-pick r249517. rdar://problem/56000099
 
     Mail appears to be double inverting code copied from Notes, Xcode, or Terminal.

Modified: branches/safari-608-branch/Source/WebCore/page/Frame.h (251336 => 251337)


--- branches/safari-608-branch/Source/WebCore/page/Frame.h	2019-10-20 17:32:45 UTC (rev 251336)
+++ branches/safari-608-branch/Source/WebCore/page/Frame.h	2019-10-20 17:32:48 UTC (rev 251337)
@@ -221,6 +221,7 @@
     WEBCORE_EXPORT Node* deepestNodeAtLocation(const FloatPoint& viewportLocation);
     WEBCORE_EXPORT Node* nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, SecurityOrigin* = nullptr);
     WEBCORE_EXPORT Node* nodeRespondingToDoubleClickEvent(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation);
+    WEBCORE_EXPORT Node* nodeRespondingToInteraction(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation);
     WEBCORE_EXPORT Node* nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation);
     WEBCORE_EXPORT Node* approximateNodeAtViewportLocationLegacy(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation);
 
@@ -334,7 +335,10 @@
 #if PLATFORM(IOS_FAMILY)
     void betterApproximateNode(const IntPoint& testPoint, const NodeQualifier&, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect);
     bool hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult&, IntPoint& center);
-    Node* qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, const NodeQualifier&, bool shouldApproximate);
+    
+    enum class ShouldApproximate : bool { No, Yes };
+    enum class ShouldFindRootEditableElement : bool { No, Yes };
+    Node* qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, const NodeQualifier&, ShouldApproximate, ShouldFindRootEditableElement = ShouldFindRootEditableElement::Yes);
 
     void setTimersPausedInternal(bool);
 

Modified: branches/safari-608-branch/Source/WebCore/page/ios/FrameIOS.mm (251336 => 251337)


--- branches/safari-608-branch/Source/WebCore/page/ios/FrameIOS.mm	2019-10-20 17:32:45 UTC (rev 251336)
+++ branches/safari-608-branch/Source/WebCore/page/ios/FrameIOS.mm	2019-10-20 17:32:48 UTC (rev 251337)
@@ -266,8 +266,12 @@
     return true;
 }
 
-Node* Frame::qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, const NodeQualifier& nodeQualifierFunction, bool shouldApproximate)
+Node* Frame::qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, const NodeQualifier& nodeQualifierFunction, ShouldApproximate shouldApproximate, ShouldFindRootEditableElement shouldFindRootEditableElement)
 {
+#if !USE(UIKIT_EDITING)
+    UNUSED_PARAM(shouldFindRootEditableElement);
+#endif
+
     adjustedViewportLocation = viewportLocation;
 
     IntPoint testCenter;
@@ -282,12 +286,12 @@
     Node* approximateNode = nodeQualifierFunction(candidateInfo, 0, 0);
 
 #if USE(UIKIT_EDITING)
-    if (approximateNode && approximateNode->isContentEditable()) {
+    if (shouldFindRootEditableElement == ShouldFindRootEditableElement::Yes && approximateNode && approximateNode->isContentEditable()) {
         // If we are in editable content, we look for the root editable element.
         approximateNode = approximateNode->rootEditableElement();
         // If we have a focusable node, there is no need to approximate.
         if (approximateNode)
-            shouldApproximate = false;
+            shouldApproximate = ShouldApproximate::No;
     }
 #endif
 
@@ -298,7 +302,7 @@
     static const float unscaledSearchRadius = 15;
     int searchRadius = static_cast<int>(unscaledSearchRadius * ppiFactor / scale);
 
-    if (approximateNode && shouldApproximate) {
+    if (approximateNode && shouldApproximate == ShouldApproximate::Yes) {
         const float testOffsets[] = {
             -.3f, -.3f,
             -.6f, -.6f,
@@ -319,7 +323,7 @@
                 break;
             }
         }
-    } else if (!approximateNode && shouldApproximate) {
+    } else if (!approximateNode && shouldApproximate == ShouldApproximate::Yes) {
         // Grab the closest parent element of our failed candidate node.
         Node* candidate = candidateInfo.innerNode();
         Node* failedNode = candidate;
@@ -367,7 +371,7 @@
         IntPoint p = m_view->contentsToWindow(bestPoint);
         adjustedViewportLocation = p;
 #if USE(UIKIT_EDITING)
-        if (approximateNode->isContentEditable()) {
+        if (shouldFindRootEditableElement == ShouldFindRootEditableElement::Yes && approximateNode->isContentEditable()) {
             // When in editable content, look for the root editable node again,
             // since this could be the node found with the approximation.
             approximateNode = approximateNode->rootEditableElement();
@@ -448,12 +452,12 @@
         return nullptr;
     };
 
-    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, WTFMove(ancestorRespondingToClickEvents), true);
+    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, WTFMove(ancestorRespondingToClickEvents), ShouldApproximate::Yes);
 }
 
-Node* Frame::nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, SecurityOrigin* securityOrigin)
+static inline NodeQualifier ancestorRespondingToClickEventsNodeQualifier(SecurityOrigin* securityOrigin = nullptr)
 {
-    auto&& ancestorRespondingToClickEvents = [securityOrigin](const HitTestResult& hitTestResult, Node* terminationNode, IntRect* nodeBounds) -> Node* {
+    return [securityOrigin](const HitTestResult& hitTestResult, Node* terminationNode, IntRect* nodeBounds) -> Node* {
         if (nodeBounds)
             *nodeBounds = IntRect();
 
@@ -478,8 +482,11 @@
 
         return nullptr;
     };
+}
 
-    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, WTFMove(ancestorRespondingToClickEvents), true);
+Node* Frame::nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, SecurityOrigin* securityOrigin)
+{
+    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, ancestorRespondingToClickEventsNodeQualifier(securityOrigin), ShouldApproximate::Yes);
 }
 
 Node* Frame::nodeRespondingToDoubleClickEvent(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation)
@@ -506,9 +513,14 @@
         return nullptr;
     };
 
-    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, WTFMove(ancestorRespondingToDoubleClickEvent), true);
+    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, WTFMove(ancestorRespondingToDoubleClickEvent), ShouldApproximate::Yes);
 }
 
+Node* Frame::nodeRespondingToInteraction(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation)
+{
+    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, ancestorRespondingToClickEventsNodeQualifier(), ShouldApproximate::Yes, ShouldFindRootEditableElement::No);
+}
+
 Node* Frame::nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation)
 {
     auto&& ancestorRespondingToScrollWheelEvents = [](const HitTestResult& hitTestResult, Node* terminationNode, IntRect* nodeBounds) -> Node* {
@@ -539,7 +551,7 @@
     };
 
     FloatPoint adjustedViewportLocation;
-    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, WTFMove(ancestorRespondingToScrollWheelEvents), false);
+    return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, WTFMove(ancestorRespondingToScrollWheelEvents), ShouldApproximate::No);
 }
 
 int Frame::preferredHeight() const

Modified: branches/safari-608-branch/Source/WebKit/ChangeLog (251336 => 251337)


--- branches/safari-608-branch/Source/WebKit/ChangeLog	2019-10-20 17:32:45 UTC (rev 251336)
+++ branches/safari-608-branch/Source/WebKit/ChangeLog	2019-10-20 17:32:48 UTC (rev 251337)
@@ -1,5 +1,67 @@
 2019-10-15  Kocsen Chung  <kocsen_ch...@apple.com>
 
+        Cherry-pick r250642. rdar://problem/56280704
+
+    [iOS] When hit testing for a context menu interaction, do not consider whether the element is contenteditable
+    https://bugs.webkit.org/show_bug.cgi?id=202498
+    <rdar://problem/54723131>
+    
+    Reviewed by Tim Horton.
+    
+    Source/WebCore:
+    
+    When the user selects a context menu action, WebKit performs a hit test in order to find the
+    acted-on element on the page. This is separate from the hit test performed to generate the
+    context menu's targeted preview. Since an arbitrary amount of time can elapse between
+    preview generation and action selection, this second hit-tests might return a different
+    element.
+    
+    One case where we know a different element can be returned is in apps that dynamically
+    enable and disable editing. If editing is disabled when the first hit test occurs but is
+    enabled when the second one occurs, different elements will be returned due to
+    Frame::qualifyingNodeAtViewportLocation preferring to return the root editable element when
+    the approximate node is contenteditable.
+    
+    While the appropriate long-term fix is to only hit-test once and use that element for both
+    preview generation and action selection, this patch implements a short-term fix to address
+    the specific problem in rdar://problem/54723131 by disabling the contenteditable behavior
+    described above for context menu interaction hit testing.
+    
+    The long-term fix is tracked by <https://webkit.org/b/202499>.
+    
+    * page/Frame.h:
+    * page/ios/FrameIOS.mm:
+    (WebCore::Frame::qualifyingNodeAtViewportLocation):
+    (WebCore::Frame::approximateNodeAtViewportLocationLegacy):
+    (WebCore::ancestorRespondingToClickEventsNodeQualifier):
+    (WebCore::Frame::nodeRespondingToClickEvents):
+    (WebCore::Frame::nodeRespondingToDoubleClickEvent):
+    (WebCore::Frame::nodeRespondingToInteraction):
+    (WebCore::Frame::nodeRespondingToScrollWheelEvents):
+    
+    Source/WebKit:
+    
+    * WebProcess/WebPage/ios/WebPageIOS.mm:
+    (WebKit::WebPage::startInteractionWithElementAtPosition): Changed to call
+    WebCore::Frame::nodeRespondingToInteraction.
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250642 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2019-10-02  Andy Estes  <aes...@apple.com>
+
+            [iOS] When hit testing for a context menu interaction, do not consider whether the element is contenteditable
+            https://bugs.webkit.org/show_bug.cgi?id=202498
+            <rdar://problem/54723131>
+
+            Reviewed by Tim Horton.
+
+            * WebProcess/WebPage/ios/WebPageIOS.mm:
+            (WebKit::WebPage::startInteractionWithElementAtPosition): Changed to call
+            WebCore::Frame::nodeRespondingToInteraction.
+
+2019-10-15  Kocsen Chung  <kocsen_ch...@apple.com>
+
         Cherry-pick r250431. rdar://problem/55927251
 
     Storage Access API: document.hasStorageAccess() should return false by default

Modified: branches/safari-608-branch/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (251336 => 251337)


--- branches/safari-608-branch/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2019-10-20 17:32:45 UTC (rev 251336)
+++ branches/safari-608-branch/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2019-10-20 17:32:48 UTC (rev 251337)
@@ -2857,8 +2857,14 @@
 
 void WebPage::startInteractionWithElementAtPosition(const WebCore::IntPoint& point)
 {
+    // FIXME: We've already performed a hit test when the long-press gesture was recognized and we
+    // used that result to generate the targeted preview, but now we are hit testing again in order
+    // to perform the selected action. Since an arbitrary amount of time can elapse between
+    // generating the targeted preview and the user selecting an action, it's possible that this
+    // second hit test will find a different element than the first one, leading to bugs like
+    // <rdar://problem/54723131>. We should re-use the results of the first hit test instead.
     FloatPoint adjustedPoint;
-    m_interactionNode = m_page->mainFrame().nodeRespondingToClickEvents(point, adjustedPoint);
+    m_interactionNode = m_page->mainFrame().nodeRespondingToInteraction(point, adjustedPoint);
 }
 
 void WebPage::stopInteraction()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to