0

FLEDGE: Move encodeUtf8/decodeUtf8 to protectedAudience.

object handing of the global object, rather than browserSignals argument
to functions, as design doc feedback pointed out that it's otherwise
hard to use in frozen context mode to pollyfill TextEncoder/TextDecoder.

(Part of https://github.com/WICG/turtledove/issues/961)

Bug: 397936915
Change-Id: I084b14072ae8aac8419008410d4ee713191d77e9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6329720
Commit-Queue: Maks Orlovich <morlovich@chromium.org>
Reviewed-by: Russ Hamilton <behamilton@google.com>
Cr-Commit-Position: refs/heads/main@{#1429678}
This commit is contained in:
Maks Orlovich
2025-03-07 12:38:31 -08:00
committed by Chromium LUCI CQ
parent f2519e8e54
commit a1c3103818
14 changed files with 168 additions and 159 deletions

@@ -62,7 +62,6 @@
#include "content/services/auction_worklet/set_priority_bindings.h" #include "content/services/auction_worklet/set_priority_bindings.h"
#include "content/services/auction_worklet/set_priority_signals_override_bindings.h" #include "content/services/auction_worklet/set_priority_signals_override_bindings.h"
#include "content/services/auction_worklet/shared_storage_bindings.h" #include "content/services/auction_worklet/shared_storage_bindings.h"
#include "content/services/auction_worklet/text_conversion_helpers.h"
#include "content/services/auction_worklet/trusted_signals.h" #include "content/services/auction_worklet/trusted_signals.h"
#include "content/services/auction_worklet/trusted_signals_kvv2_manager.h" #include "content/services/auction_worklet/trusted_signals_kvv2_manager.h"
#include "content/services/auction_worklet/trusted_signals_request_manager.h" #include "content/services/auction_worklet/trusted_signals_request_manager.h"
@@ -943,11 +942,6 @@ bool BidderWorklet::V8State::SetBrowserSignals(
return false; return false;
} }
if (base::FeatureList::IsEnabled(features::kFledgeTextConversionHelpers)) {
context_recycler.text_conversion_helpers()->ReInitialize(context,
browser_signals);
}
if (!context_recycler.report_win_lazy_filler()->FillInObject( if (!context_recycler.report_win_lazy_filler()->FillInObject(
browser_signal_modeling_signals, browser_signal_join_count, browser_signal_modeling_signals, browser_signal_join_count,
!is_for_additional_bid !is_for_additional_bid
@@ -1132,6 +1126,10 @@ void BidderWorklet::V8State::ReportWin(
v8::Local<v8::Context> context = context_recycler_scope.GetContext(); v8::Local<v8::Context> context = context_recycler_scope.GetContext();
AuctionV8Logger v8_logger(v8_helper_.get(), context); AuctionV8Logger v8_logger(v8_helper_.get(), context);
// We want this before RunScript, both because it's meant to be visible
// to globals, and because we don't want to overwrite existing globals.
context_recycler.AddTextConversionHelpers();
v8::LocalVector<v8::Value> args(isolate); v8::LocalVector<v8::Value> args(isolate);
if (!AppendJsonValueOrNull(v8_helper_.get(), context, if (!AppendJsonValueOrNull(v8_helper_.get(), context,
base::OptionalToPtr(auction_signals_json), base::OptionalToPtr(auction_signals_json),
@@ -1176,7 +1174,6 @@ void BidderWorklet::V8State::ReportWin(
} }
context_recycler.AddReportWinBrowserSignalsLazyFiller(); context_recycler.AddReportWinBrowserSignalsLazyFiller();
context_recycler.AddTextConversionHelpers();
DeprecatedUrlLazyFiller deprecated_render_url( DeprecatedUrlLazyFiller deprecated_render_url(
v8_helper_.get(), &v8_logger, &browser_signal_render_url, v8_helper_.get(), &v8_logger, &browser_signal_render_url,
@@ -1929,10 +1926,6 @@ BidderWorklet::V8State::RunGenerateBidOnce(
} }
v8::Local<v8::Object> browser_signals = v8::Object::New(isolate); v8::Local<v8::Object> browser_signals = v8::Object::New(isolate);
if (base::FeatureList::IsEnabled(features::kFledgeTextConversionHelpers)) {
context_recycler->text_conversion_helpers()->ReInitialize(context,
browser_signals);
}
gin::Dictionary browser_signals_dict(isolate, browser_signals); gin::Dictionary browser_signals_dict(isolate, browser_signals);
// TODO(crbug.com/336164429): Construct the fields of browser signals lazily. // TODO(crbug.com/336164429): Construct the fields of browser signals lazily.
if (!browser_signals_dict.Set("topWindowHostname", if (!browser_signals_dict.Set("topWindowHostname",
@@ -2166,6 +2159,10 @@ BidderWorklet::V8State::CreateContextRecyclerAndRunTopLevelForGenerateBid(
v8::Local<v8::Context> context = context_recycler_scope.GetContext(); v8::Local<v8::Context> context = context_recycler_scope.GetContext();
TRACE_EVENT_NESTABLE_ASYNC_END0("fledge", "get_bidder_context", trace_id); TRACE_EVENT_NESTABLE_ASYNC_END0("fledge", "get_bidder_context", trace_id);
// We want this before RunScript, both because it's meant to be visible
// to globals, and because we don't want to overwrite existing globals.
context_recycler->AddTextConversionHelpers();
v8::Local<v8::UnboundScript> unbound_worklet_script = v8::Local<v8::UnboundScript> unbound_worklet_script =
worklet_script_.Get(v8_helper_->isolate()); worklet_script_.Get(v8_helper_->isolate());
@@ -2203,7 +2200,6 @@ BidderWorklet::V8State::CreateContextRecyclerAndRunTopLevelForGenerateBid(
context_recycler->AddSetBidBindings(); context_recycler->AddSetBidBindings();
context_recycler->AddSetPriorityBindings(); context_recycler->AddSetPriorityBindings();
context_recycler->AddSetPrioritySignalsOverrideBindings(); context_recycler->AddSetPrioritySignalsOverrideBindings();
context_recycler->AddTextConversionHelpers();
context_recycler->AddInterestGroupLazyFiller(); context_recycler->AddInterestGroupLazyFiller();
context_recycler->AddBiddingBrowserSignalsLazyFiller(); context_recycler->AddBiddingBrowserSignalsLazyFiller();

@@ -4961,19 +4961,35 @@ TEST_F(BidderWorkletTest, GenerateBidInterestGroupCreativeScanningMetadata) {
TEST_F(BidderWorkletTest, GenerateBidTextConversions) { TEST_F(BidderWorkletTest, GenerateBidTextConversions) {
RunGenerateBidExpectingExpressionIsTrue( RunGenerateBidExpectingExpressionIsTrue(
R"(!('encodeUtf8' in browserSignals))"); R"(!('protectedAudience' in globalThis))");
RunGenerateBidExpectingExpressionIsTrue(
R"(!('decodeUtf8' in browserSignals))");
} }
TEST_F(BidderWorkletTextConversionsTest, GenerateBidTextConversions) { TEST_F(BidderWorkletTextConversionsTest, GenerateBidTextConversions) {
RunGenerateBidExpectingExpressionIsTrue(R"('encodeUtf8' in browserSignals)"); RunGenerateBidExpectingExpressionIsTrue(
RunGenerateBidExpectingExpressionIsTrue(R"('decodeUtf8' in browserSignals)"); R"('encodeUtf8' in protectedAudience)");
RunGenerateBidExpectingExpressionIsTrue(
R"('decodeUtf8' in protectedAudience)");
RunGenerateBidExpectingExpressionIsTrue( RunGenerateBidExpectingExpressionIsTrue(
"browserSignals.encodeUtf8('A')[0] === 65"); "protectedAudience.encodeUtf8('A')[0] === 65");
RunGenerateBidExpectingExpressionIsTrue( RunGenerateBidExpectingExpressionIsTrue(
"browserSignals.decodeUtf8(new Uint8Array([65, 32, 68])) === 'A D'"); "protectedAudience.decodeUtf8(new Uint8Array([65, 32, 68])) === 'A D'");
}
// Make sure we don't stomp over an existing user protectedAudience
TEST_F(BidderWorkletTextConversionsTest, GenerateBidNoGlobalStomp) {
const char kScript[] = R"(
function protectedAudience() {
return {bid: 1, render:"https://response.test/"};
}
function generateBid() {
return protectedAudience();
}
)";
RunGenerateBidWithJavascriptExpectingResult(kScript,
TestBidBuilder().Build());
} }
class BidderWorkletCreativeScanningTest : public BidderWorkletTest { class BidderWorkletCreativeScanningTest : public BidderWorkletTest {
@@ -7853,22 +7869,33 @@ TEST_F(BidderWorkletTest, ReportWinTopLevelTimeout) {
TEST_F(BidderWorkletTest, ReportWinTextConversions) { TEST_F(BidderWorkletTest, ReportWinTextConversions) {
RunReportWinWithFunctionBodyExpectingResult( RunReportWinWithFunctionBodyExpectingResult(
"sendReportTo('https://foo.test?' + ('encodeUtf8' in browserSignals))", "sendReportTo('https://foo.test?' + ('protectedAudience' in globalThis))",
GURL("https://foo.test/?false"));
RunReportWinWithFunctionBodyExpectingResult(
"sendReportTo('https://foo.test?' + ('decodeUtf8' in browserSignals))",
GURL("https://foo.test/?false")); GURL("https://foo.test/?false"));
} }
TEST_F(BidderWorkletTextConversionsTest, ReportWinTextConversions) { TEST_F(BidderWorkletTextConversionsTest, ReportWinTextConversions) {
RunReportWinWithFunctionBodyExpectingResult( RunReportWinWithFunctionBodyExpectingResult(
"sendReportTo('https://foo.test?' + ('encodeUtf8' in browserSignals))", "sendReportTo('https://foo.test?' + ('encodeUtf8' in protectedAudience))",
GURL("https://foo.test/?true")); GURL("https://foo.test/?true"));
RunReportWinWithFunctionBodyExpectingResult( RunReportWinWithFunctionBodyExpectingResult(
"sendReportTo('https://foo.test?' + ('decodeUtf8' in browserSignals))", "sendReportTo('https://foo.test?' + ('decodeUtf8' in protectedAudience))",
GURL("https://foo.test/?true")); GURL("https://foo.test/?true"));
} }
// Make sure we don't stomp over an existing user protectedAudience object.
TEST_F(BidderWorkletTextConversionsTest, ReportWinNoGlobalStomp) {
const char kScript[] = R"(
function protectedAudience() {
sendReportTo("https://foo.test");
}
function reportWin() {
protectedAudience();
}
)";
RunReportWinWithJavascriptExpectingResult(kScript, GURL("https://foo.test"));
}
TEST_F(BidderWorkletTest, SendReportToLongUrl) { TEST_F(BidderWorkletTest, SendReportToLongUrl) {
// Copying large URLs can cause flaky generateBid() timeouts with the default // Copying large URLs can cause flaky generateBid() timeouts with the default
// value, even on the standard debug bots. // value, even on the standard debug bots.

@@ -22,6 +22,7 @@
#include "content/services/auction_worklet/for_debugging_only_bindings.h" #include "content/services/auction_worklet/for_debugging_only_bindings.h"
#include "content/services/auction_worklet/private_aggregation_bindings.h" #include "content/services/auction_worklet/private_aggregation_bindings.h"
#include "content/services/auction_worklet/private_model_training_bindings.h" #include "content/services/auction_worklet/private_model_training_bindings.h"
#include "content/services/auction_worklet/public/cpp/auction_worklet_features.h"
#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h" #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
#include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom.h" #include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom.h"
#include "content/services/auction_worklet/public/mojom/real_time_reporting.mojom.h" #include "content/services/auction_worklet/public/mojom/real_time_reporting.mojom.h"
@@ -6143,6 +6144,10 @@ TEST_F(ContextRecyclerTest, RegisterAdMacroBindings) {
} }
TEST_F(ContextRecyclerTest, EncodeUtf8) { TEST_F(ContextRecyclerTest, EncodeUtf8) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kFledgeTextConversionHelpers);
const char kScript[] = R"( const char kScript[] = R"(
function assertEq(l, r, label) { function assertEq(l, r, label) {
if (l !== r) if (l !== r)
@@ -6160,24 +6165,24 @@ TEST_F(ContextRecyclerTest, EncodeUtf8) {
} }
function test1() { function test1() {
assertByteArray(encoderObj.encodeUtf8('ABC'), assertByteArray(protectedAudience.encodeUtf8('ABC'),
[65, 66, 67]); [65, 66, 67]);
} }
function test2() { function test2() {
assertByteArray(encoderObj.encodeUtf8('A \u0490'), assertByteArray(protectedAudience.encodeUtf8('A \u0490'),
[65, 32, 0xD2, 0x90]); [65, 32, 0xD2, 0x90]);
} }
// Unmatched surrogate. // Unmatched surrogate.
function test3() { function test3() {
assertByteArray(encoderObj.encodeUtf8('A\uD800C'), assertByteArray(protectedAudience.encodeUtf8('A\uD800C'),
[65, 0xEF, 0xBF, 0xBD, 67]); [65, 0xEF, 0xBF, 0xBD, 67]);
} }
// Matched surrogate. // Matched surrogate.
function test4() { function test4() {
assertByteArray(encoderObj.encodeUtf8('A\uD83D\uDE02C'), assertByteArray(protectedAudience.encodeUtf8('A\uD83D\uDE02C'),
[65, 0xF0, 0x9F, 0x98, 0x82, 67]); [65, 0xF0, 0x9F, 0x98, 0x82, 67]);
} }
@@ -6186,7 +6191,7 @@ TEST_F(ContextRecyclerTest, EncodeUtf8) {
let obj = { let obj = {
toString: () => "ABC" toString: () => "ABC"
}; };
assertByteArray(encoderObj.encodeUtf8(obj), assertByteArray(protectedAudience.encodeUtf8(obj),
[65, 66, 67]); [65, 66, 67]);
} }
)"; )";
@@ -6203,13 +6208,6 @@ TEST_F(ContextRecyclerTest, EncodeUtf8) {
for (const char* test : {"test1", "test2", "test3", "test4", "test5"}) { for (const char* test : {"test1", "test2", "test3", "test4", "test5"}) {
SCOPED_TRACE(test); SCOPED_TRACE(test);
ContextRecyclerScope scope(context_recycler); ContextRecyclerScope scope(context_recycler);
v8::Local<v8::Context> context = scope.GetContext();
v8::Local<v8::Object> obj = v8::Object::New(helper_->isolate());
context_recycler.text_conversion_helpers()->ReInitialize(context, obj);
context->Global()
->Set(context, helper_->CreateStringFromLiteral("encoderObj"), obj)
.Check();
std::vector<std::string> error_msgs; std::vector<std::string> error_msgs;
Run(scope, script, test, error_msgs); Run(scope, script, test, error_msgs);
@@ -6218,10 +6216,14 @@ TEST_F(ContextRecyclerTest, EncodeUtf8) {
} }
TEST_F(ContextRecyclerTest, EncodeUtf8Failure) { TEST_F(ContextRecyclerTest, EncodeUtf8Failure) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kFledgeTextConversionHelpers);
const char kScript[] = R"( const char kScript[] = R"(
// Not enough arguments. // Not enough arguments.
function test1() { function test1() {
encoderObj.encodeUtf8(); protectedAudience.encodeUtf8();
} }
// String conversion failure. // String conversion failure.
@@ -6229,7 +6231,7 @@ TEST_F(ContextRecyclerTest, EncodeUtf8Failure) {
let obj = { let obj = {
toString: () => { throw 'ouch' } toString: () => { throw 'ouch' }
}; };
encoderObj.encodeUtf8(obj); protectedAudience.encodeUtf8(obj);
} }
)"; )";
@@ -6254,13 +6256,6 @@ TEST_F(ContextRecyclerTest, EncodeUtf8Failure) {
for (const auto& test : kTests) { for (const auto& test : kTests) {
SCOPED_TRACE(test.functionName); SCOPED_TRACE(test.functionName);
ContextRecyclerScope scope(context_recycler); ContextRecyclerScope scope(context_recycler);
v8::Local<v8::Context> context = scope.GetContext();
v8::Local<v8::Object> obj = v8::Object::New(helper_->isolate());
context_recycler.text_conversion_helpers()->ReInitialize(context, obj);
context->Global()
->Set(context, helper_->CreateStringFromLiteral("encoderObj"), obj)
.Check();
std::vector<std::string> error_msgs; std::vector<std::string> error_msgs;
Run(scope, script, test.functionName, error_msgs); Run(scope, script, test.functionName, error_msgs);
@@ -6269,6 +6264,10 @@ TEST_F(ContextRecyclerTest, EncodeUtf8Failure) {
} }
TEST_F(ContextRecyclerTest, DecodeUtf8) { TEST_F(ContextRecyclerTest, DecodeUtf8) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kFledgeTextConversionHelpers);
const char kScript[] = R"( const char kScript[] = R"(
function assertEq(l, r, label) { function assertEq(l, r, label) {
if (l !== r) if (l !== r)
@@ -6286,32 +6285,33 @@ TEST_F(ContextRecyclerTest, DecodeUtf8) {
} }
function test1() { function test1() {
assertString(encoderObj.decodeUtf8(new Uint8Array([65, 66, 67])), assertString(protectedAudience.decodeUtf8(new Uint8Array([65, 66, 67])),
'ABC'); 'ABC');
} }
function test2() { function test2() {
assertString(encoderObj.decodeUtf8(new Uint8Array([65, 32, 0xD2, 0x90])), assertString(protectedAudience.decodeUtf8(
new Uint8Array([65, 32, 0xD2, 0x90])),
'A \u0490'); 'A \u0490');
} }
// Broken utf-8 --- gets a replacement character. // Broken utf-8 --- gets a replacement character.
function test3() { function test3() {
assertString(encoderObj.decodeUtf8(new Uint8Array([65, 32, 0xD2])), assertString(protectedAudience.decodeUtf8(new Uint8Array([65, 32, 0xD2])),
'A \uFFFD'); 'A \uFFFD');
} }
// Utf-8 for just a single surrogate. Every byte ended up replaced with a // Utf-8 for just a single surrogate. Every byte ended up replaced with a
// replacement character. // replacement character.
function test4() { function test4() {
assertString(encoderObj.decodeUtf8(new Uint8Array( assertString(protectedAudience.decodeUtf8(new Uint8Array(
[65, 32, 0xED, 0xA0, 0x80, 66])), [65, 32, 0xED, 0xA0, 0x80, 66])),
'A \uFFFD\uFFFD\uFFFDB'); 'A \uFFFD\uFFFD\uFFFDB');
} }
// Utf-8 for something that requires two Utf-16 characters. // Utf-8 for something that requires two Utf-16 characters.
function test5() { function test5() {
assertString(encoderObj.decodeUtf8(new Uint8Array( assertString(protectedAudience.decodeUtf8(new Uint8Array(
[65, 0xF0, 0x9F, 0x98, 0x82, 67])), [65, 0xF0, 0x9F, 0x98, 0x82, 67])),
'A\uD83D\uDE02C'); 'A\uD83D\uDE02C');
} }
@@ -6323,9 +6323,9 @@ TEST_F(ContextRecyclerTest, DecodeUtf8) {
for (let i = 0; i < fullView.length; ++i) for (let i = 0; i < fullView.length; ++i)
fullView[i] = 65 + i; fullView[i] = 65 + i;
let partialView = new Uint8Array(buffer, 2, 3); let partialView = new Uint8Array(buffer, 2, 3);
assertString(encoderObj.decodeUtf8(fullView), assertString(protectedAudience.decodeUtf8(fullView),
'ABCDEFGH'); 'ABCDEFGH');
assertString(encoderObj.decodeUtf8(partialView), assertString(protectedAudience.decodeUtf8(partialView),
'CDE'); 'CDE');
} }
)"; )";
@@ -6343,13 +6343,6 @@ TEST_F(ContextRecyclerTest, DecodeUtf8) {
{"test1", "test2", "test3", "test4", "test5", "test6"}) { {"test1", "test2", "test3", "test4", "test5", "test6"}) {
SCOPED_TRACE(test); SCOPED_TRACE(test);
ContextRecyclerScope scope(context_recycler); ContextRecyclerScope scope(context_recycler);
v8::Local<v8::Context> context = scope.GetContext();
v8::Local<v8::Object> obj = v8::Object::New(helper_->isolate());
context_recycler.text_conversion_helpers()->ReInitialize(context, obj);
context->Global()
->Set(context, helper_->CreateStringFromLiteral("encoderObj"), obj)
.Check();
std::vector<std::string> error_msgs; std::vector<std::string> error_msgs;
Run(scope, script, test, error_msgs); Run(scope, script, test, error_msgs);
@@ -6358,15 +6351,19 @@ TEST_F(ContextRecyclerTest, DecodeUtf8) {
} }
TEST_F(ContextRecyclerTest, DecodeUtf8Failure) { TEST_F(ContextRecyclerTest, DecodeUtf8Failure) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kFledgeTextConversionHelpers);
const char kScript[] = R"( const char kScript[] = R"(
// Not enough arguments. // Not enough arguments.
function test1() { function test1() {
encoderObj.decodeUtf8(); protectedAudience.decodeUtf8();
} }
// Wrong type. // Wrong type.
function test2() { function test2() {
encoderObj.decodeUtf8([65,66]); protectedAudience.decodeUtf8([65,66]);
} }
)"; )";
@@ -6393,13 +6390,6 @@ TEST_F(ContextRecyclerTest, DecodeUtf8Failure) {
for (const auto& test : kTests) { for (const auto& test : kTests) {
SCOPED_TRACE(test.functionName); SCOPED_TRACE(test.functionName);
ContextRecyclerScope scope(context_recycler); ContextRecyclerScope scope(context_recycler);
v8::Local<v8::Context> context = scope.GetContext();
v8::Local<v8::Object> obj = v8::Object::New(helper_->isolate());
context_recycler.text_conversion_helpers()->ReInitialize(context, obj);
context->Global()
->Set(context, helper_->CreateStringFromLiteral("encoderObj"), obj)
.Check();
std::vector<std::string> error_msgs; std::vector<std::string> error_msgs;
Run(scope, script, test.functionName, error_msgs); Run(scope, script, test.functionName, error_msgs);

@@ -49,7 +49,6 @@
#include "content/services/auction_worklet/report_bindings.h" #include "content/services/auction_worklet/report_bindings.h"
#include "content/services/auction_worklet/seller_lazy_filler.h" #include "content/services/auction_worklet/seller_lazy_filler.h"
#include "content/services/auction_worklet/shared_storage_bindings.h" #include "content/services/auction_worklet/shared_storage_bindings.h"
#include "content/services/auction_worklet/text_conversion_helpers.h"
#include "content/services/auction_worklet/trusted_signals.h" #include "content/services/auction_worklet/trusted_signals.h"
#include "content/services/auction_worklet/trusted_signals_kvv2_manager.h" #include "content/services/auction_worklet/trusted_signals_kvv2_manager.h"
#include "content/services/auction_worklet/webidl_compat.h" #include "content/services/auction_worklet/webidl_compat.h"
@@ -905,6 +904,10 @@ SellerWorklet::V8State::CreateContextRecyclerAndRunTopLevel(
v8::Local<v8::Context> context = context_recycler_scope.GetContext(); v8::Local<v8::Context> context = context_recycler_scope.GetContext();
TRACE_EVENT_NESTABLE_ASYNC_END0("fledge", "get_seller_context", trace_id); TRACE_EVENT_NESTABLE_ASYNC_END0("fledge", "get_seller_context", trace_id);
// We want this before RunScript, both because it's meant to be visible
// to globals, and because we don't want to overwrite existing globals.
context_recycler->AddTextConversionHelpers();
v8::Local<v8::UnboundScript> unbound_worklet_script = v8::Local<v8::UnboundScript> unbound_worklet_script =
worklet_script_.Get(v8_helper_->isolate()); worklet_script_.Get(v8_helper_->isolate());
@@ -922,7 +925,6 @@ SellerWorklet::V8State::CreateContextRecyclerAndRunTopLevel(
permissions_policy_state_->private_aggregation_allowed, permissions_policy_state_->private_aggregation_allowed,
/*reserved_once_allowed=*/true); /*reserved_once_allowed=*/true);
context_recycler->AddRealTimeReportingBindings(); context_recycler->AddRealTimeReportingBindings();
context_recycler->AddTextConversionHelpers();
if (base::FeatureList::IsEnabled(network::features::kSharedStorageAPI)) { if (base::FeatureList::IsEnabled(network::features::kSharedStorageAPI)) {
context_recycler->AddSharedStorageBindings( context_recycler->AddSharedStorageBindings(
shared_storage_host_remote_.is_bound() shared_storage_host_remote_.is_bound()
@@ -1141,10 +1143,6 @@ void SellerWorklet::V8State::ScoreAd(
} }
context_recycler->seller_browser_signals_lazy_filler()->FillInObject( context_recycler->seller_browser_signals_lazy_filler()->FillInObject(
browser_signal_render_url, &ad_components, browser_signals); browser_signal_render_url, &ad_components, browser_signals);
if (base::FeatureList::IsEnabled(features::kFledgeTextConversionHelpers)) {
context_recycler->text_conversion_helpers()->ReInitialize(context,
browser_signals);
}
// TODO(crbug.com/336164429): Construct the fields of browser signals lazily. // TODO(crbug.com/336164429): Construct the fields of browser signals lazily.
if (!browser_signals_dict.Set("topWindowHostname", if (!browser_signals_dict.Set("topWindowHostname",
top_window_origin_.host()) || top_window_origin_.host()) ||
@@ -1680,6 +1678,10 @@ void SellerWorklet::V8State::ReportResult(
v8::Local<v8::Context> context = context_recycler_scope.GetContext(); v8::Local<v8::Context> context = context_recycler_scope.GetContext();
AuctionV8Logger v8_logger(v8_helper_.get(), context); AuctionV8Logger v8_logger(v8_helper_.get(), context);
// We want this before RunScript, both because it's meant to be visible
// to globals, and because we don't want to overwrite existing globals.
context_recycler.AddTextConversionHelpers();
v8::LocalVector<v8::Value> args(isolate); v8::LocalVector<v8::Value> args(isolate);
context_recycler.EnsureAuctionConfigLazyFillers( context_recycler.EnsureAuctionConfigLazyFillers(
@@ -1703,12 +1705,6 @@ void SellerWorklet::V8State::ReportResult(
v8::Local<v8::Object> browser_signals = v8::Object::New(isolate); v8::Local<v8::Object> browser_signals = v8::Object::New(isolate);
gin::Dictionary browser_signals_dict(isolate, browser_signals); gin::Dictionary browser_signals_dict(isolate, browser_signals);
context_recycler.AddTextConversionHelpers();
if (base::FeatureList::IsEnabled(features::kFledgeTextConversionHelpers)) {
context_recycler.text_conversion_helpers()->ReInitialize(context,
browser_signals);
}
context_recycler.AddSellerBrowserSignalsLazyFiller(); context_recycler.AddSellerBrowserSignalsLazyFiller();
// Passing null for ad_components here since we do not want creative scanning // Passing null for ad_components here since we do not want creative scanning
// info here. // info here.

@@ -1951,24 +1951,36 @@ TEST_F(SellerWorkletTest, ScoreAdAdComponentsCreativeScanningMetadata) {
TEST_F(SellerWorkletTest, ScoreAdTextConversions) { TEST_F(SellerWorkletTest, ScoreAdTextConversions) {
RunScoreAdWithReturnValueExpectingResult( RunScoreAdWithReturnValueExpectingResult(
R"('encodeUtf8' in browserSignals? 3 : 2)", 2); R"('protectedAudience' in globalThis? 3 : 2)", 2);
RunScoreAdWithReturnValueExpectingResult(
R"('decodeUtf8' in browserSignals? 3 : 2)", 2);
} }
TEST_F(SellerWorkletTextConversionsTest, ScoreAdTextConversions) { TEST_F(SellerWorkletTextConversionsTest, ScoreAdTextConversions) {
RunScoreAdWithReturnValueExpectingResult( RunScoreAdWithReturnValueExpectingResult(
R"('encodeUtf8' in browserSignals? 3 : 2)", 3); R"('encodeUtf8' in protectedAudience? 3 : 2)", 3);
RunScoreAdWithReturnValueExpectingResult( RunScoreAdWithReturnValueExpectingResult(
R"('decodeUtf8' in browserSignals? 3 : 2)", 3); R"('decodeUtf8' in protectedAudience? 3 : 2)", 3);
RunScoreAdWithReturnValueExpectingResult("browserSignals.encodeUtf8('A')[0]",
65);
RunScoreAdWithReturnValueExpectingResult( RunScoreAdWithReturnValueExpectingResult(
"browserSignals.decodeUtf8(new Uint8Array([65, 68])) === 'AD' ? 3 : 2", "protectedAudience.encodeUtf8('A')[0]", 65);
RunScoreAdWithReturnValueExpectingResult(
"protectedAudience.decodeUtf8(new Uint8Array([65, 68])) === 'AD' ? 3 : 2",
3); 3);
} }
TEST_F(SellerWorkletTextConversionsTest, ScoreAdNoGlobalStomp) {
const char kScript[] = R"(
function protectedAudience() {
return 5;
}
function scoreAd() {
return protectedAudience();
}
)";
RunScoreAdWithJavascriptExpectingResult(kScript, 5);
}
TEST_F(SellerWorkletTest, ScoreAdBid) { TEST_F(SellerWorkletTest, ScoreAdBid) {
bid_ = 5; bid_ = 5;
RunScoreAdWithReturnValueExpectingResult("bid", 5); RunScoreAdWithReturnValueExpectingResult("bid", 5);
@@ -3460,26 +3472,38 @@ TEST_F(SellerWorkletTest, ReportResultNoAdComponentsCreativeScanningMetadata) {
TEST_F(SellerWorkletTest, ReportResultTextConversions) { TEST_F(SellerWorkletTest, ReportResultTextConversions) {
RunReportResultCreatedScriptExpectingResult( RunReportResultCreatedScriptExpectingResult(
"('encodeUtf8' in browserSignals) ? 2 : 1", "('protectedAudience' in globalThis) ? 2 : 1",
/*extra_code=*/std::string(), "1",
/*expected_report_url=*/std::nullopt);
RunReportResultCreatedScriptExpectingResult(
"('decodeUtf8' in browserSignals) ? 2 : 1",
/*extra_code=*/std::string(), "1", /*extra_code=*/std::string(), "1",
/*expected_report_url=*/std::nullopt); /*expected_report_url=*/std::nullopt);
} }
TEST_F(SellerWorkletTextConversionsTest, ReportResultTextConversions) { TEST_F(SellerWorkletTextConversionsTest, ReportResultTextConversions) {
RunReportResultCreatedScriptExpectingResult( RunReportResultCreatedScriptExpectingResult(
"('encodeUtf8' in browserSignals) ? 2 : 1", "('encodeUtf8' in protectedAudience) ? 2 : 1",
/*extra_code=*/std::string(), "2", /*extra_code=*/std::string(), "2",
/*expected_report_url=*/std::nullopt); /*expected_report_url=*/std::nullopt);
RunReportResultCreatedScriptExpectingResult( RunReportResultCreatedScriptExpectingResult(
"('decodeUtf8' in browserSignals) ? 2 : 1", "('decodeUtf8' in protectedAudience) ? 2 : 1",
/*extra_code=*/std::string(), "2", /*extra_code=*/std::string(), "2",
/*expected_report_url=*/std::nullopt); /*expected_report_url=*/std::nullopt);
} }
TEST_F(SellerWorkletTextConversionsTest, ReportResultNoGlobalStomp) {
const char kScript[] = R"(
function protectedAudience() {
sendReportTo('https://report.test/');
}
function reportResult() {
protectedAudience();
}
)";
RunReportResultWithJavascriptExpectingResult(
kScript,
/*expected_signals_for_winner=*/"null",
/*expected_report_url=*/GURL("https://report.test"));
}
TEST_F(SellerWorkletTest, ReportResultTopWindowOrigin) { TEST_F(SellerWorkletTest, ReportResultTopWindowOrigin) {
top_window_origin_ = url::Origin::Create(GURL("https://foo.test/")); top_window_origin_ = url::Origin::Create(GURL("https://foo.test/"));
RunReportResultCreatedScriptExpectingResult( RunReportResultCreatedScriptExpectingResult(

@@ -7,6 +7,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "content/services/auction_worklet/auction_v8_helper.h" #include "content/services/auction_worklet/auction_v8_helper.h"
#include "content/services/auction_worklet/public/cpp/auction_worklet_features.h"
#include "content/services/auction_worklet/webidl_compat.h" #include "content/services/auction_worklet/webidl_compat.h"
#include "v8/include/v8-array-buffer.h" #include "v8/include/v8-array-buffer.h"
#include "v8/include/v8-exception.h" #include "v8/include/v8-exception.h"
@@ -21,14 +22,10 @@ TextConversionHelpers::TextConversionHelpers(AuctionV8Helper* v8_helper)
: v8_helper_(v8_helper) {} : v8_helper_(v8_helper) {}
void TextConversionHelpers::AttachToContext(v8::Local<v8::Context> context) { void TextConversionHelpers::AttachToContext(v8::Local<v8::Context> context) {
// We do everything in ReInitialize, since we attach to an object and not if (!base::FeatureList::IsEnabled(features::kFledgeTextConversionHelpers)) {
// globally. return;
} }
void TextConversionHelpers::Reset() {}
void TextConversionHelpers::ReInitialize(v8::Local<v8::Context> context,
v8::Local<v8::Object> object) {
v8::Local<v8::External> v8_this = v8::Local<v8::External> v8_this =
v8::External::New(v8_helper_->isolate(), this); v8::External::New(v8_helper_->isolate(), this);
v8::Local<v8::Function> encode = v8::Local<v8::Function> encode =
@@ -37,14 +34,21 @@ void TextConversionHelpers::ReInitialize(v8::Local<v8::Context> context,
v8::Local<v8::Function> decode = v8::Local<v8::Function> decode =
v8::Function::New(context, &TextConversionHelpers::DecodeUtf8, v8_this) v8::Function::New(context, &TextConversionHelpers::DecodeUtf8, v8_this)
.ToLocalChecked(); .ToLocalChecked();
object v8::Local<v8::Object> pa_object = v8::Object::New(v8_helper_->isolate());
context->Global()
->Set(context, v8_helper_->CreateStringFromLiteral("protectedAudience"),
pa_object)
.Check();
pa_object
->Set(context, v8_helper_->CreateStringFromLiteral("encodeUtf8"), encode) ->Set(context, v8_helper_->CreateStringFromLiteral("encodeUtf8"), encode)
.Check(); .Check();
object pa_object
->Set(context, v8_helper_->CreateStringFromLiteral("decodeUtf8"), decode) ->Set(context, v8_helper_->CreateStringFromLiteral("decodeUtf8"), decode)
.Check(); .Check();
} }
void TextConversionHelpers::Reset() {}
void TextConversionHelpers::EncodeUtf8( void TextConversionHelpers::EncodeUtf8(
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
TextConversionHelpers* bindings = static_cast<TextConversionHelpers*>( TextConversionHelpers* bindings = static_cast<TextConversionHelpers*>(

@@ -22,9 +22,6 @@ class CONTENT_EXPORT TextConversionHelpers : public Bindings {
void AttachToContext(v8::Local<v8::Context> context) override; void AttachToContext(v8::Local<v8::Context> context) override;
void Reset() override; void Reset() override;
void ReInitialize(v8::Local<v8::Context> context,
v8::Local<v8::Object> object);
private: private:
static void EncodeUtf8(const v8::FunctionCallbackInfo<v8::Value>& args); static void EncodeUtf8(const v8::FunctionCallbackInfo<v8::Value>& args);
static void DecodeUtf8(const v8::FunctionCallbackInfo<v8::Value>& args); static void DecodeUtf8(const v8::FunctionCallbackInfo<v8::Value>& args);

@@ -212,13 +212,9 @@ function validateBrowserSignals(browserSignals, isGenerateBid) {
throw 'Wrong seller ' + browserSignals.seller; throw 'Wrong seller ' + browserSignals.seller;
if ('topLevelSeller' in browserSignals) if ('topLevelSeller' in browserSignals)
throw 'Wrong topLevelSeller ' + browserSignals.topLevelSeller; throw 'Wrong topLevelSeller ' + browserSignals.topLevelSeller;
if (!(browserSignals.decodeUtf8 instanceof Function))
throw 'Wrong decodeUtf8';
if (!(browserSignals.encodeUtf8 instanceof Function))
throw 'Wrong encodeUtf8';
if (isGenerateBid) { if (isGenerateBid) {
if (Object.keys(browserSignals).length !== 13) { if (Object.keys(browserSignals).length !== 11) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }
@@ -241,7 +237,7 @@ function validateBrowserSignals(browserSignals, isGenerateBid) {
if (browserSignals.multiBidLimit !== 1) if (browserSignals.multiBidLimit !== 1)
throw 'Wrong multiBidLimit ' + browserSignals.multiBidLimit; throw 'Wrong multiBidLimit ' + browserSignals.multiBidLimit;
} else { } else {
if (Object.keys(browserSignals).length !== 18) { if (Object.keys(browserSignals).length !== 16) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }

@@ -192,13 +192,9 @@ function validateBrowserSignals(browserSignals, isGenerateBid) {
throw 'Wrong seller ' + browserSignals.seller; throw 'Wrong seller ' + browserSignals.seller;
if (!browserSignals.topLevelSeller.startsWith('https://b.test')) if (!browserSignals.topLevelSeller.startsWith('https://b.test'))
throw 'Wrong topLevelSeller ' + browserSignals.topLevelSeller; throw 'Wrong topLevelSeller ' + browserSignals.topLevelSeller;
if (!(browserSignals.decodeUtf8 instanceof Function))
throw 'Wrong decodeUtf8';
if (!(browserSignals.encodeUtf8 instanceof Function))
throw 'Wrong encodeUtf8';
if (isGenerateBid) { if (isGenerateBid) {
if (Object.keys(browserSignals).length !== 14) { if (Object.keys(browserSignals).length !== 12) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }
@@ -221,7 +217,7 @@ function validateBrowserSignals(browserSignals, isGenerateBid) {
if (browserSignals.multiBidLimit !== 1) if (browserSignals.multiBidLimit !== 1)
throw 'Wrong multiBidLimit ' + browserSignals.multiBidLimit; throw 'Wrong multiBidLimit ' + browserSignals.multiBidLimit;
} else { } else {
if (Object.keys(browserSignals).length !== 19) { if (Object.keys(browserSignals).length !== 17) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }

@@ -171,14 +171,10 @@ function validateBrowserSignals(browserSignals, isScoreAd) {
throw 'Wrong renderUrl ' + browserSignals.renderUrl; throw 'Wrong renderUrl ' + browserSignals.renderUrl;
if (browserSignals.bidCurrency != 'USD') if (browserSignals.bidCurrency != 'USD')
throw 'Wrong bidCurrency ' + browserSignals.bidCurrency; throw 'Wrong bidCurrency ' + browserSignals.bidCurrency;
if (!(browserSignals.decodeUtf8 instanceof Function))
throw 'Wrong decodeUtf8';
if (!(browserSignals.encodeUtf8 instanceof Function))
throw 'Wrong encodeUtf8';
// Fields that vary by method. // Fields that vary by method.
if (isScoreAd) { if (isScoreAd) {
if (Object.keys(browserSignals).length !== 14) { if (Object.keys(browserSignals).length !== 12) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }
@@ -201,7 +197,7 @@ function validateBrowserSignals(browserSignals, isScoreAd) {
throw 'Wrong forDebuggingOnlySampling ' + throw 'Wrong forDebuggingOnlySampling ' +
browserSignals.forDebuggingOnlySampling; browserSignals.forDebuggingOnlySampling;
} else { } else {
if (Object.keys(browserSignals).length !== 15) { if (Object.keys(browserSignals).length !== 13) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }

@@ -185,16 +185,12 @@ function validateBrowserSignals(browserSignals, isScoreAd) {
throw 'Wrong renderURL ' + browserSignals.renderURL; throw 'Wrong renderURL ' + browserSignals.renderURL;
if (browserSignals.renderUrl !== "https://example.com/render") if (browserSignals.renderUrl !== "https://example.com/render")
throw 'Wrong renderUrl ' + browserSignals.renderUrl; throw 'Wrong renderUrl ' + browserSignals.renderUrl;
if (browserSignals.bidCurrency !== 'CAD') if (browserSignals.bidCurrency !== 'CAD')
throw 'Wrong bidCurrency ' + browserSignals.bidCurrency; throw 'Wrong bidCurrency ' + browserSignals.bidCurrency;
if (!(browserSignals.decodeUtf8 instanceof Function))
throw 'Wrong decodeUtf8';
if (!(browserSignals.encodeUtf8 instanceof Function))
throw 'Wrong encodeUtf8';
// Fields that vary by method. // Fields that vary by method.
if (isScoreAd) { if (isScoreAd) {
if (Object.keys(browserSignals).length !== 14) { if (Object.keys(browserSignals).length !== 12) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }
@@ -217,7 +213,7 @@ function validateBrowserSignals(browserSignals, isScoreAd) {
throw 'Wrong forDebuggingOnlySampling ' + throw 'Wrong forDebuggingOnlySampling ' +
browserSignals.forDebuggingOnlySampling; browserSignals.forDebuggingOnlySampling;
} else { } else {
if (Object.keys(browserSignals).length !== 13) { if (Object.keys(browserSignals).length !== 11) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }

@@ -193,14 +193,10 @@ function validateBrowserSignals(browserSignals, isScoreAd) {
throw 'Wrong dataVersion ' + browserSignals.dataVersion; throw 'Wrong dataVersion ' + browserSignals.dataVersion;
if (browserSignals.bidCurrency !== 'USD') if (browserSignals.bidCurrency !== 'USD')
throw 'Wrong bidCurrency ' + browserSignals.bidCurrency; throw 'Wrong bidCurrency ' + browserSignals.bidCurrency;
if (!(browserSignals.decodeUtf8 instanceof Function))
throw 'Wrong decodeUtf8';
if (!(browserSignals.encodeUtf8 instanceof Function))
throw 'Wrong encodeUtf8';
// Fields that vary by method. // Fields that vary by method.
if (isScoreAd) { if (isScoreAd) {
if (Object.keys(browserSignals).length !== 13) { if (Object.keys(browserSignals).length !== 11) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }
@@ -221,7 +217,7 @@ function validateBrowserSignals(browserSignals, isScoreAd) {
throw 'Wrong forDebuggingOnlySampling ' + throw 'Wrong forDebuggingOnlySampling ' +
browserSignals.forDebuggingOnlySampling; browserSignals.forDebuggingOnlySampling;
} else { } else {
if (Object.keys(browserSignals).length !== 12) { if (Object.keys(browserSignals).length !== 10) {
throw 'Wrong number of browser signals fields ' + throw 'Wrong number of browser signals fields ' +
JSON.stringify(browserSignals); JSON.stringify(browserSignals);
} }

@@ -51,11 +51,6 @@ subsetTest(promise_test, async test => {
// Remove deprecated field, if present. // Remove deprecated field, if present.
delete browserSignals.prevWins; delete browserSignals.prevWins;
// encode/decode utf-8 are tested separately, and aren't
// suitable to equality testing.
delete browserSignals.encodeUtf8;
delete browserSignals.decodeUtf8;
if (!deepEquals(browserSignals, expectedBrowserSignals)) if (!deepEquals(browserSignals, expectedBrowserSignals))
throw "Unexpected browserSignals: " + JSON.stringify(browserSignals);` throw "Unexpected browserSignals: " + JSON.stringify(browserSignals);`
}); });

@@ -97,19 +97,19 @@ async function testConversionException(test, conversionBody) {
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
await testConversion( await testConversion(
test, `let result = browserSignals.encodeUtf8('ABC\u0490'); test, `let result = protectedAudience.encodeUtf8('ABC\u0490');
assertByteArray(result, [65, 66, 67, 0xD2, 0x90])`); assertByteArray(result, [65, 66, 67, 0xD2, 0x90])`);
}, 'encodeUtf8 - basic'); }, 'encodeUtf8 - basic');
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
await testConversion( await testConversion(
test, `let result = browserSignals.encodeUtf8('A\uD800C'); test, `let result = protectedAudience.encodeUtf8('A\uD800C');
assertByteArray(result, [65, 0xEF, 0xBF, 0xBD, 67])`); assertByteArray(result, [65, 0xEF, 0xBF, 0xBD, 67])`);
}, 'encodeUtf8 - mismatched surrogate gets replaced'); }, 'encodeUtf8 - mismatched surrogate gets replaced');
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
await testConversion( await testConversion(
test, `let result = browserSignals.encodeUtf8('A\uD83D\uDE02C'); test, `let result = protectedAudience.encodeUtf8('A\uD83D\uDE02C');
assertByteArray(result, [65, 0xF0, 0x9F, 0x98, 0x82, 67])`); assertByteArray(result, [65, 0xF0, 0x9F, 0x98, 0x82, 67])`);
}, 'encodeUtf8 - surrogate pair combined'); }, 'encodeUtf8 - surrogate pair combined');
@@ -118,7 +118,7 @@ subsetTest(promise_test, async test => {
let obj = { let obj = {
toString: () => "ABC" toString: () => "ABC"
}; };
let result = browserSignals.encodeUtf8(obj); let result = protectedAudience.encodeUtf8(obj);
assertByteArray(result, [65, 66, 67]) assertByteArray(result, [65, 66, 67])
`; `;
await testConversion(test, conversionBody); await testConversion(test, conversionBody);
@@ -126,7 +126,7 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
const conversionBody = ` const conversionBody = `
let result = browserSignals.encodeUtf8(); let result = protectedAudience.encodeUtf8();
`; `;
await testConversionException(test, conversionBody); await testConversionException(test, conversionBody);
}, 'encodeUtf8 - not enough arguments'); }, 'encodeUtf8 - not enough arguments');
@@ -136,7 +136,7 @@ subsetTest(promise_test, async test => {
let obj = { let obj = {
toString: () => { throw 'no go' } toString: () => { throw 'no go' }
}; };
let result = browserSignals.encodeUtf8(obj); let result = protectedAudience.encodeUtf8(obj);
`; `;
await testConversionException(test, conversionBody); await testConversionException(test, conversionBody);
}, 'encodeUtf8 - custom string conversion failure'); }, 'encodeUtf8 - custom string conversion failure');
@@ -144,7 +144,7 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
const conversionBody = ` const conversionBody = `
let input = new Uint8Array([65, 66, 0xD2, 0x90, 67]); let input = new Uint8Array([65, 66, 0xD2, 0x90, 67]);
let result = browserSignals.decodeUtf8(input); let result = protectedAudience.decodeUtf8(input);
assertString(result, 'AB\u0490C'); assertString(result, 'AB\u0490C');
`; `;
await testConversion(test, conversionBody); await testConversion(test, conversionBody);
@@ -153,7 +153,7 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
const conversionBody = ` const conversionBody = `
let input = new Uint8Array([65, 32, 0xD2]); let input = new Uint8Array([65, 32, 0xD2]);
let result = browserSignals.decodeUtf8(input); let result = protectedAudience.decodeUtf8(input);
if (result.indexOf('\uFFFD') === -1) if (result.indexOf('\uFFFD') === -1)
throw 'Should have replacement character'; throw 'Should have replacement character';
`; `;
@@ -163,7 +163,7 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
const conversionBody = ` const conversionBody = `
let input = new Uint8Array([65, 32, 0xED, 0xA0, 0x80, 66]); let input = new Uint8Array([65, 32, 0xED, 0xA0, 0x80, 66]);
let result = browserSignals.decodeUtf8(input); let result = protectedAudience.decodeUtf8(input);
if (result.indexOf('\uFFFD') === -1) if (result.indexOf('\uFFFD') === -1)
throw 'Should have replacement character'; throw 'Should have replacement character';
`; `;
@@ -173,7 +173,7 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
const conversionBody = ` const conversionBody = `
let input = new Uint8Array([65, 0xF0, 0x9F, 0x98, 0x82, 67]); let input = new Uint8Array([65, 0xF0, 0x9F, 0x98, 0x82, 67]);
let result = browserSignals.decodeUtf8(input); let result = protectedAudience.decodeUtf8(input);
assertString(result, 'A\uD83D\uDE02C'); assertString(result, 'A\uD83D\uDE02C');
`; `;
await testConversion(test, conversionBody); await testConversion(test, conversionBody);
@@ -186,9 +186,9 @@ subsetTest(promise_test, async test => {
for (let i = 0; i < fullView.length; ++i) for (let i = 0; i < fullView.length; ++i)
fullView[i] = 65 + i; fullView[i] = 65 + i;
let partialView = new Uint8Array(buffer, 2, 3); let partialView = new Uint8Array(buffer, 2, 3);
assertString(browserSignals.decodeUtf8(fullView), assertString(protectedAudience.decodeUtf8(fullView),
'ABCDEFGH'); 'ABCDEFGH');
assertString(browserSignals.decodeUtf8(partialView), assertString(protectedAudience.decodeUtf8(partialView),
'CDE'); 'CDE');
`; `;
await testConversion(test, conversionBody); await testConversion(test, conversionBody);
@@ -196,14 +196,14 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
const conversionBody = ` const conversionBody = `
let result = browserSignals.decodeUtf8(); let result = protectedAudience.decodeUtf8();
`; `;
await testConversionException(test, conversionBody); await testConversionException(test, conversionBody);
}, 'decodeUtf8 - not enough arguments'); }, 'decodeUtf8 - not enough arguments');
subsetTest(promise_test, async test => { subsetTest(promise_test, async test => {
const conversionBody = ` const conversionBody = `
let result = browserSignals.decodeUtf8([65, 32, 66]); let result = protectedAudience.decodeUtf8([65, 32, 66]);
`; `;
await testConversionException(test, conversionBody); await testConversionException(test, conversionBody);
}, 'decodeUtf8 - wrong type'); }, 'decodeUtf8 - wrong type');