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