Skip to content

Commit

Permalink
Cherry-pick 259548.840@safari-7615-branch (259842c). <bug>
Browse files Browse the repository at this point in the history
    Cherry-pick 265168@main (8e5ddea). rdar://110919134

        evaluateJavaScript: and callAsyncJavaScript: should not leave behind transient activation
        https://bugs.webkit.org/show_bug.cgi?id=258037
        rdar://107102031

        Reviewed by Ben Nham.

        JavaScript evaluated by the client app is executed as if from a user gesture, which is fine for now.
        But before this change, such JS left behind transient user activation for (currently) 5 seconds, which is not fine.

        Let's remove that sticky side effect.

        * Source/WTF/wtf/cocoa/RuntimeApplicationChecksCocoa.cpp:
        (WTF::computeSDKAlignedBehaviors):
        * Source/WTF/wtf/cocoa/RuntimeApplicationChecksCocoa.h:

        * Source/WebCore/bindings/js/ScriptController.cpp:
        (WebCore::ScriptController::executeScriptInWorld):
        (WebCore::ScriptController::executeAsynchronousUserAgentScriptInWorld):

        * Source/WebCore/dom/UserGestureIndicator.cpp:
        (WebCore::UserGestureToken::forEachImpactedDocument):
        * Source/WebCore/dom/UserGestureIndicator.h:

        * Tools/TestWebKitAPI/Tests/WebKitCocoa/AsyncFunction.mm:
        (TestWebKitAPI::TEST):

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

    Canonical link: https://commits.webkit.org/259548.840@safari-7615-branch
  • Loading branch information
beidson authored and mcatanzaro committed Jul 28, 2023
1 parent 20aeaf0 commit 4876897
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 1 deletion.
1 change: 1 addition & 0 deletions Source/WTF/wtf/cocoa/RuntimeApplicationChecksCocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ enum class SDKAlignedBehavior {
UIBackForwardSkipsHistoryItemsWithoutUserGesture,
ProgrammaticFocusDuringUserScriptShowsInputViews,
UsesGameControllerPhysicalInputProfile,
EvaluateJavaScriptWithoutTransientActivation,

NumberOfBehaviors
};
Expand Down
17 changes: 16 additions & 1 deletion Source/WebCore/bindings/js/ScriptController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,20 @@ ValueOrException ScriptController::executeScriptInWorld(DOMWrapperWorld& world,

UserGestureIndicator gestureIndicator(parameters.forceUserGesture == ForceUserGesture::Yes ? std::optional<ProcessingUserGestureState>(ProcessingUserGesture) : std::nullopt, m_frame.document());

#if PLATFORM(COCOA)
if (linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::EvaluateJavaScriptWithoutTransientActivation)) {
// Script executed by the user agent under simulated user gesture should not leave behind transient activation
if (parameters.forceUserGesture == ForceUserGesture::Yes && UserGestureIndicator::currentUserGesture()) {
UserGestureIndicator::currentUserGesture()->addDestructionObserver([](UserGestureToken& token) {
token.forEachImpactedDocument([](Document& document) {
if (auto* window = document.domWindow())
window->consumeTransientActivation();
});
});
}
}
#endif

if (!canExecuteScripts(AboutToExecuteScript) || isPaused())
return makeUnexpected(ExceptionDetails { "Cannot execute JavaScript in this document"_s });

Expand Down Expand Up @@ -710,9 +724,10 @@ ValueOrException ScriptController::executeUserAgentScriptInWorld(DOMWrapperWorld

void ScriptController::executeAsynchronousUserAgentScriptInWorld(DOMWrapperWorld& world, RunJavaScriptParameters&& parameters, ResolveFunction&& resolveCompletionHandler)
{
auto runAsAsyncFunction = parameters.runAsAsyncFunction;
auto result = executeScriptInWorld(world, WTFMove(parameters));

if (parameters.runAsAsyncFunction == RunAsAsyncFunction::No || !result || !result.value().isObject()) {
if (runAsAsyncFunction == RunAsAsyncFunction::No || !result || !result.value().isObject()) {
resolveCompletionHandler(result);
return;
}
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/dom/UserGestureIndicator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ bool UserGestureToken::isValidForDocument(const Document& document) const
return m_documentsImpactedByUserGesture.contains(document);
}

void UserGestureToken::forEachImpactedDocument(Function<void(Document&)>&& function)
{
m_documentsImpactedByUserGesture.forEach(function);
}

UserGestureIndicator::UserGestureIndicator(std::optional<ProcessingUserGestureState> state, Document* document, UserGestureType gestureType, ProcessInteractionStyle processInteractionStyle)
: m_previousToken { currentToken() }
{
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/dom/UserGestureIndicator.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ class UserGestureToken : public RefCounted<UserGestureToken>, public CanMakeWeak

bool isValidForDocument(const Document&) const;

void forEachImpactedDocument(Function<void(Document&)>&&);

private:
UserGestureToken(ProcessingUserGestureState, UserGestureType, Document*);

Expand Down
37 changes: 37 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/AsyncFunction.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#import "Test.h"
#import "TestUIDelegate.h"
#import "TestWKWebView.h"
#import "WKWebViewConfigurationExtras.h"
#import <WebKit/WKContentWorld.h>
#import <WebKit/WKFrameInfo.h>
#import <WebKit/WKProcessPoolPrivate.h>
Expand Down Expand Up @@ -320,5 +321,41 @@
EXPECT_EQ([webView _webProcessIdentifier], pid);
}

TEST(AsyncFunction, TransientActivation)
{
WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration addToWindow:NO]);

[webView synchronouslyLoadHTMLString:@"Hello"];

__block bool done = false;
[webView _evaluateJavaScriptWithoutUserGesture:@"window.internals.hasTransientActivation()" completionHandler:^(id result, NSError *error) {
EXPECT_NULL(error);
EXPECT_TRUE([result isKindOfClass:[NSNumber class]]);
EXPECT_TRUE([result isEqualToNumber:@0]);
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;

[webView callAsyncJavaScript:@"return window.internals.hasTransientActivation()" arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
EXPECT_NULL(error);
EXPECT_TRUE([result isKindOfClass:[NSNumber class]]);
EXPECT_TRUE([result isEqualToNumber:@1]);
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;

[webView _evaluateJavaScriptWithoutUserGesture:@"window.internals.hasTransientActivation()" completionHandler:^(id result, NSError *error) {
EXPECT_NULL(error);
EXPECT_TRUE([result isKindOfClass:[NSNumber class]]);
EXPECT_TRUE([result isEqualToNumber:@0]);
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;
}

} // namespace TestWebKitAPI

0 comments on commit 4876897

Please sign in to comment.