blob: 64b71eeb7a803c4e3101606ff1ae0c1a23236dea [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmath>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/containers/contains.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/task_environment.h"
#include "base/test/with_feature_override.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h"
#include "components/privacy_sandbox/privacy_sandbox_attestations/scoped_privacy_sandbox_attestations.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
#include "components/services/storage/shared_storage/shared_storage_manager.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/shared_storage_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_select_url_fenced_frame_config_observer.h"
#include "content/public/test/test_shared_storage_header_observer.h"
#include "net/base/schemeful_site.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
#include "third_party/blink/public/common/shared_storage/shared_storage_utils.h"
#include "url/url_constants.h"
#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/android/tab_model/tab_model.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#include "chrome/test/base/android/android_browser_test.h"
#else
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#endif
namespace storage {
namespace {
using OperationResult = SharedStorageManager::OperationResult;
const auto& SetOperation =
content::SharedStorageWriteOperationAndResult::SetOperation;
const auto& AppendOperation =
content::SharedStorageWriteOperationAndResult::AppendOperation;
const auto& ClearOperation =
content::SharedStorageWriteOperationAndResult::ClearOperation;
constexpr char kMainHost[] = "a.test";
constexpr char kSimplePagePath[] = "/simple.html";
constexpr char kTitle1Path[] = "/title1.html";
constexpr char kCrossOriginHost[] = "b.test";
constexpr char kThirdOriginHost[] = "c.test";
constexpr char kFourthOriginHost[] = "d.test";
constexpr char kRemainingBudgetPrefix[] = "remaining budget: ";
constexpr char kErrorTypeHistogram[] =
"Storage.SharedStorage.Worklet.Error.Type";
constexpr char kEntriesQueuedCountHistogram[] =
"Storage.SharedStorage.AsyncIterator.EntriesQueuedCount";
constexpr char kReceivedEntriesBenchmarksHistogram[] =
"Storage.SharedStorage.AsyncIterator.ReceivedEntriesBenchmarks";
constexpr char kIteratedEntriesBenchmarksHistogram[] =
"Storage.SharedStorage.AsyncIterator.IteratedEntriesBenchmarks";
constexpr char kTimingDocumentAddModuleHistogram[] =
"Storage.SharedStorage.Document.Timing.AddModule";
constexpr char kTimingDocumentRunHistogram[] =
"Storage.SharedStorage.Document.Timing.Run";
constexpr char kTimingDocumentSelectUrlHistogram[] =
"Storage.SharedStorage.Document.Timing.SelectURL";
constexpr char kTimingDocumentAppendHistogram[] =
"Storage.SharedStorage.Document.Timing.Append";
constexpr char kTimingDocumentSetHistogram[] =
"Storage.SharedStorage.Document.Timing.Set";
constexpr char kTimingDocumentDeleteHistogram[] =
"Storage.SharedStorage.Document.Timing.Delete";
constexpr char kTimingDocumentClearHistogram[] =
"Storage.SharedStorage.Document.Timing.Clear";
constexpr char kTimingWorkletAppendHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Append";
constexpr char kTimingWorkletSetHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Set";
constexpr char kTimingWorkletGetHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Get";
constexpr char kTimingWorkletLengthHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Length";
constexpr char kTimingWorkletDeleteHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Delete";
constexpr char kTimingWorkletClearHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Clear";
constexpr char kTimingWorkletKeysHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Keys.Next";
constexpr char kTimingWorkletEntriesHistogram[] =
"Storage.SharedStorage.Worklet.Timing.Entries.Next";
constexpr char kWorkletNumPerPageHistogram[] =
"Storage.SharedStorage.Worklet.NumPerPage";
constexpr char kTimingRemainingBudgetHistogram[] =
"Storage.SharedStorage.Worklet.Timing.RemainingBudget";
constexpr char kPrivateAggregationHostPipeResultHistogram[] =
"PrivacySandbox.PrivateAggregation.Host.PipeResult";
constexpr char
kPrivateAggregationHostTimeToGenerateReportRequestWithContextIdHistogram[] =
"PrivacySandbox.PrivateAggregation.Host."
"TimeToGenerateReportRequestWithContextId";
const double kBudgetAllowed = 5.0;
// In order to cut back on the total number of tests run, we deliberately only
// test three possibilities. In particular, the main host is unenrolled when the
// attestations are unenforced, leaving out the main host enrolled/attestations
// unenforced case. Since this enum is used as a parameter and combined with
// other parameters, using three instead of four cases is especially important
// on Android due to hardware limitations that constrain the total number of
// tests that can be run.
enum class EnforcementAndEnrollmentStatus {
kAttestationsUnenforced = 0,
kAttestationsEnforcedMainHostUnenrolled = 1,
kAttestationsEnforcedMainHostEnrolled = 2,
};
#if BUILDFLAG(IS_ANDROID)
base::FilePath GetChromeTestDataDir() {
return base::FilePath(FILE_PATH_LITERAL("chrome/test/data"));
}
#endif
// With `WebContentsConsoleObserver`, we can only wait for the last message in a
// group.
base::RepeatingCallback<
bool(const content::WebContentsConsoleObserver::Message& message)>
MakeFilter(std::vector<std::string> possible_last_messages) {
return base::BindRepeating(
[](std::vector<std::string> possible_last_messages,
const content::WebContentsConsoleObserver::Message& message) {
for (const std::string& possible_message : possible_last_messages) {
if (base::StartsWith(base::UTF16ToUTF8(message.message),
possible_message)) {
return true;
}
}
return false;
},
std::move(possible_last_messages));
}
std::string GetSharedStorageDisabledErrorMessage() {
return base::StrCat({"a JavaScript error: \"Error: ",
content::GetSharedStorageDisabledMessage()});
}
std::string GetSharedStorageSelectURLDisabledErrorMessage() {
return base::StrCat({"a JavaScript error: \"Error: ",
content::GetSharedStorageSelectURLDisabledMessage()});
}
std::string GetSharedStorageAddModuleDisabledErrorMessage() {
return base::StrCat({"a JavaScript error: \"Error: ",
content::GetSharedStorageAddModuleDisabledMessage()});
}
void DelayBy(base::TimeDelta delta) {
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), delta);
run_loop.Run();
}
// TODO(cammie): Find a way to ensure that histograms are available at the
// necessary time without having to resort to sleeping/polling.
void WaitForHistograms(std::vector<std::string> histogram_names) {
while (true) {
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
std::vector<std::string> still_waiting;
for (const auto& name : histogram_names) {
if (!base::StatisticsRecorder::FindHistogram(name))
still_waiting.push_back(name);
}
histogram_names = std::move(still_waiting);
if (histogram_names.empty())
break;
DelayBy(base::Seconds(1));
}
}
// Return the active RenderFrameHost loaded in the last iframe in `parent_rfh`.
content::RenderFrameHost* LastChild(content::RenderFrameHost* parent_rfh) {
int child_end = 0;
while (ChildFrameAt(parent_rfh, child_end))
child_end++;
if (child_end == 0)
return nullptr;
return ChildFrameAt(parent_rfh, child_end - 1);
}
// Create an <iframe> inside `parent_rfh`, and navigate it toward `url`.
// This returns the new RenderFrameHost associated with new document created in
// the iframe.
content::RenderFrameHost* CreateIframe(content::RenderFrameHost* parent_rfh,
const GURL& url) {
EXPECT_EQ("iframe loaded",
content::EvalJs(parent_rfh, content::JsReplace(R"(
new Promise((resolve) => {
const iframe = document.createElement("iframe");
iframe.src = $1;
iframe.onload = _ => { resolve("iframe loaded"); };
document.body.appendChild(iframe);
}))",
url)));
return LastChild(parent_rfh);
}
privacy_sandbox::PrivacySandboxAttestationsMap
MakeSharedStoragePrivacySandboxAttestationsMap(
const std::vector<GURL>& enrollee_urls,
bool enroll_for_private_aggregation = false) {
privacy_sandbox::PrivacySandboxAttestationsMap attestations_map;
auto attestations_set =
privacy_sandbox::PrivacySandboxAttestationsGatedAPISet(
{privacy_sandbox::PrivacySandboxAttestationsGatedAPI::
kSharedStorage});
if (enroll_for_private_aggregation) {
attestations_set.Put(privacy_sandbox::PrivacySandboxAttestationsGatedAPI::
kPrivateAggregation);
}
for (const GURL& url : enrollee_urls) {
attestations_map[net::SchemefulSite(url)] = attestations_set;
}
return attestations_map;
}
class MockChromeContentBrowserClient : public ChromeContentBrowserClient {
public:
bool IsSharedStorageAllowed(
content::BrowserContext* browser_context,
content::RenderFrameHost* rfh,
const url::Origin& top_frame_origin,
const url::Origin& accessing_origin,
std::string* out_debug_message = nullptr) override {
if (bypass_shared_storage_allowed_count_ > 0) {
bypass_shared_storage_allowed_count_--;
return true;
}
return ChromeContentBrowserClient::IsSharedStorageAllowed(
browser_context, rfh, top_frame_origin, accessing_origin,
out_debug_message);
}
bool IsSharedStorageSelectURLAllowed(
content::BrowserContext* browser_context,
const url::Origin& top_frame_origin,
const url::Origin& accessing_origin,
std::string* out_debug_message = nullptr) override {
if (bypass_shared_storage_select_url_allowed_count_) {
bypass_shared_storage_select_url_allowed_count_--;
return true;
}
return ChromeContentBrowserClient::IsSharedStorageSelectURLAllowed(
browser_context, top_frame_origin, accessing_origin, out_debug_message);
}
void set_bypass_shared_storage_allowed_count(int count) {
CHECK_EQ(bypass_shared_storage_allowed_count_, 0);
bypass_shared_storage_allowed_count_ = count;
}
void set_bypass_shared_storage_select_url_allowed_count(int count) {
CHECK_EQ(bypass_shared_storage_select_url_allowed_count_, 0);
bypass_shared_storage_select_url_allowed_count_ = count;
}
private:
int bypass_shared_storage_allowed_count_ = 0;
int bypass_shared_storage_select_url_allowed_count_ = 0;
};
} // namespace
class SharedStorageChromeBrowserTestBase : public PlatformBrowserTest {
public:
SharedStorageChromeBrowserTestBase() {
base::test::TaskEnvironment task_environment;
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{blink::features::kSharedStorageAPI,
features::kPrivacySandboxAdsAPIsOverride,
privacy_sandbox::
kOverridePrivacySandboxSettingsLocalTesting},
/*disabled_features=*/{});
}
~SharedStorageChromeBrowserTestBase() override = default;
void SetUpOnMainThread() override {
// `PrivacySandboxAttestations` has a member of type
// `scoped_refptr<base::SequencedTaskRunner>`, its initialization must be
// done after a browser process is created.
PlatformBrowserTest::SetUpOnMainThread();
scoped_attestations_ =
std::make_unique<privacy_sandbox::ScopedPrivacySandboxAttestations>(
privacy_sandbox::PrivacySandboxAttestations::CreateForTesting());
host_resolver()->AddRule("*", "127.0.0.1");
https_server()->AddDefaultHandlers(GetChromeTestDataDir());
https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
content::SetupCrossSiteRedirector(https_server());
SetPrefs(EnablePrivacySandbox(), AllowThirdPartyCookies());
FinishSetUp();
mock_chrome_content_browser_client_ =
std::make_unique<MockChromeContentBrowserClient>();
old_chrome_content_browser_client_ = content::SetBrowserClientForTesting(
mock_chrome_content_browser_client_.get());
}
void TearDownOnMainThread() override {
content::SetBrowserClientForTesting(old_chrome_content_browser_client_);
}
net::EmbeddedTestServer* https_server() { return &https_server_; }
content::WebContents* GetActiveWebContents() {
return chrome_test_utils::GetActiveWebContents(this);
}
content::StoragePartition* GetStoragePartition() {
return content::ToRenderFrameHost(GetActiveWebContents())
.render_frame_host()
->GetStoragePartition();
}
Profile* GetProfile() {
#if BUILDFLAG(IS_ANDROID)
return TabModelList::models()[0]->GetProfile();
#else
return browser()->profile();
#endif
}
privacy_sandbox::PrivacySandboxSettings* GetPrivacySandboxSettings() {
return PrivacySandboxSettingsFactory::GetForProfile(GetProfile());
}
// Virtual so derived classes can delay or perform additional set up before
// starting the server.
virtual void FinishSetUp() { CHECK(https_server()->Start()); }
void SetPrefs(bool enable_privacy_sandbox, bool allow_third_party_cookies) {
GetProfile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
allow_third_party_cookies
? content_settings::CookieControlsMode::kOff
: content_settings::CookieControlsMode::kBlockThirdParty));
// We need to ensure the
// `PrivacySandboxDelegate::IsPrivacySandboxRestricted()` response returns
// the negation of `enable_privacy_sandbox`.
auto* privacy_sandbox_settings = GetPrivacySandboxSettings();
if (enable_privacy_sandbox) {
privacy_sandbox_settings->SetAllPrivacySandboxAllowedForTesting();
}
auto privacy_sandbox_delegate = std::make_unique<testing::NiceMock<
privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>>();
privacy_sandbox_delegate->SetUpIsPrivacySandboxRestrictedResponse(
/*restricted=*/!enable_privacy_sandbox);
privacy_sandbox_delegate->SetUpIsIncognitoProfileResponse(
/*incognito=*/GetProfile()->IsIncognitoProfile());
privacy_sandbox_settings->SetDelegateForTesting(
std::move(privacy_sandbox_delegate));
}
void SetThirdPartyCookieSetting(const GURL& main_url) {
// We need to ensure the specific first-party URL `main_url` used by the
// test either has its third-party-cookie content setting set to
// `ContentSetting::CONTENT_SETTING_ALLOW` or
// `ContentSetting::CONTENT_SETTING_BLOCK`, according to
// `AllowThirdPartyCookies()`.
CookieSettingsFactory::GetForProfile(GetProfile())
->SetThirdPartyCookieSetting(
main_url, AllowThirdPartyCookies()
? ContentSetting::CONTENT_SETTING_ALLOW
: ContentSetting::CONTENT_SETTING_BLOCK);
}
void SetAttestationsMap(
const privacy_sandbox::PrivacySandboxAttestationsMap& attestations_map) {
privacy_sandbox::PrivacySandboxAttestations::GetInstance()
->SetAttestationsForTesting(attestations_map);
}
// Unless overridden to do otherwise, enrolls the main host to attest for
// Shared Storage exactly when `GetEnforcementAndEnrollmentStatus()` is
// `EnforcementAndEnrollmentStatus::kAttestationsEnforcedMainHostEnrolled`.
virtual void MaybeEnrollMainHost(const GURL& main_url) {
privacy_sandbox::PrivacySandboxAttestationsMap attestations_map =
(GetEnforcementAndEnrollmentStatus() ==
EnforcementAndEnrollmentStatus::kAttestationsEnforcedMainHostEnrolled)
? MakeSharedStoragePrivacySandboxAttestationsMap(
std::vector<GURL>({main_url}))
: privacy_sandbox::PrivacySandboxAttestationsMap();
SetAttestationsMap(attestations_map);
}
void
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage() {
GURL main_url = https_server()->GetURL(kMainHost, kSimplePagePath);
SetThirdPartyCookieSetting(main_url);
MaybeEnrollMainHost(main_url);
EXPECT_TRUE(NavigateToURL(GetActiveWebContents(), main_url));
}
void AddSimpleModule(const content::ToRenderFrameHost& execution_target) {
content::WebContentsConsoleObserver add_module_console_observer(
GetActiveWebContents());
add_module_console_observer.SetFilter(
MakeFilter({"Finish executing simple_module.js"}));
std::string host =
execution_target.render_frame_host()->GetLastCommittedOrigin().host();
GURL module_script_url =
https_server()->GetURL(host, "/shared_storage/simple_module.js");
EXPECT_TRUE(content::ExecJs(
execution_target,
content::JsReplace("sharedStorage.worklet.addModule($1)",
module_script_url)));
ASSERT_TRUE(add_module_console_observer.Wait());
EXPECT_LE(1u,
content::GetAttachedSharedStorageWorkletHostsCount(
execution_target.render_frame_host()->GetStoragePartition()));
EXPECT_EQ(0u,
content::GetKeepAliveSharedStorageWorkletHostsCount(
execution_target.render_frame_host()->GetStoragePartition()));
EXPECT_EQ(1u, add_module_console_observer.messages().size());
EXPECT_EQ(
"Finish executing simple_module.js",
base::UTF16ToUTF8(add_module_console_observer.messages()[0].message));
}
bool ExecuteScriptInWorklet(
const content::ToRenderFrameHost& execution_target,
const std::string& script,
const std::string& last_script_message,
bool use_select_url = false) {
content::WebContentsConsoleObserver add_module_console_observer(
GetActiveWebContents());
add_module_console_observer.SetFilter(
MakeFilter({"Finish executing customizable_module.js"}));
base::StringPairs run_function_body_replacement;
run_function_body_replacement.emplace_back("{{RUN_FUNCTION_BODY}}", script);
std::string host =
execution_target.render_frame_host()->GetLastCommittedOrigin().host();
GURL module_script_url = https_server()->GetURL(
host, net::test_server::GetFilePathWithReplacements(
"/shared_storage/customizable_module.js",
run_function_body_replacement));
EXPECT_TRUE(content::ExecJs(
execution_target,
content::JsReplace("sharedStorage.worklet.addModule($1)",
module_script_url)));
EXPECT_TRUE(add_module_console_observer.Wait());
EXPECT_LE(1u,
content::GetAttachedSharedStorageWorkletHostsCount(
execution_target.render_frame_host()->GetStoragePartition()));
EXPECT_EQ(0u,
content::GetKeepAliveSharedStorageWorkletHostsCount(
execution_target.render_frame_host()->GetStoragePartition()));
EXPECT_EQ(1u, add_module_console_observer.messages().size());
EXPECT_EQ(
"Finish executing customizable_module.js",
base::UTF16ToUTF8(add_module_console_observer.messages()[0].message));
content::WebContentsConsoleObserver script_console_observer(
GetActiveWebContents());
script_console_observer.SetFilter(MakeFilter(
{last_script_message, ExpectedSharedStorageDisabledMessage()}));
if (!use_select_url) {
content::EvalJsResult result = content::EvalJs(execution_target, R"(
sharedStorage.run('test-operation');
)");
EXPECT_TRUE(script_console_observer.Wait());
EXPECT_EQ(1u, script_console_observer.messages().size());
EXPECT_EQ(
last_script_message,
base::UTF16ToUTF8(script_console_observer.messages()[0].message));
return result.error.empty();
}
EXPECT_TRUE(
ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
// Construct and add the `TestSelectURLFencedFrameConfigObserver` to shared
// storage worklet host manager.
content::StoragePartition* storage_partition =
content::ToRenderFrameHost(GetActiveWebContents())
.render_frame_host()
->GetStoragePartition();
content::TestSelectURLFencedFrameConfigObserver config_observer(
storage_partition);
content::EvalJsResult result = EvalJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-operation',
[{url: "fenced_frames/title0.html"},
{url: "fenced_frames/title1.html"},
],
{resolveToConfig: resolveSelectURLToConfig}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)");
EXPECT_TRUE(script_console_observer.Wait());
EXPECT_EQ(1u, script_console_observer.messages().size());
EXPECT_EQ(last_script_message,
base::UTF16ToUTF8(script_console_observer.messages()[0].message));
if (!result.error.empty()) {
return false;
}
std::optional<GURL> observed_urn_uuid = config_observer.GetUrnUuid();
EXPECT_TRUE(observed_urn_uuid.has_value());
EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value()));
GURL urn_uuid = observed_urn_uuid.value();
if (!ResolveSelectURLToConfig()) {
EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec());
}
return true;
}
double RemainingBudget(const content::ToRenderFrameHost& execution_target,
bool should_add_module = false,
bool keep_alive_after_operation = true) {
if (should_add_module)
AddSimpleModule(execution_target);
content::WebContentsConsoleObserver budget_console_observer(
GetActiveWebContents());
const std::string kRemainingBudgetPrefixStr(kRemainingBudgetPrefix);
budget_console_observer.SetPattern(
base::StrCat({kRemainingBudgetPrefixStr, "*"}));
EXPECT_TRUE(ExecJs(execution_target,
content::JsReplace("window.keepWorklet = $1;",
keep_alive_after_operation)));
EXPECT_TRUE(ExecJs(execution_target, R"(
sharedStorage.run('remaining-budget-operation', {keepAlive: keepWorklet});
)"));
bool observed = budget_console_observer.Wait();
EXPECT_TRUE(observed);
if (!observed) {
return nan("");
}
EXPECT_EQ(1u, budget_console_observer.messages().size());
std::string console_message =
base::UTF16ToUTF8(budget_console_observer.messages()[0].message);
EXPECT_TRUE(base::StartsWith(console_message, kRemainingBudgetPrefixStr));
std::string result_string = console_message.substr(
kRemainingBudgetPrefixStr.size(),
console_message.size() - kRemainingBudgetPrefixStr.size());
double result = 0.0;
EXPECT_TRUE(base::StringToDouble(result_string, &result));
return result;
}
virtual bool ResolveSelectURLToConfig() const { return false; }
virtual bool EnablePrivacySandbox() const { return true; }
virtual bool AllowThirdPartyCookies() const { return true; }
virtual EnforcementAndEnrollmentStatus GetEnforcementAndEnrollmentStatus()
const {
return EnforcementAndEnrollmentStatus::kAttestationsUnenforced;
}
virtual bool EnableDebugMessages() const { return false; }
bool SuccessExpected() {
return GetEnforcementAndEnrollmentStatus() !=
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostUnenrolled &&
EnablePrivacySandbox() && AllowThirdPartyCookies();
}
std::string ExpectedSharedStorageDisabledMessage() {
return "Error: " + content::GetSharedStorageDisabledMessage();
}
protected:
base::HistogramTester histogram_tester_;
std::unique_ptr<MockChromeContentBrowserClient>
mock_chrome_content_browser_client_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
std::unique_ptr<privacy_sandbox::ScopedPrivacySandboxAttestations>
scoped_attestations_;
raw_ptr<content::ContentBrowserClient, AcrossTasksDanglingUntriaged>
old_chrome_content_browser_client_ = nullptr;
};
class SharedStorageChromeBrowserTest
: public SharedStorageChromeBrowserTestBase,
public testing::WithParamInterface<bool> {
public:
SharedStorageChromeBrowserTest() {
fenced_frame_api_change_feature_.InitWithFeatureState(
blink::features::kFencedFramesAPIChanges, ResolveSelectURLToConfig());
fenced_frame_feature_.InitAndEnableFeature(blink::features::kFencedFrames);
attestation_feature_.InitWithFeatureState(
privacy_sandbox::kEnforcePrivacySandboxAttestations,
GetEnforcementAndEnrollmentStatus() !=
EnforcementAndEnrollmentStatus::kAttestationsUnenforced);
}
~SharedStorageChromeBrowserTest() override = default;
bool ResolveSelectURLToConfig() const override { return GetParam(); }
private:
base::test::ScopedFeatureList fenced_frame_api_change_feature_;
base::test::ScopedFeatureList fenced_frame_feature_;
base::test::ScopedFeatureList attestation_feature_;
};
// We skip testing the `enable_debug_messages` parameter on Android due to
// hardware limitations that constrain the total number of tests that can be
// run.
using SharedStorageChromeBrowserParams = std::tuple<
/*enable_privacy_sandbox=*/bool,
/*allow_third_party_cookies=*/bool,
#if BUILDFLAG(IS_ANDROID)
/*enforcement_and_enrollment_status=*/EnforcementAndEnrollmentStatus>;
#else
/*enforcement_and_enrollment_status=*/EnforcementAndEnrollmentStatus,
/*enable_debug_messages=*/bool>;
#endif
class SharedStoragePrefBrowserTest
: public SharedStorageChromeBrowserTestBase,
public testing::WithParamInterface<SharedStorageChromeBrowserParams> {
public:
SharedStoragePrefBrowserTest() {
base::FieldTrialParams params;
params["ExposeDebugMessageForSettingsStatus"] =
EnableDebugMessages() ? "true" : "false";
shared_storage_feature_.InitAndEnableFeatureWithParameters(
blink::features::kSharedStorageAPI, params);
fenced_frame_api_change_feature_.InitWithFeatureState(
blink::features::kFencedFramesAPIChanges, ResolveSelectURLToConfig());
fenced_frame_feature_.InitAndEnableFeature(blink::features::kFencedFrames);
attestation_feature_.InitWithFeatureState(
privacy_sandbox::kEnforcePrivacySandboxAttestations,
GetEnforcementAndEnrollmentStatus() !=
EnforcementAndEnrollmentStatus::kAttestationsUnenforced);
}
bool ResolveSelectURLToConfig() const override { return true; }
bool EnablePrivacySandbox() const override { return std::get<0>(GetParam()); }
bool AllowThirdPartyCookies() const override {
return std::get<1>(GetParam());
}
EnforcementAndEnrollmentStatus GetEnforcementAndEnrollmentStatus()
const override {
return std::get<2>(GetParam());
}
bool EnableDebugMessages() const override {
#if BUILDFLAG(IS_ANDROID)
return false;
#else
return std::get<3>(GetParam());
#endif
}
void VerifyDebugErrorMessage(const std::string& error_message) {
ASSERT_FALSE(SuccessExpected());
size_t found_pos = error_message.find("Debug");
if (!EnableDebugMessages()) {
EXPECT_EQ(found_pos, std::string::npos);
return;
}
EXPECT_NE(found_pos, std::string::npos);
int status = (GetEnforcementAndEnrollmentStatus() ==
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostUnenrolled)
? 6
: (EnablePrivacySandbox() ? 4 : 1);
if (status == 4) {
ASSERT_FALSE(AllowThirdPartyCookies());
}
found_pos = error_message.find("status " + base::NumberToString(status));
EXPECT_NE(found_pos, std::string::npos);
}
void AddSimpleModuleWithPermissionBypassed(
const content::ToRenderFrameHost& execution_target) {
content::WebContentsConsoleObserver add_module_console_observer(
GetActiveWebContents());
add_module_console_observer.SetFilter(
MakeFilter({"Finish executing simple_module.js"}));
// Bypass the following permissions to allow one `addModule()` call.
mock_chrome_content_browser_client_
->set_bypass_shared_storage_allowed_count(1);
mock_chrome_content_browser_client_
->set_bypass_shared_storage_select_url_allowed_count(1);
EXPECT_TRUE(content::ExecJs(execution_target, R"(
sharedStorage.worklet.addModule('shared_storage/simple_module.js');
)"));
EXPECT_TRUE(add_module_console_observer.Wait());
// Shared Storage is enabled in order to `addModule()`.
EXPECT_EQ(1u, add_module_console_observer.messages().size());
EXPECT_EQ(
"Finish executing simple_module.js",
base::UTF16ToUTF8(add_module_console_observer.messages()[0].message));
}
bool ExecuteScriptInWorkletWithOuterPermissionsBypassed(
const content::ToRenderFrameHost& execution_target,
const std::string& script,
const std::string& last_script_message) {
content::WebContentsConsoleObserver add_module_console_observer(
GetActiveWebContents());
add_module_console_observer.SetFilter(
MakeFilter({"Finish executing customizable_module.js"}));
base::StringPairs run_function_body_replacement;
run_function_body_replacement.emplace_back("{{RUN_FUNCTION_BODY}}", script);
std::string host =
execution_target.render_frame_host()->GetLastCommittedOrigin().host();
GURL module_script_url = https_server()->GetURL(
host, net::test_server::GetFilePathWithReplacements(
"/shared_storage/customizable_module.js",
run_function_body_replacement));
// Bypass the following permissions to allow one call for `addModule()` and
// `run()` respectively. Any operations nested within the script run by
// `run()` will have preferences applied according to test parameters. When
// the latter disallow Shared Storage, it siumlates the situation where
// preferences are updated to block Shared Storage during the course of a
// previously allowed `run()` call.
mock_chrome_content_browser_client_
->set_bypass_shared_storage_allowed_count(2);
mock_chrome_content_browser_client_
->set_bypass_shared_storage_select_url_allowed_count(1);
EXPECT_TRUE(content::ExecJs(
execution_target,
content::JsReplace("sharedStorage.worklet.addModule($1)",
module_script_url)));
EXPECT_TRUE(add_module_console_observer.Wait());
EXPECT_EQ(1u,
content::GetAttachedSharedStorageWorkletHostsCount(
execution_target.render_frame_host()->GetStoragePartition()));
EXPECT_EQ(0u,
content::GetKeepAliveSharedStorageWorkletHostsCount(
execution_target.render_frame_host()->GetStoragePartition()));
EXPECT_EQ(1u, add_module_console_observer.messages().size());
EXPECT_EQ(
"Finish executing customizable_module.js",
base::UTF16ToUTF8(add_module_console_observer.messages()[0].message));
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
content::WebContentsConsoleObserver script_console_observer(
GetActiveWebContents());
script_console_observer.SetFilter(MakeFilter(
{last_script_message, ExpectedSharedStorageDisabledMessage()}));
content::EvalJsResult result = content::EvalJs(execution_target, R"(
sharedStorage.run('test-operation');
)");
EXPECT_TRUE(script_console_observer.Wait());
EXPECT_EQ(1u, script_console_observer.messages().size());
if (SuccessExpected()) {
EXPECT_EQ(
last_script_message,
base::UTF16ToUTF8(script_console_observer.messages()[0].message));
} else {
EXPECT_TRUE(base::StartsWith(
base::UTF16ToUTF8(script_console_observer.messages()[0].message),
ExpectedSharedStorageDisabledMessage()));
}
WaitForHistograms({kTimingDocumentRunHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
return result.error.empty();
}
private:
base::test::ScopedFeatureList shared_storage_feature_;
base::test::ScopedFeatureList fenced_frame_api_change_feature_;
base::test::ScopedFeatureList fenced_frame_feature_;
base::test::ScopedFeatureList attestation_feature_;
};
namespace {
std::string DescribePrefBrowserTestParams(
const testing::TestParamInfo<SharedStoragePrefBrowserTest::ParamType>&
info) {
return base::StrCat(
{"PrivacySandbox", std::get<0>(info.param) ? "Enabled" : "Disabled",
"_3PCookies", std::get<1>(info.param) ? "Allowed" : "Blocked",
"_Attestations",
(std::get<2>(info.param) !=
EnforcementAndEnrollmentStatus::kAttestationsUnenforced)
? base::StrCat({"Enforced_MainHost",
(std::get<2>(info.param) ==
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled)
? "Enrolled"
: "Unenrolled"})
: "Unenforced"
#if !BUILDFLAG(IS_ANDROID)
,
"_Debug", std::get<3>(info.param) ? "Enabled" : "Disabled"
#endif
});
}
} // namespace
INSTANTIATE_TEST_SUITE_P(
All,
SharedStoragePrefBrowserTest,
testing::Combine(
testing::Bool(),
testing::Bool(),
testing::Values(EnforcementAndEnrollmentStatus::kAttestationsUnenforced,
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostUnenrolled,
#if BUILDFLAG(IS_ANDROID)
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled)),
#else
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled),
testing::Bool()),
#endif
DescribePrefBrowserTestParams);
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, AddModule) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::WebContentsConsoleObserver console_observer(GetActiveWebContents());
console_observer.SetFilter(MakeFilter({"Finish executing simple_module.js"}));
content::WebContentsConsoleObserver attestations_console_observer(
GetActiveWebContents());
attestations_console_observer.SetPattern(
"Attestation check for Shared Storage on * failed.");
content::EvalJsResult result = content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.worklet.addModule('shared_storage/simple_module.js');
)");
if (!SuccessExpected()) {
// Shared Storage will be disabled.
EXPECT_TRUE(base::StartsWith(
result.error, GetSharedStorageAddModuleDisabledErrorMessage()));
VerifyDebugErrorMessage(result.error);
EXPECT_EQ(0u, console_observer.messages().size());
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1);
if (GetEnforcementAndEnrollmentStatus() ==
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostUnenrolled) {
ASSERT_TRUE(attestations_console_observer.Wait());
EXPECT_FALSE(attestations_console_observer.messages().empty());
}
return;
}
ASSERT_TRUE(console_observer.Wait());
// Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage
// should be allowed.
EXPECT_TRUE(result.error.empty());
EXPECT_EQ(1u, console_observer.messages().size());
EXPECT_EQ("Finish executing simple_module.js",
base::UTF16ToUTF8(console_observer.messages()[0].message));
EXPECT_TRUE(attestations_console_observer.messages().empty());
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, RunOperation) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
AddSimpleModuleWithPermissionBypassed(GetActiveWebContents());
content::WebContentsConsoleObserver run_op_console_observer(
GetActiveWebContents());
run_op_console_observer.SetFilter(
MakeFilter({"Finish executing \'test-operation\'"}));
content::EvalJsResult run_op_result =
content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.run(
'test-operation', {data: {'customKey': 'customValue'}});
)");
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram});
EXPECT_GE(
histogram_tester_.GetBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess),
1);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
if (!SuccessExpected()) {
// Shared Storage will be disabled.
EXPECT_TRUE(base::StartsWith(run_op_result.error,
GetSharedStorageDisabledErrorMessage()));
VerifyDebugErrorMessage(run_op_result.error);
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kErrorTypeHistogram, kWorkletNumPerPageHistogram});
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kRunWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
return;
}
ASSERT_TRUE(run_op_console_observer.Wait());
// Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage
// should be allowed.
EXPECT_TRUE(run_op_result.error.empty());
EXPECT_EQ(1u, run_op_console_observer.messages().size());
EXPECT_EQ("Finish executing \'test-operation\'",
base::UTF16ToUTF8(run_op_console_observer.messages()[0].message));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kTimingDocumentRunHistogram, kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, RunURLSelectionOperation) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
AddSimpleModuleWithPermissionBypassed(GetActiveWebContents());
content::WebContentsConsoleObserver run_url_op_console_observer(
GetActiveWebContents());
run_url_op_console_observer.SetFilter(
MakeFilter({"Finish executing \'test-url-selection-operation\'"}));
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
// Construct and add the `TestSelectURLFencedFrameConfigObserver` to shared
// storage worklet host manager.
content::StoragePartition* storage_partition = GetStoragePartition();
content::TestSelectURLFencedFrameConfigObserver config_observer(
storage_partition);
content::EvalJsResult run_url_op_result = EvalJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation',
[
{
url: "fenced_frames/title0.html"
},
{
url: "fenced_frames/title1.html",
reportingMetadata: {
"click": "fenced_frames/report1.html"
}
},
{
url: "fenced_frames/title2.html"
}
],
{
data: {'mockResult': 1},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)");
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram});
EXPECT_GE(
histogram_tester_.GetBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess),
1);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
if (!SuccessExpected()) {
// Shared Storage will be disabled.
EXPECT_TRUE(
base::StartsWith(run_url_op_result.error,
GetSharedStorageSelectURLDisabledErrorMessage()));
VerifyDebugErrorMessage(run_url_op_result.error);
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kErrorTypeHistogram, kWorkletNumPerPageHistogram});
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
return;
}
ASSERT_TRUE(run_url_op_console_observer.Wait());
// Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage
// should be allowed.
EXPECT_TRUE(run_url_op_result.error.empty());
std::optional<GURL> observed_urn_uuid = config_observer.GetUrnUuid();
EXPECT_TRUE(observed_urn_uuid.has_value());
EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value()));
GURL urn_uuid = observed_urn_uuid.value();
if (!ResolveSelectURLToConfig()) {
EXPECT_EQ(run_url_op_result.ExtractString(), observed_urn_uuid->spec());
}
EXPECT_EQ(1u, run_url_op_console_observer.messages().size());
EXPECT_EQ(
"Finish executing \'test-url-selection-operation\'",
base::UTF16ToUTF8(run_url_op_console_observer.messages()[0].message));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kTimingDocumentSelectUrlHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentSelectUrlHistogram, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, Set) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::EvalJsResult set_result = content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.set('customKey', 'customValue');
)");
if (!SuccessExpected()) {
// Shared Storage will be disabled.
EXPECT_TRUE(base::StartsWith(set_result.error,
GetSharedStorageDisabledErrorMessage()));
VerifyDebugErrorMessage(set_result.error);
return;
}
// Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage
// should be allowed.
EXPECT_TRUE(set_result.error.empty());
WaitForHistograms({kTimingDocumentSetHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentSetHistogram, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, Append) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::EvalJsResult append_result =
content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.append('customKey', 'customValue');
)");
if (!SuccessExpected()) {
// Shared Storage will be disabled.
EXPECT_TRUE(base::StartsWith(append_result.error,
GetSharedStorageDisabledErrorMessage()));
VerifyDebugErrorMessage(append_result.error);
return;
}
// Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage
// should be allowed.
EXPECT_TRUE(append_result.error.empty());
WaitForHistograms({kTimingDocumentAppendHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAppendHistogram, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, Delete) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::EvalJsResult delete_result =
content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.delete('customKey');
)");
if (!SuccessExpected()) {
// Shared Storage will be disabled.
EXPECT_TRUE(base::StartsWith(delete_result.error,
GetSharedStorageDisabledErrorMessage()));
VerifyDebugErrorMessage(delete_result.error);
return;
}
// Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage
// should be allowed.
EXPECT_TRUE(delete_result.error.empty());
WaitForHistograms({kTimingDocumentDeleteHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentDeleteHistogram, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, Clear) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::EvalJsResult clear_result =
content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.clear();
)");
if (!SuccessExpected()) {
// Shared Storage will be disabled.
EXPECT_TRUE(base::StartsWith(clear_result.error,
GetSharedStorageDisabledErrorMessage()));
VerifyDebugErrorMessage(clear_result.error);
return;
}
// Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage
// should be allowed.
EXPECT_TRUE(clear_result.error.empty());
WaitForHistograms({kTimingDocumentClearHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentClearHistogram, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletSet) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `set()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
console.log(await sharedStorage.set('key0', 'value0'));
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletSetHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletSetHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletAppend) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `append()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
console.log(await sharedStorage.append('key0', 'value0'));
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletAppendHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletAppendHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletDelete) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `delete()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
console.log(await sharedStorage.delete('key0'));
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletDeleteHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletDeleteHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletClear) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `clear()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
console.log(await sharedStorage.clear());
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletClearHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletClearHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletGet) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// To prevent failure in the case where Shared Storage is enabled, we set a
// key before retrieving it; but in the case here we expect failure, we test
// only `get()` to isolate the behavior and determine if the promise is
// rejected solely from that call.
std::string script = SuccessExpected() ? R"(
console.log(await sharedStorage.set('key0', 'value0'));
console.log(await sharedStorage.get('key0'));
console.log('Finished script');
)"
: R"(
console.log(await sharedStorage.get('key0'));
console.log('Finished script');
)";
// If `get()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), script, "Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletSetHistogram, kTimingWorkletGetHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletSetHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletGetHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletKeys) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `keys()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
for await (const key of sharedStorage.keys()) {
console.log(key);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletKeysHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletKeysHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletEntries) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `entries()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
for await (const [key, value] of sharedStorage.entries()) {
console.log(key + ';' + value);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletEntriesHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletEntriesHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletLength) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `length()` fails due to Shared Storage being disabled, there will be a
// console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
console.log(await sharedStorage.length());
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingWorkletLengthHistogram});
histogram_tester_.ExpectTotalCount(kTimingWorkletLengthHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, WorkletRemainingBudget) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
// If `remainingBudget()` fails due to Shared Storage being disabled, there
// will be a console message verified in the helper
// `ExecuteScriptInWorkletWithOuterPermissionsBypassed()` rather than an error
// message since it is wrapped in a `console.log()` call.
EXPECT_TRUE(ExecuteScriptInWorkletWithOuterPermissionsBypassed(
GetActiveWebContents(), R"(
console.log(await sharedStorage.remainingBudget());
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
if (SuccessExpected()) {
WaitForHistograms({kTimingRemainingBudgetHistogram});
histogram_tester_.ExpectTotalCount(kTimingRemainingBudgetHistogram, 1);
}
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
WorkletKeysEntries_AllIterated) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
for (let i = 0; i < 150; ++i) {
sharedStorage.set('key' + i.toString().padStart(3, '0'),
'value' + i.toString().padStart(3, '0'));
}
for await (const key of sharedStorage.keys()) {
console.log(key);
}
for await (const [key, value] of sharedStorage.entries()) {
console.log(key + ';' + value);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms(
{kWorkletNumPerPageHistogram, kErrorTypeHistogram,
kTimingDocumentAddModuleHistogram, kTimingDocumentRunHistogram,
kTimingWorkletKeysHistogram, kTimingWorkletEntriesHistogram,
kEntriesQueuedCountHistogram, kReceivedEntriesBenchmarksHistogram,
kIteratedEntriesBenchmarksHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletKeysHistogram, 151);
histogram_tester_.ExpectTotalCount(kTimingWorkletEntriesHistogram, 151);
histogram_tester_.ExpectUniqueSample(kEntriesQueuedCountHistogram, 150, 2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 70,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 80,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 90,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 100,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 70,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 80,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 90,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 100,
2);
}
// TODO(crbug.com/1453981): fix and re-enable on Android.
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
#if BUILDFLAG(IS_ANDROID)
DISABLED_WorkletKeys_PartiallyIterated
#else
WorkletKeys_PartiallyIterated
#endif // BUILDFLAG(IS_ANDROID)
) {
base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(60));
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
for (let i = 0; i < 300; ++i) {
sharedStorage.set('key' + i.toString().padStart(3, '0'),
'value' + i.toString().padStart(3, '0'));
}
var keys = sharedStorage.keys();
for (let i = 0; i < 150; ++i) {
let key_dict = await keys.next();
console.log(key_dict['value']);
}
var keys2 = sharedStorage.keys();
for (let i = 0; i < 243; ++i) {
let key_dict = await keys2.next();
console.log(key_dict['value']);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram, kErrorTypeHistogram,
kTimingDocumentAddModuleHistogram,
kTimingDocumentRunHistogram, kTimingWorkletKeysHistogram,
kEntriesQueuedCountHistogram,
kReceivedEntriesBenchmarksHistogram,
kIteratedEntriesBenchmarksHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletKeysHistogram, 150 + 243);
histogram_tester_.ExpectUniqueSample(kEntriesQueuedCountHistogram, 300, 2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 70,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 80,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 90,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 100,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 60,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 70,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 80,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 90,
0);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 100,
0);
}
// TODO(crbug.com/1453981): fix and re-enable on Android.
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
#if BUILDFLAG(IS_ANDROID)
DISABLED_WorkletEntries_PartiallyIterated
#else
WorkletEntries_PartiallyIterated
#endif // BUILDFLAG(IS_ANDROID)
) {
base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(60));
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
for (let i = 0; i < 300; ++i) {
sharedStorage.set('key' + i.toString().padStart(3, '0'),
'value' + i.toString().padStart(3, '0'));
}
var entries = sharedStorage.entries();
for (let i = 0; i < 101; ++i) {
let entry_dict = await entries.next();
console.log(entry_dict['value']);
}
var entries = sharedStorage.entries();
for (let i = 0; i < 299; ++i) {
let entry_dict = await entries.next();
console.log(entry_dict['value']);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms(
{kWorkletNumPerPageHistogram, kErrorTypeHistogram,
kTimingDocumentAddModuleHistogram, kTimingDocumentRunHistogram,
kTimingWorkletEntriesHistogram, kEntriesQueuedCountHistogram,
kReceivedEntriesBenchmarksHistogram,
kIteratedEntriesBenchmarksHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletEntriesHistogram, 101 + 299);
histogram_tester_.ExpectUniqueSample(kEntriesQueuedCountHistogram, 300, 2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 70,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 80,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 90,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 100,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 40,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 50,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 60,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 70,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 80,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 90,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 100,
0);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
WorkletKeysEntries_AllIteratedLessThanTenKeys) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
for (let i = 0; i < 5; ++i) {
sharedStorage.set('key' + i.toString().padStart(3, '0'),
'value' + i.toString().padStart(3, '0'));
}
for await (const key of sharedStorage.keys()) {
console.log(key);
}
for await (const [key, value] of sharedStorage.entries()) {
console.log(key + ';' + value);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms(
{kWorkletNumPerPageHistogram, kErrorTypeHistogram,
kTimingDocumentAddModuleHistogram, kTimingDocumentRunHistogram,
kTimingWorkletKeysHistogram, kTimingWorkletEntriesHistogram,
kEntriesQueuedCountHistogram, kReceivedEntriesBenchmarksHistogram,
kIteratedEntriesBenchmarksHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletKeysHistogram, 6);
histogram_tester_.ExpectTotalCount(kTimingWorkletEntriesHistogram, 6);
histogram_tester_.ExpectUniqueSample(kEntriesQueuedCountHistogram, 5, 2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 70,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 80,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 90,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 100,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 70,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 80,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 90,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 100,
2);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
WorkletKeysEntries_PartiallyIteratedLessThanTenKeys) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
for (let i = 0; i < 5; ++i) {
sharedStorage.set('key' + i.toString().padStart(3, '0'),
'value' + i.toString().padStart(3, '0'));
}
var keys = sharedStorage.keys();
for (let i = 0; i < 4; ++i) {
let key_dict = await keys.next();
console.log(key_dict['value']);
}
var entries = sharedStorage.entries();
for (let i = 0; i < 2; ++i) {
let entry_dict = await entries.next();
console.log(entry_dict['value']);
}
var keys2 = sharedStorage.keys();
for (let i = 0; i < 3; ++i) {
let key_dict = await keys2.next();
console.log(key_dict['value']);
}
var entries = sharedStorage.entries();
for (let i = 0; i < 1; ++i) {
let entry_dict = await entries.next();
console.log(entry_dict['value']);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms(
{kWorkletNumPerPageHistogram, kErrorTypeHistogram,
kTimingDocumentAddModuleHistogram, kTimingDocumentRunHistogram,
kTimingWorkletKeysHistogram, kTimingWorkletEntriesHistogram,
kEntriesQueuedCountHistogram, kReceivedEntriesBenchmarksHistogram,
kIteratedEntriesBenchmarksHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletKeysHistogram, 4 + 3);
histogram_tester_.ExpectTotalCount(kTimingWorkletEntriesHistogram, 2 + 1);
histogram_tester_.ExpectUniqueSample(kEntriesQueuedCountHistogram, 5, 4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 0,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 10,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 20,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 30,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 40,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 50,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 60,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 70,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 80,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 90,
4);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 100,
4);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 0,
4);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 10,
4);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 20,
4);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 30,
3);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 40,
3);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 70,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 80,
1);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 90,
0);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
WorkletKeysEntries_AllIteratedNoKeys) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
sharedStorage.set('key', 'value');
sharedStorage.delete('key');
for await (const key of sharedStorage.keys()) {
console.log(key);
}
for await (const [key, value] of sharedStorage.entries()) {
console.log(key + ';' + value);
}
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms(
{kWorkletNumPerPageHistogram, kErrorTypeHistogram,
kTimingDocumentAddModuleHistogram, kTimingDocumentRunHistogram,
kTimingWorkletKeysHistogram, kTimingWorkletEntriesHistogram,
kEntriesQueuedCountHistogram, kReceivedEntriesBenchmarksHistogram,
kIteratedEntriesBenchmarksHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletKeysHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletEntriesHistogram, 1);
histogram_tester_.ExpectUniqueSample(kEntriesQueuedCountHistogram, 0, 2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 10,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 20,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 30,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 40,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 50,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 60,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 70,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 80,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 90,
2);
histogram_tester_.ExpectBucketCount(kReceivedEntriesBenchmarksHistogram, 100,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 0,
2);
histogram_tester_.ExpectBucketCount(kIteratedEntriesBenchmarksHistogram, 10,
0);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
AddModule_InvalidScriptUrlError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
std::string invalid_url = "http://#";
content::EvalJsResult result = content::EvalJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", invalid_url));
EXPECT_EQ(
base::StrCat(
{"a JavaScript error: \"Error: The module script url is invalid.\n",
" at __const_std::string&_script__:1:24):\n",
" {sharedStorage.worklet.addModule(\"", invalid_url, "\")\n",
" ^^^^^\n"}),
result.error);
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
AddModule_CrossOriginScriptError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url = https_server()->GetURL(kCrossOriginHost,
"/shared_storage/simple_module.js");
content::EvalJsResult result = content::EvalJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
EXPECT_EQ(
base::StrCat({"a JavaScript error: \"Error: Only same origin module ",
"script is allowed.",
"\n at __const_std::string&_script__:1:24):\n ",
"{sharedStorage.worklet.addModule(\"",
script_url.spec().substr(0, 38),
"\n ^^^^^\n"}),
result.error);
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
AddModule_LoadFailureError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url = https_server()->GetURL(
kMainHost, "/shared_storage/nonexistent_module.js");
content::EvalJsResult result = content::EvalJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
EXPECT_EQ(
base::StrCat({"a JavaScript error: \"Error: Failed to load ",
script_url.spec(), " HTTP status = 404 Not Found.\"\n"}),
result.error);
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
AddModule_UnexpectedRedirectError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url = https_server()->GetURL(
kMainHost, "/server-redirect?shared_storage/simple_module.js");
content::EvalJsResult result = content::EvalJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
EXPECT_EQ(
base::StrCat({"a JavaScript error: \"Error: Unexpected redirect on ",
script_url.spec(), ".\"\n"}),
result.error);
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
AddModule_EmptyResultError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module.js");
content::EvalJsResult result = content::EvalJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
EXPECT_THAT(
result.error,
testing::HasSubstr("ReferenceError: undefinedVariable is not defined"));
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
AddModule_MultipleAddModuleError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/simple_module.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
content::EvalJsResult result = content::EvalJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url));
EXPECT_THAT(
result.error,
testing::HasSubstr("addModule() can only be invoked once per worklet"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_NotLoadedError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::EvalJsResult result = content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.run(
'test-operation', {data: {}});
)");
EXPECT_THAT(
result.error,
testing::HasSubstr(
"sharedStorage.worklet.addModule() has to be called before run()"));
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kRunWebVisible,
1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_NotRegisteredError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/simple_module.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(),
R"(
sharedStorage.run(
'test-operation-1', {data: {}});
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kRunNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_FunctionError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module2.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(),
R"(
sharedStorage.run(
'test-operation', {data: {}});
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kRunNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_ScriptError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module4.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(),
R"(
sharedStorage.run(
'test-operation', {data: {}});
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kRunNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
Run_UnexpectedCustomDataError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module5.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(),
R"(
sharedStorage.run(
'test-operation', {data: {'customField': 'customValue123'}});
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kRunNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
SelectUrl_NotLoadedError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
content::EvalJsResult result = EvalJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation-1',
[
{
url: "fenced_frames/title0.html"
}
],
{
data: {},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)");
EXPECT_THAT(result.error,
testing::HasSubstr("sharedStorage.worklet.addModule() has to be "
"called before selectURL()"));
WaitForHistograms({kErrorTypeHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLWebVisible, 1);
}
// TODO(https://crbug.com/1484437): Fix flakes.
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
DISABLED_SelectUrl_NotRegisteredError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/simple_module.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation-1',
[
{
url: "fenced_frames/title0.html"
}
],
{
data: {},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
// TODO(crbug.com/1485061): Test is flaky on ChromeOS and Windows.
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
#define MAYBE_SelectUrl_FunctionError DISABLED_SelectUrl_FunctionError
#else
#define MAYBE_SelectUrl_FunctionError SelectUrl_FunctionError
#endif
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
MAYBE_SelectUrl_FunctionError) {
// TODO(crbug.com/1485061): Test is flaky on linux-bfcache-rel.
if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
return;
}
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module2.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation',
[
{
url: "fenced_frames/title0.html"
}
],
{
data: {},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
// TODO(https://crbug.com/1484437): Fix flakes.
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
DISABLED_SelectUrl_ScriptError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module4.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation',
[
{
url: "fenced_frames/title0.html"
}
],
{
data: {},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
// TODO(https://crbug.com/1484437): Fix flakes.
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
DISABLED_SelectUrl_UnexpectedCustomDataError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module5.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation',
[
{
url: "fenced_frames/title0.html"
}
],
{
data: {'customField': 'customValue123'},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
// TODO(https://crbug.com/1484437): Fix flakes.
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
DISABLED_SelectUrl_OutOfRangeError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module6.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation-1',
[
{
url: "fenced_frames/title0.html"
}
],
{
data: {},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
// TODO(https://crbug.com/1484437): Fix flakes.
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
DISABLED_SelectUrl_ReturnValueToIntError) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL script_url =
https_server()->GetURL(kMainHost, "/shared_storage/erroneous_module6.js");
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace("sharedStorage.worklet.addModule($1)", script_url)));
EXPECT_TRUE(ExecJs(GetActiveWebContents(),
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation-2',
[
{
url: "fenced_frames/title0.html"
}
],
{
data: {},
resolveToConfig: resolveSelectURLToConfig
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kTimingDocumentAddModuleHistogram, kErrorTypeHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectBucketCount(
kErrorTypeHistogram,
blink::SharedStorageWorkletErrorType::kSelectURLNonWebVisible, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, DocumentTiming) {
base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(60));
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(content::ExecJs(GetActiveWebContents(),
R"(
sharedStorage.set('key0', 'value0');
sharedStorage.set('key1', 'value1');
sharedStorage.set('key1', 'value111');
sharedStorage.set('key2', 'value2');
sharedStorage.set('key2', 'value222', {ignoreIfPresent: true});
sharedStorage.set('key3', 'value3');
sharedStorage.append('key3', 'value333');
sharedStorage.append('key2', 'value22');
sharedStorage.append('key4', 'value4');
sharedStorage.delete('key0');
sharedStorage.delete('key2');
sharedStorage.clear();
)"));
WaitForHistograms(
{kTimingDocumentSetHistogram, kTimingDocumentAppendHistogram,
kTimingDocumentDeleteHistogram, kTimingDocumentClearHistogram});
histogram_tester_.ExpectTotalCount(kTimingDocumentSetHistogram, 6);
histogram_tester_.ExpectTotalCount(kTimingDocumentAppendHistogram, 3);
histogram_tester_.ExpectTotalCount(kTimingDocumentDeleteHistogram, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentClearHistogram, 1);
}
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, WorkletTiming) {
base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(60));
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(),
R"(
sharedStorage.set('key0', 'value0');
sharedStorage.set('key1', 'value1');
sharedStorage.set('key1', 'value111');
sharedStorage.set('key2', 'value2');
sharedStorage.set('key2', 'value222', {ignoreIfPresent: true});
sharedStorage.set('key3', 'value3');
sharedStorage.append('key3', 'value333');
sharedStorage.append('key2', 'value22');
sharedStorage.append('key4', 'value4');
console.log(await sharedStorage.get('key0'));
console.log(await sharedStorage.get('key1'));
console.log(await sharedStorage.get('key2'));
console.log(await sharedStorage.get('key3'));
console.log(await sharedStorage.get('key4'));
console.log(await sharedStorage.length());
sharedStorage.delete('key0');
sharedStorage.delete('key2');
// It's necessary to `await` this finally promise, since we are not
// using the option `keepAlive: true` in the `run()` call. The worklet
// will be closed by the browser once the worklet signals to the browser
// that the `run()` call has finished.
await sharedStorage.clear();
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(
GetActiveWebContents(),
https_server()->GetURL(kCrossOriginHost, kSimplePagePath)));
WaitForHistograms(
{kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kTimingDocumentRunHistogram, kTimingWorkletSetHistogram,
kTimingWorkletAppendHistogram, kTimingWorkletGetHistogram,
kTimingWorkletLengthHistogram, kTimingWorkletDeleteHistogram,
kTimingWorkletClearHistogram, kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletSetHistogram, 6);
histogram_tester_.ExpectTotalCount(kTimingWorkletAppendHistogram, 3);
histogram_tester_.ExpectTotalCount(kTimingWorkletGetHistogram, 5);
histogram_tester_.ExpectTotalCount(kTimingWorkletLengthHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingWorkletDeleteHistogram, 2);
histogram_tester_.ExpectTotalCount(kTimingWorkletClearHistogram, 1);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
}
// Flaky: https://crbug.com/1406845
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
DISABLED_WorkletNumPerPage_Two) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::RenderFrameHost* main_frame =
GetActiveWebContents()->GetPrimaryMainFrame();
EXPECT_TRUE(ExecuteScriptInWorklet(main_frame,
R"(
sharedStorage.set('key0', 'value0');
console.log('Finished script');
)",
"Finished script"));
content::RenderFrameHost* iframe = CreateIframe(
main_frame, https_server()->GetURL(kCrossOriginHost, kSimplePagePath));
EXPECT_TRUE(ExecuteScriptInWorklet(iframe,
R"(
sharedStorage.set('key0', 'value0');
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kTimingDocumentRunHistogram, kTimingWorkletSetHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 4);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 2);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 2, 1);
EXPECT_LE(1u,
histogram_tester_.GetAllSamples(kTimingWorkletSetHistogram).size());
}
// Flaky: https://crbug.com/1406845
IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest,
DISABLED_WorkletNumPerPage_Three) {
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
content::RenderFrameHost* main_frame =
GetActiveWebContents()->GetPrimaryMainFrame();
EXPECT_TRUE(ExecuteScriptInWorklet(main_frame,
R"(
sharedStorage.set('key0', 'value0');
console.log('Finished script');
)",
"Finished script"));
content::RenderFrameHost* iframe = CreateIframe(
main_frame, https_server()->GetURL(kCrossOriginHost, kSimplePagePath));
EXPECT_TRUE(ExecuteScriptInWorklet(iframe,
R"(
sharedStorage.set('key0', 'value0');
console.log('Finished script');
)",
"Finished script"));
content::RenderFrameHost* nested_iframe = CreateIframe(
iframe, https_server()->GetURL(kThirdOriginHost, kSimplePagePath));
EXPECT_TRUE(ExecuteScriptInWorklet(nested_iframe,
R"(
sharedStorage.set('key0', 'value0');
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kTimingDocumentRunHistogram, kTimingWorkletSetHistogram,
kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 6);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 3);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 3);
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 3, 1);
EXPECT_LE(1u,
histogram_tester_.GetAllSamples(kTimingWorkletSetHistogram).size());
}
INSTANTIATE_TEST_SUITE_P(All,
SharedStorageChromeBrowserTest,
testing::Bool(),
[](const testing::TestParamInfo<
SharedStorageChromeBrowserTest::ParamType>& info) {
return base::StrCat({"ResolveSelectURLTo",
info.param ? "Config" : "URN"});
});
class SharedStorageFencedFrameChromeBrowserTest
: public SharedStorageChromeBrowserTestBase {
public:
SharedStorageFencedFrameChromeBrowserTest() {
base::test::TaskEnvironment task_environment;
shared_storage_feature_.InitWithFeaturesAndParameters(
/*enabled_features=*/
{{blink::features::kSharedStorageAPI,
{{"SharedStorageBitBudget", base::NumberToString(kBudgetAllowed)}}}},
/*disabled_features=*/{});
fenced_frame_api_change_feature_.InitAndEnableFeature(
blink::features::kFencedFramesAPIChanges);
fenced_frame_feature_.InitAndEnableFeature(blink::features::kFencedFrames);
attestation_feature_.InitWithFeatureState(
privacy_sandbox::kEnforcePrivacySandboxAttestations,
GetEnforcementAndEnrollmentStatus() !=
EnforcementAndEnrollmentStatus::kAttestationsUnenforced);
}
~SharedStorageFencedFrameChromeBrowserTest() override = default;
bool ResolveSelectURLToConfig() const override { return true; }
EnforcementAndEnrollmentStatus GetEnforcementAndEnrollmentStatus()
const override {
return EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled;
}
void Set3rdPartyCookieAndAttestationSettingsThenNavigateToMainHostPage() {
main_url_ = https_server()->GetURL(kMainHost, kSimplePagePath);
iframe_url_ = https_server()->GetURL(kCrossOriginHost, kSimplePagePath);
new_page_url1_ = https_server()->GetURL(kThirdOriginHost, kSimplePagePath);
new_page_url2_ = https_server()->GetURL(kFourthOriginHost, kSimplePagePath);
SetThirdPartyCookieSetting(main_url_);
SetAttestationsMap(
MakeSharedStoragePrivacySandboxAttestationsMap(std::vector<GURL>(
{main_url_, iframe_url_, new_page_url1_, new_page_url2_})));
EXPECT_TRUE(NavigateToURL(GetActiveWebContents(), main_url_));
}
content::RenderFrameHost* SelectURLAndCreateFencedFrame(
content::RenderFrameHost* render_frame_host,
bool should_add_module = true,
bool keep_alive_after_operation = true) {
if (should_add_module)
AddSimpleModule(render_frame_host);
content::WebContentsConsoleObserver run_url_op_console_observer(
GetActiveWebContents());
run_url_op_console_observer.SetFilter(
MakeFilter({"Finish executing \'test-url-selection-operation\'"}));
EXPECT_TRUE(
ExecJs(render_frame_host,
content::JsReplace("window.resolveSelectURLToConfig = $1;",
ResolveSelectURLToConfig())));
EXPECT_TRUE(ExecJs(render_frame_host,
content::JsReplace("window.keepWorklet = $1;",
keep_alive_after_operation)));
// Construct and add the `TestSelectURLFencedFrameConfigObserver` to shared
// storage worklet host manager.
content::StoragePartition* storage_partition = GetStoragePartition();
content::TestSelectURLFencedFrameConfigObserver config_observer(
storage_partition);
content::EvalJsResult run_url_op_result = EvalJs(render_frame_host, R"(
(async function() {
window.select_url_result = await sharedStorage.selectURL(
'test-url-selection-operation',
[
{
url: "fenced_frames/title0.html"
},
{
url: "fenced_frames/title1.html",
reportingMetadata:
{
"click": "fenced_frames/report1.html"
}
},
{
url: "fenced_frames/title2.html"
}
],
{
data: {'mockResult': 1},
resolveToConfig: resolveSelectURLToConfig,
keepAlive: keepWorklet
}
);
if (resolveSelectURLToConfig &&
!(select_url_result instanceof FencedFrameConfig)) {
throw new Error('selectURL() did not return a FencedFrameConfig.');
}
return window.select_url_result;
})()
)");
EXPECT_TRUE(run_url_op_console_observer.Wait());
EXPECT_TRUE(run_url_op_result.error.empty());
const std::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid();
EXPECT_TRUE(observed_urn_uuid.has_value());
EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value()));
if (!ResolveSelectURLToConfig()) {
EXPECT_EQ(run_url_op_result.ExtractString(), observed_urn_uuid->spec());
}
EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value()));
EXPECT_EQ(1u, run_url_op_console_observer.messages().size());
EXPECT_EQ(
"Finish executing \'test-url-selection-operation\'",
base::UTF16ToUTF8(run_url_op_console_observer.messages()[0].message));
return content::CreateFencedFrame(
render_frame_host,
ResolveSelectURLToConfig()
? content::FencedFrameNavigationTarget("select_url_result")
: content::FencedFrameNavigationTarget(observed_urn_uuid.value()));
}
protected:
GURL main_url_;
GURL iframe_url_;
GURL new_page_url1_;
GURL new_page_url2_;
private:
base::test::ScopedFeatureList shared_storage_feature_;
base::test::ScopedFeatureList fenced_frame_api_change_feature_;
base::test::ScopedFeatureList fenced_frame_feature_;
base::test::ScopedFeatureList attestation_feature_;
};
IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameChromeBrowserTest,
FencedFrameNavigateTop_BudgetWithdrawal) {
Set3rdPartyCookieAndAttestationSettingsThenNavigateToMainHostPage();
content::RenderFrameHost* iframe =
CreateIframe(GetActiveWebContents()->GetPrimaryMainFrame(), iframe_url_);
content::RenderFrameHost* fenced_frame_root_node =
SelectURLAndCreateFencedFrame(iframe);
EXPECT_DOUBLE_EQ(RemainingBudget(iframe), kBudgetAllowed);
content::TestNavigationObserver top_navigation_observer(
GetActiveWebContents());
EXPECT_TRUE(ExecJs(
fenced_frame_root_node,
content::JsReplace("window.open($1, '_unfencedTop')", new_page_url1_)));
top_navigation_observer.Wait();
content::RenderFrameHost* new_iframe =
CreateIframe(GetActiveWebContents()->GetPrimaryMainFrame(), iframe_url_);
// After the top navigation, log(3) bits should have been withdrawn from the
// original shared storage origin.
EXPECT_DOUBLE_EQ(RemainingBudget(new_iframe, /*should_add_module=*/true),
kBudgetAllowed - std::log2(3));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms(
{kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kTimingDocumentSelectUrlHistogram, kTimingDocumentRunHistogram,
kTimingRemainingBudgetHistogram, kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 5);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentSelectUrlHistogram, 1);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 2);
histogram_tester_.ExpectTotalCount(kTimingRemainingBudgetHistogram, 2);
// In the MPArch case, some additional pageloads with worklet count 0 are
// recorded, so we do not use `ExpectUniqueSample()` here.
histogram_tester_.ExpectBucketCount(kWorkletNumPerPageHistogram, 1, 2);
EXPECT_EQ(2, histogram_tester_.GetTotalSum(kWorkletNumPerPageHistogram));
}
IN_PROC_BROWSER_TEST_F(
SharedStorageFencedFrameChromeBrowserTest,
TwoFencedFrames_DifferentURNs_EachNavigateOnce_BudgetWithdrawalTwice) {
Set3rdPartyCookieAndAttestationSettingsThenNavigateToMainHostPage();
content::RenderFrameHost* iframe1 =
CreateIframe(GetActiveWebContents()->GetPrimaryMainFrame(), iframe_url_);
content::RenderFrameHost* fenced_frame_root_node1 =
SelectURLAndCreateFencedFrame(iframe1);
EXPECT_DOUBLE_EQ(RemainingBudget(iframe1), kBudgetAllowed);
content::TestNavigationObserver top_navigation_observer1(
GetActiveWebContents());
EXPECT_TRUE(ExecJs(
fenced_frame_root_node1,
content::JsReplace("window.open($1, '_unfencedTop')", new_page_url1_)));
top_navigation_observer1.Wait();
content::RenderFrameHost* iframe2 =
CreateIframe(GetActiveWebContents()->GetPrimaryMainFrame(), iframe_url_);
// After the top navigation, log(3) bits should have been withdrawn from the
// original shared storage origin.
EXPECT_DOUBLE_EQ(RemainingBudget(iframe2, /*should_add_module=*/true),
kBudgetAllowed - std::log2(3));
content::RenderFrameHost* fenced_frame_root_node2 =
SelectURLAndCreateFencedFrame(iframe2, /*should_add_module=*/false);
EXPECT_DOUBLE_EQ(RemainingBudget(iframe2), kBudgetAllowed - std::log2(3));
content::TestNavigationObserver top_navigation_observer2(
GetActiveWebContents());
EXPECT_TRUE(ExecJs(
fenced_frame_root_node2,
content::JsReplace("window.open($1, '_unfencedTop')", new_page_url2_)));
top_navigation_observer2.Wait();
content::RenderFrameHost* iframe3 =
CreateIframe(GetActiveWebContents()->GetPrimaryMainFrame(), iframe_url_);
// After the top navigation, another log(3) bits should have been withdrawn
// from the original shared storage origin.
EXPECT_DOUBLE_EQ(RemainingBudget(iframe3, /*should_add_module=*/true),
kBudgetAllowed - std::log2(3) - std::log2(3));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms(
{kErrorTypeHistogram, kTimingDocumentAddModuleHistogram,
kTimingDocumentSelectUrlHistogram, kTimingDocumentRunHistogram,
kTimingRemainingBudgetHistogram, kWorkletNumPerPageHistogram});
histogram_tester_.ExpectUniqueSample(
kErrorTypeHistogram, blink::SharedStorageWorkletErrorType::kSuccess, 9);
histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 3);
histogram_tester_.ExpectTotalCount(kTimingDocumentSelectUrlHistogram, 2);
histogram_tester_.ExpectTotalCount(kTimingDocumentRunHistogram, 4);
histogram_tester_.ExpectTotalCount(kTimingRemainingBudgetHistogram, 4);
// In the MPArch case, some additional pageloads with worklet count 0 are
// recorded, so we do not use `ExpectUniqueSample()` here.
histogram_tester_.ExpectBucketCount(kWorkletNumPerPageHistogram, 1, 3);
EXPECT_EQ(3, histogram_tester_.GetTotalSum(kWorkletNumPerPageHistogram));
}
class SharedStoragePrivateAggregationChromeBrowserTest
: public SharedStorageChromeBrowserTestBase,
public testing::WithParamInterface<EnforcementAndEnrollmentStatus> {
public:
SharedStoragePrivateAggregationChromeBrowserTest() {
fenced_frame_api_change_feature_.InitWithFeatureState(
blink::features::kFencedFramesAPIChanges, ResolveSelectURLToConfig());
fenced_frame_feature_.InitAndEnableFeature(blink::features::kFencedFrames);
attestation_feature_.InitWithFeatureState(
privacy_sandbox::kEnforcePrivacySandboxAttestations,
GetEnforcementAndEnrollmentStatus() !=
EnforcementAndEnrollmentStatus::kAttestationsUnenforced);
private_aggregation_feature_.InitAndEnableFeature(
blink::features::kPrivateAggregationApi);
}
~SharedStoragePrivateAggregationChromeBrowserTest() override = default;
bool ResolveSelectURLToConfig() const override { return true; }
EnforcementAndEnrollmentStatus GetEnforcementAndEnrollmentStatus()
const override {
return GetParam();
}
// This always enrolls the main host for Shared Storage, but only enrolls the
// main host for Private Aggregation exactly when
// `(GetEnforcementAndEnrollmentStatus() ==
// EnforcementAndEnrollmentStatus::kAttestationsEnforcedMainHostEnrolled)` is
// true.
void MaybeEnrollMainHost(const GURL& main_url) override {
privacy_sandbox::PrivacySandboxAttestationsMap attestations_map =
MakeSharedStoragePrivacySandboxAttestationsMap(
std::vector<GURL>({main_url}),
/*enroll_for_private_aggregation=*/(
GetEnforcementAndEnrollmentStatus() ==
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled));
SetAttestationsMap(attestations_map);
}
private:
base::test::ScopedFeatureList shared_storage_feature_;
base::test::ScopedFeatureList fenced_frame_api_change_feature_;
base::test::ScopedFeatureList fenced_frame_feature_;
base::test::ScopedFeatureList attestation_feature_;
base::test::ScopedFeatureList private_aggregation_feature_;
};
INSTANTIATE_TEST_SUITE_P(
All,
SharedStoragePrivateAggregationChromeBrowserTest,
testing::Values(
EnforcementAndEnrollmentStatus::kAttestationsUnenforced,
EnforcementAndEnrollmentStatus::kAttestationsEnforcedMainHostUnenrolled,
EnforcementAndEnrollmentStatus::kAttestationsEnforcedMainHostEnrolled),
[](const testing::TestParamInfo<
SharedStoragePrivateAggregationChromeBrowserTest::ParamType>& info) {
return base::StrCat(
{"Attestations",
(info.param !=
EnforcementAndEnrollmentStatus::kAttestationsUnenforced)
? base::StrCat(
{"Enforced_MainHost",
(info.param == EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled)
? "Enrolled"
: "Unenrolled"})
: "Unenforced"});
});
IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationChromeBrowserTest,
ContributeToHistogramViaRun) {
// This always enrolls the main host for Shared Storage, but only enrolls the
// main host for Private Aggregation exactly when `ShouldEnrollMainHost()` is
// true.
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
privateAggregation.contributeToHistogram({bucket: 1n, value: 2});
console.log('Finished script');
)",
"Finished script"));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram,
kPrivateAggregationHostPipeResultHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kPrivateAggregationHostPipeResultHistogram,
SuccessExpected()
? content::GetPrivateAggregationHostPipeReportSuccessValue()
: content::GetPrivateAggregationHostPipeApiDisabledValue(),
1);
histogram_tester_.ExpectTotalCount(
kPrivateAggregationHostTimeToGenerateReportRequestWithContextIdHistogram,
0);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationChromeBrowserTest,
ContributeToHistogramViaSelectURL) {
// This always enrolls the main host for Shared Storage, but only enrolls the
// main host for Private Aggregation exactly when `ShouldEnrollMainHost()` is
// true.
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
EXPECT_TRUE(ExecuteScriptInWorklet(GetActiveWebContents(), R"(
privateAggregation.contributeToHistogram({bucket: 1n, value: 2});
console.log('Finished script');
return 1;
)",
"Finished script",
/*use_select_url=*/true));
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram,
kPrivateAggregationHostPipeResultHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kPrivateAggregationHostPipeResultHistogram,
SuccessExpected()
? content::GetPrivateAggregationHostPipeReportSuccessValue()
: content::GetPrivateAggregationHostPipeApiDisabledValue(),
1);
histogram_tester_.ExpectTotalCount(
kPrivateAggregationHostTimeToGenerateReportRequestWithContextIdHistogram,
0);
}
IN_PROC_BROWSER_TEST_P(SharedStoragePrivateAggregationChromeBrowserTest,
WithContextId_NoPrivateAggregationJS) {
// This always enrolls the main host for Shared Storage, but only enrolls the
// main host for Private Aggregation exactly when `ShouldEnrollMainHost()` is
// true.
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
AddSimpleModule(GetActiveWebContents());
content::EvalJsResult result = content::EvalJs(GetActiveWebContents(), R"(
sharedStorage.run('test-operation',
{data: {},
privateAggregationConfig: {contextId:
'example_id'}});
)");
// Navigate away to record `kWorkletNumPerPageHistogram` histogram.
EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
GURL(url::kAboutBlankURL)));
WaitForHistograms({kWorkletNumPerPageHistogram,
kPrivateAggregationHostPipeResultHistogram});
histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1);
histogram_tester_.ExpectUniqueSample(
kPrivateAggregationHostPipeResultHistogram,
SuccessExpected()
? content::GetPrivateAggregationHostPipeReportSuccessValue()
: content::GetPrivateAggregationHostPipeApiDisabledValue(),
1);
histogram_tester_.ExpectTotalCount(
kPrivateAggregationHostTimeToGenerateReportRequestWithContextIdHistogram,
SuccessExpected() ? 1 : 0);
}
class SharedStorageHeaderPrefBrowserTest : public SharedStoragePrefBrowserTest {
public:
SharedStorageHeaderPrefBrowserTest() {
shared_storage_m118_feature_.InitAndEnableFeature(
blink::features::kSharedStorageAPIM118);
}
void FinishSetUp() override {
observer_ = content::CreateAndOverrideSharedStorageHeaderObserver(
GetStoragePartition());
}
protected:
base::WeakPtr<content::TestSharedStorageHeaderObserver> observer_;
private:
base::test::ScopedFeatureList shared_storage_m118_feature_;
};
INSTANTIATE_TEST_SUITE_P(
All,
SharedStorageHeaderPrefBrowserTest,
testing::Combine(
testing::Bool(),
testing::Bool(),
testing::Values(EnforcementAndEnrollmentStatus::kAttestationsUnenforced,
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostUnenrolled,
#if BUILDFLAG(IS_ANDROID)
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled)),
#else
EnforcementAndEnrollmentStatus::
kAttestationsEnforcedMainHostEnrolled),
testing::Bool()),
#endif
DescribePrefBrowserTestParams);
IN_PROC_BROWSER_TEST_P(SharedStorageHeaderPrefBrowserTest, Basic) {
net::test_server::ControllableHttpResponse response(https_server(),
kTitle1Path);
ASSERT_TRUE(https_server()->Start());
Set3rdPartyCookieAndMainHostAttestationSettingsThenNavigateToMainHostPage();
GURL fetch_url = https_server()->GetURL(kMainHost, kTitle1Path);
EXPECT_TRUE(content::ExecJs(
GetActiveWebContents(),
content::JsReplace(R"(
fetch($1, {sharedStorageWritable: true});
)",
fetch_url.spec()),
content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
response.WaitForRequest();
ASSERT_TRUE(base::Contains(response.http_request()->headers,
"Sec-Shared-Storage-Writable"));
EXPECT_EQ(response.http_request()->content, "");
response.Send(
/*http_status=*/net::HTTP_OK,
/*content_type=*/"text/plain;charset=UTF-8",
/*content=*/{}, /*cookies=*/{}, /*extra_headers=*/
{"Shared-Storage-Write: clear, "
"set;key=\"hello\";value=\"world\";ignore_if_present, "
"append;key=hello;value=there"});
ASSERT_TRUE(observer_);
if (!SuccessExpected()) {
// Shared Storage is disabled, so the `SharedStorageHeaderObserver` ignores
// the header and no operations are invoked.
EXPECT_TRUE(observer_->header_results().empty());
EXPECT_TRUE(observer_->operations().empty());
response.Done();
return;
}
// Shared Storage is enabled.
observer_->WaitForOperations(3);
url::Origin fetch_origin = url::Origin::Create(fetch_url);
EXPECT_EQ(observer_->header_results().size(), 1u);
EXPECT_EQ(observer_->header_results().front().first, fetch_origin);
EXPECT_THAT(observer_->header_results().front().second,
testing::ElementsAre(true, true, true));
EXPECT_THAT(observer_->operations(),
testing::ElementsAre(
ClearOperation(fetch_origin, OperationResult::kSuccess),
SetOperation(fetch_origin, "hello", "world", true,
OperationResult::kSet),
AppendOperation(fetch_origin, "hello", "there",
OperationResult::kSet)));
response.Done();
content::WebContentsConsoleObserver console_observer(GetActiveWebContents());
ExecuteScriptInWorklet(GetActiveWebContents(), R"(
console.log(await sharedStorage.get('hello'));
console.log(await sharedStorage.length());
console.log('Finished script');
)",
"Finished script");
EXPECT_EQ(5u, console_observer.messages().size());
EXPECT_EQ("Start executing customizable_module.js",
base::UTF16ToUTF8(console_observer.messages()[0].message));
EXPECT_EQ("Finish executing customizable_module.js",
base::UTF16ToUTF8(console_observer.messages()[1].message));
EXPECT_EQ("worldthere",
base::UTF16ToUTF8(console_observer.messages()[2].message));
EXPECT_EQ("1", base::UTF16ToUTF8(console_observer.messages()[3].message));
EXPECT_EQ("Finished script",
base::UTF16ToUTF8(console_observer.messages()[4].message));
}
} // namespace storage