blob: 7e1d7e0daf9704058b32de804978eed56336c769 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:37 +00001/*
2 * Copyright (C) 2014 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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
13 * distribution.
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.
17 *
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.
29 */
30// This gets all concatenated module descriptors in the release mode.
31const allDescriptors = [];
32let applicationDescriptor;
33const _loadedScripts = {};
34
35// FIXME: This is a workaround to force Closure compiler provide
36// the standard ES6 runtime for all modules. This should be removed
37// once Closure provides standard externs for Map et al.
38for (const k of []) { // eslint-disable-line
39}
40
41(function() {
42const baseUrl = self.location ? self.location.origin + self.location.pathname : '';
43self._importScriptPathPrefix = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
44})();
45
Patrick Hulceb8c09f32018-06-11 21:28:22 +000046const REMOTE_MODULE_FALLBACK_REVISION = '@010ddcfda246975d194964ccf20038ebbdec6084';
47
Blink Reformat4c46d092018-04-07 15:32:37 +000048/**
49 * @unrestricted
50 */
51var Runtime = class { // eslint-disable-line
52 /**
53 * @param {!Array.<!Runtime.ModuleDescriptor>} descriptors
54 */
55 constructor(descriptors) {
56 /** @type {!Array<!Runtime.Module>} */
57 this._modules = [];
58 /** @type {!Object<string, !Runtime.Module>} */
59 this._modulesMap = {};
60 /** @type {!Array<!Runtime.Extension>} */
61 this._extensions = [];
62 /** @type {!Object<string, !function(new:Object)>} */
63 this._cachedTypeClasses = {};
64 /** @type {!Object<string, !Runtime.ModuleDescriptor>} */
65 this._descriptorsMap = {};
66
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000067 for (let i = 0; i < descriptors.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +000068 this._registerModule(descriptors[i]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000069 }
Blink Reformat4c46d092018-04-07 15:32:37 +000070 }
71
72 /**
73 * @param {string} url
74 * @return {!Promise.<string>}
75 */
76 static loadResourcePromise(url) {
77 return new Promise(load);
78
79 /**
80 * @param {function(?)} fulfill
81 * @param {function(*)} reject
82 */
83 function load(fulfill, reject) {
84 const xhr = new XMLHttpRequest();
85 xhr.open('GET', url, true);
86 xhr.onreadystatechange = onreadystatechange;
87
88 /**
89 * @param {Event} e
90 */
91 function onreadystatechange(e) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000092 if (xhr.readyState !== XMLHttpRequest.DONE) {
Blink Reformat4c46d092018-04-07 15:32:37 +000093 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000094 }
Blink Reformat4c46d092018-04-07 15:32:37 +000095
Patrick Hulceb8c09f32018-06-11 21:28:22 +000096 // DevTools Proxy server can mask 404s as 200s, check the body to be sure
97 const status = /^HTTP\/1.1 404/.test(e.target.response) ? 404 : xhr.status;
98
99 if ([0, 200, 304].indexOf(status) === -1) // Testing harness file:/// results in 0.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000100 {
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000101 reject(new Error('While loading from url ' + url + ' server responded with a status of ' + status));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000102 } else {
Blink Reformat4c46d092018-04-07 15:32:37 +0000103 fulfill(e.target.response);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000104 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000105 }
106 xhr.send(null);
107 }
108 }
109
110 /**
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000111 * @param {string} url
112 * @return {!Promise.<string>}
113 */
114 static loadResourcePromiseWithFallback(url) {
115 return Runtime.loadResourcePromise(url).catch(err => {
116 const urlWithFallbackVersion = url.replace(/@[0-9a-f]{40}/, REMOTE_MODULE_FALLBACK_REVISION);
117 // TODO(phulce): mark fallbacks in module.json and modify build script instead
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000118 if (urlWithFallbackVersion === url || !url.includes('audits_worker_module')) {
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000119 throw err;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000120 }
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000121 return Runtime.loadResourcePromise(urlWithFallbackVersion);
122 });
123 }
124
125 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000126 * http://tools.ietf.org/html/rfc3986#section-5.2.4
127 * @param {string} path
128 * @return {string}
129 */
130 static normalizePath(path) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000131 if (path.indexOf('..') === -1 && path.indexOf('.') === -1) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000132 return path;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000133 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000134
135 const normalizedSegments = [];
136 const segments = path.split('/');
137 for (let i = 0; i < segments.length; i++) {
138 const segment = segments[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000139 if (segment === '.') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000140 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000141 } else if (segment === '..') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000142 normalizedSegments.pop();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000143 } else if (segment) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000144 normalizedSegments.push(segment);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000145 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000146 }
147 let normalizedPath = normalizedSegments.join('/');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000148 if (normalizedPath[normalizedPath.length - 1] === '/') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000149 return normalizedPath;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000150 }
151 if (path[0] === '/' && normalizedPath) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000152 normalizedPath = '/' + normalizedPath;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000153 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000154 if ((path[path.length - 1] === '/') || (segments[segments.length - 1] === '.') ||
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000155 (segments[segments.length - 1] === '..')) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000156 normalizedPath = normalizedPath + '/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000157 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000158
159 return normalizedPath;
160 }
161
162 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000163 * @param {string} scriptName
164 * @param {string=} base
165 * @return {string}
166 */
167 static getResourceURL(scriptName, base) {
168 const sourceURL = (base || self._importScriptPathPrefix) + scriptName;
169 const schemaIndex = sourceURL.indexOf('://') + 3;
170 let pathIndex = sourceURL.indexOf('/', schemaIndex);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000171 if (pathIndex === -1) {
Mandy Chen20b48f62019-09-18 19:28:22 +0000172 pathIndex = sourceURL.length;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000173 }
Mandy Chen20b48f62019-09-18 19:28:22 +0000174 return sourceURL.substring(0, pathIndex) + Runtime.normalizePath(sourceURL.substring(pathIndex));
175 }
176
177 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000178 * @param {!Array.<string>} scriptNames
179 * @param {string=} base
180 * @return {!Promise.<undefined>}
181 */
182 static _loadScriptsPromise(scriptNames, base) {
183 /** @type {!Array<!Promise<undefined>>} */
184 const promises = [];
185 /** @type {!Array<string>} */
186 const urls = [];
187 const sources = new Array(scriptNames.length);
188 let scriptToEval = 0;
189 for (let i = 0; i < scriptNames.length; ++i) {
190 const scriptName = scriptNames[i];
Mandy Chen20b48f62019-09-18 19:28:22 +0000191 const sourceURL = Runtime.getResourceURL(scriptName, base);
Blink Reformat4c46d092018-04-07 15:32:37 +0000192
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000193 if (_loadedScripts[sourceURL]) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000194 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000195 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000196 urls.push(sourceURL);
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000197 const loadResourcePromise =
198 base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
199 promises.push(
200 loadResourcePromise.then(scriptSourceLoaded.bind(null, i), scriptSourceLoaded.bind(null, i, undefined)));
Blink Reformat4c46d092018-04-07 15:32:37 +0000201 }
202 return Promise.all(promises).then(undefined);
203
204 /**
205 * @param {number} scriptNumber
206 * @param {string=} scriptSource
207 */
208 function scriptSourceLoaded(scriptNumber, scriptSource) {
209 sources[scriptNumber] = scriptSource || '';
210 // Eval scripts as fast as possible.
211 while (typeof sources[scriptToEval] !== 'undefined') {
212 evaluateScript(urls[scriptToEval], sources[scriptToEval]);
213 ++scriptToEval;
214 }
215 }
216
217 /**
218 * @param {string} sourceURL
219 * @param {string=} scriptSource
220 */
221 function evaluateScript(sourceURL, scriptSource) {
222 _loadedScripts[sourceURL] = true;
223 if (!scriptSource) {
224 // Do not reject, as this is normal in the hosted mode.
225 console.error('Empty response arrived for script \'' + sourceURL + '\'');
226 return;
227 }
228 self.eval(scriptSource + '\n//# sourceURL=' + sourceURL);
229 }
230 }
231
232 /**
233 * @param {string} url
234 * @param {boolean} appendSourceURL
235 * @return {!Promise<undefined>}
236 */
237 static _loadResourceIntoCache(url, appendSourceURL) {
238 return Runtime.loadResourcePromise(url).then(
239 cacheResource.bind(this, url), cacheResource.bind(this, url, undefined));
240
241 /**
242 * @param {string} path
243 * @param {string=} content
244 */
245 function cacheResource(path, content) {
246 if (!content) {
247 console.error('Failed to load resource: ' + path);
248 return;
249 }
250 const sourceURL = appendSourceURL ? Runtime.resolveSourceURL(path) : '';
251 Runtime.cachedResources[path] = content + sourceURL;
252 }
253 }
254
255 /**
256 * @return {!Promise}
257 */
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000258 static async appStarted() {
259 return Runtime._appStartedPromise;
Blink Reformat4c46d092018-04-07 15:32:37 +0000260 }
261
262 /**
263 * @param {string} appName
264 * @return {!Promise.<undefined>}
265 */
266 static async startApplication(appName) {
267 console.timeStamp('Runtime.startApplication');
268
269 const allDescriptorsByName = {};
270 for (let i = 0; i < allDescriptors.length; ++i) {
271 const d = allDescriptors[i];
272 allDescriptorsByName[d['name']] = d;
273 }
274
275 if (!applicationDescriptor) {
276 let data = await Runtime.loadResourcePromise(appName + '.json');
277 applicationDescriptor = JSON.parse(data);
278 let descriptor = applicationDescriptor;
279 while (descriptor.extends) {
280 data = await Runtime.loadResourcePromise(descriptor.extends + '.json');
281 descriptor = JSON.parse(data);
282 applicationDescriptor.modules = descriptor.modules.concat(applicationDescriptor.modules);
283 }
284 }
285
286 const configuration = applicationDescriptor.modules;
287 const moduleJSONPromises = [];
288 const coreModuleNames = [];
289 for (let i = 0; i < configuration.length; ++i) {
290 const descriptor = configuration[i];
291 const name = descriptor['name'];
292 const moduleJSON = allDescriptorsByName[name];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000293 if (moduleJSON) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000294 moduleJSONPromises.push(Promise.resolve(moduleJSON));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000295 } else {
Blink Reformat4c46d092018-04-07 15:32:37 +0000296 moduleJSONPromises.push(Runtime.loadResourcePromise(name + '/module.json').then(JSON.parse.bind(JSON)));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000297 }
298 if (descriptor['type'] === 'autostart') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000299 coreModuleNames.push(name);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000300 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000301 }
302
303 const moduleDescriptors = await Promise.all(moduleJSONPromises);
304
305 for (let i = 0; i < moduleDescriptors.length; ++i) {
306 moduleDescriptors[i].name = configuration[i]['name'];
307 moduleDescriptors[i].condition = configuration[i]['condition'];
308 moduleDescriptors[i].remote = configuration[i]['type'] === 'remote';
309 }
310 self.runtime = new Runtime(moduleDescriptors);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000311 if (coreModuleNames) {
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000312 await self.runtime._loadAutoStartModules(coreModuleNames);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000313 }
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000314 Runtime._appStartedPromiseCallback();
Blink Reformat4c46d092018-04-07 15:32:37 +0000315 }
316
317 /**
318 * @param {string} appName
319 * @return {!Promise.<undefined>}
320 */
321 static startWorker(appName) {
322 return Runtime.startApplication(appName).then(sendWorkerReady);
323
324 function sendWorkerReady() {
325 self.postMessage('workerReady');
326 }
327 }
328
329 /**
330 * @param {string} name
331 * @return {?string}
332 */
333 static queryParam(name) {
Ingvar Stepanyand48ae082018-11-07 04:38:32 +0000334 return Runtime._queryParamsObject.get(name);
Blink Reformat4c46d092018-04-07 15:32:37 +0000335 }
336
337 /**
338 * @return {string}
339 */
340 static queryParamsString() {
341 return location.search;
342 }
343
344 /**
345 * @return {!Object}
346 */
347 static _experimentsSetting() {
348 try {
349 return /** @type {!Object} */ (
350 JSON.parse(self.localStorage && self.localStorage['experiments'] ? self.localStorage['experiments'] : '{}'));
351 } catch (e) {
352 console.error('Failed to parse localStorage[\'experiments\']');
353 return {};
354 }
355 }
356
357 static _assert(value, message) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000358 if (value) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000359 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000360 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000361 Runtime._originalAssert.call(Runtime._console, value, message + ' ' + new Error().stack);
362 }
363
364 /**
365 * @param {string} platform
366 */
367 static setPlatform(platform) {
368 Runtime._platform = platform;
369 }
370
371 /**
372 * @param {!Object} descriptor
373 * @return {boolean}
374 */
375 static _isDescriptorEnabled(descriptor) {
376 const activatorExperiment = descriptor['experiment'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000377 if (activatorExperiment === '*') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000378 return Runtime.experiments.supportEnabled();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000379 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000380 if (activatorExperiment && activatorExperiment.startsWith('!') &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000381 Runtime.experiments.isEnabled(activatorExperiment.substring(1))) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000382 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000383 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000384 if (activatorExperiment && !activatorExperiment.startsWith('!') &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000385 !Runtime.experiments.isEnabled(activatorExperiment)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000386 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000387 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000388 const condition = descriptor['condition'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000389 if (condition && !condition.startsWith('!') && !Runtime.queryParam(condition)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000390 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000391 }
392 if (condition && condition.startsWith('!') && Runtime.queryParam(condition.substring(1))) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000393 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000394 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000395 return true;
396 }
397
398 /**
399 * @param {string} path
400 * @return {string}
401 */
402 static resolveSourceURL(path) {
403 let sourceURL = self.location.href;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000404 if (self.location.search) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000405 sourceURL = sourceURL.replace(self.location.search, '');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000406 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000407 sourceURL = sourceURL.substring(0, sourceURL.lastIndexOf('/') + 1) + path;
408 return '\n/*# sourceURL=' + sourceURL + ' */';
409 }
410
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000411 /**
412 * @param {function(string):string} localizationFunction
413 */
414 static setL10nCallback(localizationFunction) {
415 Runtime._l10nCallback = localizationFunction;
416 }
417
Blink Reformat4c46d092018-04-07 15:32:37 +0000418 useTestBase() {
419 Runtime._remoteBase = 'http://localhost:8000/inspector-sources/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000420 if (Runtime.queryParam('debugFrontend')) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000421 Runtime._remoteBase += 'debug/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000422 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000423 }
424
425 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000426 * @param {string} moduleName
427 * @return {!Runtime.Module}
428 */
429 module(moduleName) {
430 return this._modulesMap[moduleName];
431 }
432
433 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000434 * @param {!Runtime.ModuleDescriptor} descriptor
435 */
436 _registerModule(descriptor) {
437 const module = new Runtime.Module(this, descriptor);
438 this._modules.push(module);
439 this._modulesMap[descriptor['name']] = module;
440 }
441
442 /**
443 * @param {string} moduleName
444 * @return {!Promise.<undefined>}
445 */
446 loadModulePromise(moduleName) {
447 return this._modulesMap[moduleName]._loadPromise();
448 }
449
450 /**
451 * @param {!Array.<string>} moduleNames
452 * @return {!Promise.<!Array.<*>>}
453 */
454 _loadAutoStartModules(moduleNames) {
455 const promises = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000456 for (let i = 0; i < moduleNames.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000457 promises.push(this.loadModulePromise(moduleNames[i]));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000458 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000459 return Promise.all(promises);
460 }
461
462 /**
463 * @param {!Runtime.Extension} extension
464 * @param {?function(function(new:Object)):boolean} predicate
465 * @return {boolean}
466 */
467 _checkExtensionApplicability(extension, predicate) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000468 if (!predicate) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000469 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000470 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000471 const contextTypes = extension.descriptor().contextTypes;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000472 if (!contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000473 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000474 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000475 for (let i = 0; i < contextTypes.length; ++i) {
476 const contextType = this._resolve(contextTypes[i]);
477 const isMatching = !!contextType && predicate(contextType);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000478 if (isMatching) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000479 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000480 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000481 }
482 return false;
483 }
484
485 /**
486 * @param {!Runtime.Extension} extension
487 * @param {?Object} context
488 * @return {boolean}
489 */
490 isExtensionApplicableToContext(extension, context) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000491 if (!context) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000492 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000493 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000494 return this._checkExtensionApplicability(extension, isInstanceOf);
495
496 /**
497 * @param {!Function} targetType
498 * @return {boolean}
499 */
500 function isInstanceOf(targetType) {
501 return context instanceof targetType;
502 }
503 }
504
505 /**
506 * @param {!Runtime.Extension} extension
507 * @param {!Set.<!Function>=} currentContextTypes
508 * @return {boolean}
509 */
510 isExtensionApplicableToContextTypes(extension, currentContextTypes) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000511 if (!extension.descriptor().contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000512 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000513 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000514
515 return this._checkExtensionApplicability(extension, currentContextTypes ? isContextTypeKnown : null);
516
517 /**
518 * @param {!Function} targetType
519 * @return {boolean}
520 */
521 function isContextTypeKnown(targetType) {
522 return currentContextTypes.has(targetType);
523 }
524 }
525
526 /**
527 * @param {*} type
528 * @param {?Object=} context
529 * @param {boolean=} sortByTitle
530 * @return {!Array.<!Runtime.Extension>}
531 */
532 extensions(type, context, sortByTitle) {
533 return this._extensions.filter(filter).sort(sortByTitle ? titleComparator : orderComparator);
534
535 /**
536 * @param {!Runtime.Extension} extension
537 * @return {boolean}
538 */
539 function filter(extension) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000540 if (extension._type !== type && extension._typeClass() !== type) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000541 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000542 }
543 if (!extension.enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000544 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000545 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000546 return !context || extension.isApplicable(context);
547 }
548
549 /**
550 * @param {!Runtime.Extension} extension1
551 * @param {!Runtime.Extension} extension2
552 * @return {number}
553 */
554 function orderComparator(extension1, extension2) {
555 const order1 = extension1.descriptor()['order'] || 0;
556 const order2 = extension2.descriptor()['order'] || 0;
557 return order1 - order2;
558 }
559
560 /**
561 * @param {!Runtime.Extension} extension1
562 * @param {!Runtime.Extension} extension2
563 * @return {number}
564 */
565 function titleComparator(extension1, extension2) {
566 const title1 = extension1.title() || '';
567 const title2 = extension2.title() || '';
568 return title1.localeCompare(title2);
569 }
570 }
571
572 /**
573 * @param {*} type
574 * @param {?Object=} context
575 * @return {?Runtime.Extension}
576 */
577 extension(type, context) {
578 return this.extensions(type, context)[0] || null;
579 }
580
581 /**
582 * @param {*} type
583 * @param {?Object=} context
584 * @return {!Promise.<!Array.<!Object>>}
585 */
586 allInstances(type, context) {
587 return Promise.all(this.extensions(type, context).map(extension => extension.instance()));
588 }
589
590 /**
591 * @return {?function(new:Object)}
592 */
593 _resolve(typeName) {
594 if (!this._cachedTypeClasses[typeName]) {
595 const path = typeName.split('.');
596 let object = self;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000597 for (let i = 0; object && (i < path.length); ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000598 object = object[path[i]];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000599 }
600 if (object) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000601 this._cachedTypeClasses[typeName] = /** @type function(new:Object) */ (object);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000602 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000603 }
604 return this._cachedTypeClasses[typeName] || null;
605 }
606
607 /**
Alexei Filippovf2e42e42019-04-04 21:40:45 +0000608 * @param {function(new:T)} constructorFunction
609 * @return {!T}
610 * @template T
Blink Reformat4c46d092018-04-07 15:32:37 +0000611 */
612 sharedInstance(constructorFunction) {
613 if (Runtime._instanceSymbol in constructorFunction &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000614 Object.getOwnPropertySymbols(constructorFunction).includes(Runtime._instanceSymbol)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000615 return constructorFunction[Runtime._instanceSymbol];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000616 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000617
618 const instance = new constructorFunction();
619 constructorFunction[Runtime._instanceSymbol] = instance;
620 return instance;
621 }
622};
623
Ingvar Stepanyand48ae082018-11-07 04:38:32 +0000624/** @type {!URLSearchParams} */
625Runtime._queryParamsObject = new URLSearchParams(Runtime.queryParamsString());
Blink Reformat4c46d092018-04-07 15:32:37 +0000626
627Runtime._instanceSymbol = Symbol('instance');
628
629/**
630 * @type {!Object.<string, string>}
631 */
632Runtime.cachedResources = {
633 __proto__: null
634};
635
636
637Runtime._console = console;
638Runtime._originalAssert = console.assert;
639
640
641Runtime._platform = '';
642
643
644/**
645 * @unrestricted
646 */
647Runtime.ModuleDescriptor = class {
648 constructor() {
649 /**
650 * @type {string}
651 */
652 this.name;
653
654 /**
655 * @type {!Array.<!Runtime.ExtensionDescriptor>}
656 */
657 this.extensions;
658
659 /**
660 * @type {!Array.<string>|undefined}
661 */
662 this.dependencies;
663
664 /**
665 * @type {!Array.<string>}
666 */
667 this.scripts;
668
669 /**
670 * @type {string|undefined}
671 */
672 this.condition;
673
674 /**
675 * @type {boolean|undefined}
676 */
677 this.remote;
678 }
679};
680
681/**
682 * @unrestricted
683 */
684Runtime.ExtensionDescriptor = class {
685 constructor() {
686 /**
687 * @type {string}
688 */
689 this.type;
690
691 /**
692 * @type {string|undefined}
693 */
694 this.className;
695
696 /**
697 * @type {string|undefined}
698 */
699 this.factoryName;
700
701 /**
702 * @type {!Array.<string>|undefined}
703 */
704 this.contextTypes;
705 }
706};
707
708/**
709 * @unrestricted
710 */
711Runtime.Module = class {
712 /**
713 * @param {!Runtime} manager
714 * @param {!Runtime.ModuleDescriptor} descriptor
715 */
716 constructor(manager, descriptor) {
717 this._manager = manager;
718 this._descriptor = descriptor;
719 this._name = descriptor.name;
720 /** @type {!Array<!Runtime.Extension>} */
721 this._extensions = [];
722
723 /** @type {!Map<string, !Array<!Runtime.Extension>>} */
724 this._extensionsByClassName = new Map();
725 const extensions = /** @type {?Array.<!Runtime.ExtensionDescriptor>} */ (descriptor.extensions);
726 for (let i = 0; extensions && i < extensions.length; ++i) {
727 const extension = new Runtime.Extension(this, extensions[i]);
728 this._manager._extensions.push(extension);
729 this._extensions.push(extension);
730 }
731 this._loadedForTest = false;
732 }
733
734 /**
735 * @return {string}
736 */
737 name() {
738 return this._name;
739 }
740
741 /**
742 * @return {boolean}
743 */
744 enabled() {
745 return Runtime._isDescriptorEnabled(this._descriptor);
746 }
747
748 /**
749 * @param {string} name
750 * @return {string}
751 */
752 resource(name) {
753 const fullName = this._name + '/' + name;
754 const content = Runtime.cachedResources[fullName];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000755 if (!content) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000756 throw new Error(fullName + ' not preloaded. Check module.json');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000757 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000758 return content;
759 }
760
761 /**
762 * @return {!Promise.<undefined>}
763 */
764 _loadPromise() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000765 if (!this.enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000766 return Promise.reject(new Error('Module ' + this._name + ' is not enabled'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000767 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000768
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000769 if (this._pendingLoadPromise) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000770 return this._pendingLoadPromise;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000771 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000772
773 const dependencies = this._descriptor.dependencies;
774 const dependencyPromises = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000775 for (let i = 0; dependencies && i < dependencies.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000776 dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPromise());
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000777 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000778
779 this._pendingLoadPromise = Promise.all(dependencyPromises)
780 .then(this._loadResources.bind(this))
781 .then(this._loadScripts.bind(this))
782 .then(() => this._loadedForTest = true);
783
784 return this._pendingLoadPromise;
785 }
786
787 /**
788 * @return {!Promise.<undefined>}
789 * @this {Runtime.Module}
790 */
791 _loadResources() {
792 const resources = this._descriptor['resources'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000793 if (!resources || !resources.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000794 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000795 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000796 const promises = [];
797 for (let i = 0; i < resources.length; ++i) {
798 const url = this._modularizeURL(resources[i]);
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000799 const isHtml = url.endsWith('.html');
800 promises.push(Runtime._loadResourceIntoCache(url, !isHtml /* appendSourceURL */));
Blink Reformat4c46d092018-04-07 15:32:37 +0000801 }
802 return Promise.all(promises).then(undefined);
803 }
804
805 /**
806 * @return {!Promise.<undefined>}
807 */
808 _loadScripts() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000809 if (!this._descriptor.scripts || !this._descriptor.scripts.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000810 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000811 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000812
813 // Module namespaces.
814 // NOTE: Update scripts/special_case_namespaces.json if you add a special cased namespace.
815 // The namespace keyword confuses clang-format.
816 // clang-format off
817 const specialCases = {
818 'sdk': 'SDK',
819 'js_sdk': 'JSSDK',
820 'browser_sdk': 'BrowserSDK',
821 'ui': 'UI',
822 'object_ui': 'ObjectUI',
Joel Einbinder3f23eb22018-05-14 23:27:51 +0000823 'javascript_metadata': 'JavaScriptMetadata',
Blink Reformat4c46d092018-04-07 15:32:37 +0000824 'perf_ui': 'PerfUI',
825 'har_importer': 'HARImporter',
826 'sdk_test_runner': 'SDKTestRunner',
827 'cpu_profiler_test_runner': 'CPUProfilerTestRunner'
828 };
829 const namespace = specialCases[this._name] || this._name.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
830 self[namespace] = self[namespace] || {};
831 // clang-format on
832 return Runtime._loadScriptsPromise(this._descriptor.scripts.map(this._modularizeURL, this), this._remoteBase());
833 }
834
835 /**
836 * @param {string} resourceName
837 */
838 _modularizeURL(resourceName) {
839 return Runtime.normalizePath(this._name + '/' + resourceName);
840 }
841
842 /**
843 * @return {string|undefined}
844 */
845 _remoteBase() {
846 return !Runtime.queryParam('debugFrontend') && this._descriptor.remote && Runtime._remoteBase || undefined;
847 }
848
849 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000850 * @param {string} resourceName
851 * @return {!Promise.<string>}
852 */
853 fetchResource(resourceName) {
854 const base = this._remoteBase();
855 const sourceURL = Runtime.getResourceURL(this._modularizeURL(resourceName), base);
856 return base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
857 }
858
859 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000860 * @param {string} value
861 * @return {string}
862 */
863 substituteURL(value) {
864 const base = this._remoteBase() || '';
865 return value.replace(/@url\(([^\)]*?)\)/g, convertURL.bind(this));
866
867 function convertURL(match, url) {
868 return base + this._modularizeURL(url);
869 }
870 }
871};
872
873
874/**
875 * @unrestricted
876 */
877Runtime.Extension = class {
878 /**
879 * @param {!Runtime.Module} module
880 * @param {!Runtime.ExtensionDescriptor} descriptor
881 */
882 constructor(module, descriptor) {
883 this._module = module;
884 this._descriptor = descriptor;
885
886 this._type = descriptor.type;
887 this._hasTypeClass = this._type.charAt(0) === '@';
888
889 /**
890 * @type {?string}
891 */
892 this._className = descriptor.className || null;
893 this._factoryName = descriptor.factoryName || null;
894 }
895
896 /**
897 * @return {!Object}
898 */
899 descriptor() {
900 return this._descriptor;
901 }
902
903 /**
904 * @return {!Runtime.Module}
905 */
906 module() {
907 return this._module;
908 }
909
910 /**
911 * @return {boolean}
912 */
913 enabled() {
914 return this._module.enabled() && Runtime._isDescriptorEnabled(this.descriptor());
915 }
916
917 /**
918 * @return {?function(new:Object)}
919 */
920 _typeClass() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000921 if (!this._hasTypeClass) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000922 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000923 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000924 return this._module._manager._resolve(this._type.substring(1));
925 }
926
927 /**
928 * @param {?Object} context
929 * @return {boolean}
930 */
931 isApplicable(context) {
932 return this._module._manager.isExtensionApplicableToContext(this, context);
933 }
934
935 /**
936 * @return {!Promise.<!Object>}
937 */
938 instance() {
939 return this._module._loadPromise().then(this._createInstance.bind(this));
940 }
941
942 /**
943 * @return {boolean}
944 */
945 canInstantiate() {
946 return !!(this._className || this._factoryName);
947 }
948
949 /**
950 * @return {!Object}
951 */
952 _createInstance() {
953 const className = this._className || this._factoryName;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000954 if (!className) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000955 throw new Error('Could not instantiate extension with no class');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000956 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000957 const constructorFunction = self.eval(/** @type {string} */ (className));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000958 if (!(constructorFunction instanceof Function)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000959 throw new Error('Could not instantiate: ' + className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000960 }
961 if (this._className) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000962 return this._module._manager.sharedInstance(constructorFunction);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000963 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000964 return new constructorFunction(this);
965 }
966
967 /**
968 * @return {string}
969 */
970 title() {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000971 const title = this._descriptor['title-' + Runtime._platform] || this._descriptor['title'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000972 if (title && Runtime._l10nCallback) {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000973 return Runtime._l10nCallback(title);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000974 }
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000975 return title;
Blink Reformat4c46d092018-04-07 15:32:37 +0000976 }
977
978 /**
979 * @param {function(new:Object)} contextType
980 * @return {boolean}
981 */
982 hasContextType(contextType) {
983 const contextTypes = this.descriptor().contextTypes;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000984 if (!contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000985 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000986 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000987 for (let i = 0; i < contextTypes.length; ++i) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000988 if (contextType === this._module._manager._resolve(contextTypes[i])) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000989 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000990 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000991 }
992 return false;
993 }
994};
995
996/**
997 * @unrestricted
998 */
999Runtime.ExperimentsSupport = class {
1000 constructor() {
1001 this._supportEnabled = Runtime.queryParam('experiments') !== null;
1002 this._experiments = [];
1003 this._experimentNames = {};
1004 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +00001005 /** @type {!Set<string>} */
1006 this._serverEnabled = new Set();
Blink Reformat4c46d092018-04-07 15:32:37 +00001007 }
1008
1009 /**
1010 * @return {!Array.<!Runtime.Experiment>}
1011 */
1012 allConfigurableExperiments() {
1013 const result = [];
1014 for (let i = 0; i < this._experiments.length; i++) {
1015 const experiment = this._experiments[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001016 if (!this._enabledTransiently[experiment.name]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001017 result.push(experiment);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001018 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001019 }
1020 return result;
1021 }
1022
1023 /**
1024 * @return {boolean}
1025 */
1026 supportEnabled() {
1027 return this._supportEnabled;
1028 }
1029
1030 /**
1031 * @param {!Object} value
1032 */
1033 _setExperimentsSetting(value) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001034 if (!self.localStorage) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001035 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001036 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001037 self.localStorage['experiments'] = JSON.stringify(value);
1038 }
1039
1040 /**
1041 * @param {string} experimentName
1042 * @param {string} experimentTitle
1043 * @param {boolean=} hidden
1044 */
1045 register(experimentName, experimentTitle, hidden) {
1046 Runtime._assert(!this._experimentNames[experimentName], 'Duplicate registration of experiment ' + experimentName);
1047 this._experimentNames[experimentName] = true;
1048 this._experiments.push(new Runtime.Experiment(this, experimentName, experimentTitle, !!hidden));
1049 }
1050
1051 /**
1052 * @param {string} experimentName
1053 * @return {boolean}
1054 */
1055 isEnabled(experimentName) {
1056 this._checkExperiment(experimentName);
Pavel Feldmanc0395912018-08-03 02:05:00 +00001057 // Check for explicitly disabled experiments first - the code could call setEnable(false) on the experiment enabled
1058 // by default and we should respect that.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001059 if (Runtime._experimentsSetting()[experimentName] === false) {
Pavel Feldmanc0395912018-08-03 02:05:00 +00001060 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001061 }
1062 if (this._enabledTransiently[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001063 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001064 }
1065 if (this._serverEnabled.has(experimentName)) {
Jeff Fisher85b08e12019-06-13 22:24:21 +00001066 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001067 }
1068 if (!this.supportEnabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001069 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001070 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001071
1072 return !!Runtime._experimentsSetting()[experimentName];
1073 }
1074
1075 /**
1076 * @param {string} experimentName
1077 * @param {boolean} enabled
1078 */
1079 setEnabled(experimentName, enabled) {
1080 this._checkExperiment(experimentName);
1081 const experimentsSetting = Runtime._experimentsSetting();
1082 experimentsSetting[experimentName] = enabled;
1083 this._setExperimentsSetting(experimentsSetting);
1084 }
1085
1086 /**
1087 * @param {!Array.<string>} experimentNames
1088 */
1089 setDefaultExperiments(experimentNames) {
1090 for (let i = 0; i < experimentNames.length; ++i) {
1091 this._checkExperiment(experimentNames[i]);
1092 this._enabledTransiently[experimentNames[i]] = true;
1093 }
1094 }
1095
1096 /**
Jeff Fisher85b08e12019-06-13 22:24:21 +00001097 * @param {!Array.<string>} experimentNames
1098 */
1099 setServerEnabledExperiments(experimentNames) {
1100 for (const experiment of experimentNames) {
1101 this._checkExperiment(experiment);
1102 this._serverEnabled.add(experiment);
1103 }
1104 }
1105
1106 /**
Blink Reformat4c46d092018-04-07 15:32:37 +00001107 * @param {string} experimentName
1108 */
1109 enableForTest(experimentName) {
1110 this._checkExperiment(experimentName);
1111 this._enabledTransiently[experimentName] = true;
1112 }
1113
1114 clearForTest() {
1115 this._experiments = [];
1116 this._experimentNames = {};
1117 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +00001118 this._serverEnabled.clear();
Blink Reformat4c46d092018-04-07 15:32:37 +00001119 }
1120
1121 cleanUpStaleExperiments() {
1122 const experimentsSetting = Runtime._experimentsSetting();
1123 const cleanedUpExperimentSetting = {};
1124 for (let i = 0; i < this._experiments.length; ++i) {
1125 const experimentName = this._experiments[i].name;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001126 if (experimentsSetting[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001127 cleanedUpExperimentSetting[experimentName] = true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001128 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001129 }
1130 this._setExperimentsSetting(cleanedUpExperimentSetting);
1131 }
1132
1133 /**
1134 * @param {string} experimentName
1135 */
1136 _checkExperiment(experimentName) {
1137 Runtime._assert(this._experimentNames[experimentName], 'Unknown experiment ' + experimentName);
1138 }
1139};
1140
1141/**
1142 * @unrestricted
1143 */
1144Runtime.Experiment = class {
1145 /**
1146 * @param {!Runtime.ExperimentsSupport} experiments
1147 * @param {string} name
1148 * @param {string} title
1149 * @param {boolean} hidden
1150 */
1151 constructor(experiments, name, title, hidden) {
1152 this.name = name;
1153 this.title = title;
1154 this.hidden = hidden;
1155 this._experiments = experiments;
1156 }
1157
1158 /**
1159 * @return {boolean}
1160 */
1161 isEnabled() {
1162 return this._experiments.isEnabled(this.name);
1163 }
1164
1165 /**
1166 * @param {boolean} enabled
1167 */
1168 setEnabled(enabled) {
1169 this._experiments.setEnabled(this.name, enabled);
1170 }
1171};
1172
Blink Reformat4c46d092018-04-07 15:32:37 +00001173// This must be constructed after the query parameters have been parsed.
1174Runtime.experiments = new Runtime.ExperimentsSupport();
1175
1176/** @type {Function} */
Pavel Feldman63e89eb2018-11-25 05:47:09 +00001177Runtime._appStartedPromiseCallback;
1178Runtime._appStartedPromise = new Promise(fulfil => Runtime._appStartedPromiseCallback = fulfil);
Vidal Diazleal0b6aff42019-04-20 01:15:43 +00001179
1180/** @type {function(string):string} */
1181Runtime._l10nCallback;
1182
Blink Reformat4c46d092018-04-07 15:32:37 +00001183/**
1184 * @type {?string}
1185 */
1186Runtime._remoteBase;
1187(function validateRemoteBase() {
James Lissiak821d2282019-05-15 15:32:04 +00001188 if (location.href.startsWith('devtools://devtools/bundled/') && Runtime.queryParam('remoteBase')) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001189 const versionMatch = /\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(Runtime.queryParam('remoteBase'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001190 if (versionMatch) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001191 Runtime._remoteBase = `${location.origin}/remote/serve_file/${versionMatch[1]}/`;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001192 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001193 }
1194})();
1195
1196
1197/**
1198 * @interface
1199 */
1200function ServicePort() {
1201}
1202
1203ServicePort.prototype = {
1204 /**
1205 * @param {function(string)} messageHandler
1206 * @param {function(string)} closeHandler
1207 */
1208 setHandlers(messageHandler, closeHandler) {},
1209
1210 /**
1211 * @param {string} message
1212 * @return {!Promise<boolean>}
1213 */
1214 send(message) {},
1215
1216 /**
1217 * @return {!Promise<boolean>}
1218 */
1219 close() {}
1220};
1221
1222/** @type {!Runtime} */
1223var runtime; // eslint-disable-line