[shared storage] Implement sharedStorage.batchUpdate() for PA worklet
Add sharedStorage.batchUpdate() function. Parse arguments into the 'methods' sequence and a 'with_lock' optional flag, and propagate the result to the browser process to invoke the `SharedStorageLockManager::SharedStorageBatchUpdate()` API. This allows developers to perform multiple Shared Storage operations atomically within a single lock, as part of the Web Lock integration proposal: - https://github.com/WICG/shared-storage/pull/199 - https://github.com/WICG/shared-storage/pull/205 Fuchsia-Binary-Size: Size increase is unavoidable. Bug: 373899210 Change-Id: Ic6e9f794d78523ec9f6b87f37fb5e91f17635c58 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6072850 Commit-Queue: Yao Xiao <yaoxia@chromium.org> Reviewed-by: Maks Orlovich <morlovich@chromium.org> Reviewed-by: Cammie Smith Barnes <cammie@chromium.org> Reviewed-by: Giovanni Ortuno Urquidi <ortuno@chromium.org> Cr-Commit-Position: refs/heads/main@{#1401673}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
671c5ce475
commit
f0cab8e234
content
browser
services
third_party/blink/web_tests/external/wpt/shared-storage
@ -80,4 +80,32 @@ void AuctionSharedStorageHost::SharedStorageUpdate(
|
||||
ToWebFeature(source_auction_worklet_function));
|
||||
}
|
||||
|
||||
void AuctionSharedStorageHost::SharedStorageBatchUpdate(
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
methods_with_options,
|
||||
const std::optional<std::string>& with_lock,
|
||||
auction_worklet::mojom::AuctionWorkletFunction
|
||||
source_auction_worklet_function) {
|
||||
if (with_lock && with_lock->starts_with('-')) {
|
||||
receiver_set_.ReportBadMessage("Reserved lock name");
|
||||
return;
|
||||
}
|
||||
|
||||
FrameTreeNodeId main_frame_id =
|
||||
receiver_set_.current_context()
|
||||
.auction_runner_rfh->GetOutermostMainFrame()
|
||||
->GetFrameTreeNodeId();
|
||||
|
||||
storage_partition_->GetSharedStorageRuntimeManager()
|
||||
->lock_manager()
|
||||
.SharedStorageBatchUpdate(std::move(methods_with_options), with_lock,
|
||||
receiver_set_.current_context().worklet_origin,
|
||||
AccessScope::kProtectedAudienceWorklet,
|
||||
main_frame_id, base::DoNothing());
|
||||
|
||||
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
|
||||
receiver_set_.current_context().auction_runner_rfh,
|
||||
ToWebFeature(source_auction_worklet_function));
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -43,6 +43,12 @@ class CONTENT_EXPORT AuctionSharedStorageHost
|
||||
method_with_options,
|
||||
auction_worklet::mojom::AuctionWorkletFunction
|
||||
source_auction_worklet_function) override;
|
||||
void SharedStorageBatchUpdate(
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
methods_with_options,
|
||||
const std::optional<std::string>& with_lock,
|
||||
auction_worklet::mojom::AuctionWorkletFunction
|
||||
source_auction_worklet_function) override;
|
||||
|
||||
private:
|
||||
struct ReceiverContext;
|
||||
|
@ -11184,14 +11184,7 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest, ModifierMethodTypeHierarchy) {
|
||||
/*selected_buyer_and_seller_reporting_id=*/std::nullopt,
|
||||
/*ad_component_descriptors=*/std::nullopt,
|
||||
/*modeling_signals=*/std::nullopt,
|
||||
/*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/{},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
/*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
|
||||
|
||||
v8_helpers_[0]->v8_runner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
@ -11232,12 +11225,7 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{method_and_error.second},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
{method_and_error.second});
|
||||
}
|
||||
}
|
||||
|
||||
@ -11264,12 +11252,7 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:5 Uncaught TypeError: The \"shared-storage\" "
|
||||
"Permissions Policy denied the method on sharedStorage."},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
"Permissions Policy denied the method on sharedStorage."});
|
||||
}
|
||||
}
|
||||
|
||||
@ -11305,12 +11288,7 @@ TEST_F(
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{method_and_error.second},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
{method_and_error.second});
|
||||
}
|
||||
}
|
||||
|
||||
@ -11334,12 +11312,7 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:5 Uncaught TypeError: The shared storage method "
|
||||
"object constructor cannot be called as a function."},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
"object constructor cannot be called as a function."});
|
||||
}
|
||||
}
|
||||
|
||||
@ -11369,14 +11342,7 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
/*selected_buyer_and_seller_reporting_id=*/std::nullopt,
|
||||
/*ad_component_descriptors=*/std::nullopt,
|
||||
/*modeling_signals=*/std::nullopt,
|
||||
/*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/{},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
/*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
|
||||
}
|
||||
|
||||
v8_helpers_[0]->v8_runner()->PostTask(
|
||||
@ -11411,12 +11377,252 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught Error 123."},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
{"https://url.test/:6 Uncaught Error 123."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_NoArguments) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
sharedStorage.batchUpdate();
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught TypeError: sharedStorage.batchUpdate(): "
|
||||
"at least 1 argument(s) are required."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_MethodsNotAnObject) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
sharedStorage.batchUpdate(123);
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught TypeError: sharedStorage.batchUpdate(): "
|
||||
"Trouble converting argument 'methods' to a Sequence."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_MethodsNotASequence) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
sharedStorage.batchUpdate({});
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught TypeError: sharedStorage.batchUpdate(): "
|
||||
"Trouble converting argument 'methods' to a Sequence."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_ErrorIteratingOverMethods) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
let o = {};
|
||||
o[Symbol.iterator] = {};
|
||||
|
||||
sharedStorage.batchUpdate(o);
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:9 Uncaught TypeError: sharedStorage.batchUpdate(): "
|
||||
"Trouble iterating over argument 'methods'."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_MethodsSequenceElementInvalidType) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
sharedStorage.batchUpdate([123]);
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught TypeError: Failed to convert value to "
|
||||
"'SharedStorageModifierMethod'."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_MethodsSequenceElementUserDefinedType) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
|
||||
class SharedStorageClearMethod {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
sharedStorage.batchUpdate([new SharedStorageClearMethod()]);
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:11 Uncaught TypeError: Failed to convert value to "
|
||||
"'SharedStorageModifierMethod'."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_ReservedLockName) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
sharedStorage.batchUpdate([], {withLock: '-abc'});
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught TypeError: sharedStorage.batchUpdate(): "
|
||||
"Lock name cannot start with '-'."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_PermissionsPolicyError) {
|
||||
permissions_policy_state_ = mojom::AuctionWorkletPermissionsPolicyState::New(
|
||||
/*private_aggregation_allowed=*/true,
|
||||
/*shared_storage_allowed=*/false);
|
||||
|
||||
// Skip setting up `shared_storage_hosts_`, to be consistent with the
|
||||
// permissions policy's enabled status. This matches production behavior.
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
sharedStorage.batchUpdate([]);
|
||||
)"),
|
||||
/*expected_bids=*/nullptr,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught TypeError: The \"shared-storage\" "
|
||||
"Permissions Policy denied the method on sharedStorage."});
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
SharedStorageBatchUpdate_Success) {
|
||||
auction_worklet::TestAuctionSharedStorageHost test_shared_storage_host;
|
||||
|
||||
mojo::Receiver<auction_worklet::mojom::AuctionSharedStorageHost> receiver(
|
||||
&test_shared_storage_host);
|
||||
shared_storage_hosts_[0] = receiver.BindNewPipeAndPassRemote();
|
||||
|
||||
RunGenerateBidWithJavascriptExpectingResult(
|
||||
CreateGenerateBidScript(
|
||||
R"({ad: "ad", bid:1, render:"https://response.test/" })",
|
||||
/*extra_code=*/R"(
|
||||
sharedStorage.batchUpdate([]);
|
||||
|
||||
sharedStorage.batchUpdate([], {withLock: 'lock1'});
|
||||
|
||||
sharedStorage.batchUpdate([
|
||||
new SharedStorageSetMethod('a', 'b'),
|
||||
new SharedStorageAppendMethod('c', 'd'),
|
||||
new SharedStorageDeleteMethod('e'),
|
||||
new SharedStorageClearMethod({withLock: 'lock2'})
|
||||
], {withLock: 'lock3'});
|
||||
)"),
|
||||
/*expected_bids=*/
|
||||
mojom::BidderWorkletBid::New(
|
||||
auction_worklet::mojom::BidRole::kUnenforcedKAnon, "\"ad\"", 1,
|
||||
/*bid_currency=*/std::nullopt,
|
||||
/*ad_cost=*/std::nullopt,
|
||||
blink::AdDescriptor(GURL("https://response.test/")),
|
||||
/*selected_buyer_and_seller_reporting_id=*/std::nullopt,
|
||||
/*ad_component_descriptors=*/std::nullopt,
|
||||
/*modeling_signals=*/std::nullopt,
|
||||
/*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
|
||||
|
||||
// Make sure the shared storage mojom methods are invoked as they use a
|
||||
// dedicated pipe.
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
using BatchRequest =
|
||||
auction_worklet::TestAuctionSharedStorageHost::BatchRequest;
|
||||
|
||||
std::vector<content::MethodWithOptionsPtr> batch_methods1;
|
||||
std::vector<content::MethodWithOptionsPtr> batch_methods2;
|
||||
std::vector<content::MethodWithOptionsPtr> batch_methods3;
|
||||
batch_methods3.push_back(MojomSetMethod(/*key=*/u"a",
|
||||
/*value=*/u"b",
|
||||
/*ignore_if_present=*/false));
|
||||
batch_methods3.push_back(MojomAppendMethod(/*key=*/u"c",
|
||||
/*value=*/u"d"));
|
||||
batch_methods3.push_back(MojomDeleteMethod(/*key=*/u"e"));
|
||||
batch_methods3.push_back(MojomClearMethod(/*with_lock=*/"lock2"));
|
||||
|
||||
EXPECT_THAT(
|
||||
test_shared_storage_host.observed_batch_requests(),
|
||||
testing::ElementsAre(
|
||||
BatchRequest(std::move(batch_methods1),
|
||||
/*with_lock=*/std::nullopt,
|
||||
mojom::AuctionWorkletFunction::kBidderGenerateBid),
|
||||
BatchRequest(std::move(batch_methods2),
|
||||
/*with_lock=*/"lock1",
|
||||
mojom::AuctionWorkletFunction::kBidderGenerateBid),
|
||||
BatchRequest(std::move(batch_methods3),
|
||||
/*with_lock=*/"lock3",
|
||||
mojom::AuctionWorkletFunction::kBidderGenerateBid)));
|
||||
|
||||
v8_helpers_[0]->v8_runner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
[](scoped_refptr<AuctionV8Helper> v8_helper) {
|
||||
v8_helper->isolate()->RequestGarbageCollectionForTesting(
|
||||
v8::Isolate::kFullGarbageCollection);
|
||||
},
|
||||
v8_helpers_[0]));
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
@ -11448,14 +11654,7 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
/*selected_buyer_and_seller_reporting_id=*/std::nullopt,
|
||||
/*ad_component_descriptors=*/std::nullopt,
|
||||
/*modeling_signals=*/std::nullopt,
|
||||
/*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/{},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
/*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
|
||||
|
||||
// Make sure the shared storage mojom methods are invoked as they use a
|
||||
// dedicated pipe.
|
||||
@ -11503,12 +11702,7 @@ TEST_F(BidderWorkletSharedStorageAPIEnabledTest,
|
||||
/*expected_data_version=*/std::nullopt,
|
||||
/*expected_errors=*/
|
||||
{"https://url.test/:6 Uncaught TypeError: The \"shared-storage\" "
|
||||
"Permissions Policy denied the method on sharedStorage."},
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
/*expected_set_priority=*/std::nullopt,
|
||||
/*expected_update_priority_signals_overrides=*/{},
|
||||
/*expected_pa_requests=*/{});
|
||||
"Permissions Policy denied the method on sharedStorage."});
|
||||
|
||||
permissions_policy_state_ =
|
||||
mojom::AuctionWorkletPermissionsPolicyState::New(
|
||||
|
@ -22,4 +22,13 @@ interface AuctionSharedStorageHost {
|
||||
SharedStorageUpdate(
|
||||
network.mojom.SharedStorageModifierMethodWithOptions method_with_options,
|
||||
AuctionWorkletFunction source_auction_worklet_function);
|
||||
|
||||
// Handle each modifier method within `methods_with_options`. If `with_lock`
|
||||
// is provided, the methods within the batch will be executed with a lock
|
||||
// acquired on the resource with name `with_lock`. `with_lock` shouldn't start
|
||||
// with '-'.
|
||||
SharedStorageBatchUpdate(
|
||||
array<network.mojom.SharedStorageModifierMethodWithOptions> methods_with_options,
|
||||
string? with_lock,
|
||||
AuctionWorkletFunction source_auction_worklet_function);
|
||||
};
|
||||
|
@ -13,6 +13,10 @@
|
||||
#include "content/services/auction_worklet/public/mojom/auction_shared_storage_host.mojom.h"
|
||||
#include "content/services/auction_worklet/webidl_compat.h"
|
||||
#include "gin/converter.h"
|
||||
#include "gin/handle.h"
|
||||
#include "gin/public/gin_embedders.h"
|
||||
#include "gin/public/wrapper_info.h"
|
||||
#include "gin/wrappable.h"
|
||||
#include "services/network/public/cpp/shared_storage_utils.h"
|
||||
#include "services/network/public/mojom/shared_storage.mojom.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
@ -253,49 +257,35 @@ CreateMojomClearMethodFromParameters(
|
||||
std::move(method), std::move(with_lock));
|
||||
}
|
||||
|
||||
// SharedStorageMethod represents a method for modifying shared storage. It
|
||||
// manages its own lifecycle through weak reference handling to support
|
||||
// automatic garbage collection.
|
||||
class SharedStorageMethod {
|
||||
// SharedStorageMethod represents a method for modifying shared storage. This
|
||||
// class inherits from gin::Wrappable to leverage gin's JavaScript object
|
||||
// lifetime management capabilities. When the JavaScript object is garbage
|
||||
// collected, the corresponding C++ object will be properly cleaned up.
|
||||
class SharedStorageMethod : public gin::Wrappable<SharedStorageMethod> {
|
||||
public:
|
||||
// Constructs a SharedStorageMethod with a Mojom method and sets up
|
||||
// weak reference management.
|
||||
//
|
||||
// Responsibilities:
|
||||
// - Create a V8 External wrapping the C++ object
|
||||
// - Establish a persistent, weak reference to the External
|
||||
// - Set the External as an internal field of the JavaScript object
|
||||
// - Ensure proper cleanup when the JavaScript object is garbage collected
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
|
||||
SharedStorageMethod(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> obj,
|
||||
network::mojom::SharedStorageModifierMethodWithOptionsPtr mojom_method)
|
||||
: mojom_method_(std::move(mojom_method)) {
|
||||
v8::Local<v8::External> external = v8::External::New(isolate, this);
|
||||
persistent_external_.Reset(isolate, external);
|
||||
obj->SetInternalField(0, external);
|
||||
persistent_external_.SetWeak(this, WeakCallback,
|
||||
v8::WeakCallbackType::kParameter);
|
||||
gin::Handle<SharedStorageMethod> handler = gin::CreateHandle(isolate, this);
|
||||
// Use an index that won't interfere with gin's reserved indexes.
|
||||
obj->SetInternalField(gin::kNumberOfInternalFields, handler.ToV8());
|
||||
}
|
||||
|
||||
// Weak callback invoked by V8's garbage collector when the associated
|
||||
// JavaScript object becomes unreachable.
|
||||
//
|
||||
// Responsibilities:
|
||||
// - Clear the persistent external reference
|
||||
// - Delete the SharedStorageMethod instance
|
||||
static void WeakCallback(
|
||||
const v8::WeakCallbackInfo<SharedStorageMethod>& data) {
|
||||
SharedStorageMethod* method = data.GetParameter();
|
||||
method->persistent_external_.Reset();
|
||||
delete method;
|
||||
const network::mojom::SharedStorageModifierMethodWithOptionsPtr&
|
||||
mojom_method() const {
|
||||
return mojom_method_;
|
||||
}
|
||||
|
||||
private:
|
||||
network::mojom::SharedStorageModifierMethodWithOptionsPtr mojom_method_;
|
||||
v8::Persistent<v8::External> persistent_external_;
|
||||
};
|
||||
|
||||
gin::WrapperInfo SharedStorageMethod::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
} // namespace
|
||||
|
||||
SharedStorageBindings::SharedStorageBindings(
|
||||
@ -351,6 +341,17 @@ void SharedStorageBindings::AttachToContext(v8::Local<v8::Context> context) {
|
||||
clear_method_function)
|
||||
.Check();
|
||||
|
||||
// batchUpdate() is part of the Web Locks integration launch.
|
||||
if (base::FeatureList::IsEnabled(blink::features::kSharedStorageWebLocks)) {
|
||||
v8::Local<v8::Function> batch_update_function =
|
||||
v8::Function::New(context, &SharedStorageBindings::BatchUpdate, v8_this)
|
||||
.ToLocalChecked();
|
||||
shared_storage
|
||||
->Set(context, v8_helper_->CreateStringFromLiteral("batchUpdate"),
|
||||
batch_update_function)
|
||||
.Check();
|
||||
}
|
||||
|
||||
context->Global()
|
||||
->Set(context, v8_helper_->CreateStringFromLiteral("sharedStorage"),
|
||||
shared_storage)
|
||||
@ -367,7 +368,8 @@ void SharedStorageBindings::AttachToContext(v8::Local<v8::Context> context) {
|
||||
v8::FunctionTemplate::New(v8_helper_->isolate(),
|
||||
&SharedStorageBindings::SetMethodConstructor,
|
||||
v8_this);
|
||||
set_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
set_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(
|
||||
gin::kNumberOfInternalFields + 1);
|
||||
set_method_ctor_template->Inherit(base_modifier_method_template);
|
||||
set_method_ctor_template->SetClassName(
|
||||
v8_helper_->CreateStringFromLiteral(kSharedStorageSetMethodName));
|
||||
@ -383,7 +385,8 @@ void SharedStorageBindings::AttachToContext(v8::Local<v8::Context> context) {
|
||||
v8::FunctionTemplate::New(
|
||||
v8_helper_->isolate(),
|
||||
&SharedStorageBindings::AppendMethodConstructor, v8_this);
|
||||
append_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
append_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(
|
||||
gin::kNumberOfInternalFields + 1);
|
||||
append_method_ctor_template->Inherit(base_modifier_method_template);
|
||||
append_method_ctor_template->SetClassName(
|
||||
v8_helper_->CreateStringFromLiteral(kSharedStorageAppendMethodName));
|
||||
@ -400,7 +403,8 @@ void SharedStorageBindings::AttachToContext(v8::Local<v8::Context> context) {
|
||||
v8::FunctionTemplate::New(
|
||||
v8_helper_->isolate(),
|
||||
&SharedStorageBindings::DeleteMethodConstructor, v8_this);
|
||||
delete_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
delete_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(
|
||||
gin::kNumberOfInternalFields + 1);
|
||||
delete_method_ctor_template->Inherit(base_modifier_method_template);
|
||||
delete_method_ctor_template->SetClassName(
|
||||
v8_helper_->CreateStringFromLiteral(kSharedStorageDeleteMethodName));
|
||||
@ -417,7 +421,8 @@ void SharedStorageBindings::AttachToContext(v8::Local<v8::Context> context) {
|
||||
v8::FunctionTemplate::New(
|
||||
v8_helper_->isolate(),
|
||||
&SharedStorageBindings::ClearMethodConstructor, v8_this);
|
||||
clear_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
clear_method_ctor_template->InstanceTemplate()->SetInternalFieldCount(
|
||||
gin::kNumberOfInternalFields + 1);
|
||||
clear_method_ctor_template->Inherit(base_modifier_method_template);
|
||||
clear_method_ctor_template->SetClassName(
|
||||
v8_helper_->CreateStringFromLiteral(kSharedStorageClearMethodName));
|
||||
@ -510,6 +515,131 @@ void SharedStorageBindings::Clear(
|
||||
std::move(mojom_method), bindings->source_auction_worklet_function_);
|
||||
}
|
||||
|
||||
// static
|
||||
void SharedStorageBindings::BatchUpdate(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
SharedStorageBindings* bindings = static_cast<SharedStorageBindings*>(
|
||||
v8::External::Cast(*args.Data())->Value());
|
||||
AuctionV8Helper* v8_helper = bindings->v8_helper_;
|
||||
v8::Isolate* isolate = v8_helper->isolate();
|
||||
|
||||
AuctionV8Helper::TimeLimitScope time_limit_scope(v8_helper->GetTimeLimit());
|
||||
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
mojom_methods;
|
||||
|
||||
scoped_refptr<AuctionV8Helper> ref_v8_helper(v8_helper);
|
||||
auto collect_methods_callback = base::BindRepeating(
|
||||
[](scoped_refptr<AuctionV8Helper> v8_helper,
|
||||
AuctionV8Helper::TimeLimitScope& time_limit_scope,
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>&
|
||||
mojom_methods,
|
||||
v8::Local<v8::Value> method_val) -> IdlConvert::Status {
|
||||
v8::Isolate* isolate = v8_helper->isolate();
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
|
||||
static constexpr char kTypeConversionError[] =
|
||||
"Failed to convert value to 'SharedStorageModifierMethod'";
|
||||
|
||||
v8::Local<v8::Object> method_obj;
|
||||
if (!method_val->ToObject(context).ToLocal(&method_obj)) {
|
||||
return IdlConvert::Status::MakeErrorMessage(kTypeConversionError);
|
||||
}
|
||||
|
||||
if (method_obj->InternalFieldCount() !=
|
||||
gin::kNumberOfInternalFields + 1) {
|
||||
return IdlConvert::Status::MakeErrorMessage(kTypeConversionError);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> internal_val =
|
||||
method_obj->GetInternalField(gin::kNumberOfInternalFields)
|
||||
.As<v8::Value>();
|
||||
|
||||
SharedStorageMethod* modifier_method = nullptr;
|
||||
if (!gin::ConvertFromV8(isolate, internal_val, &modifier_method)) {
|
||||
return IdlConvert::Status::MakeErrorMessage(kTypeConversionError);
|
||||
}
|
||||
|
||||
if (modifier_method && modifier_method->mojom_method()) {
|
||||
mojom_methods.push_back(modifier_method->mojom_method().Clone());
|
||||
} else {
|
||||
return IdlConvert::Status::MakeErrorMessage(kTypeConversionError);
|
||||
}
|
||||
|
||||
return IdlConvert::Status::MakeSuccess();
|
||||
},
|
||||
ref_v8_helper, std::ref(time_limit_scope), std::ref(mojom_methods));
|
||||
|
||||
static constexpr char kErrorPrefix[] = "sharedStorage.batchUpdate(): ";
|
||||
static constexpr char kSequenceConversionError[] =
|
||||
"Trouble converting argument 'methods' to a Sequence.";
|
||||
|
||||
ArgsConverter args_converter(v8_helper, time_limit_scope, kErrorPrefix, &args,
|
||||
/*min_required_args=*/1);
|
||||
|
||||
if (args_converter.is_success() && !args[0]->IsObject()) {
|
||||
args_converter.SetStatus(IdlConvert::Status::MakeErrorMessage(
|
||||
base::StrCat({kErrorPrefix, kSequenceConversionError})));
|
||||
}
|
||||
|
||||
std::initializer_list<std::string_view> error_subject = {
|
||||
"argument 'methods'"};
|
||||
|
||||
v8::Local<v8::Object> iterable = args[0].As<v8::Object>();
|
||||
v8::Local<v8::Object> iterator_factory;
|
||||
|
||||
if (args_converter.is_success()) {
|
||||
args_converter.SetStatus(IdlConvert::CheckForSequence(
|
||||
isolate, kErrorPrefix, error_subject, iterable, iterator_factory));
|
||||
|
||||
if (iterator_factory.IsEmpty()) {
|
||||
if (args_converter.is_success()) {
|
||||
args_converter.SetStatus(IdlConvert::Status::MakeErrorMessage(
|
||||
base::StrCat({kErrorPrefix, kSequenceConversionError})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args_converter.is_success()) {
|
||||
args_converter.SetStatus(IdlConvert::ConvertSequence(
|
||||
v8_helper, kErrorPrefix, error_subject, iterable, iterator_factory,
|
||||
std::move(collect_methods_callback)));
|
||||
}
|
||||
|
||||
std::optional<std::string> with_lock;
|
||||
if (args_converter.is_success() && args.Length() > 1) {
|
||||
DictConverter options_dict_converter(
|
||||
v8_helper, time_limit_scope,
|
||||
"sharedStorage.batchUpdate 'options' argument ", args[1]);
|
||||
options_dict_converter.GetOptional("withLock", with_lock);
|
||||
args_converter.SetStatus(options_dict_converter.TakeStatus());
|
||||
}
|
||||
|
||||
if (args_converter.is_failed()) {
|
||||
args_converter.TakeStatus().PropagateErrorsToV8(v8_helper);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bindings->shared_storage_permissions_policy_allowed_) {
|
||||
isolate->ThrowException(v8::Exception::TypeError(
|
||||
gin::StringToV8(isolate, kPermissionsPolicyError)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Fail for reserved lock name.
|
||||
// https://w3c.github.io/web-locks/#resource-name
|
||||
if (with_lock && with_lock->starts_with('-')) {
|
||||
isolate->ThrowException(v8::Exception::TypeError(gin::StringToV8(
|
||||
isolate,
|
||||
base::StrCat({kErrorPrefix, "Lock name cannot start with '-'"}))));
|
||||
return;
|
||||
}
|
||||
|
||||
bindings->shared_storage_host_->SharedStorageBatchUpdate(
|
||||
std::move(mojom_methods), with_lock,
|
||||
bindings->source_auction_worklet_function_);
|
||||
}
|
||||
|
||||
// static
|
||||
void SharedStorageBindings::SetMethodConstructor(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
@ -39,6 +39,7 @@ class CONTENT_EXPORT SharedStorageBindings : public Bindings {
|
||||
static void Append(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Delete(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Clear(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void BatchUpdate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void SetMethodConstructor(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/to_string.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "content/public/test/shared_storage_test_utils.h"
|
||||
#include "content/services/auction_worklet/auction_v8_helper.h"
|
||||
#include "content/services/auction_worklet/public/cpp/auction_downloader.h"
|
||||
#include "net/base/net_errors.h"
|
||||
@ -167,6 +168,33 @@ TestAuctionSharedStorageHost::Request::operator=(Request&& other) = default;
|
||||
bool TestAuctionSharedStorageHost::Request::operator==(
|
||||
const Request& rhs) const = default;
|
||||
|
||||
TestAuctionSharedStorageHost::BatchRequest::BatchRequest(
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
methods_with_options,
|
||||
const std::optional<std::string>& with_lock,
|
||||
mojom::AuctionWorkletFunction source_auction_worklet_function)
|
||||
: methods_with_options(std::move(methods_with_options)),
|
||||
with_lock(with_lock),
|
||||
source_auction_worklet_function(source_auction_worklet_function) {}
|
||||
|
||||
TestAuctionSharedStorageHost::BatchRequest::~BatchRequest() = default;
|
||||
|
||||
TestAuctionSharedStorageHost::BatchRequest::BatchRequest(
|
||||
const BatchRequest& other)
|
||||
: methods_with_options(
|
||||
content::CloneSharedStorageMethods(other.methods_with_options)),
|
||||
with_lock(other.with_lock),
|
||||
source_auction_worklet_function(other.source_auction_worklet_function) {}
|
||||
|
||||
TestAuctionSharedStorageHost::BatchRequest::BatchRequest(BatchRequest&& other) =
|
||||
default;
|
||||
TestAuctionSharedStorageHost::BatchRequest&
|
||||
TestAuctionSharedStorageHost::BatchRequest::operator=(BatchRequest&& other) =
|
||||
default;
|
||||
|
||||
bool TestAuctionSharedStorageHost::BatchRequest::operator==(
|
||||
const BatchRequest& rhs) const = default;
|
||||
|
||||
TestAuctionSharedStorageHost::TestAuctionSharedStorageHost() = default;
|
||||
|
||||
TestAuctionSharedStorageHost::~TestAuctionSharedStorageHost() = default;
|
||||
@ -180,6 +208,17 @@ void TestAuctionSharedStorageHost::SharedStorageUpdate(
|
||||
Request(std::move(method_with_options), source_auction_worklet_function));
|
||||
}
|
||||
|
||||
void TestAuctionSharedStorageHost::SharedStorageBatchUpdate(
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
methods_with_options,
|
||||
const std::optional<std::string>& with_lock,
|
||||
auction_worklet::mojom::AuctionWorkletFunction
|
||||
source_auction_worklet_function) {
|
||||
observed_batch_requests_.emplace_back(
|
||||
BatchRequest(std::move(methods_with_options), with_lock,
|
||||
source_auction_worklet_function));
|
||||
}
|
||||
|
||||
void TestAuctionSharedStorageHost::ClearObservedRequests() {
|
||||
observed_requests_.clear();
|
||||
}
|
||||
|
@ -99,6 +99,27 @@ class TestAuctionSharedStorageHost : public mojom::AuctionSharedStorageHost {
|
||||
bool operator==(const Request& rhs) const;
|
||||
};
|
||||
|
||||
struct BatchRequest {
|
||||
BatchRequest(
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
methods_with_options,
|
||||
const std::optional<std::string>& with_lock,
|
||||
mojom::AuctionWorkletFunction source_auction_worklet_function);
|
||||
~BatchRequest();
|
||||
|
||||
BatchRequest(const BatchRequest& other);
|
||||
BatchRequest& operator=(const BatchRequest& other) = delete;
|
||||
BatchRequest(BatchRequest&& other);
|
||||
BatchRequest& operator=(BatchRequest&& other);
|
||||
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
methods_with_options;
|
||||
std::optional<std::string> with_lock;
|
||||
mojom::AuctionWorkletFunction source_auction_worklet_function;
|
||||
|
||||
bool operator==(const BatchRequest& rhs) const;
|
||||
};
|
||||
|
||||
TestAuctionSharedStorageHost();
|
||||
|
||||
~TestAuctionSharedStorageHost() override;
|
||||
@ -109,15 +130,26 @@ class TestAuctionSharedStorageHost : public mojom::AuctionSharedStorageHost {
|
||||
method_with_options,
|
||||
auction_worklet::mojom::AuctionWorkletFunction
|
||||
source_auction_worklet_function) override;
|
||||
void SharedStorageBatchUpdate(
|
||||
std::vector<network::mojom::SharedStorageModifierMethodWithOptionsPtr>
|
||||
methods_with_options,
|
||||
const std::optional<std::string>& with_lock,
|
||||
auction_worklet::mojom::AuctionWorkletFunction
|
||||
source_auction_worklet_function) override;
|
||||
|
||||
const std::vector<Request>& observed_requests() const {
|
||||
return observed_requests_;
|
||||
}
|
||||
|
||||
const std::vector<BatchRequest>& observed_batch_requests() const {
|
||||
return observed_batch_requests_;
|
||||
}
|
||||
|
||||
void ClearObservedRequests();
|
||||
|
||||
private:
|
||||
std::vector<Request> observed_requests_;
|
||||
std::vector<BatchRequest> observed_batch_requests_;
|
||||
};
|
||||
|
||||
class TestAuctionNetworkEventsHandler
|
||||
|
102
third_party/blink/web_tests/external/wpt/shared-storage/web-locks-pa-worklet-batch-update.tentative.https.window.js
vendored
Normal file
102
third_party/blink/web_tests/external/wpt/shared-storage/web-locks-pa-worklet-batch-update.tentative.https.window.js
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/common/utils.js
|
||||
// META: script=/fledge/tentative/resources/fledge-util.sub.js
|
||||
// META: script=/common/subset-tests.js
|
||||
// META: script=/shared-storage/resources/util.js
|
||||
// META: script=/fenced-frame/resources/utils.js
|
||||
// META: timeout=long
|
||||
|
||||
"use strict;"
|
||||
|
||||
subsetTest(promise_test, async test => {
|
||||
let worklet = await sharedStorage.createWorklet('resources/simple-module.js');
|
||||
|
||||
const ancestor_key = token();
|
||||
let url0 = generateURL("/shared-storage/resources/frame0.html",
|
||||
[ancestor_key]);
|
||||
let url1 = generateURL("/shared-storage/resources/frame1.html",
|
||||
[ancestor_key]);
|
||||
|
||||
// Override the default resource path, as we are not running within the Fledge
|
||||
// repository.
|
||||
RESOURCE_PATH = '/fledge/tentative/resources/';
|
||||
|
||||
const pa_uuid = generateUuid(test);
|
||||
|
||||
let biddingLogicURL = createBiddingScriptURL(
|
||||
{
|
||||
generateBid:
|
||||
`
|
||||
sharedStorage.batchUpdate([
|
||||
new SharedStorageAppendMethod('key', 'a'),
|
||||
new SharedStorageAppendMethod('key', 'a')
|
||||
], {withLock: 'lock1'});
|
||||
|
||||
return {};
|
||||
`
|
||||
});
|
||||
|
||||
let decisionLogicURL = createDecisionScriptURL(pa_uuid);
|
||||
|
||||
// Invoke `selectURL()` to perform the following steps:
|
||||
// 1. Acquires the lock.
|
||||
// 2. Reads the current value at the given key.
|
||||
// 3. Waits for 500ms.
|
||||
// 4. Sets the shared storage value to the read value appended with the given letter.
|
||||
// 5. Releases the lock.
|
||||
//
|
||||
// After 100ms, run a Protected Audience auction that starts a worklet that:
|
||||
// - Acquires the same named lock.
|
||||
// - Executes two `append` methods, each appending the same letter.
|
||||
//
|
||||
// Expected behavior: After both of them finish, the value at the given key
|
||||
// should contain the letter repeated three times.
|
||||
//
|
||||
// This demonstrates that:
|
||||
// 1. The `withLock` option is effective, preventing the `batchUpdate()`
|
||||
// method interfering with the "get and set" operation. If the lock were
|
||||
// not used, the final value would likely be a single letter.
|
||||
// 2. `batchUpdate()` correctly executes all `append` methods within the
|
||||
// batch.
|
||||
//
|
||||
// Note: This test remains valid even if the `batchUpdate()` call happens
|
||||
// outside the critical section protected by the lock within the worklet. The
|
||||
// test effectively demonstrates mutual exclusion as long as there's a
|
||||
// reasonable chance for `batchUpdate()` to occur while the worklet is still
|
||||
// running.
|
||||
let select_url_result = await worklet.selectURL(
|
||||
"get-wait-set-within-lock",
|
||||
[{url: url0}, {url: url1}],
|
||||
{data: {'key': 'key',
|
||||
'lock_name': 'lock1',
|
||||
'append_letter': 'a'},
|
||||
resolveToConfig: true});
|
||||
|
||||
// Busy wait for 100ms.
|
||||
const startWaitTime = Date.now();
|
||||
while (Date.now() - startWaitTime < 100) {}
|
||||
|
||||
// Run a Protected Audience auction which triggers `append()` with the same
|
||||
// lock and the same letter.
|
||||
await joinGroupAndRunBasicFledgeTestExpectingNoWinner(
|
||||
test,
|
||||
{
|
||||
uuid: pa_uuid,
|
||||
interestGroupOverrides: {
|
||||
name: pa_uuid,
|
||||
biddingLogicURL: biddingLogicURL,
|
||||
},
|
||||
auctionConfigOverrides: {
|
||||
decisionLogicURL: decisionLogicURL
|
||||
}
|
||||
});
|
||||
|
||||
attachFencedFrame(select_url_result, 'opaque-ads');
|
||||
const result = await nextValueFromServer(ancestor_key);
|
||||
assert_equals(result, "frame1_loaded");
|
||||
|
||||
await verifyKeyValueForOrigin('key', 'aaa', location.origin);
|
||||
|
||||
await deleteKeyForOrigin('key', location.origin);
|
||||
}, 'Test for batchUpdate() with a batch lock in a Protected Audience Worklet context');
|
Reference in New Issue
Block a user