blob: 6cc4544f525f2d4c05a9b2db295b54ca5f41f999 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/origin_trials/trial_token.h"
#include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/html/html_head_element.h"
#include "third_party/blink/renderer/core/html/html_meta_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/core/testing/null_execution_context.h"
#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
namespace {
const char kFrobulateTrialName[] = "Frobulate";
const char kFrobulateDeprecationTrialName[] = "FrobulateDeprecation";
const char kFrobulateNavigationTrialName[] = "FrobulateNavigation";
const char kFrobulateThirdPartyTrialName[] = "FrobulateThirdParty";
const char kFrobulateEnabledOrigin[] = "https://www.example.com";
const char kFrobulateEnabledOriginUnsecure[] = "http://www.example.com";
// Names of UMA histograms
const char kResultHistogram[] = "OriginTrials.ValidationResult";
// Trial token placeholder for mocked calls to validator
const char kTokenPlaceholder[] = "The token contents are not used";
class MockTokenValidator : public TrialTokenValidator {
public:
MockTokenValidator()
: response_(OriginTrialTokenStatus::kNotSupported), call_count_(0) {}
~MockTokenValidator() override = default;
// blink::WebTrialTokenValidator implementation
TrialTokenResult ValidateToken(base::StringPiece token,
const url::Origin& origin,
base::Time current_time) const override {
call_count_++;
return response_;
}
TrialTokenResult ValidateToken(base::StringPiece token,
const url::Origin& origin,
const url::Origin* script_origin,
base::Time current_time) const override {
return ValidateToken(token, origin, current_time);
}
// Useful methods for controlling the validator
void SetResponse(OriginTrialTokenStatus status,
const std::string& feature,
base::Time expiry = base::Time(),
bool is_third_party = false) {
response_.status = status;
response_.feature_name = feature;
response_.expiry_time = expiry;
response_.is_third_party = is_third_party;
}
int CallCount() { return call_count_; }
private:
TrialTokenResult response_;
mutable int call_count_;
DISALLOW_COPY_AND_ASSIGN(MockTokenValidator);
};
} // namespace
class OriginTrialContextTest : public testing::Test {
protected:
OriginTrialContextTest()
: token_validator_(new MockTokenValidator),
execution_context_(MakeGarbageCollected<NullExecutionContext>()),
histogram_tester_(new HistogramTester()) {
execution_context_->GetOriginTrialContext()
->SetTrialTokenValidatorForTesting(
std::unique_ptr<MockTokenValidator>(token_validator_));
}
~OriginTrialContextTest() override {
execution_context_->NotifyContextDestroyed();
}
MockTokenValidator* TokenValidator() { return token_validator_; }
void UpdateSecurityOrigin(const String& origin) {
KURL page_url(origin);
scoped_refptr<SecurityOrigin> page_origin =
SecurityOrigin::Create(page_url);
execution_context_->GetSecurityContext().SetSecurityOrigin(page_origin);
}
bool IsFeatureEnabled(const String& origin, OriginTrialFeature feature) {
UpdateSecurityOrigin(origin);
return IsFeatureEnabled(feature);
}
bool IsFeatureEnabled(OriginTrialFeature feature) {
// Need at least one token to ensure the token validator is called.
execution_context_->GetOriginTrialContext()->AddToken(kTokenPlaceholder);
return execution_context_->GetOriginTrialContext()->IsFeatureEnabled(
feature);
}
bool IsFeatureEnabledForThirdPartyOrigin(const String& origin,
const String& script_origin,
OriginTrialFeature feature) {
UpdateSecurityOrigin(origin);
KURL script_url(script_origin);
scoped_refptr<const SecurityOrigin> script_security_origin =
SecurityOrigin::Create(script_url);
// Need at least one token to ensure the token validator is called.
execution_context_->GetOriginTrialContext()->AddTokenFromExternalScript(
kTokenPlaceholder, script_security_origin.get());
return execution_context_->GetOriginTrialContext()->IsFeatureEnabled(
feature);
}
base::Time GetFeatureExpiry(OriginTrialFeature feature) {
return execution_context_->GetOriginTrialContext()->GetFeatureExpiry(
feature);
}
std::unique_ptr<Vector<OriginTrialFeature>> GetEnabledNavigationFeatures() {
return execution_context_->GetOriginTrialContext()
->GetEnabledNavigationFeatures();
}
bool ActivateNavigationFeature(OriginTrialFeature feature) {
execution_context_->GetOriginTrialContext()
->ActivateNavigationFeaturesFromInitiator({feature});
return execution_context_->GetOriginTrialContext()
->IsNavigationFeatureActivated(feature);
}
void ExpectStatusUniqueMetric(OriginTrialTokenStatus status, int count) {
histogram_tester_->ExpectUniqueSample(kResultHistogram,
static_cast<int>(status), count);
}
void ExpecStatusTotalMetric(int total) {
histogram_tester_->ExpectTotalCount(kResultHistogram, total);
}
private:
MockTokenValidator* token_validator_;
Persistent<NullExecutionContext> execution_context_;
std::unique_ptr<HistogramTester> histogram_tester_;
};
TEST_F(OriginTrialContextTest, EnabledNonExistingTrial) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName);
bool is_non_existing_feature_enabled = IsFeatureEnabled(
kFrobulateEnabledOrigin, OriginTrialFeature::kNonExisting);
EXPECT_FALSE(is_non_existing_feature_enabled);
// Status metric should be updated.
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kSuccess, 1);
}
// The feature should be enabled if a valid token for the origin is provided
TEST_F(OriginTrialContextTest, EnabledSecureRegisteredOrigin) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName);
bool is_origin_enabled = IsFeatureEnabled(
kFrobulateEnabledOrigin, OriginTrialFeature::kOriginTrialsSampleAPI);
EXPECT_TRUE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
// Status metric should be updated.
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kSuccess, 1);
// kOriginTrialsSampleAPI is not a navigation feature, so shouldn't be
// included in GetEnabledNavigationFeatures().
EXPECT_EQ(nullptr, GetEnabledNavigationFeatures());
}
// The feature should be enabled if a valid token for a deprecation trial for
// the origin is provided.
TEST_F(OriginTrialContextTest, EnabledSecureRegisteredOriginDeprecation) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateDeprecationTrialName);
bool is_origin_enabled =
IsFeatureEnabled(kFrobulateEnabledOrigin,
OriginTrialFeature::kOriginTrialsSampleAPIDeprecation);
EXPECT_TRUE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
// Status metric should be updated.
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kSuccess, 1);
// kOriginTrialsSampleAPIDeprecation is not a navigation feature, so shouldn't
// be included in GetEnabledNavigationFeatures().
EXPECT_EQ(nullptr, GetEnabledNavigationFeatures());
}
// ... but if the browser says it's invalid for any reason, that's enough to
// reject.
TEST_F(OriginTrialContextTest, InvalidTokenResponseFromPlatform) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kMalformed,
kFrobulateTrialName);
bool is_origin_enabled = IsFeatureEnabled(
kFrobulateEnabledOrigin, OriginTrialFeature::kOriginTrialsSampleAPI);
EXPECT_FALSE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
// Status metric should be updated.
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kMalformed, 1);
}
// The feature should not be enabled if the origin is insecure, even if a valid
// token for the origin is provided
TEST_F(OriginTrialContextTest, EnabledNonSecureRegisteredOrigin) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName);
bool is_origin_enabled =
IsFeatureEnabled(kFrobulateEnabledOriginUnsecure,
OriginTrialFeature::kOriginTrialsSampleAPI);
EXPECT_FALSE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kInsecure, 1);
}
// The feature should be enabled if the origin is insecure, for a valid token
// for a deprecation trial.
TEST_F(OriginTrialContextTest,
EnabledNonSecureRegisteredOriginDeprecationWithToken) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateDeprecationTrialName);
bool is_origin_enabled =
IsFeatureEnabled(kFrobulateEnabledOriginUnsecure,
OriginTrialFeature::kOriginTrialsSampleAPIDeprecation);
EXPECT_TRUE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kSuccess, 1);
}
// The feature should not be enabled if the origin is insecure, without a valid
// token for a deprecation trial.
TEST_F(OriginTrialContextTest,
EnabledNonSecureRegisteredOriginDeprecationNoToken) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName);
bool is_origin_enabled =
IsFeatureEnabled(kFrobulateEnabledOriginUnsecure,
OriginTrialFeature::kOriginTrialsSampleAPIDeprecation);
EXPECT_FALSE(is_origin_enabled);
}
// The feature should not be enabled if token is valid and enabled for third
// party origin but trial is not enabled for third party origin.
TEST_F(OriginTrialContextTest, EnabledNonThirdPartyTrialWithThirdPartyToken) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName, base::Time(), true);
bool is_origin_enabled = IsFeatureEnabledForThirdPartyOrigin(
kFrobulateEnabledOrigin, kFrobulateEnabledOrigin,
OriginTrialFeature::kOriginTrialsSampleAPI);
EXPECT_FALSE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kFeatureDisabled, 1);
}
// The feature should not be enabled if token is enabled for third
// party origin but it's not injected by external script.
TEST_F(OriginTrialContextTest, ThirdPartyTokenNotFromExternalScript) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kWrongOrigin,
kFrobulateThirdPartyTrialName, base::Time(),
true);
bool is_origin_enabled = IsFeatureEnabledForThirdPartyOrigin(
kFrobulateEnabledOrigin, kFrobulateEnabledOrigin,
OriginTrialFeature::kOriginTrialsSampleAPIThirdParty);
EXPECT_FALSE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kWrongOrigin, 1);
}
// The feature should not be enabled if token is injected from insecure external
// script even if document origin is secure.
TEST_F(OriginTrialContextTest, ThirdPartyTokenFromInsecureExternalScript) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateThirdPartyTrialName, base::Time(),
true);
bool is_origin_enabled = IsFeatureEnabledForThirdPartyOrigin(
kFrobulateEnabledOrigin, kFrobulateEnabledOriginUnsecure,
OriginTrialFeature::kOriginTrialsSampleAPIThirdParty);
EXPECT_FALSE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kInsecure, 1);
}
// The feature should not be enabled if token is injected from insecure external
// script when the document origin is also insecure.
TEST_F(OriginTrialContextTest,
ThirdPartyTokenFromInsecureExternalScriptOnInsecureDocument) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateThirdPartyTrialName, base::Time(),
true);
bool is_origin_enabled = IsFeatureEnabledForThirdPartyOrigin(
kFrobulateEnabledOriginUnsecure, kFrobulateEnabledOriginUnsecure,
OriginTrialFeature::kOriginTrialsSampleAPIThirdParty);
EXPECT_FALSE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kInsecure, 1);
}
// The feature should not be enabled if token is injected from secure external
// script when the document is insecure.
TEST_F(OriginTrialContextTest, ThirdPartyTokenOnInsecureDocument) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateThirdPartyTrialName, base::Time(),
true);
bool is_origin_enabled = IsFeatureEnabledForThirdPartyOrigin(
kFrobulateEnabledOriginUnsecure, kFrobulateEnabledOrigin,
OriginTrialFeature::kOriginTrialsSampleAPIThirdParty);
EXPECT_FALSE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kInsecure, 1);
}
// The feature should be enabled if 1) token is valid for third party origin
// 2) token is enabled for third party origin and 3) trial is enabled for
// third party origin.
TEST_F(OriginTrialContextTest, EnabledThirdPartyTrialWithThirdPartyToken) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateThirdPartyTrialName, base::Time(),
true);
bool is_origin_enabled = IsFeatureEnabledForThirdPartyOrigin(
kFrobulateEnabledOrigin, kFrobulateEnabledOrigin,
OriginTrialFeature::kOriginTrialsSampleAPIThirdParty);
EXPECT_TRUE(is_origin_enabled);
EXPECT_EQ(1, TokenValidator()->CallCount());
ExpectStatusUniqueMetric(OriginTrialTokenStatus::kSuccess, 1);
}
TEST_F(OriginTrialContextTest, ParseHeaderValue) {
std::unique_ptr<Vector<String>> tokens;
ASSERT_TRUE(tokens = OriginTrialContext::ParseHeaderValue(" foo\t "));
ASSERT_EQ(1u, tokens->size());
EXPECT_EQ("foo", (*tokens)[0]);
ASSERT_TRUE(tokens = OriginTrialContext::ParseHeaderValue(" \" bar \" "));
ASSERT_EQ(1u, tokens->size());
EXPECT_EQ(" bar ", (*tokens)[0]);
ASSERT_TRUE(tokens = OriginTrialContext::ParseHeaderValue(" foo, bar"));
ASSERT_EQ(2u, tokens->size());
EXPECT_EQ("foo", (*tokens)[0]);
EXPECT_EQ("bar", (*tokens)[1]);
ASSERT_TRUE(tokens =
OriginTrialContext::ParseHeaderValue(",foo, ,bar,,' ', ''"));
ASSERT_EQ(3u, tokens->size());
EXPECT_EQ("foo", (*tokens)[0]);
EXPECT_EQ("bar", (*tokens)[1]);
EXPECT_EQ(" ", (*tokens)[2]);
ASSERT_TRUE(tokens =
OriginTrialContext::ParseHeaderValue(" \"abc\" , 'def',g"));
ASSERT_EQ(3u, tokens->size());
EXPECT_EQ("abc", (*tokens)[0]);
EXPECT_EQ("def", (*tokens)[1]);
EXPECT_EQ("g", (*tokens)[2]);
ASSERT_TRUE(tokens = OriginTrialContext::ParseHeaderValue(
" \"a\\b\\\"c'd\", 'e\\f\\'g' "));
ASSERT_EQ(2u, tokens->size());
EXPECT_EQ("ab\"c'd", (*tokens)[0]);
EXPECT_EQ("ef'g", (*tokens)[1]);
ASSERT_TRUE(tokens =
OriginTrialContext::ParseHeaderValue("\"ab,c\" , 'd,e'"));
ASSERT_EQ(2u, tokens->size());
EXPECT_EQ("ab,c", (*tokens)[0]);
EXPECT_EQ("d,e", (*tokens)[1]);
ASSERT_TRUE(tokens = OriginTrialContext::ParseHeaderValue(" "));
EXPECT_EQ(0u, tokens->size());
ASSERT_TRUE(tokens = OriginTrialContext::ParseHeaderValue(""));
EXPECT_EQ(0u, tokens->size());
ASSERT_TRUE(tokens = OriginTrialContext::ParseHeaderValue(" ,, \"\" "));
EXPECT_EQ(0u, tokens->size());
}
TEST_F(OriginTrialContextTest, ParseHeaderValue_NotCommaSeparated) {
EXPECT_FALSE(OriginTrialContext::ParseHeaderValue("foo bar"));
EXPECT_FALSE(OriginTrialContext::ParseHeaderValue("\"foo\" 'bar'"));
EXPECT_FALSE(OriginTrialContext::ParseHeaderValue("foo 'bar'"));
EXPECT_FALSE(OriginTrialContext::ParseHeaderValue("\"foo\" bar"));
}
TEST_F(OriginTrialContextTest, FeaturePolicy) {
// Create a dummy window/document with an OriginTrialContext.
auto dummy = std::make_unique<DummyPageHolder>();
LocalDOMWindow* window = dummy->GetFrame().DomWindow();
OriginTrialContext* context = window->GetOriginTrialContext();
// Enable the sample origin trial API ("Frobulate").
context->AddFeature(OriginTrialFeature::kOriginTrialsSampleAPI);
EXPECT_TRUE(
context->IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPI));
// Make a mock feature name map with "frobulate".
FeatureNameMap feature_map;
feature_map.Set("frobulate", mojom::blink::FeaturePolicyFeature::kFrobulate);
// Attempt to parse the "frobulate" feature policy. This will only work if the
// feature policy is successfully enabled via the origin trial.
scoped_refptr<const SecurityOrigin> security_origin =
SecurityOrigin::CreateFromString(kFrobulateEnabledOrigin);
PolicyParserMessageBuffer logger;
ParsedFeaturePolicy result;
result = FeaturePolicyParser::ParseFeaturePolicyForTest(
"frobulate", security_origin, nullptr, logger, feature_map, window);
EXPECT_TRUE(logger.GetMessages().IsEmpty());
ASSERT_EQ(1u, result.size());
EXPECT_EQ(mojom::blink::FeaturePolicyFeature::kFrobulate, result[0].feature);
}
TEST_F(OriginTrialContextTest, GetEnabledNavigationFeatures) {
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateNavigationTrialName);
EXPECT_TRUE(
IsFeatureEnabled(kFrobulateEnabledOrigin,
OriginTrialFeature::kOriginTrialsSampleAPINavigation));
auto enabled_navigation_features = GetEnabledNavigationFeatures();
ASSERT_NE(nullptr, enabled_navigation_features.get());
EXPECT_EQ(WTF::Vector<OriginTrialFeature>(
{OriginTrialFeature::kOriginTrialsSampleAPINavigation}),
*enabled_navigation_features.get());
}
TEST_F(OriginTrialContextTest, ActivateNavigationFeature) {
EXPECT_TRUE(ActivateNavigationFeature(
OriginTrialFeature::kOriginTrialsSampleAPINavigation));
EXPECT_FALSE(
ActivateNavigationFeature(OriginTrialFeature::kOriginTrialsSampleAPI));
}
TEST_F(OriginTrialContextTest, GetTokenExpiryTimeIgnoresIrrelevantTokens) {
UpdateSecurityOrigin(kFrobulateEnabledOrigin);
base::Time nowish = base::Time::Now();
// A non-success response shouldn't affect Frobulate's expiry time.
TokenValidator()->SetResponse(OriginTrialTokenStatus::kMalformed,
kFrobulateTrialName,
nowish + base::TimeDelta::FromDays(2));
EXPECT_FALSE(IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPI));
EXPECT_EQ(base::Time(),
GetFeatureExpiry(OriginTrialFeature::kOriginTrialsSampleAPI));
// A different trial shouldn't affect Frobulate's expiry time.
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateDeprecationTrialName,
nowish + base::TimeDelta::FromDays(3));
EXPECT_TRUE(
IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPIDeprecation));
EXPECT_EQ(base::Time(),
GetFeatureExpiry(OriginTrialFeature::kOriginTrialsSampleAPI));
// A valid trial should update the expiry time.
base::Time expected_expiry = nowish + base::TimeDelta::FromDays(1);
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName, expected_expiry);
EXPECT_TRUE(IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPI));
EXPECT_EQ(expected_expiry,
GetFeatureExpiry(OriginTrialFeature::kOriginTrialsSampleAPI));
}
TEST_F(OriginTrialContextTest, LastExpiryForFeatureIsUsed) {
UpdateSecurityOrigin(kFrobulateEnabledOrigin);
base::Time plusone = base::Time::Now() + base::TimeDelta::FromDays(1);
base::Time plustwo = plusone + base::TimeDelta::FromDays(1);
base::Time plusthree = plustwo + base::TimeDelta::FromDays(1);
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName, plusone);
EXPECT_TRUE(IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPI));
EXPECT_EQ(plusone,
GetFeatureExpiry(OriginTrialFeature::kOriginTrialsSampleAPI));
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName, plusthree);
EXPECT_TRUE(IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPI));
EXPECT_EQ(plusthree,
GetFeatureExpiry(OriginTrialFeature::kOriginTrialsSampleAPI));
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName, plustwo);
EXPECT_TRUE(IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPI));
EXPECT_EQ(plusthree,
GetFeatureExpiry(OriginTrialFeature::kOriginTrialsSampleAPI));
}
TEST_F(OriginTrialContextTest, ImpliedFeatureExpiryTimesAreUpdated) {
UpdateSecurityOrigin(kFrobulateEnabledOrigin);
base::Time tomorrow = base::Time::Now() + base::TimeDelta::FromDays(1);
TokenValidator()->SetResponse(OriginTrialTokenStatus::kSuccess,
kFrobulateTrialName, tomorrow);
EXPECT_TRUE(IsFeatureEnabled(OriginTrialFeature::kOriginTrialsSampleAPI));
EXPECT_EQ(tomorrow, GetFeatureExpiry(
OriginTrialFeature::kOriginTrialsSampleAPIImplied));
}
} // namespace blink