+ You can manually opt in to a previously released RTV using this form.
+
+
+ Warning: This is an advanced setting. It is intended for use while debugging AMP runtime code, and is
+ not meant to be used to publish AMP documents. To opt out of this setting, clear the RTV in the input field.
+
+
+
+
+
+
diff --git a/tools/experiments/experiments.js b/tools/experiments/experiments.js
index c4b0404a2f1d..f398b635c73a 100644
--- a/tools/experiments/experiments.js
+++ b/tools/experiments/experiments.js
@@ -34,6 +34,10 @@ setReportError(reportError);
const COOKIE_MAX_AGE_DAYS = 180; // 6 month
const MS_PER_DAY = 24 * 60 * 60 * 1000;
const COOKIE_MAX_AGE_MS = COOKIE_MAX_AGE_DAYS * MS_PER_DAY;
+const RTV_COOKIE_MAX_AGE_MS = MS_PER_DAY / 2;
+
+const RTV_PATTERN = /^\d{15}$/;
+
/**
* @typedef {{
* id: string,
@@ -49,8 +53,9 @@ let ExperimentDef;
* interpreted by the server to deliver a different version of the AMP
* JS libraries.
*/
-const CANARY_EXPERIMENT_ID = 'dev-channel';
-const RC_EXPERIMENT_ID = 'rc-channel';
+const EXPERIMENTAL_CHANNEL_ID = 'experimental-channel';
+const BETA_CHANNEL_ID = 'beta-channel';
+const RTV_CHANNEL_ID = 'rtv-channel';
/**
* The different states of the AMP_CANARY cookie.
@@ -65,7 +70,7 @@ const AMP_CANARY_COOKIE = {
const CHANNELS = [
// Experimental Channel
{
- id: CANARY_EXPERIMENT_ID,
+ id: EXPERIMENTAL_CHANNEL_ID,
name: 'AMP Experimental Channel (more info)',
spec:
'https://github.com/ampproject/amphtml/blob/master/' +
@@ -73,7 +78,7 @@ const CHANNELS = [
},
// Beta Channel
{
- id: RC_EXPERIMENT_ID,
+ id: BETA_CHANNEL_ID,
name: 'AMP Beta Channel (more info)',
spec:
'https://github.com/ampproject/amphtml/blob/master/' +
@@ -138,6 +143,32 @@ function build() {
} else {
redirect.setAttribute('hidden', '');
}
+
+ const rtvInput = document.getElementById('rtv');
+ const rtvButton = document.getElementById('rtv-submit');
+ rtvInput.addEventListener('input', () => {
+ rtvButton.disabled = rtvInput.value && !RTV_PATTERN.test(rtvInput.value);
+ rtvButton.textContent = rtvInput.value ? 'opt-in' : 'opt-out';
+ });
+ rtvButton.addEventListener('click', () => {
+ if (!rtvInput.value) {
+ showConfirmation_(
+ 'Do you really want to opt out of RTV?',
+ setAmpCanaryCookie_.bind(null, AMP_CANARY_COOKIE.DISABLED)
+ );
+ } else if (RTV_PATTERN.test(rtvInput.value)) {
+ showConfirmation_(
+ `Do you really want to opt in to RTV ${rtvInput.value}?`,
+ setAmpCanaryCookie_.bind(null, rtvInput.value)
+ );
+ }
+ });
+
+ if (isExperimentOn_(RTV_CHANNEL_ID)) {
+ rtvInput.value = getCookie(window, 'AMP_CANARY');
+ rtvInput.dispatchEvent(new Event('input'));
+ document.getElementById('rtv-details').open = true;
+ }
}
/**
@@ -237,24 +268,31 @@ function updateExperimentRow(experiment) {
* @return {boolean}
*/
function isExperimentOn_(id) {
- if (id == CANARY_EXPERIMENT_ID) {
- return getCookie(window, 'AMP_CANARY') == AMP_CANARY_COOKIE.CANARY;
- } else if (id == RC_EXPERIMENT_ID) {
- return getCookie(window, 'AMP_CANARY') == AMP_CANARY_COOKIE.RC;
+ switch (id) {
+ case EXPERIMENTAL_CHANNEL_ID:
+ return getCookie(window, 'AMP_CANARY') == AMP_CANARY_COOKIE.CANARY;
+ case BETA_CHANNEL_ID:
+ return getCookie(window, 'AMP_CANARY') == AMP_CANARY_COOKIE.RC;
+ case RTV_CHANNEL_ID:
+ return RTV_PATTERN.test(getCookie(window, 'AMP_CANARY'));
+ default:
+ return isExperimentOn(window, /*OK*/ id);
}
- return isExperimentOn(window, /*OK*/ id);
}
/**
* Opts in to / out of the "canary" or "rc" runtime types by setting the
* AMP_CANARY cookie.
- * @param {string} cookieState One of AMP_CANARY_COOKIE.{DISABLED|CANARY|RC}
+ * @param {string} cookieState One of AMP_CANARY_COOKIE.{DISABLED|CANARY|RC} or
+ * a 15-digit RTV.
*/
function setAmpCanaryCookie_(cookieState) {
- const validUntil =
- cookieState != AMP_CANARY_COOKIE.DISABLED
- ? Date.now() + COOKIE_MAX_AGE_MS
- : 0;
+ let validUntil = 0;
+ if (RTV_PATTERN.test(cookieState)) {
+ validUntil = Date.now() + RTV_COOKIE_MAX_AGE_MS;
+ } else if (cookieState != AMP_CANARY_COOKIE.DISABLED) {
+ validUntil = Date.now() + COOKIE_MAX_AGE_MS;
+ }
const cookieOptions = {
// Set explicit domain, so the cookie gets sent to sub domains.
domain: location.hostname,
@@ -279,17 +317,17 @@ function setAmpCanaryCookie_(cookieState) {
function toggleExperiment_(id, name, opt_on) {
const currentlyOn = isExperimentOn_(id);
const on = opt_on === undefined ? !currentlyOn : opt_on;
- // Protect against click jacking.
+ // Protect against accidental choice.
const confirmMessage = on
- ? 'Do you really want to activate the AMP experiment'
- : 'Do you really want to deactivate the AMP experiment';
+ ? 'Do you really want to activate the AMP experiment?'
+ : 'Do you really want to deactivate the AMP experiment?';
showConfirmation_(`${confirmMessage}: "${name}"`, () => {
- if (id == CANARY_EXPERIMENT_ID) {
+ if (id == EXPERIMENTAL_CHANNEL_ID) {
setAmpCanaryCookie_(
on ? AMP_CANARY_COOKIE.CANARY : AMP_CANARY_COOKIE.DISABLED
);
- } else if (id == RC_EXPERIMENT_ID) {
+ } else if (id == BETA_CHANNEL_ID) {
setAmpCanaryCookie_(
on ? AMP_CANARY_COOKIE.RC : AMP_CANARY_COOKIE.DISABLED
);
@@ -348,7 +386,7 @@ function getAmpConfig() {
xhr.send(null);
return promise
.then(text => {
- const match = text.match(/self\.AMP_CONFIG=([^;]+)/);
+ const match = text.match(/self\.AMP_CONFIG=(\{.+?\})/);
if (!match) {
throw new Error("Can't find AMP_CONFIG in: " + text);
}