Skip to content

Commit

Permalink
[M115] Add lifetimeMs field to replace expiration
Browse files Browse the repository at this point in the history
The web standard convention is to use milliseconds instead of seconds.

To maintain compatibility, expiration will still work, and both
lifetimeMs (in the interest group definition) and expiration (in
joinAdInterestGroup() will work).

(cherry picked from commit 9e7b6a6)

Bug: 1451033
Change-Id: Icecab5ae63374d5c696f4e8f549d6336ae559c2e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4591331
Commit-Queue: Caleb Raitto <caraitto@chromium.org>
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1153970}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4604291
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Auto-Submit: Caleb Raitto <caraitto@chromium.org>
Cr-Commit-Position: refs/branch-heads/5790@{#552}
Cr-Branched-From: 1d71a33-refs/heads/main@{#1148114}
  • Loading branch information
caraitto authored and Chromium LUCI CQ committed Jun 9, 2023
1 parent 280df2a commit 1f869c2
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 28 deletions.
96 changes: 95 additions & 1 deletion content/browser/interest_group/interest_group_browsertest.cc
Expand Up @@ -2554,7 +2554,98 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
unsupportedField: 'In ad component',
}]
},
/*joinDurationSec=*/1);
/*joinDurationSec=*/1000);
} catch (e) {
return e.toString();
}
return 'done';
})())",
test_origin.Serialize().c_str(),
url.spec().c_str())));
WaitForAccessObserved({
{TestInterestGroupObserver::kJoin, test_origin, "cars"},
});
auto storage_groups = GetInterestGroupsForOwner(test_origin);
ASSERT_EQ(storage_groups.size(), 1u);
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
JoinInterestGroupMissingLifetimeMs) {
GURL url = https_server_->GetURL("a.test", "/echo");
url::Origin test_origin = url::Origin::Create(url);
ASSERT_TRUE(NavigateToURL(shell(), url));
AttachInterestGroupObserver();

EXPECT_EQ(
"TypeError: Failed to execute 'joinAdInterestGroup' on 'Navigator': "
"Missing required field lifetimeMs",
EvalJs(shell(),
JsReplace(R"(
(async function() {
try {
await navigator.joinAdInterestGroup(
{
name: 'cars',
owner: $1,
});
} catch (e) {
return e.toString();
}
return 'done';
})())",
test_origin.Serialize().c_str(), url.spec().c_str())));
WaitForAccessObserved({});
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, JoinInterestGroupLifetimeMs) {
GURL url = https_server_->GetURL("a.test", "/echo");
url::Origin test_origin = url::Origin::Create(url);
ASSERT_TRUE(NavigateToURL(shell(), url));
AttachInterestGroupObserver();

EXPECT_EQ("done", EvalJs(shell(), JsReplace(R"(
(async function() {
try {
await navigator.joinAdInterestGroup(
{
name: 'cars',
owner: $1,
lifetimeMs: 1000000
});
} catch (e) {
return e.toString();
}
return 'done';
})())",
test_origin.Serialize().c_str(),
url.spec().c_str())));
WaitForAccessObserved({
{TestInterestGroupObserver::kJoin, test_origin, "cars"},
});
auto storage_groups = GetInterestGroupsForOwner(test_origin);
ASSERT_EQ(storage_groups.size(), 1u);
// Unfortunately, we can't use MOCK_TIME in browser tests, and blink code
// doesn't run in unit tests, so we can't verify the expiry time, since
// base::Time clocks can skew backwards.
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
JoinInterestGroupLifetimeMsAndDeprecatedDuration) {
GURL url = https_server_->GetURL("a.test", "/echo");
url::Origin test_origin = url::Origin::Create(url);
ASSERT_TRUE(NavigateToURL(shell(), url));
AttachInterestGroupObserver();

EXPECT_EQ("done", EvalJs(shell(), JsReplace(R"(
(async function() {
try {
await navigator.joinAdInterestGroup(
{
name: 'cars',
owner: $1,
lifetimeMs: 1000000
},
/*joinDurationSec=*/1000);
} catch (e) {
return e.toString();
}
Expand All @@ -2567,6 +2658,9 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
});
auto storage_groups = GetInterestGroupsForOwner(test_origin);
ASSERT_EQ(storage_groups.size(), 1u);
// Unfortunately, we can't use MOCK_TIME in browser tests, and blink code
// doesn't run in unit tests, so we can't verify the expiry time, since
// base::Time clocks can skew backwards.
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
Expand Down
Expand Up @@ -26,6 +26,10 @@ dictionary AuctionAdInterestGroup {
required USVString owner;
required USVString name;

// TODO(crbug.com/1451034): Make required once support for old
// joinAdInterestGroup() duration parameter can be removed.
double lifetimeMs;

double priority;
boolean enableBiddingSignalsPrioritization;
record<DOMString, double> priorityVector;
Expand Down
100 changes: 75 additions & 25 deletions third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
Expand Up @@ -467,6 +467,30 @@ scoped_refptr<const SecurityOrigin> ParseOrigin(const String& origin_string) {

// joinAdInterestGroup() copy functions.

// TODO(crbug.com/1451034): Remove method when old expiration is removed.
bool CopyLifetimeIdlToMojo(ExceptionState& exception_state,
absl::optional<double> lifetime_seconds,
const AuctionAdInterestGroup& input,
mojom::blink::InterestGroup& output) {
absl::optional<base::TimeDelta> lifetime_old =
lifetime_seconds
? absl::optional<base::TimeDelta>(base::Seconds(*lifetime_seconds))
: absl::nullopt;
absl::optional<base::TimeDelta> lifetime_new =
input.hasLifetimeMs() ? absl::optional<base::TimeDelta>(
base::Milliseconds(input.lifetimeMs()))
: absl::nullopt;
if (lifetime_old && !lifetime_new) {
lifetime_new = lifetime_old;
}
if (!lifetime_new) {
exception_state.ThrowTypeError(ErrorMissingRequired("lifetimeMs"));
return false;
}
output.expiry = base::Time::Now() + *lifetime_new;
return true;
}

bool CopyOwnerFromIdlToMojo(const ExecutionContext& execution_context,
ExceptionState& exception_state,
const AuctionAdInterestGroup& input,
Expand Down Expand Up @@ -2234,6 +2258,40 @@ bool HandleOldDictNamesRun(AuctionAdConfig* config,
return true;
}

// TODO(crbug.com/1451034): Remove indirection method
// JoinAdInterestGroupInternal() when old expiration is removed.
ScriptPromise JoinAdInterestGroupInternal(
ScriptState* script_state,
Navigator& navigator,
AuctionAdInterestGroup* group,
absl::optional<double> duration_seconds,
ExceptionState& exception_state) {
if (!navigator.DomWindow()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
"The document has no window associated.");
return ScriptPromise();
}
RecordCommonFledgeUseCounters(navigator.DomWindow()->document());
const ExecutionContext* context = ExecutionContext::From(script_state);
if (!context->IsFeatureEnabled(
mojom::blink::PermissionsPolicyFeature::kJoinAdInterestGroup)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotAllowedError,
"Feature join-ad-interest-group is not enabled by Permissions Policy");
return ScriptPromise();
}
if (!base::FeatureList::IsEnabled(
blink::features::kAdInterestGroupAPIRestrictedPolicyByDefault) &&
FeatureWouldBeBlockedByRestrictedPermissionsPolicy(navigator)) {
AddWarningMessageToConsole(script_state, "join-ad-interest-group",
"joinAdInterestGroup");
}

return NavigatorAuction::From(ExecutionContext::From(script_state), navigator)
.joinAdInterestGroup(script_state, group, duration_seconds,
exception_state);
}

} // namespace

NavigatorAuction::AuctionHandle::JsonResolved::JsonResolved(
Expand Down Expand Up @@ -2547,7 +2605,7 @@ const char NavigatorAuction::kSupplementName[] = "NavigatorAuction";
ScriptPromise NavigatorAuction::joinAdInterestGroup(
ScriptState* script_state,
AuctionAdInterestGroup* mutable_group,
double duration_seconds,
absl::optional<double> lifetime_seconds,
ExceptionState& exception_state) {
const ExecutionContext* context = ExecutionContext::From(script_state);

Expand All @@ -2558,7 +2616,10 @@ ScriptPromise NavigatorAuction::joinAdInterestGroup(
const AuctionAdInterestGroup* group = mutable_group;

auto mojo_group = mojom::blink::InterestGroup::New();
mojo_group->expiry = base::Time::Now() + base::Seconds(duration_seconds);
if (!CopyLifetimeIdlToMojo(exception_state, lifetime_seconds, *group,
*mojo_group)) {
return ScriptPromise();
}
if (!CopyOwnerFromIdlToMojo(*context, exception_state, *group, *mojo_group))
return ScriptPromise();
mojo_group->name = group->name();
Expand Down Expand Up @@ -2662,30 +2723,19 @@ ScriptPromise NavigatorAuction::joinAdInterestGroup(
AuctionAdInterestGroup* group,
double duration_seconds,
ExceptionState& exception_state) {
if (!navigator.DomWindow()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
"The document has no window associated.");
return ScriptPromise();
}
RecordCommonFledgeUseCounters(navigator.DomWindow()->document());
const ExecutionContext* context = ExecutionContext::From(script_state);
if (!context->IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kJoinAdInterestGroup)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotAllowedError,
"Feature join-ad-interest-group is not enabled by Permissions Policy");
return ScriptPromise();
}
if (!base::FeatureList::IsEnabled(
blink::features::kAdInterestGroupAPIRestrictedPolicyByDefault) &&
FeatureWouldBeBlockedByRestrictedPermissionsPolicy(navigator)) {
AddWarningMessageToConsole(script_state, "join-ad-interest-group",
"joinAdInterestGroup");
}
return JoinAdInterestGroupInternal(script_state, navigator, group,
duration_seconds, exception_state);
}

return From(ExecutionContext::From(script_state), navigator)
.joinAdInterestGroup(script_state, group, duration_seconds,
exception_state);
/* static */
ScriptPromise NavigatorAuction::joinAdInterestGroup(
ScriptState* script_state,
Navigator& navigator,
AuctionAdInterestGroup* group,
ExceptionState& exception_state) {
return JoinAdInterestGroupInternal(script_state, navigator, group,
/*duration_seconds=*/absl::nullopt,
exception_state);
}

ScriptPromise NavigatorAuction::leaveAdInterestGroup(
Expand Down
Expand Up @@ -51,13 +51,17 @@ class MODULES_EXPORT NavigatorAuction final
// TODO(crbug.com/1441988): Make `const AuctionAdInterestGroup*` after rename.
ScriptPromise joinAdInterestGroup(ScriptState*,
AuctionAdInterestGroup*,
double,
absl::optional<double>,
ExceptionState&);
static ScriptPromise joinAdInterestGroup(ScriptState*,
Navigator&,
AuctionAdInterestGroup*,
double,
ExceptionState&);
static ScriptPromise joinAdInterestGroup(ScriptState*,
Navigator&,
AuctionAdInterestGroup*,
ExceptionState&);
ScriptPromise leaveAdInterestGroup(ScriptState*,
const AuctionAdInterestGroupKey*,
ExceptionState&);
Expand Down
Expand Up @@ -17,7 +17,7 @@ typedef (USVString or FencedFrameConfig) UrnOrConfig;
SecureContext
] partial interface Navigator {
[CallWith=ScriptState, Measure, RaisesException]
Promise<void> joinAdInterestGroup(AuctionAdInterestGroup group, double durationSeconds);
Promise<void> joinAdInterestGroup(AuctionAdInterestGroup group, optional double durationSeconds);

[CallWith=ScriptState, Measure, RaisesException]
Promise<void> leaveAdInterestGroup(optional AuctionAdInterestGroupKey group);
Expand Down

0 comments on commit 1f869c2

Please sign in to comment.