To expose your feature via theOrigin Trials framework, there are a few code changes required.
First, you’ll need to configureruntime_enabled_features.json5
. If you don‘t have a Blink’sRuntime Enabled Feature flag yet, you will need to add an entry in this file.
The following fields of an entry are relevant:
name
: The name of your runtime enabled feature, e.g."MyFeature"
.origin_trial_feature_name
: The name of your runtime enabled feature in the origin trial. This can be the same as your runtime feature flag (i.e.name
field), or different. Eventually, this configured name will be used in the origin trials developer console.origin_trial_os
: Specifies a[list]
of platforms where they will allow the trial to be enabled. The list values are case-insensitive, but must match one of the definedOS_<platform>
macros (seebuild_config.h
).origin_trial_allows_third_party
: Must be enabled to allow third-party tokens to work correctly. Set to true, if (and only if) you intend to support third-party matching.base_feature
: Generates abase::Feature
in theblink::features
namespace if the value is not"none"
. It helps to control the Origin Trial remotely. See alsoGenerate abase::Feature
instance from a Blink Feature.Not specific to Origin Trial:
status
: Controls when the runtime enabled feature is enabled in Blink. See alsothe Status table.base_feature_status
: Controls when thebase::Feature
defined bybase_feature
is enabled.More details are explained in the json5 file and in the above linked doc.
If the runtime enabled feature flag isused in C++, you will have to change all callers of the no-argument overload ofRuntimeEnabledFeatures::MyFeatureEnabled()
to the overload that takes aconst FeatureContext*
. You can pass anExecutionContext
here, e.g. usingExecutionContext::From(ScriptState*)
.
RuntimeEnabledFeature flag name, trial name andbase::Feature
are all the same:
{ name:"MyFeature",//Generates`RuntimeEnabledFeatures::MyFeatureEnabled()` origin_trial_feature_name:"MyFeature", status:"experimental",//No need to specify base_feature.},
RuntimeEnabledFeature flag name, trial name, andbase::Feature
name are different:
{ name:"MyFeature", origin_trial_feature_name:"MyFeatureTrial", base_feature:"MyBaseFeature",//Generates blink::features::kMyBaseFeature status:"experimental",},
Trial limited to specific platform:
{ name:"MyFeature", origin_trial_feature_name:"MyFeature", origin_trial_os:["android"], status:"experimental",},
Because WebView is built as part of the"android"
os target, it is not possible to exclude a trial from WebView if it is enabled on Android.
If the feature under trial can be enabled on WebView alongside other Android platforms, this is preferred.
In situations where this is not feasible, the recommended solution is to explicitly disable the origin trial inAwMainDelegate::BasicStartupComplete()
inaw_main_delegate.cc
by appending theembedder_support::kOriginTrialDisabledFeatures
switch with the disabled trial names as values.
Seehttps://crrev.com/c/3733267 for an example of how this can be done.
Once configured, there are two mechanisms to gate access to your feature behind an origin trial. You can use either mechanism, or both, as appropriate to your feature implementation.
A native C++ method that you can call in Blink code at runtime to expose your feature:
boolRuntimeEnabledFeatures::MyFeatureEnabled(ExecutionContext*)
RuntimeEnabledFeatures::MyFeatureEnabled(ExecutionContext*)
as often as necessary to gate access to your feature.An IDL attribute [RuntimeEnabled] that you can use to automatically generate code to expose and hide JavaScript methods/attributes/objects.
[RuntimeEnabled=MyFeature]partial interfaceNavigator{ readonly attributeMyFeatureManager myFeature;}
You can also run experiment for new CSS properties with origin trial. After you have configured your feature inruntime_enabled_features.json5
as above, head tocss_properties.json5
. As explained in the file, you useruntime_flag
to associate the CSS property with the feature you just defined. This will automatically link the CSS property to the origin trial defined in the runtime feature. It will be available in both JavaScript (Element.style
) and CSS (including@supports
) when the trial is enabled.
OriginTrialsSampleAPI
and subsequently an origin trial namedFrobulate
.Element.style
. This issue is tracked in crbug/1041993.base::Feature
(optional)Given the following example:
{ name:"MyFeature", origin_trial_feature_name:"MyFeature", base_feature:"MyFeature", status:"experimental",},
[RuntimeEnabled=MyFeature]interfaceMyFeatureAPI{ readonly attributebool dummy;}
// third_party/blink/.../my_feature_api.ccboolMyFeatureAPI::ConnectToBrowser(){if(base::FeatureList::IsEnabled(blink::features::kMyFeature){// Do something}returnfalse;}
The above example shows a new feature relies on abase::Feature
generated from thebase_feature
definition in json file, e.g.blink::features::kMyFeature
, in addition to the runtime enabled feature flagMyFeature
. However, their values are not associated.
In addition, due to thelimitation, the runtime enabled feature flag is not available in the browser processby default:
if you need to know in the browser process whether a feature should be enabled, then you will have to either have the renderer inform it at runtime, or else just assume that it's always enabled, and gate access to the feature from the renderer.
MyFeature
doesn't automatically turning onblink::features::kMyFeature
, and vice versa.To mitigate the issue, there are several options:
base::Feature
, e.g.kMyFeature
And letting Origin Trial decide when your feature (via runtime enabled feature flagblink::features::MyFeature
) is available, as suggested in the above quote. Thebase::Feature
can be enabled via a remote Finch config, or by updating the default value in C++.
However, after the Origin Trial ends, it will be impossible to ramp up the feature by Finch if the part controlled byMyFeature
cannot be enabled independently. For example, if you have a new Web APIMyFeatureAPI
, enablingMyFeature
will just make the IDL available to everyone without the Blink/browser implementation.
MakeMyFeature
depend onblink::features::kMyFeature
so that the feature is not enabled iffeatures::kMyFeatures
is not enabled. Inthird_party/blink/renderer/core/origin_trials/origin_trial_context.cc:
boolOriginTrialContext::CanEnableTrialFromName(constStringView& trial_name){...if(trial_name=="MyFeature"){return base::FeatureList::IsEnabled(blink::features::kMyFeatures);}}
Add custom relationship forMyFeature
andblink::features::kMyFeature
to handle your use case.
ReadDetermine how your feature is initialized: Depends on the status of a base::Feature first. If the mappings described there don't meet your use case, refer to the following examples.
Incontent/child/runtime_features.cc:
voidSetCustomizedRuntimeFeaturesFromCombinedArgs(const base::CommandLine& command_line){// Example 1: https://bit.ly/configuring-trust-tokens// Example 2: https://crrev.com/c/3878922/14/content/child/runtime_features.cc}
Once the feature is created, in order to run the origin trial you need to track how often users use your feature. You can do it in two ways.
Add your feature counter to the end ofwebdx_feature.mojom
(orweb_feature.mojom
if it‘s a feature that’s somehow not expected to be described in theweb platform dx repository)"
enumWebDXFeature{// ... kLastFeatureBeforeYours=1235,// Here, increment the last feature count before yours by 1. kMyFeature=1236, kNumberOfFeatures,// This enum value must be last.};
Runupdate_use_counter_feature_enum.py
to update the UMA mappings.
Increment your feature counter in C++ code.
#include"third_party/blink/renderer/platform/instrumentation/use_counter.h"// ...if(RuntimeEnabledFeatures::MyFeatureEnabled(context)){UseCounter::Count(context,WebFeature::kMyFeature);}
Add [[MeasureAs=“WebDXFeature::kMyFeature”]] IDL attribute
partial interfaceNavigator{[RuntimeEnabled=MyFeature,MeasureAs="WebDXFeature::kMyFeature"] readonly attributeMyFeatureManager myFeature;
Alternatively, if your feature counter doesn't fit as a WebDXFeature use counter, make it a WebFeature instead and drop the WebDXFeature:: prefix (and quotes) in the [[MeasureAs]] attribute above, or use [Measure] instead and follow the [Measure] IDL attribute naming convention.
Add your use counter towebdx_feature.mojom
(or alternatively toweb_feature.mojom
). The code to increment your feature counter will be generated in the V8 bindings code automatically.
enumWebDXFeature{// ... kLastFeatureBeforeYours=1235,// Here, increment the last feature count before yours by 1. kMyFeature=1236, kNumberOfFeatures,// This enum value must be last.};
When using the [RuntimeEnabled] IDL attribute, you should add web tests to verify that the V8 bindings code is working as expected. Depending on how your feature is exposed, you'll want tests for the exposed interfaces, as well as tests for script-added tokens. For examples, refer to the existing tests inorigin_trials/webexposed.
What you can‘t do, because of the nature of these origin trials, is know at either browser or renderer startup time whether your feature is going to be used in the current page/context. This means that if you require lots of expensive processing to begin (say you index the user’s hard drive, or scan an entire city for interesting weather patterns,) that you will have to either do it on browser startup forall users, just in case it's used, or do it on first access. (If you go with first access, then only people trying the experiment will notice the delay, and hopefully only the first time they use it.). We are investigating providing a method likeOriginTrials::myFeatureShouldInitialize()
that will hint if you should do startup initialization. For example, this could include checks for trials that have been revoked (or throttled) due to usage, if the entire origin trials framework has been disabled, etc. The method would be conservative and assume initialization is required, but it could avoid expensive startup in some known scenarios.
Similarly, if you need to know in the browser process whether a feature should be enabled, then you will have to either have the renderer inform it at runtime, or else just assume that it's always enabled, and gate access to the feature from the renderer.
To test an origin trial feature during development, follow these steps:
Usegenerate_token.py
to generate a token signed with the test private key. You can generate signed tokens for any origin that you need to help you test, including localhost or 127.0.0.1. Example:
tools/origin_trials/generate_token.py http://localhost:8000MyFeature
There are additional flags to generate third-party tokens, set the expiry date, and control other options. See the command help for details (--help
). For example, to generate a third-party token, withuser subset exclusion:
tools/origin_trials/generate_token.py--is-third-party--usage-restriction=subset http://localhost:8000MyFeature
Copy the token from the end of the output and use it in a<meta>
tag or anOrigin-Trial
header as described in theDeveloper Guide.
Run Chrome with the test public key by passing:--origin-trial-public-key=dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=
You can also run Chrome with both the test public key and the default public key along side by passing:--origin-trial-public-key=dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=,fMS4mpO6buLQ/QMd+zJmxzty/VQ6B1EUZqoCU04zoRU=
The--origin-trial-public-key
switch is not needed withcontent_shell
, as it uses the test public key by default.
The test private key is stored in the repo attools/origin_trials/eftest.key
. It's also used by Origin Trials unit tests and web tests.
If you cannot set command-line switches (e.g., on Chrome OS), you can also directly modifychrome_origin_trial_policy.cc
.
To see additional information about origin trial token parsing (including reasons for failures, or token names for successful tokens), you can add these switches:
--vmodule=trial_token=2,origin_trial_context=1
If you are building withis_debug=false
, then you will also need to adddcheck_always_on=true
to your build options, and add this to the command line:
--enable-logging=stderr