Skip to content

Commit

Permalink
ITP treats client injected JavaScript as user interaction
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=260807
rdar://113989281

Reviewed by Brent Fulgham and John Wilander.

JavaScript evaluated by the client app is treated as if it was from a user gesture, so ITP
currently logs this as a user interaction. This leads to cases where a page can be granted
storage privileges without a meaningful user interaction.

The enum ProcessInteractionStyle already exists to indicate that logging user interaction
for a key press should be delayed until the key press is actually handled. Add another value
to this enum to indicate that a user gesture should never be logged as user interaction by ITP.

* Source/WebCore/bindings/js/ScriptController.cpp:
(WebCore::ScriptController::executeScriptInWorld):
* Source/WebCore/dom/UserGestureIndicator.h:
* Tools/TestWebKitAPI/Tests/WebKitCocoa/ResourceLoadStatistics.mm:
(TEST):

Canonical link: https://commits.webkit.org/267381@main
  • Loading branch information
charliewolfe committed Aug 29, 2023
1 parent 54b5e2b commit e2a9577
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Source/WebCore/bindings/js/ScriptController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ ValueOrException ScriptController::executeScriptInWorld(DOMWrapperWorld& world,
m_frame.loader().client().notifyPageOfAppBoundBehavior();
#endif

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

if (parameters.forceUserGesture == ForceUserGesture::Yes && UserGestureIndicator::currentUserGesture() && parameters.removeTransientActivation == RemoveTransientActivation::Yes) {
UserGestureIndicator::currentUserGesture()->addDestructionObserver([](UserGestureToken& token) {
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/dom/UserGestureIndicator.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class UserGestureIndicator {
WEBCORE_EXPORT static bool processingUserGestureForMedia();

// If a document is provided, its last known user gesture timestamp is updated.
enum class ProcessInteractionStyle { Immediate, Delayed };
enum class ProcessInteractionStyle { Immediate, Delayed, Never };
WEBCORE_EXPORT explicit UserGestureIndicator(std::optional<ProcessingUserGestureState>, Document* = nullptr, UserGestureType = UserGestureType::ActivationTriggering, ProcessInteractionStyle = ProcessInteractionStyle::Immediate, std::optional<WTF::UUID> authorizationToken = std::nullopt);
WEBCORE_EXPORT explicit UserGestureIndicator(RefPtr<UserGestureToken>, UserGestureToken::GestureScope = UserGestureToken::GestureScope::All, UserGestureToken::IsPropagatedFromFetch = UserGestureToken::IsPropagatedFromFetch::No);
WEBCORE_EXPORT ~UserGestureIndicator();
Expand Down
61 changes: 61 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/ResourceLoadStatistics.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#import "PlatformUtilities.h"
#import "TestNavigationDelegate.h"
#import "TestWKWebView.h"
#import "WKWebViewConfigurationExtras.h"
#import <WebCore/SQLiteDatabase.h>
#import <WebCore/SQLiteStatement.h>
#import <WebKit/WKFoundation.h>
Expand Down Expand Up @@ -1354,3 +1355,63 @@ - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)tas
databaseAfterMigration.close();
EXPECT_WK_STREQ(columns.last(), "mostRecentWebPushInteractionTime");
}

TEST(ResourceLoadStatistics, ClientEvaluatedJavaScriptDoesNotLogUserInteraction)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]).get()]);
[configuration setWebsiteDataStore:dataStore.get()];
[dataStore _setResourceLoadStatisticsEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) configuration:configuration.get()]);
[webView loadHTMLString:@"" baseURL:[NSURL URLWithString:@"http://webkit.org"]];
[webView _test_waitForDidFinishNavigation];

__block bool done = false;
[dataStore removeDataOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeResourceLoadStatistics] modifiedSince:[NSDate distantPast] completionHandler:^ {
done = true;
}];
TestWebKitAPI::Util::run(&done);

done = false;
[webView evaluateJavaScript:@"" completionHandler:^(id value, NSError *error) {
done = true;
}];
TestWebKitAPI::Util::run(&done);

done = false;
[dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeResourceLoadStatistics] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 0u);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}

TEST(ResourceLoadStatistics, UserGestureLogsUserInteraction)
{
auto configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]).get()]);
[configuration setWebsiteDataStore:dataStore.get()];
[dataStore _setResourceLoadStatisticsEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) configuration:configuration]);
[webView loadHTMLString:@"" baseURL:[NSURL URLWithString:@"http://webkit.org"]];
[webView _test_waitForDidFinishNavigation];

__block bool done = false;
[dataStore removeDataOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeResourceLoadStatistics] modifiedSince:[NSDate distantPast] completionHandler:^ {
done = true;
}];
TestWebKitAPI::Util::run(&done);

done = false;
[webView evaluateJavaScript:@"internals.withUserGesture(() => {});" completionHandler:^(id value, NSError *error) {
done = true;
}];
TestWebKitAPI::Util::run(&done);

done = false;
[dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeResourceLoadStatistics] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
EXPECT_EQ(records.count, 1u);
done = true;
}];
TestWebKitAPI::Util::run(&done);
}

0 comments on commit e2a9577

Please sign in to comment.