Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 81d9a71bf3fe87ceee2879ae834a005b181ab861
      
https://github.com/WebKit/WebKit/commit/81d9a71bf3fe87ceee2879ae834a005b181ab861
  Author: Ben Nham <[email protected]>
  Date:   2026-10-06 (Tue, 06 Oct 2026)

  Changed paths:
    M Source/WebCore/bindings/js/ScriptController.cpp
    M Source/WebCore/dom/UserGestureIndicator.cpp
    M Source/WebCore/dom/UserGestureIndicator.h
    M Source/WebCore/loader/FrameLoaderClient.h
    M Source/WebCore/page/LocalDOMWindow.cpp
    M Source/WebCore/page/LocalDOMWindow.h
    M Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in
    M Source/WebKit/UIProcess/WebPageProxy.cpp
    M Source/WebKit/UIProcess/WebPageProxy.h
    M Source/WebKit/UIProcess/WebPageProxy.messages.in
    M Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
    M Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.h
    M Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp
    M Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.h
    M Source/WebKit/WebProcess/WebCoreSupport/WebRemoteFrameClient.cpp
    M Source/WebKit/WebProcess/WebCoreSupport/WebRemoteFrameClient.h
    M Source/WebKit/WebProcess/WebPage/WebPage.cpp
    M Source/WebKit/WebProcess/WebPage/WebPage.h
    M Source/WebKit/WebProcess/WebPage/WebPage.messages.in
    M Source/WebKit/WebProcess/WebProcess.cpp
    M Tools/TestWebKitAPI/Tests/WebKit/WKWebView/AsyncFunction.mm
    M Tools/TestWebKitAPI/Tests/WebKit/WKWebView/SiteIsolation-1.mm

  Log Message:
  -----------
  REGRESSION(316338@main): "Copy to clipboard" button in Bitbucket does not 
always copy the first time
https://bugs.webkit.org/show_bug.cgi?id=324512
rdar://186521201

Reviewed by Brady Eidson.

Script evaluated on behalf of the client (e.g. -evaluateJavaScript:) runs with 
a forced user gesture
token which gives the page transient activation. When that token is destroyed 
(which might occur
asynchronously with respect to the original -evaluateJavaScript: call), we 
consume the transient
activation.

The issue with this is that this can end up consuming a transient activation 
associated with a real
user action. For instance:

1. Client calls -evaluateJavaScript:, which creates a forced user gesture token 
that updates the
   activation timestamp on associated windows.
2. User clicks on page, which overwrites the activation timestamp from (1) on 
associated windows.
3. The token associated with -evaluateJavaScript: in (1) is destroyed and 
consumes the activation
   from (2).
4. Now the page tries to write to the clipboard via 
`navigator.clipboard.writeText`, but fails the
   transient activation check since the activation from (2) is already consumed.

To fix this, make LocalDOMWindow track two pieces of state:

1. The last activation timestamp associated with real user action.
2. A list of all forced user gesture token identifiers and timestamps created by
   executeScriptInWorld that activated this window.

The last activation timestamp is then the maximum of the activation timestamp 
associated with real
user actions and the maximum activation timestamp from all forced user gesture 
tokens.

When a forced user gesture token is destroyed, we remove that token from all 
associated windows, so
it can't overwrite the last activation timestamp associated with a real user 
action as in the
example above.

This differs somewhat from the approach used in 321448@main since each window 
keeps a list of
associated forced user gesture token identifiers, rather than keeping track of 
a stack of activation
timestamps. A list is preferable because the tokens created by 
-evaluateJavaScript: are not
necessarily destroyed in LIFO order (since gesture tokens are forwarded to some 
async calls such as
timer callbacks).

This also addresses the Site Isolation-related FIXME in 321448@main. Instead of 
consuming the
activation of every frame in every process when a forced user gesture token is 
destroyed, we modify
the UpdateUserActivationState messageto include an optional token identifier 
associated with forced
user gesture tokens. When those tokens are deestroyed, we broadcast a 
RevokeForcedUserActivation
message to all other processes on the page. This only removes that token from 
the list in (2) above
from windows in other processes.

Tests: Tools/TestWebKitAPI/Tests/WebKit/WKWebView/AsyncFunction.mm
       Tools/TestWebKitAPI/Tests/WebKit/WKWebView/SiteIsolation-1.mm

* Source/WebCore/bindings/js/ScriptController.cpp:
(WebCore::ScriptController::executeScriptInWorld):
* Source/WebCore/dom/UserGestureIndicator.cpp:
(WebCore::UserGestureToken::UserGestureToken):
(WebCore::UserGestureToken::~UserGestureToken):
(WebCore::UserGestureToken::create):
(WebCore::UserGestureToken::didGrantForcedActivation):
(WebCore::UserGestureToken::didGrantForcedActivationInOtherProcesses):
(WebCore::UserGestureIndicator::UserGestureIndicator):
(WebCore::UserGestureToken::forEachImpactedDocument): Deleted.
* Source/WebCore/dom/UserGestureIndicator.h:
(WebCore::UserGestureToken::identifier const):
(WebCore::UserGestureToken::removesTransientActivation const):
* Source/WebCore/loader/FrameLoaderClient.h:
(WebCore::FrameLoaderClient::didNotifyUserActivation):
(WebCore::FrameLoaderClient::didRevokeForcedUserActivation):
* Source/WebCore/page/LocalDOMWindow.cpp:
(WebCore::LocalDOMWindow::hasTransientActivation const):
(WebCore::LocalDOMWindow::lastActivationTimestamp const):
(WebCore::LocalDOMWindow::updateActivation):
(WebCore::LocalDOMWindow::consumeLastActivationIfNecessary):
(WebCore::LocalDOMWindow::revokeForcedActivation):
(WebCore::updateActivationTimestampAndNotify):
(WebCore::LocalDOMWindow::notifyActivated):
(WebCore::LocalDOMWindow::consumeTransientActivation):
* Source/WebCore/page/LocalDOMWindow.h:
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::validatedUserActivationSenderProcess):
(WebKit::WebPageProxy::didNotifyUserActivation):
(WebKit::WebPageProxy::didRevokeForcedUserActivation):
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/UIProcess/WebPageProxy.messages.in:
* Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::didNotifyUserActivation):
(WebKit::WebFrameLoaderClient::didRevokeForcedUserActivation):
* Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.h:
* Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp:
(WebKit::WebLocalFrameLoaderClient::didNotifyUserActivation):
(WebKit::WebLocalFrameLoaderClient::didRevokeForcedUserActivation):
* Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.h:
* Source/WebKit/WebProcess/WebCoreSupport/WebRemoteFrameClient.cpp:
(WebKit::WebRemoteFrameClient::didNotifyUserActivation):
* Source/WebKit/WebProcess/WebCoreSupport/WebRemoteFrameClient.h:
* Source/WebKit/WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::updateUserActivationState):
(WebKit::WebPage::revokeForcedUserActivation):
* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebPage/WebPage.messages.in:
* Source/WebKit/WebProcess/WebProcess.cpp:
(WebKit::WebProcess::userGestureTokenIdentifier):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/AsyncFunction.mm:
(TestWebKitAPI::hasTransientActivation):
(TestWebKitAPI::simulateUserGesture):
(TestWebKitAPI::TEST(AsyncFunction, 
TransientActivationFromUserGestureIsPreserved)):
(TestWebKitAPI::evaluateJavaScriptWithUserGesture):
(TestWebKitAPI::evaluateJavaScriptWithoutUserGesture):
(TestWebKitAPI::TEST(AsyncFunction, 
TransientActivationFromOverlappingForcedUserGesturesIsRevoked)):
(TestWebKitAPI::TEST(AsyncFunction, 
TransientActivationConsumedDuringForcedUserGestureIsNotRestored)):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/SiteIsolation-1.mm:
(TestWebKitAPI::evaluateJavaScriptInFrame):
(TestWebKitAPI::hasTransientActivationInFrame):
(TestWebKitAPI::TEST(SiteIsolation, 
TransientActivationFromForcedUserGestureInCrossOriginIframeIsRevoked)):
(TestWebKitAPI::TEST(SiteIsolation, 
TransientActivationFromUserGestureIsPreservedAfterForcedUserGestureInCrossOriginIframe)):
(TestWebKitAPI::TEST(SiteIsolation, 
TransientActivationFromForcedUserGesturePostedToCrossOriginIframeIsRevoked)):
* Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in:

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



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to