blob: 6eab3c6790ffd1512b386055eaff9bade7b356e4 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// 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 <ostream>
#include <vector>
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/origin_trials/origin_trials.h"
#include "third_party/blink/public/common/origin_trials/trial_token.h"
#include "third_party/blink/public/common/origin_trials/trial_token_result.h"
#include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
#include "third_party/blink/renderer/platform/bindings/origin_trial_features.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/runtime_feature_state/runtime_feature_state_override_context.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "v8/include/v8.h"
namespace blink {
namespace {
constexpr char kDefaultTrialName[] = "UNKNOWN";
void RecordTokenValidationResultHistogram(OriginTrialTokenStatus status) {
UMA_HISTOGRAM_ENUMERATION("OriginTrials.ValidationResult", status);
}
bool IsWhitespace(UChar chr) {
return (chr == ' ') || (chr == '\t');
}
bool SkipWhiteSpace(const String& str, unsigned& pos) {
unsigned len = str.length();
while (pos < len && IsWhitespace(str[pos]))
++pos;
return pos < len;
}
// Extracts a quoted or unquoted token from an HTTP header. If the token was a
// quoted string, this also removes the quotes and unescapes any escaped
// characters. Also skips all whitespace before and after the token.
String ExtractTokenOrQuotedString(const String& header_value, unsigned& pos) {
unsigned len = header_value.length();
String result;
if (!SkipWhiteSpace(header_value, pos))
return String();
if (header_value[pos] == '\'' || header_value[pos] == '"') {
StringBuilder out;
// Quoted string, append characters until matching quote is found,
// unescaping as we go.
UChar quote = header_value[pos++];
while (pos < len && header_value[pos] != quote) {
if (header_value[pos] == '\\')
pos++;
if (pos < len)
out.Append(header_value[pos++]);
}
if (pos < len)
pos++;
result = out.ToString();
} else {
// Unquoted token. Consume all characters until whitespace or comma.
int start_pos = pos;
while (pos < len && !IsWhitespace(header_value[pos]) &&
header_value[pos] != ',')
pos++;
result = header_value.Substring(start_pos, pos - start_pos);
}
SkipWhiteSpace(header_value, pos);
return result;
}
// Returns whether the given feature can be activated across navigations. Only
// features reviewed and approved by security reviewers can be activated across
// navigations.
bool IsCrossNavigationFeature(OriginTrialFeature feature) {
return origin_trials::FeatureEnabledForNavigation(feature);
}
std::ostream& operator<<(std::ostream& stream, OriginTrialTokenStatus status) {
// Included for debug builds only for reduced binary size.
#ifndef NDEBUG
switch (status) {
case OriginTrialTokenStatus::kSuccess:
return stream << "kSuccess";
case OriginTrialTokenStatus::kNotSupported:
return stream << "kNotSupported";
case OriginTrialTokenStatus::kInsecure:
return stream << "kInsecure";
case OriginTrialTokenStatus::kExpired:
return stream << "kExpired";
case OriginTrialTokenStatus::kWrongOrigin:
return stream << "kWrongOrigin";
case OriginTrialTokenStatus::kInvalidSignature:
return stream << "kInvalidSignature";
case OriginTrialTokenStatus::kMalformed:
return stream << "kMalformed";
case OriginTrialTokenStatus::kWrongVersion:
return stream << "kWrongVersion";
case OriginTrialTokenStatus::kFeatureDisabled:
return stream << "kFeatureDisabled";
case OriginTrialTokenStatus::kTokenDisabled:
return stream << "kTokenDisabled";
case OriginTrialTokenStatus::kFeatureDisabledForUser:
return stream << "kFeatureDisabledForUser";
case OriginTrialTokenStatus::kUnknownTrial:
return stream << "kUnknownTrial";
}
NOTREACHED();
return stream;
#else
return stream << (static_cast<int>(status));
#endif // ifndef NDEBUG
}
// Merges `OriginTrialStatus` from different tokens for the same trial.
// Some combinations of status should never occur, such as
// s1 == kOSNotSupported && s2 == kEnabled.
OriginTrialStatus MergeOriginTrialStatus(OriginTrialStatus s1,
OriginTrialStatus s2) {
using Status = OriginTrialStatus;
if (s1 == Status::kEnabled || s2 == Status::kEnabled) {
return Status::kEnabled;
}
// kOSNotSupported status comes from OS support checks that are generated
// at compile time.
if (s1 == Status::kOSNotSupported || s2 == Status::kOSNotSupported) {
return Status::kOSNotSupported;
}
// kTrialNotAllowed status comes from `CanEnableTrialFromName` check.
if (s1 == Status::kTrialNotAllowed || s2 == Status::kTrialNotAllowed) {
return Status::kTrialNotAllowed;
}
return Status::kValidTokenNotProvided;
}
} // namespace
// TODO(crbug.com/607555): Mark `TrialToken` as copyable.
OriginTrialTokenResult::OriginTrialTokenResult(
const String& raw_token,
OriginTrialTokenStatus status,
const absl::optional<TrialToken>& parsed_token)
: raw_token(raw_token), status(status), parsed_token(parsed_token) {}
OriginTrialContext::OriginTrialContext(ExecutionContext* context)
: trial_token_validator_(std::make_unique<TrialTokenValidator>()),
context_(context) {}
void OriginTrialContext::SetTrialTokenValidatorForTesting(
std::unique_ptr<TrialTokenValidator> validator) {
trial_token_validator_ = std::move(validator);
}
// static
std::unique_ptr<Vector<String>> OriginTrialContext::ParseHeaderValue(
const String& header_value) {
std::unique_ptr<Vector<String>> tokens(new Vector<String>);
unsigned pos = 0;
unsigned len = header_value.length();
while (pos < len) {
String token = ExtractTokenOrQuotedString(header_value, pos);
if (!token.empty())
tokens->push_back(token);
// Make sure tokens are comma-separated.
if (pos < len && header_value[pos++] != ',')
return nullptr;
}
return tokens;
}
// static
void OriginTrialContext::AddTokensFromHeader(ExecutionContext* context,
const String& header_value) {
if (header_value.empty())
return;
std::unique_ptr<Vector<String>> tokens(ParseHeaderValue(header_value));
if (!tokens)
return;
AddTokens(context, tokens.get());
}
// static
void OriginTrialContext::AddTokens(ExecutionContext* context,
const Vector<String>* tokens) {
if (!tokens || tokens->empty())
return;
DCHECK(context && context->GetOriginTrialContext());
context->GetOriginTrialContext()->AddTokens(*tokens);
}
// static
void OriginTrialContext::ActivateWorkerInheritedFeatures(
ExecutionContext* context,
const Vector<OriginTrialFeature>* features) {
if (!features || features->empty())
return;
DCHECK(context && context->GetOriginTrialContext());
DCHECK(context->IsDedicatedWorkerGlobalScope() ||
context->IsWorkletGlobalScope());
context->GetOriginTrialContext()->ActivateWorkerInheritedFeatures(*features);
}
// static
void OriginTrialContext::ActivateNavigationFeaturesFromInitiator(
ExecutionContext* context,
const Vector<OriginTrialFeature>* features) {
if (!features || features->empty())
return;
DCHECK(context && context->GetOriginTrialContext());
context->GetOriginTrialContext()->ActivateNavigationFeaturesFromInitiator(
*features);
}
// static
std::unique_ptr<Vector<String>> OriginTrialContext::GetTokens(
ExecutionContext* execution_context) {
DCHECK(execution_context);
const OriginTrialContext* context =
execution_context->GetOriginTrialContext();
if (!context || context->trial_results_.empty())
return nullptr;
auto tokens = std::make_unique<Vector<String>>();
for (const auto& entry : context->trial_results_) {
const OriginTrialResult& trial_result = entry.value;
for (const OriginTrialTokenResult& token_result :
trial_result.token_results) {
tokens->push_back(token_result.raw_token);
}
}
return tokens;
}
// static
std::unique_ptr<Vector<OriginTrialFeature>>
OriginTrialContext::GetInheritedTrialFeatures(
ExecutionContext* execution_context) {
DCHECK(execution_context);
const OriginTrialContext* context =
execution_context->GetOriginTrialContext();
return context ? context->GetInheritedTrialFeatures() : nullptr;
}
// static
std::unique_ptr<Vector<OriginTrialFeature>>
OriginTrialContext::GetEnabledNavigationFeatures(
ExecutionContext* execution_context) {
DCHECK(execution_context);
const OriginTrialContext* context =
execution_context->GetOriginTrialContext();
return context ? context->GetEnabledNavigationFeatures() : nullptr;
}
std::unique_ptr<Vector<OriginTrialFeature>>
OriginTrialContext::GetInheritedTrialFeatures() const {
if (enabled_features_.empty()) {
return nullptr;
}
std::unique_ptr<Vector<OriginTrialFeature>> result =
std::make_unique<Vector<OriginTrialFeature>>();
// TODO(crbug.com/1083407): Handle features from
// |navigation_activated_features_| and |feature_expiry_times_| expiry.
for (const OriginTrialFeature& feature : enabled_features_) {
result->push_back(feature);
}
return result;
}
std::unique_ptr<Vector<OriginTrialFeature>>
OriginTrialContext::GetEnabledNavigationFeatures() const {
if (enabled_features_.empty())
return nullptr;
std::unique_ptr<Vector<OriginTrialFeature>> result =
std::make_unique<Vector<OriginTrialFeature>>();
for (const OriginTrialFeature& feature : enabled_features_) {
if (IsCrossNavigationFeature(feature)) {
result->push_back(feature);
}
}
return result->empty() ? nullptr : std::move(result);
}
void OriginTrialContext::AddToken(const String& token) {
AddTokenInternal(token, GetCurrentOriginInfo(), nullptr);
}
void OriginTrialContext::AddTokenFromExternalScript(
const String& token,
const Vector<scoped_refptr<SecurityOrigin>>& external_origins) {
Vector<OriginInfo> script_origins;
for (const scoped_refptr<SecurityOrigin>& origin : external_origins) {
OriginInfo origin_info = {.origin = origin,
.is_secure = origin->IsPotentiallyTrustworthy()};
DVLOG(1) << "AddTokenFromExternalScript: " << origin->ToString()
<< ", secure = " << origin_info.is_secure;
script_origins.push_back(origin_info);
}
AddTokenInternal(token, GetCurrentOriginInfo(), &script_origins);
}
void OriginTrialContext::AddTokenInternal(
const String& token,
const OriginInfo origin,
const Vector<OriginInfo>* script_origins) {
if (token.empty())
return;
bool enabled = EnableTrialFromToken(token, origin, script_origins);
if (enabled) {
// Only install pending features if the provided token is valid.
// Otherwise, there was no change to the list of enabled features.
InitializePendingFeatures();
}
}
void OriginTrialContext::AddTokens(const Vector<String>& tokens) {
if (tokens.empty())
return;
bool found_valid = false;
OriginInfo origin_info = GetCurrentOriginInfo();
for (const String& token : tokens) {
if (!token.empty()) {
if (EnableTrialFromToken(token, origin_info))
found_valid = true;
}
}
if (found_valid) {
// Only install pending features if at least one of the provided tokens are
// valid. Otherwise, there was no change to the list of enabled features.
InitializePendingFeatures();
}
}
void OriginTrialContext::ActivateWorkerInheritedFeatures(
const Vector<OriginTrialFeature>& features) {
for (const OriginTrialFeature& feature : features) {
enabled_features_.insert(feature);
}
InitializePendingFeatures();
}
void OriginTrialContext::ActivateNavigationFeaturesFromInitiator(
const Vector<OriginTrialFeature>& features) {
for (const OriginTrialFeature& feature : features) {
if (IsCrossNavigationFeature(feature)) {
navigation_activated_features_.insert(feature);
}
}
InitializePendingFeatures();
}
void OriginTrialContext::InitializePendingFeatures() {
if (!enabled_features_.size() && !navigation_activated_features_.size())
return;
auto* window = DynamicTo<LocalDOMWindow>(context_.Get());
// Normally, LocalDOMWindow::document() doesn't need to be null-checked.
// However, this is a rare function that can get called between when the
// LocalDOMWindow is constructed and the Document is installed. We are not
// ready for script in that case, so bail out.
if (!window || !window->document())
return;
ScriptState* script_state = ToScriptStateForMainWorld(window->GetFrame());
if (!script_state)
return;
if (!script_state->ContextIsValid())
return;
ScriptState::Scope scope(script_state);
bool added_binding_feature =
InstallFeatures(enabled_features_, *window->document(), script_state);
added_binding_feature |= InstallFeatures(navigation_activated_features_,
*window->document(), script_state);
if (added_binding_feature) {
// Also allow V8 to install conditional features now.
script_state->GetIsolate()->InstallConditionalFeatures(
script_state->GetContext());
}
}
bool OriginTrialContext::InstallFeatures(
const HashSet<OriginTrialFeature>& features,
Document& document,
ScriptState* script_state) {
bool added_binding_features = false;
for (OriginTrialFeature enabled_feature : features) {
if (installed_features_.Contains(enabled_feature))
continue;
installed_features_.insert(enabled_feature);
if (InstallSettingFeature(document, enabled_feature))
continue;
InstallPropertiesPerFeature(script_state, enabled_feature);
added_binding_features = true;
// TODO(https://crbug.com/1410817): add support for workers/non-frames that
// are enabling origin trials to send their information to the browser too.
if (context_->IsWindow() && feature_to_tokens_.Contains(enabled_feature)) {
context_->GetRuntimeFeatureStateOverrideContext()
->ApplyOriginTrialOverride(
enabled_feature, feature_to_tokens_.find(enabled_feature)->value);
}
}
return added_binding_features;
}
bool OriginTrialContext::InstallSettingFeature(
Document& document,
OriginTrialFeature enabled_feature) {
switch (enabled_feature) {
case OriginTrialFeature::kAutoDarkMode:
if (document.GetSettings())
document.GetSettings()->SetForceDarkModeEnabled(true);
return true;
default:
return false;
}
}
void OriginTrialContext::AddFeature(OriginTrialFeature feature) {
enabled_features_.insert(feature);
InitializePendingFeatures();
}
bool OriginTrialContext::IsFeatureEnabled(OriginTrialFeature feature) const {
return enabled_features_.Contains(feature) ||
navigation_activated_features_.Contains(feature);
}
base::Time OriginTrialContext::GetFeatureExpiry(OriginTrialFeature feature) {
if (!IsFeatureEnabled(feature))
return base::Time();
auto it = feature_expiry_times_.find(feature);
if (it == feature_expiry_times_.end())
return base::Time();
return it->value;
}
bool OriginTrialContext::IsNavigationFeatureActivated(
OriginTrialFeature feature) const {
return navigation_activated_features_.Contains(feature);
}
void OriginTrialContext::AddForceEnabledTrials(
const Vector<String>& trial_names) {
bool is_valid = false;
for (const auto& trial_name : trial_names) {
DCHECK(origin_trials::IsTrialValid(trial_name.Utf8()));
is_valid |=
EnableTrialFromName(trial_name, /*expiry_time=*/base::Time::Max())
.status == OriginTrialStatus::kEnabled;
}
if (is_valid) {
// Only install pending features if at least one trial is valid. Otherwise
// there was no change to the list of enabled features.
InitializePendingFeatures();
}
}
bool OriginTrialContext::CanEnableTrialFromName(const StringView& trial_name) {
if (trial_name == "Portals")
return base::FeatureList::IsEnabled(features::kPortals);
if (trial_name == "PrivacySandboxAdsAPIs")
return base::FeatureList::IsEnabled(features::kPrivacySandboxAdsAPIs);
if (trial_name == "FencedFrames")
return base::FeatureList::IsEnabled(features::kFencedFrames);
if (trial_name == "AdInterestGroupAPI")
return base::FeatureList::IsEnabled(features::kInterestGroupStorage);
if (trial_name == "TrustTokens")
return base::FeatureList::IsEnabled(network::features::kPrivateStateTokens);
if (trial_name == "SpeculationRulesPrefetch") {
return base::FeatureList::IsEnabled(
features::kSpeculationRulesPrefetchProxy);
}
if (trial_name == "SpeculationRulesPrefetchFuture") {
return base::FeatureList::IsEnabled(
features::kSpeculationRulesPrefetchFuture);
}
if (trial_name == "PendingBeaconAPI") {
return base::FeatureList::IsEnabled(features::kPendingBeaconAPI);
}
if (trial_name == "BackForwardCacheSendNotRestoredReasons") {
return base::FeatureList::IsEnabled(
features::kBackForwardCacheSendNotRestoredReasons);
}
return true;
}
Vector<OriginTrialFeature> OriginTrialContext::RestrictedFeaturesForTrial(
const String& trial_name) {
if (trial_name == "PrivacySandboxAdsAPIs") {
Vector<OriginTrialFeature> restricted;
if (!base::FeatureList::IsEnabled(features::kInterestGroupStorage))
restricted.push_back(OriginTrialFeature::kFledge);
if (!base::FeatureList::IsEnabled(features::kBrowsingTopics))
restricted.push_back(OriginTrialFeature::kTopicsAPI);
if (!base::FeatureList::IsEnabled(features::kBrowsingTopics) ||
!base::FeatureList::IsEnabled(features::kBrowsingTopicsXHR)) {
restricted.push_back(OriginTrialFeature::kTopicsXHR);
}
if (!base::FeatureList::IsEnabled(features::kConversionMeasurement))
restricted.push_back(OriginTrialFeature::kAttributionReporting);
if (!base::FeatureList::IsEnabled(features::kFencedFrames))
restricted.push_back(OriginTrialFeature::kFencedFrames);
if (!base::FeatureList::IsEnabled(features::kSharedStorageAPI))
restricted.push_back(OriginTrialFeature::kSharedStorageAPI);
return restricted;
}
return {};
}
OriginTrialFeaturesEnabled OriginTrialContext::EnableTrialFromName(
const String& trial_name,
base::Time expiry_time) {
Vector<OriginTrialFeature> origin_trial_features =
Vector<OriginTrialFeature>();
if (!CanEnableTrialFromName(trial_name)) {
DVLOG(1) << "EnableTrialFromName: cannot enable trial " << trial_name;
OriginTrialFeaturesEnabled result = {OriginTrialStatus::kTrialNotAllowed,
origin_trial_features};
return result;
}
Vector<OriginTrialFeature> restricted =
RestrictedFeaturesForTrial(trial_name);
bool did_enable_feature = false;
for (OriginTrialFeature feature :
origin_trials::FeaturesForTrial(trial_name.Utf8())) {
if (!origin_trials::FeatureEnabledForOS(feature)) {
DVLOG(1) << "EnableTrialFromName: feature " << static_cast<int>(feature)
<< " is disabled on current OS.";
continue;
}
if (restricted.Contains(feature)) {
DVLOG(1) << "EnableTrialFromName: feature " << static_cast<int>(feature)
<< " is restricted from being enabled via the trial: "
<< trial_name << ".";
continue;
}
did_enable_feature = true;
enabled_features_.insert(feature);
origin_trial_features.push_back(feature);
// Use the latest expiry time for the feature.
if (GetFeatureExpiry(feature) < expiry_time)
feature_expiry_times_.Set(feature, expiry_time);
// Also enable any features implied by this feature.
for (OriginTrialFeature implied_feature :
origin_trials::GetImpliedFeatures(feature)) {
enabled_features_.insert(implied_feature);
origin_trial_features.push_back(implied_feature);
// Use the latest expiry time for the implied feature.
if (GetFeatureExpiry(implied_feature) < expiry_time)
feature_expiry_times_.Set(implied_feature, expiry_time);
}
}
OriginTrialFeaturesEnabled result = {
(did_enable_feature ? OriginTrialStatus::kEnabled
: OriginTrialStatus::kOSNotSupported),
origin_trial_features};
return result;
}
bool OriginTrialContext::EnableTrialFromToken(const String& token,
const OriginInfo origin_info) {
return EnableTrialFromToken(token, origin_info, nullptr);
}
bool OriginTrialContext::EnableTrialFromToken(
const String& token,
const OriginInfo origin_info,
const Vector<OriginInfo>* script_origins) {
DCHECK(!token.empty());
OriginTrialStatus trial_status = OriginTrialStatus::kValidTokenNotProvided;
StringUTF8Adaptor token_string(token);
// TODO(https://crbug.com/1153336): Remove explicit validator.
// Since |blink::SecurityOrigin::IsPotentiallyTrustworthy| is the source of
// security information in this context, use that explicitly, instead of
// relying on the default in |TrialTokenValidator|
Vector<TrialTokenValidator::OriginInfo> script_url_origins;
if (script_origins) {
for (const OriginInfo& script_origin_info : *script_origins) {
script_url_origins.emplace_back(script_origin_info.origin->ToUrlOrigin(),
script_origin_info.is_secure);
}
}
TrialTokenResult token_result =
trial_token_validator_->ValidateTokenAndTrialWithOriginInfo(
token_string.AsStringPiece(),
TrialTokenValidator::OriginInfo(origin_info.origin->ToUrlOrigin(),
origin_info.is_secure),
script_url_origins, base::Time::Now());
DVLOG(1) << "EnableTrialFromToken: token_result = " << token_result.Status()
<< ", token = " << token;
if (token_result.Status() == OriginTrialTokenStatus::kSuccess) {
String trial_name =
String::FromUTF8(token_result.ParsedToken()->feature_name().data(),
token_result.ParsedToken()->feature_name().size());
OriginTrialFeaturesEnabled result = EnableTrialFromName(
trial_name, token_result.ParsedToken()->expiry_time());
trial_status = result.status;
// Go through the features and map them to the token that enabled them.
for (OriginTrialFeature const& feature : result.features) {
auto feature_iter = feature_to_tokens_.find(feature);
// A feature may have 0 to many tokens associated with it.
if (feature_iter == feature_to_tokens_.end()) {
auto token_vector = Vector<String>();
token_vector.push_back(token);
feature_to_tokens_.insert(feature, token_vector);
} else {
auto mapped_tokens = feature_to_tokens_.at(feature);
mapped_tokens.push_back(token);
feature_to_tokens_.Set(feature, mapped_tokens);
}
}
}
RecordTokenValidationResultHistogram(token_result.Status());
CacheToken(token, token_result, trial_status);
return trial_status == OriginTrialStatus::kEnabled;
}
void OriginTrialContext::CacheToken(const String& raw_token,
const TrialTokenResult& token_result,
OriginTrialStatus trial_status) {
String trial_name =
token_result.ParsedToken() &&
token_result.Status() != OriginTrialTokenStatus::kUnknownTrial
? String::FromUTF8(token_result.ParsedToken()->feature_name().data(),
token_result.ParsedToken()->feature_name().size())
: kDefaultTrialName;
// Does nothing if key already exists.
auto& trial_result =
trial_results_
.insert(trial_name,
OriginTrialResult{
trial_name,
OriginTrialStatus::kValidTokenNotProvided,
/* token_results */ {},
})
.stored_value->value;
trial_result.status =
MergeOriginTrialStatus(trial_result.status, trial_status);
trial_result.token_results.push_back(OriginTrialTokenResult{
raw_token, token_result.Status(),
token_result.ParsedToken()
? absl::make_optional(*token_result.ParsedToken())
: absl::nullopt});
}
void OriginTrialContext::Trace(Visitor* visitor) const {
visitor->Trace(context_);
}
const SecurityOrigin* OriginTrialContext::GetSecurityOrigin() {
const SecurityOrigin* origin;
CHECK(context_);
// Determines the origin to be validated against tokens:
// - For the purpose of origin trials, we consider worklets as running in the
// same context as the originating document. Thus, the special logic here
// to use the origin from the document context.
if (auto* scope = DynamicTo<WorkletGlobalScope>(context_.Get()))
origin = scope->DocumentSecurityOrigin();
else
origin = context_->GetSecurityOrigin();
return origin;
}
bool OriginTrialContext::IsSecureContext() {
bool is_secure = false;
CHECK(context_);
// Determines if this is a secure context:
// - For worklets, they are currently spec'd to not be secure, given their
// scope has unique origin:
// https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
// - For the purpose of origin trials, we consider worklets as running in the
// same context as the originating document. Thus, the special logic here
// to check the secure status of the document context.
if (auto* scope = DynamicTo<WorkletGlobalScope>(context_.Get())) {
is_secure = scope->DocumentSecureContext();
} else {
is_secure = context_->IsSecureContext();
}
return is_secure;
}
OriginTrialContext::OriginInfo OriginTrialContext::GetCurrentOriginInfo() {
return {.origin = GetSecurityOrigin(), .is_secure = IsSecureContext()};
}
} // namespace blink