Skip to content

Commit

Permalink
[shared storage] add implementation for blink-style worklet
Browse files Browse the repository at this point in the history
What: currently the worklet lives in a ThreadPool thread and the JS bindings are added via native v8 / Gin library. This CL added the
implementation to use the blink worklet pattern: create the thread via ThreadedWorkletMessagingProxy, and rely on IDLs & blink functions for the JS bindings and function execution. For this CL, the only thing this worklet can do is to execute a module script (still using the existing custom network loader to download the script content), which makes the new system testable E2E.

Why: with the IDL, worklet functions will align more with the web standard (e.g. JS function input param validation), and will be easier to extend (e.g. add new bindings); easier to connect to devtool/debugger.

How:
- Create classes SharedStorageWorkletMessagingProxy/WorkletThread/GlobalScope similar to the pattern creating regular blink worklet.
- The thread is created in agent_scheduling_group.cc (which is NOT associated with an ExecutionContext)
- When there’s a need for the main thread’s `execution_context`, we’ll either disable the code path (e.g. for devtool connection), or use default values (e.g. for many params in the GlobalScopeCreationParams). Created a meta bug to track those special-cased scenarios (crbug/1419253); the default values and skipped code paths are not needed for the initial migration work.
- Pass in the main thread task runner (as a counterpart for ParentExecutionContextTaskRunners when there's no ExecutionContext)
for the worklet thread to notify the main thread about the its termination.

Fuchsia-Binary-Size: Size increase is unavoidable.
Bug: 1414951
Change-Id: I37039b87fba466ae3aa1ba99cbeda58d1b0b19c3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4284641
Reviewed-by: Nate Chapin <japhet@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Cammie Smith Barnes <cammie@chromium.org>
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1117746}
  • Loading branch information
yaoxiachromium authored and Chromium LUCI CQ committed Mar 15, 2023
1 parent ae39685 commit dfb8931
Show file tree
Hide file tree
Showing 48 changed files with 1,359 additions and 63 deletions.
55 changes: 55 additions & 0 deletions content/browser/shared_storage/shared_storage_browsertest.cc
Expand Up @@ -2862,6 +2862,61 @@ INSTANTIATE_TEST_SUITE_P(All,
testing::Bool(),
describe_param);

// TODO(yaoxia): when the majority of the blink-style worklet migration is done,
// we should remove this test suite and just parameterize the existing tests.
class BlinkStyleSharedStorageBrowserTest : public SharedStorageBrowserTestBase {
public:
BlinkStyleSharedStorageBrowserTest() {
scoped_feature_list_.InitWithFeaturesAndParameters(
/*enabled_features=*/
{{blink::features::kSharedStorageAPI,
{{"SharedStorageWorkletImplementationType", "blink_style"}}}},
/*disabled_features=*/{});
}

~BlinkStyleSharedStorageBrowserTest() override = default;

private:
base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(BlinkStyleSharedStorageBrowserTest, AddModule_Success) {
GURL main_frame_url = https_server()->GetURL("a.test", kSimplePagePath);
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));

base::StringPairs run_function_body_replacement;
run_function_body_replacement.emplace_back("{{SCRIPT_BODY}}", "let a = 1;");

GURL module_script_url = https_server()->GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/shared_storage/customizable_script.js",
run_function_body_replacement));

EXPECT_TRUE(ExecJs(
shell()->web_contents(),
JsReplace("sharedStorage.worklet.addModule($1)", module_script_url)));
}

IN_PROC_BROWSER_TEST_F(BlinkStyleSharedStorageBrowserTest, AddModule_Failure) {
GURL main_frame_url = https_server()->GetURL("a.test", kSimplePagePath);
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));

base::StringPairs run_function_body_replacement;
run_function_body_replacement.emplace_back("{{SCRIPT_BODY}}", "a;");

GURL module_script_url = https_server()->GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/shared_storage/customizable_script.js",
run_function_body_replacement));

EvalJsResult result = EvalJs(
shell()->web_contents(),
JsReplace("sharedStorage.worklet.addModule($1)", module_script_url));

EXPECT_THAT(result.error,
testing::HasSubstr("ReferenceError: a is not defined"));
}

class SharedStorageAllowURNsInIframesBrowserTest
: public base::test::WithFeatureOverride,
public SharedStorageBrowserTestBase {
Expand Down
23 changes: 17 additions & 6 deletions content/renderer/agent_scheduling_group.cc
Expand Up @@ -23,11 +23,13 @@
#include "ipc/ipc_channel_mojo.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sync_channel.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "third_party/blink/public/mojom/page/page.mojom.h"
#include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/public/web/web_remote_frame.h"
#include "third_party/blink/public/web/web_shared_storage_worklet_thread.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/public/web/web_view_client.h"

Expand Down Expand Up @@ -97,17 +99,17 @@ class SelfOwnedWebViewClient : public blink::WebViewClient {
// A thread for running shared storage worklet operations. It hosts a worklet
// environment belonging to one Document. The object owns itself, cleaning up
// when the worklet has shut down.
class SelfOwnedSharedStorageWorkletThread {
class LegacySelfOwnedSharedStorageWorkletThread {
public:
SelfOwnedSharedStorageWorkletThread(
LegacySelfOwnedSharedStorageWorkletThread(
scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
mojo::PendingReceiver<blink::mojom::SharedStorageWorkletService> receiver)
: main_thread_runner_(std::move(main_thread_runner)) {
DCHECK(main_thread_runner_->BelongsToCurrentThread());

auto disconnect_handler = base::BindPostTask(
main_thread_runner_,
base::BindOnce(&SelfOwnedSharedStorageWorkletThread::
base::BindOnce(&LegacySelfOwnedSharedStorageWorkletThread::
OnSharedStorageWorkletServiceDestroyed,
weak_factory_.GetWeakPtr()));

Expand All @@ -134,7 +136,8 @@ class SelfOwnedSharedStorageWorkletThread {
base::SequenceBound<shared_storage_worklet::SharedStorageWorkletServiceImpl>
worklet_thread_;

base::WeakPtrFactory<SelfOwnedSharedStorageWorkletThread> weak_factory_{this};
base::WeakPtrFactory<LegacySelfOwnedSharedStorageWorkletThread> weak_factory_{
this};
};

} // namespace
Expand Down Expand Up @@ -446,8 +449,16 @@ void AgentSchedulingGroup::CreateFrame(mojom::CreateFrameParamsPtr params) {

void AgentSchedulingGroup::CreateSharedStorageWorkletService(
mojo::PendingReceiver<blink::mojom::SharedStorageWorkletService> receiver) {
new SelfOwnedSharedStorageWorkletThread(
agent_group_scheduler_->DefaultTaskRunner(), std::move(receiver));
switch (blink::features::kSharedStorageWorkletImplementationType.Get()) {
case blink::features::SharedStorageWorkletImplementationType::kLegacy:
new LegacySelfOwnedSharedStorageWorkletThread(
agent_group_scheduler_->DefaultTaskRunner(), std::move(receiver));
break;
case blink::features::SharedStorageWorkletImplementationType::kBlinkStyle:
blink::WebSharedStorageWorkletThread::Start(
agent_group_scheduler_->DefaultTaskRunner(), std::move(receiver));
break;
}
}

void AgentSchedulingGroup::BindAssociatedInterfaces(
Expand Down
1 change: 1 addition & 0 deletions content/test/content_unittests_bundle_data.filelist
Expand Up @@ -1550,6 +1550,7 @@ data/set_cookie.html
data/set_cookie.html.mock-http-headers
data/set_document_cookie.html
data/shared_storage/customizable_module.js
data/shared_storage/customizable_script.js
data/shared_storage/erroneous_function_module.js
data/shared_storage/erroneous_module.js
data/shared_storage/getter_module.js
Expand Down
4 changes: 4 additions & 0 deletions content/test/data/shared_storage/customizable_script.js
@@ -0,0 +1,4 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{{SCRIPT_BODY}}
12 changes: 10 additions & 2 deletions third_party/blink/PRESUBMIT.py
Expand Up @@ -81,8 +81,16 @@ def source_file_filter(path):
'third_party/blink/public/mojom/worker/subresource_loader_updater',
'third_party/blink/public/mojom/loader/transferrable_url_loader',
'third_party/blink/public/mojom/loader/code_cache',
'media/mojo/mojom/interface_factory', 'media/mojo/mojom/audio_decoder',
'media/mojo/mojom/audio_encoder', 'media/mojo/mojom/video_decoder',
# The `shared_storage_worklet_service` and `private_aggregation_host`
# are tentatively included here when shared storage is migrating to
# the blink-style worklet infrastructure.
# TODO(crbug.com/1414951): Remove once the migration completes.
'third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service',
'third_party/blink/public/mojom/private_aggregation/private_aggregation_host',
'media/mojo/mojom/interface_factory',
'media/mojo/mojom/audio_decoder',
'media/mojo/mojom/audio_encoder',
'media/mojo/mojom/video_decoder',
'media/mojo/mojom/media_metrics_provider')

for f in input_api.AffectedFiles(file_filter=source_file_filter):
Expand Down
9 changes: 9 additions & 0 deletions third_party/blink/common/features.cc
Expand Up @@ -298,6 +298,15 @@ const base::FeatureParam<int>
kSharedStorageMaxAllowedFencedFrameDepthForSelectURL = {
&kSharedStorageAPI,
"SharedStorageMaxAllowedFencedFrameDepthForSelectURL", 1};
const base::FeatureParam<SharedStorageWorkletImplementationType>::Option
shared_storage_worklet_implementation_types[] = {
{SharedStorageWorkletImplementationType::kLegacy, "legacy"},
{SharedStorageWorkletImplementationType::kBlinkStyle, "blink_style"}};
const base::FeatureParam<SharedStorageWorkletImplementationType>
kSharedStorageWorkletImplementationType = {
&kSharedStorageAPI, "SharedStorageWorkletImplementationType",
SharedStorageWorkletImplementationType::kLegacy,
&shared_storage_worklet_implementation_types};

BASE_FEATURE(kSharedStorageSelectURLLimit,
"SharedStorageSelectURLLimit",
Expand Down
12 changes: 12 additions & 0 deletions third_party/blink/common/tokens/tokens_mojom_traits.cc
Expand Up @@ -103,6 +103,12 @@ bool UnionTraits<blink::mojom::WorkletTokenDataView, blink::WorkletToken>::Read(
*output = token;
return ret;
}
case DataView::Tag::kSharedStorageWorkletToken: {
blink::SharedStorageWorkletToken token;
bool ret = input.ReadSharedStorageWorkletToken(&token);
*output = token;
return ret;
}
}
return false;
}
Expand Down Expand Up @@ -173,6 +179,12 @@ bool UnionTraits<
*output = token;
return ret;
}
case DataView::Tag::kSharedStorageWorkletToken: {
blink::SharedStorageWorkletToken token;
bool ret = input.ReadSharedStorageWorkletToken(&token);
*output = token;
return ret;
}
case DataView::Tag::kShadowRealmToken: {
blink::ShadowRealmToken token;
bool ret = input.ReadShadowRealmToken(&token);
Expand Down
1 change: 1 addition & 0 deletions third_party/blink/public/BUILD.gn
Expand Up @@ -383,6 +383,7 @@ source_set("blink_headers") {
"web/web_serialized_script_value.h",
"web/web_serialized_script_value_version.h",
"web/web_settings.h",
"web/web_shared_storage_worklet_thread.h",
"web/web_shared_worker.h",
"web/web_shared_worker_client.h",
"web/web_testing_support.h",
Expand Down
14 changes: 14 additions & 0 deletions third_party/blink/public/common/features.h
Expand Up @@ -74,6 +74,16 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kFullUserAgent);
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPath2DPaintCache);
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPrivacySandboxAdsAPIs);

enum class SharedStorageWorkletImplementationType {
// The worklet thread is created via base::SequenceBound, and JS bindings are
// added with native v8 and/or Gin library.
kLegacy,

// Use the blink worklet pattern (i.e. blink::ThreadedWorkletMessagingProxy,
// blink::WorkerThread, IDL, etc.) to create the thread and add JS bindings.
kBlinkStyle,
};

BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kSharedStorageAPI);
// Maximum number of URLs allowed to be included in the input parameter for
// runURLSelectionOperation().
Expand Down Expand Up @@ -128,6 +138,10 @@ BLINK_COMMON_EXPORT extern const base::FeatureParam<base::TimeDelta>
// main frame has fenced frame depth 1, etc).
BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
kSharedStorageMaxAllowedFencedFrameDepthForSelectURL;
// The implementation type of the worklet.
BLINK_COMMON_EXPORT extern const base::FeatureParam<
SharedStorageWorkletImplementationType>
kSharedStorageWorkletImplementationType;

// If enabled, limits the number of times per origin per pageload that
// `sharedStorage.selectURL()` is allowed to be invoked.
Expand Down
8 changes: 7 additions & 1 deletion third_party/blink/public/common/tokens/tokens.h
Expand Up @@ -79,11 +79,16 @@ using LayoutWorkletToken = base::TokenType<class LayoutWorkletTokenTypeMarker>;
// Identifies a paint worklet.
using PaintWorkletToken = base::TokenType<class PaintWorkletTokenTypeMarker>;

// Identifies a shared storage worklet.
using SharedStorageWorkletToken =
base::TokenType<class SharedStorageWorkletTokenTypeMarker>;

// Can represent any type of WorkletToken.
using WorkletToken = MultiToken<AnimationWorkletToken,
AudioWorkletToken,
LayoutWorkletToken,
PaintWorkletToken>;
PaintWorkletToken,
SharedStorageWorkletToken>;

////////////////////////////////////////////////////////////////////////////////
// SHADOW REALM TOKENS
Expand Down Expand Up @@ -115,6 +120,7 @@ using ExecutionContextToken = MultiToken<LocalFrameToken,
AudioWorkletToken,
LayoutWorkletToken,
PaintWorkletToken,
SharedStorageWorkletToken,
ShadowRealmToken>;

// Identifies a blink::PortalContents / blink::HTMLPortalElement in the
Expand Down
20 changes: 20 additions & 0 deletions third_party/blink/public/common/tokens/tokens_mojom_traits.h
Expand Up @@ -160,6 +160,13 @@ struct StructTraits<blink::mojom::PaintWorkletTokenDataView,
blink::mojom::PaintWorkletTokenDataView,
blink::PaintWorkletToken> {};

template <>
struct StructTraits<blink::mojom::SharedStorageWorkletTokenDataView,
blink::SharedStorageWorkletToken>
: public blink::TokenMojomTraitsHelper<
blink::mojom::SharedStorageWorkletTokenDataView,
blink::SharedStorageWorkletToken> {};

template <>
struct BLINK_COMMON_EXPORT
UnionTraits<blink::mojom::WorkletTokenDataView, blink::WorkletToken> {
Expand All @@ -180,6 +187,8 @@ struct BLINK_COMMON_EXPORT
return DataView::Tag::kLayoutWorkletToken;
case blink::WorkletToken::IndexOf<blink::PaintWorkletToken>():
return DataView::Tag::kPaintWorkletToken;
case blink::WorkletToken::IndexOf<blink::SharedStorageWorkletToken>():
return DataView::Tag::kSharedStorageWorkletToken;
}
base::ImmediateCrash();
}
Expand All @@ -200,6 +209,10 @@ struct BLINK_COMMON_EXPORT
const blink::WorkletToken& token) {
return token.GetAs<blink::PaintWorkletToken>();
}
static const blink::SharedStorageWorkletToken& shared_storage_worklet_token(
const blink::WorkletToken& token) {
return token.GetAs<blink::SharedStorageWorkletToken>();
}
};

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -265,6 +278,9 @@ struct BLINK_COMMON_EXPORT
return DataView::Tag::kLayoutWorkletToken;
case blink::ExecutionContextToken::IndexOf<blink::PaintWorkletToken>():
return DataView::Tag::kPaintWorkletToken;
case blink::ExecutionContextToken::IndexOf<
blink::SharedStorageWorkletToken>():
return DataView::Tag::kSharedStorageWorkletToken;
case blink::ExecutionContextToken::IndexOf<blink::ShadowRealmToken>():
return DataView::Tag::kShadowRealmToken;
}
Expand Down Expand Up @@ -303,6 +319,10 @@ struct BLINK_COMMON_EXPORT
const blink::ExecutionContextToken& token) {
return token.GetAs<blink::PaintWorkletToken>();
}
static const blink::SharedStorageWorkletToken& shared_storage_worklet_token(
const blink::ExecutionContextToken& token) {
return token.GetAs<blink::SharedStorageWorkletToken>();
}
static const blink::ShadowRealmToken& shadow_realm_token(
const blink::ExecutionContextToken& token) {
return token.GetAs<blink::ShadowRealmToken>();
Expand Down
6 changes: 6 additions & 0 deletions third_party/blink/public/mojom/tokens/tokens.mojom
Expand Up @@ -78,11 +78,16 @@ struct PaintWorkletToken {
mojo_base.mojom.UnguessableToken value;
};

struct SharedStorageWorkletToken {
mojo_base.mojom.UnguessableToken value;
};

union WorkletToken {
AnimationWorkletToken animation_worklet_token;
AudioWorkletToken audio_worklet_token;
LayoutWorkletToken layout_worklet_token;
PaintWorkletToken paint_worklet_token;
SharedStorageWorkletToken shared_storage_worklet_token;
};

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -118,6 +123,7 @@ union ExecutionContextToken {
AudioWorkletToken audio_worklet_token;
LayoutWorkletToken layout_worklet_token;
PaintWorkletToken paint_worklet_token;
SharedStorageWorkletToken shared_storage_worklet_token;
ShadowRealmToken shadow_realm_token;
};

Expand Down
1 change: 1 addition & 0 deletions third_party/blink/public/web/DEPS
Expand Up @@ -10,6 +10,7 @@ include_rules = [
"+base/strings",
"+base/time/time.h",
"+base/threading/thread_checker.h",
"+base/task/single_thread_task_runner.h",
"+build/build_config.h",
"+build/buildflag.h",
"+cc/input/browser_controls_state.h",
Expand Down
28 changes: 28 additions & 0 deletions third_party/blink/public/web/web_shared_storage_worklet_thread.h
@@ -0,0 +1,28 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SHARED_STORAGE_WORKLET_THREAD_H_
#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SHARED_STORAGE_WORKLET_THREAD_H_

#include "base/memory/scoped_refptr.h"
#include "base/task/single_thread_task_runner.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom-forward.h"
#include "third_party/blink/public/platform/web_common.h"

namespace blink {

// An interface to start a self-owned shared storage worklet thread.
class BLINK_EXPORT WebSharedStorageWorkletThread {
public:
static void Start(
scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
mojo::PendingReceiver<mojom::SharedStorageWorkletService> receiver);

virtual ~WebSharedStorageWorkletThread() = default;
};

} // namespace blink

#endif // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SHARED_STORAGE_WORKLET_THREAD_H_
Expand Up @@ -111,6 +111,16 @@ v8::Local<v8::Value> ScriptEvaluationResult::GetExceptionForModule() const {
return value_;
}

v8::Local<v8::Value> ScriptEvaluationResult::GetExceptionForWorklet() const {
#if DCHECK_IS_ON()
DCHECK_EQ(script_type_, mojom::blink::ScriptType::kClassic);
#endif
DCHECK_EQ(result_type_, ResultType::kException);
DCHECK(!value_.IsEmpty());

return value_;
}

v8::Local<v8::Value> ScriptEvaluationResult::GetExceptionForClassicForTesting()
const {
DCHECK_EQ(result_type_, ResultType::kException);
Expand Down

0 comments on commit dfb8931

Please sign in to comment.