2 * Copyright (C) 2014 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 // This gets all concatenated module descriptors in the release mode.
32 var allDescriptors
= [];
33 var applicationDescriptor
;
34 var _loadedScripts
= {};
36 // FIXME: This is a workaround to force Closure compiler provide
37 // the standard ES6 runtime for all modules. This should be removed
38 // once Closure provides standard externs for Map et al.
43 * @return {!Promise.<string>}
45 function loadResourcePromise(url
)
47 return new Promise(load
);
50 * @param {function(?)} fulfill
51 * @param {function(*)} reject
53 function load(fulfill
, reject
)
55 var xhr
= new XMLHttpRequest();
56 xhr
.open("GET", url
, true);
57 xhr
.onreadystatechange
= onreadystatechange
;
62 function onreadystatechange(e
)
64 if (xhr
.readyState
!== 4)
67 if ([0, 200, 304].indexOf(xhr
.status
) === -1) // Testing harness file:/// results in 0.
68 reject(new Error("While loading from url " + url
+ " server responded with a status of " + xhr
.status
));
70 fulfill(e
.target
.response
);
77 * http://tools.ietf.org/html/rfc3986#section-5.2.4
78 * @param {string} path
81 function normalizePath(path
)
83 if (path
.indexOf("..") === -1 && path
.indexOf('.') === -1)
86 var normalizedSegments
= [];
87 var segments
= path
.split("/");
88 for (var i
= 0; i
< segments
.length
; i
++) {
89 var segment
= segments
[i
];
92 else if (segment
=== "..")
93 normalizedSegments
.pop();
95 normalizedSegments
.push(segment
);
97 var normalizedPath
= normalizedSegments
.join("/");
98 if (normalizedPath
[normalizedPath
.length
- 1] === "/")
99 return normalizedPath
;
100 if (path
[0] === "/" && normalizedPath
)
101 normalizedPath
= "/" + normalizedPath
;
102 if ((path
[path
.length
- 1] === "/") || (segments
[segments
.length
- 1] === ".") || (segments
[segments
.length
- 1] === ".."))
103 normalizedPath
= normalizedPath
+ "/";
105 return normalizedPath
;
109 * @param {!Array.<string>} scriptNames
110 * @param {string=} base
111 * @return {!Promise.<undefined>}
113 function loadScriptsPromise(scriptNames
, base
)
115 /** @type {!Array.<!Promise.<string>>} */
117 /** @type {!Array.<string>} */
119 var sources
= new Array(scriptNames
.length
);
120 var scriptToEval
= 0;
121 for (var i
= 0; i
< scriptNames
.length
; ++i
) {
122 var scriptName
= scriptNames
[i
];
123 var sourceURL
= (base
|| self
._importScriptPathPrefix
) + scriptName
;
124 var schemaIndex
= sourceURL
.indexOf("://") + 3;
125 sourceURL
= sourceURL
.substring(0, schemaIndex
) + normalizePath(sourceURL
.substring(schemaIndex
));
126 if (_loadedScripts
[sourceURL
])
128 urls
.push(sourceURL
);
129 promises
.push(loadResourcePromise(sourceURL
).then(scriptSourceLoaded
.bind(null, i
), scriptSourceLoaded
.bind(null, i
, undefined)));
131 return Promise
.all(promises
).then(undefined);
134 * @param {number} scriptNumber
135 * @param {string=} scriptSource
137 function scriptSourceLoaded(scriptNumber
, scriptSource
)
139 sources
[scriptNumber
] = scriptSource
|| "";
140 // Eval scripts as fast as possible.
141 while (typeof sources
[scriptToEval
] !== "undefined") {
142 evaluateScript(urls
[scriptToEval
], sources
[scriptToEval
]);
148 * @param {string} sourceURL
149 * @param {string=} scriptSource
151 function evaluateScript(sourceURL
, scriptSource
)
153 _loadedScripts
[sourceURL
] = true;
155 // Do not reject, as this is normal in the hosted mode.
156 console
.error("Empty response arrived for script '" + sourceURL
+ "'");
159 self
.eval(scriptSource
+ "\n//# sourceURL=" + sourceURL
);
164 var baseUrl
= self
.location
? self
.location
.origin
+ self
.location
.pathname
: "";
165 self
._importScriptPathPrefix
= baseUrl
.substring(0, baseUrl
.lastIndexOf("/") + 1);
170 * @param {!Array.<!Runtime.ModuleDescriptor>} descriptors
171 * @param {!Array.<string>=} coreModuleNames
173 function Runtime(descriptors
, coreModuleNames
)
176 * @type {!Array.<!Runtime.Module>}
180 * @type {!Object.<string, !Runtime.Module>}
182 this._modulesMap
= {};
184 * @type {!Array.<!Runtime.Extension>}
186 this._extensions
= [];
189 * @type {!Object.<string, !function(new:Object)>}
191 this._cachedTypeClasses
= {};
194 * @type {!Object.<string, !Runtime.ModuleDescriptor>}
196 this._descriptorsMap
= {};
198 for (var i
= 0; i
< descriptors
.length
; ++i
)
199 this._registerModule(descriptors
[i
]);
201 this._loadAutoStartModules(coreModuleNames
);
205 * @type {!Object.<string, string>}
207 Runtime
._queryParamsObject
= { __proto__
: null };
210 * @type {!Object.<string, string>}
212 Runtime
.cachedResources
= { __proto__
: null };
217 Runtime
.isReleaseMode = function()
219 return !!allDescriptors
.length
;
223 * @param {string} appName
225 Runtime
.startApplication = function(appName
)
227 console
.timeStamp("Runtime.startApplication");
229 var allDescriptorsByName
= {};
230 for (var i
= 0; Runtime
.isReleaseMode() && i
< allDescriptors
.length
; ++i
) {
231 var d
= allDescriptors
[i
];
232 allDescriptorsByName
[d
["name"]] = d
;
235 var applicationPromise
;
236 if (applicationDescriptor
)
237 applicationPromise
= Promise
.resolve(applicationDescriptor
);
239 applicationPromise
= loadResourcePromise(appName
+ ".json").then(JSON
.parse
.bind(JSON
));
241 applicationPromise
.then(parseModuleDescriptors
);
244 * @param {!Array.<!Object>} configuration
246 function parseModuleDescriptors(configuration
)
248 var moduleJSONPromises
= [];
249 var coreModuleNames
= [];
250 for (var i
= 0; i
< configuration
.length
; ++i
) {
251 var descriptor
= configuration
[i
];
252 if (descriptor
["type"] === "worker")
254 var name
= descriptor
["name"];
255 var moduleJSON
= allDescriptorsByName
[name
];
257 moduleJSONPromises
.push(Promise
.resolve(moduleJSON
));
259 moduleJSONPromises
.push(loadResourcePromise(name
+ "/module.json").then(JSON
.parse
.bind(JSON
)));
260 if (descriptor
["type"] === "autostart")
261 coreModuleNames
.push(name
);
264 Promise
.all(moduleJSONPromises
).then(instantiateRuntime
);
266 * @param {!Array.<!Object>} moduleDescriptors
268 function instantiateRuntime(moduleDescriptors
)
270 for (var i
= 0; !Runtime
.isReleaseMode() && i
< moduleDescriptors
.length
; ++i
) {
271 moduleDescriptors
[i
]["name"] = configuration
[i
]["name"];
272 moduleDescriptors
[i
]["condition"] = configuration
[i
]["condition"];
274 self
.runtime
= new Runtime(moduleDescriptors
, coreModuleNames
);
280 * @param {string} name
283 Runtime
.queryParam = function(name
)
285 return Runtime
._queryParamsObject
[name
] || null;
289 * @param {!Array.<string>} banned
292 Runtime
.constructQueryParams = function(banned
)
295 for (var key
in Runtime
._queryParamsObject
) {
296 if (!key
|| banned
.indexOf(key
) !== -1)
298 params
.push(key
+ "=" + Runtime
._queryParamsObject
[key
]);
300 return params
.length
? "?" + params
.join("&") : "";
306 Runtime
._experimentsSetting = function()
309 return /** @type {!Object} */ (JSON
.parse(self
.localStorage
&& self
.localStorage
["experiments"] ? self
.localStorage
["experiments"] : "{}"));
311 console
.error("Failed to parse localStorage['experiments']");
317 * @param {!Array.<!Promise.<T, !Error>>} promises
318 * @return {!Promise.<!Array.<T>>}
321 Runtime
._some = function(promises
)
324 var wasRejected
= [];
325 for (var i
= 0; i
< promises
.length
; ++i
) {
326 // Workaround closure compiler bug.
327 var handlerFunction
= /** @type {function()} */ (handler
.bind(promises
[i
], i
));
328 all
.push(promises
[i
].catch(handlerFunction
));
331 return Promise
.all(all
).then(filterOutFailuresResults
);
334 * @param {!Array.<T>} results
335 * @return {!Array.<T>}
338 function filterOutFailuresResults(results
)
341 for (var i
= 0; i
< results
.length
; ++i
) {
343 filtered
.push(results
[i
]);
350 * @param {number} index
353 function handler(index
, e
)
355 wasRejected
[index
] = true;
356 console
.error(e
.stack
);
360 Runtime
._console
= console
;
361 Runtime
._originalAssert
= console
.assert
;
362 Runtime
._assert = function(value
, message
)
366 Runtime
._originalAssert
.call(Runtime
._console
, value
, message
+ " " + new Error().stack
);
369 Runtime
.prototype = {
370 useTestBase: function()
372 Runtime
._remoteBase
= "http://localhost:8000/inspector-sources/";
376 * @param {!Runtime.ModuleDescriptor} descriptor
378 _registerModule: function(descriptor
)
380 var module
= new Runtime
.Module(this, descriptor
);
381 this._modules
.push(module
);
382 this._modulesMap
[descriptor
["name"]] = module
;
386 * @param {string} moduleName
387 * @return {!Promise.<undefined>}
389 loadModulePromise: function(moduleName
)
391 return this._modulesMap
[moduleName
]._loadPromise();
395 * @param {!Array.<string>} moduleNames
396 * @return {!Promise.<!Array.<*>>}
398 _loadAutoStartModules: function(moduleNames
)
401 for (var i
= 0; i
< moduleNames
.length
; ++i
) {
402 if (Runtime
.isReleaseMode())
403 this._modulesMap
[moduleNames
[i
]]._loaded
= true;
405 promises
.push(this.loadModulePromise(moduleNames
[i
]));
407 return Promise
.all(promises
);
411 * @param {!Runtime.Extension} extension
412 * @param {?function(function(new:Object)):boolean} predicate
415 _checkExtensionApplicability: function(extension
, predicate
)
419 var contextTypes
= /** @type {!Array.<string>|undefined} */ (extension
.descriptor().contextTypes
);
422 for (var i
= 0; i
< contextTypes
.length
; ++i
) {
423 var contextType
= this._resolve(contextTypes
[i
]);
424 var isMatching
= !!contextType
&& predicate(contextType
);
432 * @param {!Runtime.Extension} extension
433 * @param {?Object} context
436 isExtensionApplicableToContext: function(extension
, context
)
440 return this._checkExtensionApplicability(extension
, isInstanceOf
);
443 * @param {!Function} targetType
446 function isInstanceOf(targetType
)
448 return context
instanceof targetType
;
453 * @param {!Runtime.Extension} extension
454 * @param {!Set.<!Function>=} currentContextTypes
457 isExtensionApplicableToContextTypes: function(extension
, currentContextTypes
)
459 if (!extension
.descriptor().contextTypes
)
462 return this._checkExtensionApplicability(extension
, currentContextTypes
? isContextTypeKnown
: null);
465 * @param {!Function} targetType
468 function isContextTypeKnown(targetType
)
470 return currentContextTypes
.has(targetType
);
476 * @param {?Object=} context
477 * @return {!Array.<!Runtime.Extension>}
479 extensions: function(type
, context
)
481 return this._extensions
.filter(filter
).sort(orderComparator
);
484 * @param {!Runtime.Extension} extension
487 function filter(extension
)
489 if (extension
._type
!== type
&& extension
._typeClass() !== type
)
491 if (!extension
.enabled())
493 return !context
|| extension
.isApplicable(context
);
497 * @param {!Runtime.Extension} extension1
498 * @param {!Runtime.Extension} extension2
501 function orderComparator(extension1
, extension2
)
503 var order1
= extension1
.descriptor()["order"] || 0;
504 var order2
= extension2
.descriptor()["order"] || 0;
505 return order1
- order2
;
511 * @param {?Object=} context
512 * @return {?Runtime.Extension}
514 extension: function(type
, context
)
516 return this.extensions(type
, context
)[0] || null;
521 * @param {?Object=} context
522 * @return {!Promise.<!Array.<!Object>>}
524 instancesPromise: function(type
, context
)
526 var extensions
= this.extensions(type
, context
);
528 for (var i
= 0; i
< extensions
.length
; ++i
)
529 promises
.push(extensions
[i
].instancePromise());
530 return Runtime
._some(promises
);
535 * @param {?Object=} context
536 * @return {!Promise.<!Object>}
538 instancePromise: function(type
, context
)
540 var extension
= this.extension(type
, context
);
542 return Promise
.reject(new Error("No such extension: " + type
+ " in given context."));
543 return extension
.instancePromise();
547 * @return {?function(new:Object)}
549 _resolve: function(typeName
)
551 if (!this._cachedTypeClasses
[typeName
]) {
552 var path
= typeName
.split(".");
554 for (var i
= 0; object
&& (i
< path
.length
); ++i
)
555 object
= object
[path
[i
]];
557 this._cachedTypeClasses
[typeName
] = /** @type function(new:Object) */(object
);
559 return this._cachedTypeClasses
[typeName
] || null;
566 Runtime
.ModuleDescriptor = function()
574 * @type {!Array.<!Runtime.ExtensionDescriptor>}
579 * @type {!Array.<string>|undefined}
584 * @type {!Array.<string>}
589 * @type {boolean|undefined}
597 Runtime
.ExtensionDescriptor = function()
605 * @type {string|undefined}
610 * @type {!Array.<string>|undefined}
617 * @param {!Runtime} manager
618 * @param {!Runtime.ModuleDescriptor} descriptor
620 Runtime
.Module = function(manager
, descriptor
)
622 this._manager
= manager
;
623 this._descriptor
= descriptor
;
624 this._name
= descriptor
.name
;
625 /** @type {!Object.<string, ?Object>} */
626 this._instanceMap
= {};
627 var extensions
= /** @type {?Array.<!Runtime.ExtensionDescriptor>} */ (descriptor
.extensions
);
628 for (var i
= 0; extensions
&& i
< extensions
.length
; ++i
)
629 this._manager
._extensions
.push(new Runtime
.Extension(this, extensions
[i
]));
630 this._loaded
= false;
633 Runtime
.Module
.prototype = {
647 var activatorExperiment
= this._descriptor
["experiment"];
648 if (activatorExperiment
&& !Runtime
.experiments
.isEnabled(activatorExperiment
))
650 var condition
= this._descriptor
["condition"];
651 if (condition
&& !Runtime
.queryParam(condition
))
657 * @param {string} name
660 resource: function(name
)
662 var fullName
= this._name
+ "/" + name
;
663 var content
= Runtime
.cachedResources
[fullName
];
665 throw new Error(fullName
+ " not preloaded. Check module.json");
670 * @return {!Promise.<undefined>}
672 _loadPromise: function()
675 return Promise
.resolve();
678 return Promise
.reject(new Error("Module " + this._name
+ " is not enabled"));
680 if (this._pendingLoadPromise
)
681 return this._pendingLoadPromise
;
683 var dependencies
= this._descriptor
.dependencies
;
684 var dependencyPromises
= [];
685 for (var i
= 0; dependencies
&& i
< dependencies
.length
; ++i
)
686 dependencyPromises
.push(this._manager
._modulesMap
[dependencies
[i
]]._loadPromise());
688 this._pendingLoadPromise
= Promise
.all(dependencyPromises
)
689 .then(this._loadResources
.bind(this))
690 .then(this._loadScripts
.bind(this))
691 .then(markAsLoaded
.bind(this));
693 return this._pendingLoadPromise
;
696 * @this {Runtime.Module}
698 function markAsLoaded()
700 delete this._pendingLoadPromise
;
706 * @return {!Promise.<undefined>}
707 * @this {Runtime.Module}
709 _loadResources: function()
711 var resources
= this._descriptor
["resources"];
713 return Promise
.resolve();
715 for (var i
= 0; i
< resources
.length
; ++i
) {
716 var url
= this._modularizeURL(resources
[i
]);
717 promises
.push(loadResourcePromise(url
).then(cacheResource
.bind(this, url
), cacheResource
.bind(this, url
, undefined)));
719 return Promise
.all(promises
).then(undefined);
722 * @param {string} path
723 * @param {string=} content
725 function cacheResource(path
, content
)
728 console
.error("Failed to load resource: " + path
);
731 var sourceURL
= window
.location
.href
;
732 if (window
.location
.search
)
733 sourceURL
= sourceURL
.replace(window
.location
.search
, "");
734 sourceURL
= sourceURL
.substring(0, sourceURL
.lastIndexOf("/") + 1) + path
;
735 Runtime
.cachedResources
[path
] = content
+ "\n/*# sourceURL=" + sourceURL
+ " */";
740 * @return {!Promise.<undefined>}
742 _loadScripts: function()
744 if (!this._descriptor
.scripts
)
745 return Promise
.resolve();
747 if (Runtime
.isReleaseMode())
748 return loadScriptsPromise([this._name
+ "_module.js"], this._remoteBase());
750 return loadScriptsPromise(this._descriptor
.scripts
.map(this._modularizeURL
, this));
754 * @param {string} resourceName
756 _modularizeURL: function(resourceName
)
758 return normalizePath(this._name
+ "/" + resourceName
);
762 * @return {string|undefined}
764 _remoteBase: function()
766 return this._descriptor
.remote
&& Runtime
._remoteBase
|| undefined;
770 * @param {string} value
773 substituteURL: function(value
)
775 var base
= this._remoteBase() || "";
776 return value
.replace(/@url\(([^\)]*?)\)/g, convertURL
.bind(this));
778 function convertURL(match
, url
)
780 return base
+ this._modularizeURL(url
);
785 * @param {string} className
786 * @param {!Runtime.Extension} extension
789 _instance: function(className
, extension
)
791 if (className
in this._instanceMap
)
792 return this._instanceMap
[className
];
794 var constructorFunction
= window
.eval(className
);
795 if (!(constructorFunction
instanceof Function
)) {
796 this._instanceMap
[className
] = null;
800 var instance
= new constructorFunction(extension
);
801 this._instanceMap
[className
] = instance
;
808 * @param {!Runtime.Module} module
809 * @param {!Runtime.ExtensionDescriptor} descriptor
811 Runtime
.Extension = function(module
, descriptor
)
813 this._module
= module
;
814 this._descriptor
= descriptor
;
816 this._type
= descriptor
.type
;
817 this._hasTypeClass
= this._type
.charAt(0) === "@";
822 this._className
= descriptor
.className
|| null;
825 Runtime
.Extension
.prototype = {
829 descriptor: function()
831 return this._descriptor
;
835 * @return {!Runtime.Module}
847 var activatorExperiment
= this.descriptor()["experiment"];
848 if (activatorExperiment
&& activatorExperiment
.startsWith("!") && Runtime
.experiments
.isEnabled(activatorExperiment
.substring(1)))
850 if (activatorExperiment
&& !activatorExperiment
.startsWith("!") && !Runtime
.experiments
.isEnabled(activatorExperiment
))
852 var condition
= this.descriptor()["condition"];
853 if (condition
&& !Runtime
.queryParam(condition
))
855 return this._module
.enabled();
859 * @return {?function(new:Object)}
861 _typeClass: function()
863 if (!this._hasTypeClass
)
865 return this._module
._manager
._resolve(this._type
.substring(1));
869 * @param {?Object} context
872 isApplicable: function(context
)
874 return this._module
._manager
.isExtensionApplicableToContext(this, context
);
878 * @return {!Promise.<!Object>}
880 instancePromise: function()
882 if (!this._className
)
883 return Promise
.reject(new Error("No class name in extension"));
884 var className
= this._className
;
886 return Promise
.resolve(this._instance
);
888 return this._module
._loadPromise().then(constructInstance
.bind(this));
892 * @this {Runtime.Extension}
894 function constructInstance()
896 var result
= this._module
._instance(className
, this);
898 return Promise
.reject("Could not instantiate: " + className
);
904 * @param {string} platform
907 title: function(platform
)
909 // FIXME: should be WebInspector.UIString() but runtime is not l10n aware yet.
910 return this._descriptor
["title-" + platform
] || this._descriptor
["title"];
917 Runtime
.ExperimentsSupport = function()
919 this._supportEnabled
= Runtime
.queryParam("experiments") !== null;
920 this._experiments
= [];
921 this._experimentNames
= {};
922 this._enabledTransiently
= {};
925 Runtime
.ExperimentsSupport
.prototype = {
927 * @return {!Array.<!Runtime.Experiment>}
929 allConfigurableExperiments: function()
932 for (var i
= 0; i
< this._experiments
.length
; i
++) {
933 var experiment
= this._experiments
[i
];
934 if (!this._enabledTransiently
[experiment
.name
])
935 result
.push(experiment
);
943 supportEnabled: function()
945 return this._supportEnabled
;
949 * @param {!Object} value
951 _setExperimentsSetting: function(value
)
953 if (!self
.localStorage
)
955 self
.localStorage
["experiments"] = JSON
.stringify(value
);
959 * @param {string} experimentName
960 * @param {string} experimentTitle
961 * @param {boolean=} hidden
963 register: function(experimentName
, experimentTitle
, hidden
)
965 Runtime
._assert(!this._experimentNames
[experimentName
], "Duplicate registration of experiment " + experimentName
);
966 this._experimentNames
[experimentName
] = true;
967 this._experiments
.push(new Runtime
.Experiment(this, experimentName
, experimentTitle
, !!hidden
));
971 * @param {string} experimentName
974 isEnabled: function(experimentName
)
976 this._checkExperiment(experimentName
);
978 if (this._enabledTransiently
[experimentName
])
980 if (!this.supportEnabled())
983 return !!Runtime
._experimentsSetting()[experimentName
];
987 * @param {string} experimentName
988 * @param {boolean} enabled
990 setEnabled: function(experimentName
, enabled
)
992 this._checkExperiment(experimentName
);
993 var experimentsSetting
= Runtime
._experimentsSetting();
994 experimentsSetting
[experimentName
] = enabled
;
995 this._setExperimentsSetting(experimentsSetting
);
999 * @param {!Array.<string>} experimentNames
1001 setDefaultExperiments: function(experimentNames
)
1003 for (var i
= 0; i
< experimentNames
.length
; ++i
) {
1004 this._checkExperiment(experimentNames
[i
]);
1005 this._enabledTransiently
[experimentNames
[i
]] = true;
1010 * @param {string} experimentName
1012 enableForTest: function(experimentName
)
1014 this._checkExperiment(experimentName
);
1015 this._enabledTransiently
[experimentName
] = true;
1018 clearForTest: function()
1020 this._experiments
= [];
1021 this._experimentNames
= {};
1022 this._enabledTransiently
= {};
1025 cleanUpStaleExperiments: function()
1027 var experimentsSetting
= Runtime
._experimentsSetting();
1028 var cleanedUpExperimentSetting
= {};
1029 for (var i
= 0; i
< this._experiments
.length
; ++i
) {
1030 var experimentName
= this._experiments
[i
].name
;
1031 if (experimentsSetting
[experimentName
])
1032 cleanedUpExperimentSetting
[experimentName
] = true;
1034 this._setExperimentsSetting(cleanedUpExperimentSetting
);
1038 * @param {string} experimentName
1040 _checkExperiment: function(experimentName
)
1042 Runtime
._assert(this._experimentNames
[experimentName
], "Unknown experiment " + experimentName
);
1048 * @param {!Runtime.ExperimentsSupport} experiments
1049 * @param {string} name
1050 * @param {string} title
1051 * @param {boolean} hidden
1053 Runtime
.Experiment = function(experiments
, name
, title
, hidden
)
1057 this.hidden
= hidden
;
1058 this._experiments
= experiments
;
1061 Runtime
.Experiment
.prototype = {
1065 isEnabled: function()
1067 return this._experiments
.isEnabled(this.name
);
1071 * @param {boolean} enabled
1073 setEnabled: function(enabled
)
1075 this._experiments
.setEnabled(this.name
, enabled
);
1079 {(function parseQueryParameters()
1081 var queryParams
= location
.search
;
1084 var params
= queryParams
.substring(1).split("&");
1085 for (var i
= 0; i
< params
.length
; ++i
) {
1086 var pair
= params
[i
].split("=");
1087 var name
= pair
.shift();
1088 Runtime
._queryParamsObject
[name
] = pair
.join("=");
1093 // This must be constructed after the query parameters have been parsed.
1094 Runtime
.experiments
= new Runtime
.ExperimentsSupport();
1099 Runtime
._remoteBase
= Runtime
.queryParam("remoteBase");
1101 /** @type {!Runtime} */