Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 1 | # Integrating a feature with the origin trials framework |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 2 | |
| 3 | To expose your feature via the origin trials framework, there are a few code |
| 4 | changes required. |
| 5 | |
| 6 | [TOC] |
| 7 | |
| 8 | ## Code Changes |
| 9 | |
Jason Chase | dbd8b973 | 2020-11-26 17:18:43 | [diff] [blame] | 10 | NOTE: You can land these code changes before requesting to run an origin trial. |
| 11 | These code changes make it possible to control a feature via an origin trial, |
| 12 | but don't require an origin trial to be approved. For more on the process, see |
| 13 | [Running an Origin Trial]. |
| 14 | |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 15 | ### Runtime Enabled Features |
| 16 | |
Kent Tamura | b10f7eda | 2017-09-15 06:45:20 | [diff] [blame] | 17 | First, you’ll need to configure [runtime\_enabled\_features.json5]. This is |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 18 | explained in the file, but you use `origin_trial_feature_name` to associate your |
| 19 | runtime feature flag with a name for your origin trial. The name can be the |
| 20 | same as your runtime feature flag, or different. Eventually, this configured |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 21 | name will be used in the origin trials developer console. You can have both |
| 22 | `status: experimental` and `origin_trial_feature_name` if you want your feature |
| 23 | to be enabled either by using the `--enable-experimental-web-platform-features` |
| 24 | flag **or** the origin trial. |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 25 | |
Christian Biesinger | 0651e825 | 2022-01-25 17:18:08 | [diff] [blame] | 26 | You will have to change all callers of the no-argument overload of |
| 27 | `RuntimeEnabledFeatures::FooEnabled()` to the overload that takes a |
| 28 | `const FeatureContext*`. You can pass an `ExecutionContext` here |
| 29 | (e.g. using `ExecutionContext::From(ScriptState*)`). |
| 30 | |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 31 | You may have a feature that is not available on all platforms, or need to limit |
| 32 | the trial to specific platforms. Use `origin_trial_os: [list]` to specify which |
| 33 | platforms will allow the trial to be enabled. The list values are case- |
| 34 | insensitive, but must match one of the defined `OS_<platform>` macros (see |
| 35 | [build_config.h]). |
| 36 | |
| 37 | #### Examples |
| 38 | |
| 39 | Flag name and trial name are the same: |
| 40 | ``` |
| 41 | { |
| 42 | name: "MyFeature", |
| 43 | origin_trial_feature_name: "MyFeature", |
| 44 | status: "experimental", |
| 45 | }, |
| 46 | ``` |
| 47 | Flag name and trial name are different: |
| 48 | ``` |
| 49 | { |
| 50 | name: "MyFeature", |
| 51 | origin_trial_feature_name: "MyFeatureTrial", |
| 52 | status: "experimental", |
| 53 | }, |
| 54 | ``` |
| 55 | Trial limited to specific platform: |
| 56 | ``` json |
| 57 | { |
| 58 | name: "MyFeature", |
| 59 | origin_trial_feature_name: "MyFeature", |
| 60 | origin_trial_os: ["android"], |
| 61 | status: "experimental", |
| 62 | }, |
| 63 | ``` |
| 64 | |
Rodney Ding | 533e41a | 2020-01-15 20:17:18 | [diff] [blame] | 65 | ### CSS Properties |
| 66 | |
| 67 | You can also run experiment for new CSS properties with origin trial. After you |
| 68 | have configured your feature in [runtime\_enabled\_features.json5] as above, head |
| 69 | to [css\_properties.json5]. As explained in the file, you use `runtime_flag` to associate |
| 70 | the CSS property with the feature you just defined. This will automatically link the CSS |
| 71 | property to the origin trial defined in the runtime feature. It will be available |
| 72 | in both JavaScript (`Element.style`) and CSS (including `@supports`) when the trial |
| 73 | is enabled. |
| 74 | |
| 75 | |
| 76 | **Example:** [origin-trial-test-property] defines a test css property controlled via |
| 77 | runtime feature `OriginTrialsSampleAPI` and subsequently an origin trial named `Frobulate`. |
| 78 | |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 79 | ### Gating Access |
| 80 | |
| 81 | Once configured, there are two mechanisms to gate access to your feature behind |
| 82 | an origin trial. You can use either mechanism, or both, as appropriate to your |
| 83 | feature implementation. |
| 84 | |
| 85 | 1. A native C++ method that you can call in Blink code at runtime to expose your |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 86 | feature: `bool RuntimeEnabledFeatures::MyFeatureEnabled(ExecutionContext*)` |
| 87 | 2. An IDL attribute \[[RuntimeEnabled]\] that you can use to automatically |
| 88 | generate code to expose and hide JavaScript methods/attributes/objects. |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 89 | ``` |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 90 | [RuntimeEnabled=MyFeature] |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 91 | partial interface Navigator { |
| 92 | readonly attribute MyFeatureManager myFeature; |
| 93 | } |
| 94 | ``` |
| 95 | |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 96 | **NOTE:** Your feature implementation must not persist the result of the enabled |
| 97 | check. Your code should simply call |
| 98 | `RuntimeEnabledFeatures::MyFeatureEnabled(ExecutionContext*)` as often as |
| 99 | necessary to gate access to your feature. |
| 100 | |
Rodney Ding | 533e41a | 2020-01-15 20:17:18 | [diff] [blame] | 101 | **NOTE:** For CSS properties, you do not need to edit the IDL files, as the exposure |
| 102 | on the [CSSStyleDeclaration] is handled at runtime. |
| 103 | |
| 104 | **ISSUE:** In the rare cases where the origin trial token is added via script after |
| 105 | the css style declaration, the css property will be enabled and is fully functional, |
| 106 | however it will not appear on the [CSSStyleDeclaration] interface, i.e. not accessible |
| 107 | in `Element.style`. This issue is tracked in crbug/1041993. |
| 108 | |
Ruslan Burakov | 53cefa7 | 2019-05-09 08:22:25 | [diff] [blame] | 109 | ### Web Feature Counting |
| 110 | |
| 111 | Once the feature is created, in order to run the origin trial you need to track |
| 112 | how often users use your feature. You can do it in two ways. |
| 113 | |
| 114 | #### Increment counter in your c++ code. |
| 115 | |
| 116 | 1. Add your feature counter to end of [web\_feature.mojom]: |
| 117 | |
| 118 | ``` |
| 119 | enum WebFeature { |
| 120 | // ... |
| 121 | kLastFeatureBeforeYours = 1235, |
| 122 | // Here, increment the last feature count before yours by 1. |
| 123 | kMyFeature = 1236, |
| 124 | |
| 125 | kNumberOfFeatures, // This enum value must be last. |
| 126 | }; |
| 127 | ``` |
| 128 | 2. Run [update\_use\_counter\_feature\_enum.py] to update the UMA mapping. |
| 129 | |
| 130 | 3. Increment your feature counter in c++ code. |
| 131 | ```c++ |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 132 | #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" |
Ruslan Burakov | 53cefa7 | 2019-05-09 08:22:25 | [diff] [blame] | 133 | |
| 134 | // ... |
| 135 | |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 136 | if (RuntimeEnabledFeatures::MyFeatureEnabled(context)) { |
Ruslan Burakov | 53cefa7 | 2019-05-09 08:22:25 | [diff] [blame] | 137 | UseCounter::Count(context, WebFeature::kMyFeature); |
| 138 | } |
| 139 | ``` |
| 140 | |
| 141 | #### Update counter with \[Measure\] IDL attribute |
| 142 | |
| 143 | 1. Add \[[Measure]\] IDL attribute |
| 144 | ``` |
| 145 | partial interface Navigator { |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 146 | [RuntimeEnabled=MyFeature, Measure] |
Ruslan Burakov | 53cefa7 | 2019-05-09 08:22:25 | [diff] [blame] | 147 | readonly attribute MyFeatureManager myFeature; |
| 148 | ``` |
| 149 | |
| 150 | 2. The code to increment your feature counter will be generated in V8 |
| 151 | automatically. But it requires you to follow \[[Measure]\] IDL attribute |
| 152 | naming convention when you will add your feature counter to |
| 153 | [web\_feature.mojom]. |
| 154 | ``` |
| 155 | enum WebFeature { |
| 156 | // ... |
| 157 | kLastFeatureBeforeYours = 1235, |
| 158 | // Here, increment the last feature count before yours by 1. |
| 159 | kV8Navigator_MyFeature_AttributeGetter = 1236, |
| 160 | |
| 161 | kNumberOfFeatures, // This enum value must be last. |
| 162 | }; |
| 163 | ``` |
| 164 | |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 165 | ## Limitations |
| 166 | |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 167 | What you can't do, because of the nature of these origin trials, is know at |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 168 | either browser or renderer startup time whether your feature is going to be used |
| 169 | in the current page/context. This means that if you require lots of expensive |
| 170 | processing to begin (say you index the user's hard drive, or scan an entire city |
| 171 | for interesting weather patterns,) that you will have to either do it on browser |
| 172 | startup for *all* users, just in case it's used, or do it on first access. (If |
| 173 | you go with first access, then only people trying the experiment will notice the |
| 174 | delay, and hopefully only the first time they use it.). We are investigating |
| 175 | providing a method like `OriginTrials::myFeatureShouldInitialize()` that will |
| 176 | hint if you should do startup initialization. For example, this could include |
| 177 | checks for trials that have been revoked (or throttled) due to usage, if the |
| 178 | entire origin trials framework has been disabled, etc. The method would be |
| 179 | conservative and assume initialization is required, but it could avoid expensive |
| 180 | startup in some known scenarios. |
| 181 | |
| 182 | Similarly, if you need to know in the browser process whether a feature should |
| 183 | be enabled, then you will have to either have the renderer inform it at runtime, |
| 184 | or else just assume that it's always enabled, and gate access to the feature |
| 185 | from the renderer. |
| 186 | |
| 187 | ## Testing |
| 188 | |
Steve Kobes | 6d752cb | 2019-01-16 01:37:46 | [diff] [blame] | 189 | To test an origin trial feature during development, follow these steps: |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 190 | |
Steve Kobes | 6d752cb | 2019-01-16 01:37:46 | [diff] [blame] | 191 | 1. Use [generate_token.py] to generate a token signed with the test private key. |
| 192 | You can generate signed tokens for any origin that you need to help you test, |
| 193 | including localhost or 127.0.0.1. Example: |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 194 | |
Steve Kobes | 6d752cb | 2019-01-16 01:37:46 | [diff] [blame] | 195 | ``` |
| 196 | tools/origin_trials/generate_token.py http://localhost:8000 MyFeature |
| 197 | ``` |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 198 | |
Jason Chase | dbd8b973 | 2020-11-26 17:18:43 | [diff] [blame] | 199 | There are additional flags to generate third-party tokens, set the expiry |
| 200 | date, and control other options. See the command help for details (`--help`). |
| 201 | For example, to generate a third-party token, with [user subset exclusion]: |
| 202 | |
| 203 | ``` |
| 204 | tools/origin_trials/generate_token.py --is-third-party --usage-restriction=subset http://localhost:8000 MyFeature |
| 205 | ``` |
| 206 | |
Steve Kobes | 6d752cb | 2019-01-16 01:37:46 | [diff] [blame] | 207 | 2. Copy the token from the end of the output and use it in a `<meta>` tag or |
| 208 | an `Origin-Trial` header as described in the [Developer Guide]. |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 209 | |
Steve Kobes | 6d752cb | 2019-01-16 01:37:46 | [diff] [blame] | 210 | 3. Run Chrome with the test public key by passing: |
| 211 | `--origin-trial-public-key=dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=` |
| 212 | |
Andrii Sagaidak | dbc6a77 | 2019-12-12 03:43:39 | [diff] [blame] | 213 | You can also run Chrome with both the test public key and the default public key along side by passing: |
| 214 | `--origin-trial-public-key=dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=,fMS4mpO6buLQ/QMd+zJmxzty/VQ6B1EUZqoCU04zoRU=` |
| 215 | |
Steve Kobes | 6d752cb | 2019-01-16 01:37:46 | [diff] [blame] | 216 | The `--origin-trial-public-key` switch is not needed with `content_shell`, as it |
| 217 | uses the test public key by default. |
| 218 | |
| 219 | The test private key is stored in the repo at `tools/origin_trials/eftest.key`. |
| 220 | It's also used by Origin Trials unit tests and web tests. |
| 221 | |
| 222 | If you cannot set command-line switches (e.g., on Chrome OS), you can also |
| 223 | directly modify [chrome_origin_trial_policy.cc]. |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 224 | |
Mason Freed | 9ee18c76 | 2020-08-18 20:57:21 | [diff] [blame] | 225 | To see additional information about origin trial token parsing (including reasons |
| 226 | for failures, or token names for successful tokens), you can add these switches: |
| 227 | |
| 228 | `--vmodule=trial_token=2,origin_trial_context=1` |
| 229 | |
| 230 | If you are building with `is_debug=false`, then you will also need to add |
| 231 | `dcheck_always_on=true` to your build options, and add this to the command line: |
| 232 | |
| 233 | `--enable-logging=stderr` |
| 234 | |
| 235 | |
Kent Tamura | 59ffb02 | 2018-11-27 05:30:56 | [diff] [blame] | 236 | ### Web Tests |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 237 | When using the \[RuntimeEnabled\] IDL attribute, you should add web tests |
Ian Clelland | a94fcfb | 2017-07-20 03:43:48 | [diff] [blame] | 238 | to verify that the V8 bindings code is working as expected. Depending on how |
chasej | 0ac7dd2 | 2017-02-23 18:16:17 | [diff] [blame] | 239 | your feature is exposed, you'll want tests for the exposed interfaces, as well |
| 240 | as tests for script-added tokens. For examples, refer to the existing tests in |
| 241 | [origin_trials/webexposed]. |
| 242 | |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 243 | [build_config.h]: /build/build_config.h |
| 244 | [chrome_origin_trial_policy.cc]: /chrome/common/origin_trials/chrome_origin_trial_policy.cc |
chasej | c74a4c9c | 2017-02-10 20:38:09 | [diff] [blame] | 245 | [generate_token.py]: /tools/origin_trials/generate_token.py |
| 246 | [Developer Guide]: https://github.com/jpchase/OriginTrials/blob/gh-pages/developer-guide.md |
Allen Robinson | b0efcfa | 2019-06-25 13:24:04 | [diff] [blame] | 247 | [RuntimeEnabled]: /third_party/blink/renderer/bindings/IDLExtendedAttributes.md#RuntimeEnabled_i_m_a_c |
Kent Tamura | 59ffb02 | 2018-11-27 05:30:56 | [diff] [blame] | 248 | [origin_trials/webexposed]: /third_party/blink/web_tests/http/tests/origin_trials/webexposed/ |
Kent Tamura | 6943cf79 | 2018-04-09 05:24:54 | [diff] [blame] | 249 | [runtime\_enabled\_features.json5]: /third_party/blink/renderer/platform/runtime_enabled_features.json5 |
Lucas Furukawa Gadani | 6c24cdae | 2018-04-27 00:28:40 | [diff] [blame] | 250 | [trial_token_unittest.cc]: /third_party/blink/common/origin_trials/trial_token_unittest.cc |
Stephen McGruer | 2669ba3d | 2022-05-17 20:38:53 | [diff] [blame] | 251 | [web\_feature.mojom]: /third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom |
Ruslan Burakov | 53cefa7 | 2019-05-09 08:22:25 | [diff] [blame] | 252 | [update\_use\_counter\_feature\_enum.py]: /tools/metrics/histograms/update_use_counter_feature_enum.py |
| 253 | [Measure]: /third_party/blink/renderer/bindings/IDLExtendedAttributes.md#Measure_i_m_a_c |
Rodney Ding | 533e41a | 2020-01-15 20:17:18 | [diff] [blame] | 254 | [css\_properties.json5]: /third_party/blink/renderer/core/css/css_properties.json5 |
| 255 | [origin-trial-test-property]: https://chromium.googlesource.com/chromium/src/+/ff2ab8b89745602c8300322c2a0158e210178c7e/third_party/blink/renderer/core/css/css_properties.json5#2635 |
| 256 | [CSSStyleDeclaration]: /third_party/blink/renderer/core/css/css_style_declaration.idl |
Jason Chase | dbd8b973 | 2020-11-26 17:18:43 | [diff] [blame] | 257 | [Running an Origin Trial]: https://www.chromium.org/blink/origin-trials/running-an-origin-trial |
| 258 | [user subset exclusion]: https://docs.google.com/document/d/1xALH9W7rWmX0FpjudhDeS2TNTEOXuPn4Tlc9VmuPdHA/edit#heading=h.myaz1twlipw |