Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: ea5905fd9777612df86ae3b8471c24c60e9935b7
      
https://github.com/WebKit/WebKit/commit/ea5905fd9777612df86ae3b8471c24c60e9935b7
  Author: Wenson Hsieh <[email protected]>
  Date:   2024-11-07 (Thu, 07 Nov 2024)

  Changed paths:
    A 
LayoutTests/pointerevents/ios/dispatch-pointerout-after-synthetic-click-quirk-expected.txt
    A 
LayoutTests/pointerevents/ios/dispatch-pointerout-after-synthetic-click-quirk.html
    M Source/WebCore/Headers.cmake
    M Source/WebCore/SaferCPPExpectations/NoUncountedMemberCheckerExpectations
    M Source/WebCore/WebCore.xcodeproj/project.pbxproj
    M Source/WebCore/page/ChromeClient.h
    M Source/WebCore/page/PointerCaptureController.cpp
    M Source/WebCore/page/PointerCaptureController.h
    M Source/WebCore/page/Quirks.cpp
    M Source/WebCore/page/Quirks.h
    A Source/WebCore/platform/SyntheticClickResult.h
    M Source/WebCore/testing/Internals.cpp
    M Source/WebCore/testing/Internals.h
    M Source/WebCore/testing/Internals.idl
    M Source/WebKit/UIProcess/PageClient.h
    M Source/WebKit/UIProcess/WebPageProxy.h
    M Source/WebKit/UIProcess/WebPageProxy.messages.in
    M Source/WebKit/UIProcess/ios/PageClientImplIOS.h
    M Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
    M Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
    M Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
    M Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm
    M Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
    M Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h
    M Source/WebKit/WebProcess/WebPage/WebPage.cpp
    M Source/WebKit/WebProcess/WebPage/WebPage.h
    M Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

  Log Message:
  -----------
  soylent.com: dropdown menu dismisses when trying to tap on links
https://bugs.webkit.org/show_bug.cgi?id=282476
rdar://113314067

Reviewed by Aditya Keerthi.

On soylent.com, the site navigation dropdown contains a `pointerout` event 
listener which dismisses
the entire dropdown when dispatched. This, when combined with synthetic mouse 
events and content
observation, makes it impossible to actually navigate to any links in the 
header, since we get the
following sequence of events:

a.  User begins a touch over a link inside the dropdown menu, dispatching 
`pointerdown`
b.  User ends the touch, dispatching `pointerup`
c.  Because the touch has ended, we immediately dispatch `pointerout`, and the 
page closes the menu
d.  In parallel, a synthetic click is recognized in the UI process, dispatching 
a synthetic click to
    the page
e.  This synthetic click hits the `body` element (because the dropdown menu has 
already closed), and
    we don't click the link

To fix this, we add a quirk to ensure that step (c) now happens after the 
synthetic click is
dispatched to the page on `soylent.com`; additionally, this quirk *prevents* 
the `pointerout` event
from being dispatched after ending the touch, in the case where the synthetic 
click resulted in a
nontrivial content observation (such that the synthetic click state remains in 
hover state). This
latter adjustment is required in order to prevent the dropdown menu from 
immediately closing after
opening the menu, as a result of dispatching `pointerout`.

At a high level, this bug boils down to the fact that when using a 
touch-capable device, `pointer*`
events track the user's touch state and follow the life cycle of touch events, 
rather than mapping
to the compatibility mouse events (synthetic clicks) that are dispatched in 
response to tapping
elements on the page. soylent.com presents an interesting situation where the 
webpage relies on
*both* compatibility mouse events (so that the menu opens when tapping on the 
dropdown button), as
well as pointer events (for knowing when to dismiss the menu). The behavior 
introduced in this quirk
essentially bridges this gap by allowing `pointerout` to honor the synthetic 
mouse hover and clicks
dispatched when tapping the screen, but is likely too risky to deploy in the 
general case.

* 
LayoutTests/pointerevents/ios/dispatch-pointerout-after-synthetic-click-quirk-expected.txt:
 Added.
* 
LayoutTests/pointerevents/ios/dispatch-pointerout-after-synthetic-click-quirk.html:
 Added.

Add a layout test to exercise this quirk.

* Source/WebCore/Headers.cmake:

Add `SyntheticClickResult.h`.

* Source/WebCore/SaferCPPExpectations/NoUncountedMemberCheckerExpectations:

Update Safer C++ expectations, now that `m_page` is a weak pointer.

* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/page/ChromeClient.h:
(WebCore::ChromeClient::callAfterPendingSyntheticClick):

Add a chrome client hook to invoke a callback after waiting for a pending 
synthetic click completes.
This may be invoked immediately or asynchronously, with the following results:

• `Failed`        The gesture was not recognized as a synthetic click
• `Click`         The synthetic click was dispatched
• `Hover`         The synthetic click stopped at mouse hover state
• `PageInvalid`   The page was invalidated (either because the tab was closed, 
or we navigated away)

In the case of `Hover` and `PageInvalid`, we avoid dispatching the 
`pointer(out|leave)` events.

* Source/WebCore/page/PointerCaptureController.cpp:
(WebCore::PointerCaptureController::dispatchOverOrOutEvent):
(WebCore::PointerCaptureController::dispatchEnterOrLeaveEvent):

Pull these two helpers out as private methods on `PointerCaptureController`, so 
we can call into
them asynchronously below.

(WebCore::PointerCaptureController::dispatchEventForTouchAtIndex):

Use `callAfterPendingSyntheticClick` to defer the `pointerout` and 
`pointerleave` events until after
the synthetic click has completed (and even withhold them altogether, in the 
case of synthetic
hover).

(WebCore::PointerCaptureController::cancelPointer):
* Source/WebCore/page/PointerCaptureController.h:
* Source/WebCore/page/Quirks.cpp:
(WebCore::Quirks::isYahooMail const):
(WebCore::Quirks::isDomain const):
(WebCore::Quirks::domainStartsWith const):
(WebCore::Quirks::needsFormControlToBeMouseFocusable const):
(WebCore::Quirks::isTouchBarUpdateSuppressedForHiddenContentEditable const):
(WebCore::Quirks::isNeverRichlyEditableForTouchBar const):
(WebCore::Quirks::shouldSuppressAutocorrectionAndAutocapitalizationInHiddenEditableAreas
 const):
(WebCore::Quirks::shouldDisableWritingSuggestionsByDefault const):
(WebCore::Quirks::isAmazon const):
(WebCore::Quirks::isGoogleMaps const):
(WebCore::Quirks::shouldDispatchSimulatedMouseEvents const):
(WebCore::Quirks::shouldPreventDispatchOfTouchEvent const):
(WebCore::Quirks::shouldAvoidResizingWhenInputViewBoundsChange const):
(WebCore::Quirks::needsDeferKeyDownAndKeyPressTimersUntilNextEditingCommand 
const):
(WebCore::Quirks::shouldBypassBackForwardCache const):
(WebCore::Quirks::shouldBypassAsyncScriptDeferring const):
(WebCore::Quirks::shouldAvoidPastingImagesAsWebContent const):
(WebCore::Quirks::requestStorageAccessAndHandleClick const):
(WebCore::Quirks::requiresUserGestureToPauseInPictureInPicture const):
(WebCore::Quirks::shouldDisableEndFullscreenEventWhenEnteringPictureInPictureFromFullscreenQuirk
 const):
(WebCore::Quirks::allowLayeredFullscreenVideos const):
(WebCore::Quirks::shouldPreventOrientationMediaQueryFromEvaluatingToLandscape 
const):
(WebCore::Quirks::shouldFlipScreenDimensions const):
(WebCore::Quirks::shouldIgnoreTextAutoSizing const):
(WebCore::Quirks::scriptToEvaluateBeforeRunningScriptFromURL):
(WebCore::Quirks::shouldHideCoarsePointerCharacteristics const):
(WebCore::Quirks::implicitMuteWhenVolumeSetToZero const):
(WebCore::Quirks::shouldDispatchPointerOutAfterHandlingSyntheticClick const):
(WebCore::Quirks::needsMozillaFileTypeForDataTransfer const):
(WebCore::Quirks::setTopDocumentURLForTesting):
(WebCore::Quirks::topDocumentURL const):

Add the quirk, and also add plumbing for a testing-only hook to allow tests 
that have access to
`window.internals` to override the top document URL used for deciding quirks. 
The new layout test
uses this mechanism.

(WebCore::isYahooMail): Deleted.

Turn this into a method instead, so that we can use `topDocumentURL` which 
accounts for the new
override URL for testing.

* Source/WebCore/page/Quirks.h:
* Source/WebCore/platform/SyntheticClickResult.h: Added.

Add a new enum type; see above for more details.

* Source/WebCore/testing/Internals.cpp:
(WebCore::Internals::setTopDocumentURLForQuirks):
* Source/WebCore/testing/Internals.h:
* Source/WebCore/testing/Internals.idl:

Add a new testing hook to simulate top URLs for quirks; see above for more 
details.

* Source/WebKit/UIProcess/PageClient.h:
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/UIProcess/WebPageProxy.messages.in:

Add plumbing for an IPC message that returns whether or not a potential tap is 
in progress on iOS.
See below for more details.

* Source/WebKit/UIProcess/ios/PageClientImplIOS.h:
* Source/WebKit/UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::isPotentialTapInProgress const):
* Source/WebKit/UIProcess/ios/WKContentViewInteraction.h:
* Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView isPotentialTapInProgress]):
* Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::isPotentialTapInProgress):
* Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::callAfterPendingSyntheticClick):
* Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h:
* Source/WebKit/WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::close):
(WebKit::WebPage::didCommitLoad):

Invoke the pending synthetic click callback with `PageInvalid`, if needed.

(WebKit::WebPage::callAfterPendingSyntheticClick):

Add an empty stub for non-iOS platforms.

* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::handleSyntheticClick):
(WebKit::WebPage::didHandleTapAsHover):
(WebKit::WebPage::didFinishContentChangeObserving):
(WebKit::WebPage::completeSyntheticClick):
(WebKit::WebPage::potentialTapAtPosition):
(WebKit::WebPage::commitPotentialTapFailed):

Add calls to invoke the pending synthetic click callback throughout the various 
checkpoints during
synthetic mouse event dispatch: `Failed` (for when the potential tap node is 
rejected), `Hover` (for
when content change observation fires), and `Click` (for when the click is 
dispatched).

(WebKit::WebPage::invokePendingSyntheticClickCallback):
(WebKit::WebPage::callAfterPendingSyntheticClick):

Implement a new helper method on `WebPage` that invokes the callback after the 
current synthetic
click event has been dispatched. This avoids racing against gesture state in 
the UI process, by
round-tripping to `WKContentView` to determine whether or not the single tap 
gesture recognizer has
fired before stashing the callback in `m_pendingSyntheticClickCallback` on the 
`WebPage`.

Canonical link: https://commits.webkit.org/286270@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to