blob: e958c077a004e5280a314a9de8d9ba07e4deb351 [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 */
Blink Reformat4c46d092018-04-07 15:32:37 +000030const _loadedScripts = {};
31
Blink Reformat4c46d092018-04-07 15:32:37 +000032(function() {
33const baseUrl = self.location ? self.location.origin + self.location.pathname : '';
34self._importScriptPathPrefix = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
35})();
36
Patrick Hulceb8c09f32018-06-11 21:28:22 +000037const REMOTE_MODULE_FALLBACK_REVISION = '@010ddcfda246975d194964ccf20038ebbdec6084';
38
Blink Reformat4c46d092018-04-07 15:32:37 +000039/**
40 * @unrestricted
41 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +000042class Runtime {
Blink Reformat4c46d092018-04-07 15:32:37 +000043 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +000044 * @param {!Array.<!ModuleDescriptor>} descriptors
Blink Reformat4c46d092018-04-07 15:32:37 +000045 */
46 constructor(descriptors) {
47 /** @type {!Array<!Runtime.Module>} */
48 this._modules = [];
49 /** @type {!Object<string, !Runtime.Module>} */
50 this._modulesMap = {};
Tim van der Lippe99e59b82019-09-30 20:00:59 +000051 /** @type {!Array<!Extension>} */
Blink Reformat4c46d092018-04-07 15:32:37 +000052 this._extensions = [];
53 /** @type {!Object<string, !function(new:Object)>} */
54 this._cachedTypeClasses = {};
Tim van der Lippe99e59b82019-09-30 20:00:59 +000055 /** @type {!Object<string, !ModuleDescriptor>} */
Blink Reformat4c46d092018-04-07 15:32:37 +000056 this._descriptorsMap = {};
57
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000058 for (let i = 0; i < descriptors.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +000059 this._registerModule(descriptors[i]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000060 }
Blink Reformat4c46d092018-04-07 15:32:37 +000061 }
62
63 /**
64 * @param {string} url
65 * @return {!Promise.<string>}
66 */
67 static loadResourcePromise(url) {
68 return new Promise(load);
69
70 /**
71 * @param {function(?)} fulfill
72 * @param {function(*)} reject
73 */
74 function load(fulfill, reject) {
75 const xhr = new XMLHttpRequest();
76 xhr.open('GET', url, true);
77 xhr.onreadystatechange = onreadystatechange;
78
79 /**
80 * @param {Event} e
81 */
82 function onreadystatechange(e) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000083 if (xhr.readyState !== XMLHttpRequest.DONE) {
Blink Reformat4c46d092018-04-07 15:32:37 +000084 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000085 }
Blink Reformat4c46d092018-04-07 15:32:37 +000086
Patrick Hulceb8c09f32018-06-11 21:28:22 +000087 // DevTools Proxy server can mask 404s as 200s, check the body to be sure
88 const status = /^HTTP\/1.1 404/.test(e.target.response) ? 404 : xhr.status;
89
90 if ([0, 200, 304].indexOf(status) === -1) // Testing harness file:/// results in 0.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000091 {
Patrick Hulceb8c09f32018-06-11 21:28:22 +000092 reject(new Error('While loading from url ' + url + ' server responded with a status of ' + status));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000093 } else {
Blink Reformat4c46d092018-04-07 15:32:37 +000094 fulfill(e.target.response);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000095 }
Blink Reformat4c46d092018-04-07 15:32:37 +000096 }
97 xhr.send(null);
98 }
99 }
100
101 /**
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000102 * @param {string} url
103 * @return {!Promise.<string>}
104 */
105 static loadResourcePromiseWithFallback(url) {
106 return Runtime.loadResourcePromise(url).catch(err => {
107 const urlWithFallbackVersion = url.replace(/@[0-9a-f]{40}/, REMOTE_MODULE_FALLBACK_REVISION);
108 // TODO(phulce): mark fallbacks in module.json and modify build script instead
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000109 if (urlWithFallbackVersion === url || !url.includes('audits_worker_module')) {
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000110 throw err;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000111 }
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000112 return Runtime.loadResourcePromise(urlWithFallbackVersion);
113 });
114 }
115
116 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000117 * http://tools.ietf.org/html/rfc3986#section-5.2.4
118 * @param {string} path
119 * @return {string}
120 */
121 static normalizePath(path) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000122 if (path.indexOf('..') === -1 && path.indexOf('.') === -1) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000123 return path;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000124 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000125
126 const normalizedSegments = [];
127 const segments = path.split('/');
128 for (let i = 0; i < segments.length; i++) {
129 const segment = segments[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000130 if (segment === '.') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000131 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000132 } else if (segment === '..') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000133 normalizedSegments.pop();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000134 } else if (segment) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000135 normalizedSegments.push(segment);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000136 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000137 }
138 let normalizedPath = normalizedSegments.join('/');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000139 if (normalizedPath[normalizedPath.length - 1] === '/') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000140 return normalizedPath;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000141 }
142 if (path[0] === '/' && normalizedPath) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000143 normalizedPath = '/' + normalizedPath;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000144 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000145 if ((path[path.length - 1] === '/') || (segments[segments.length - 1] === '.') ||
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000146 (segments[segments.length - 1] === '..')) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000147 normalizedPath = normalizedPath + '/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000148 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000149
150 return normalizedPath;
151 }
152
153 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000154 * @param {string} scriptName
155 * @param {string=} base
156 * @return {string}
157 */
158 static getResourceURL(scriptName, base) {
159 const sourceURL = (base || self._importScriptPathPrefix) + scriptName;
160 const schemaIndex = sourceURL.indexOf('://') + 3;
161 let pathIndex = sourceURL.indexOf('/', schemaIndex);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000162 if (pathIndex === -1) {
Mandy Chen20b48f62019-09-18 19:28:22 +0000163 pathIndex = sourceURL.length;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000164 }
Mandy Chen20b48f62019-09-18 19:28:22 +0000165 return sourceURL.substring(0, pathIndex) + Runtime.normalizePath(sourceURL.substring(pathIndex));
166 }
167
168 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000169 * @param {!Array.<string>} scriptNames
170 * @param {string=} base
171 * @return {!Promise.<undefined>}
172 */
173 static _loadScriptsPromise(scriptNames, base) {
174 /** @type {!Array<!Promise<undefined>>} */
175 const promises = [];
176 /** @type {!Array<string>} */
177 const urls = [];
178 const sources = new Array(scriptNames.length);
179 let scriptToEval = 0;
180 for (let i = 0; i < scriptNames.length; ++i) {
181 const scriptName = scriptNames[i];
Mandy Chen20b48f62019-09-18 19:28:22 +0000182 const sourceURL = Runtime.getResourceURL(scriptName, base);
Blink Reformat4c46d092018-04-07 15:32:37 +0000183
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000184 if (_loadedScripts[sourceURL]) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000185 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000186 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000187 urls.push(sourceURL);
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000188 const loadResourcePromise =
189 base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
190 promises.push(
191 loadResourcePromise.then(scriptSourceLoaded.bind(null, i), scriptSourceLoaded.bind(null, i, undefined)));
Blink Reformat4c46d092018-04-07 15:32:37 +0000192 }
193 return Promise.all(promises).then(undefined);
194
195 /**
196 * @param {number} scriptNumber
197 * @param {string=} scriptSource
198 */
199 function scriptSourceLoaded(scriptNumber, scriptSource) {
200 sources[scriptNumber] = scriptSource || '';
201 // Eval scripts as fast as possible.
202 while (typeof sources[scriptToEval] !== 'undefined') {
203 evaluateScript(urls[scriptToEval], sources[scriptToEval]);
204 ++scriptToEval;
205 }
206 }
207
208 /**
209 * @param {string} sourceURL
210 * @param {string=} scriptSource
211 */
212 function evaluateScript(sourceURL, scriptSource) {
213 _loadedScripts[sourceURL] = true;
214 if (!scriptSource) {
215 // Do not reject, as this is normal in the hosted mode.
216 console.error('Empty response arrived for script \'' + sourceURL + '\'');
217 return;
218 }
219 self.eval(scriptSource + '\n//# sourceURL=' + sourceURL);
220 }
221 }
222
223 /**
224 * @param {string} url
225 * @param {boolean} appendSourceURL
226 * @return {!Promise<undefined>}
227 */
228 static _loadResourceIntoCache(url, appendSourceURL) {
229 return Runtime.loadResourcePromise(url).then(
230 cacheResource.bind(this, url), cacheResource.bind(this, url, undefined));
231
232 /**
233 * @param {string} path
234 * @param {string=} content
235 */
236 function cacheResource(path, content) {
237 if (!content) {
238 console.error('Failed to load resource: ' + path);
239 return;
240 }
241 const sourceURL = appendSourceURL ? Runtime.resolveSourceURL(path) : '';
242 Runtime.cachedResources[path] = content + sourceURL;
243 }
244 }
245
246 /**
247 * @return {!Promise}
248 */
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000249 static async appStarted() {
250 return Runtime._appStartedPromise;
Blink Reformat4c46d092018-04-07 15:32:37 +0000251 }
252
253 /**
254 * @param {string} appName
255 * @return {!Promise.<undefined>}
256 */
257 static async startApplication(appName) {
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000258 console.timeStamp('Root.Runtime.startApplication');
Blink Reformat4c46d092018-04-07 15:32:37 +0000259
260 const allDescriptorsByName = {};
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000261 for (let i = 0; i < Root.allDescriptors.length; ++i) {
262 const d = Root.allDescriptors[i];
Blink Reformat4c46d092018-04-07 15:32:37 +0000263 allDescriptorsByName[d['name']] = d;
264 }
265
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000266 if (!Root.applicationDescriptor) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000267 let data = await Runtime.loadResourcePromise(appName + '.json');
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000268 Root.applicationDescriptor = JSON.parse(data);
269 let descriptor = Root.applicationDescriptor;
Blink Reformat4c46d092018-04-07 15:32:37 +0000270 while (descriptor.extends) {
271 data = await Runtime.loadResourcePromise(descriptor.extends + '.json');
272 descriptor = JSON.parse(data);
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000273 Root.applicationDescriptor.modules = descriptor.modules.concat(Root.applicationDescriptor.modules);
Blink Reformat4c46d092018-04-07 15:32:37 +0000274 }
275 }
276
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000277 const configuration = Root.applicationDescriptor.modules;
Blink Reformat4c46d092018-04-07 15:32:37 +0000278 const moduleJSONPromises = [];
279 const coreModuleNames = [];
280 for (let i = 0; i < configuration.length; ++i) {
281 const descriptor = configuration[i];
282 const name = descriptor['name'];
283 const moduleJSON = allDescriptorsByName[name];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000284 if (moduleJSON) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000285 moduleJSONPromises.push(Promise.resolve(moduleJSON));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000286 } else {
Blink Reformat4c46d092018-04-07 15:32:37 +0000287 moduleJSONPromises.push(Runtime.loadResourcePromise(name + '/module.json').then(JSON.parse.bind(JSON)));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000288 }
289 if (descriptor['type'] === 'autostart') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000290 coreModuleNames.push(name);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000291 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000292 }
293
294 const moduleDescriptors = await Promise.all(moduleJSONPromises);
295
296 for (let i = 0; i < moduleDescriptors.length; ++i) {
297 moduleDescriptors[i].name = configuration[i]['name'];
298 moduleDescriptors[i].condition = configuration[i]['condition'];
299 moduleDescriptors[i].remote = configuration[i]['type'] === 'remote';
300 }
301 self.runtime = new Runtime(moduleDescriptors);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000302 if (coreModuleNames) {
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000303 await self.runtime._loadAutoStartModules(coreModuleNames);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000304 }
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000305 Runtime._appStartedPromiseCallback();
Blink Reformat4c46d092018-04-07 15:32:37 +0000306 }
307
308 /**
309 * @param {string} appName
310 * @return {!Promise.<undefined>}
311 */
312 static startWorker(appName) {
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000313 return Root.Runtime.startApplication(appName).then(sendWorkerReady);
Blink Reformat4c46d092018-04-07 15:32:37 +0000314
315 function sendWorkerReady() {
316 self.postMessage('workerReady');
317 }
318 }
319
320 /**
321 * @param {string} name
322 * @return {?string}
323 */
324 static queryParam(name) {
Ingvar Stepanyand48ae082018-11-07 04:38:32 +0000325 return Runtime._queryParamsObject.get(name);
Blink Reformat4c46d092018-04-07 15:32:37 +0000326 }
327
328 /**
329 * @return {string}
330 */
331 static queryParamsString() {
332 return location.search;
333 }
334
335 /**
336 * @return {!Object}
337 */
338 static _experimentsSetting() {
339 try {
340 return /** @type {!Object} */ (
341 JSON.parse(self.localStorage && self.localStorage['experiments'] ? self.localStorage['experiments'] : '{}'));
342 } catch (e) {
343 console.error('Failed to parse localStorage[\'experiments\']');
344 return {};
345 }
346 }
347
348 static _assert(value, message) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000349 if (value) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000350 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000351 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000352 Runtime._originalAssert.call(Runtime._console, value, message + ' ' + new Error().stack);
353 }
354
355 /**
356 * @param {string} platform
357 */
358 static setPlatform(platform) {
359 Runtime._platform = platform;
360 }
361
362 /**
363 * @param {!Object} descriptor
364 * @return {boolean}
365 */
366 static _isDescriptorEnabled(descriptor) {
367 const activatorExperiment = descriptor['experiment'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000368 if (activatorExperiment === '*') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000369 return Runtime.experiments.supportEnabled();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000370 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000371 if (activatorExperiment && activatorExperiment.startsWith('!') &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000372 Runtime.experiments.isEnabled(activatorExperiment.substring(1))) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000373 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000374 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000375 if (activatorExperiment && !activatorExperiment.startsWith('!') &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000376 !Runtime.experiments.isEnabled(activatorExperiment)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000377 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000378 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000379 const condition = descriptor['condition'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000380 if (condition && !condition.startsWith('!') && !Runtime.queryParam(condition)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000381 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000382 }
383 if (condition && condition.startsWith('!') && Runtime.queryParam(condition.substring(1))) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000384 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000385 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000386 return true;
387 }
388
389 /**
390 * @param {string} path
391 * @return {string}
392 */
393 static resolveSourceURL(path) {
394 let sourceURL = self.location.href;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000395 if (self.location.search) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000396 sourceURL = sourceURL.replace(self.location.search, '');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000397 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000398 sourceURL = sourceURL.substring(0, sourceURL.lastIndexOf('/') + 1) + path;
399 return '\n/*# sourceURL=' + sourceURL + ' */';
400 }
401
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000402 /**
403 * @param {function(string):string} localizationFunction
404 */
405 static setL10nCallback(localizationFunction) {
406 Runtime._l10nCallback = localizationFunction;
407 }
408
Blink Reformat4c46d092018-04-07 15:32:37 +0000409 useTestBase() {
410 Runtime._remoteBase = 'http://localhost:8000/inspector-sources/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000411 if (Runtime.queryParam('debugFrontend')) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000412 Runtime._remoteBase += 'debug/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000413 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000414 }
415
416 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000417 * @param {string} moduleName
418 * @return {!Runtime.Module}
419 */
420 module(moduleName) {
421 return this._modulesMap[moduleName];
422 }
423
424 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000425 * @param {!ModuleDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000426 */
427 _registerModule(descriptor) {
428 const module = new Runtime.Module(this, descriptor);
429 this._modules.push(module);
430 this._modulesMap[descriptor['name']] = module;
431 }
432
433 /**
434 * @param {string} moduleName
435 * @return {!Promise.<undefined>}
436 */
437 loadModulePromise(moduleName) {
438 return this._modulesMap[moduleName]._loadPromise();
439 }
440
441 /**
442 * @param {!Array.<string>} moduleNames
443 * @return {!Promise.<!Array.<*>>}
444 */
445 _loadAutoStartModules(moduleNames) {
446 const promises = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000447 for (let i = 0; i < moduleNames.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000448 promises.push(this.loadModulePromise(moduleNames[i]));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000449 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000450 return Promise.all(promises);
451 }
452
453 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000454 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000455 * @param {?function(function(new:Object)):boolean} predicate
456 * @return {boolean}
457 */
458 _checkExtensionApplicability(extension, predicate) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000459 if (!predicate) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000460 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000461 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000462 const contextTypes = extension.descriptor().contextTypes;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000463 if (!contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000464 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000465 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000466 for (let i = 0; i < contextTypes.length; ++i) {
467 const contextType = this._resolve(contextTypes[i]);
468 const isMatching = !!contextType && predicate(contextType);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000469 if (isMatching) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000470 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000471 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000472 }
473 return false;
474 }
475
476 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000477 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000478 * @param {?Object} context
479 * @return {boolean}
480 */
481 isExtensionApplicableToContext(extension, context) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000482 if (!context) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000483 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000484 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000485 return this._checkExtensionApplicability(extension, isInstanceOf);
486
487 /**
488 * @param {!Function} targetType
489 * @return {boolean}
490 */
491 function isInstanceOf(targetType) {
492 return context instanceof targetType;
493 }
494 }
495
496 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000497 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000498 * @param {!Set.<!Function>=} currentContextTypes
499 * @return {boolean}
500 */
501 isExtensionApplicableToContextTypes(extension, currentContextTypes) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000502 if (!extension.descriptor().contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000503 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000504 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000505
506 return this._checkExtensionApplicability(extension, currentContextTypes ? isContextTypeKnown : null);
507
508 /**
509 * @param {!Function} targetType
510 * @return {boolean}
511 */
512 function isContextTypeKnown(targetType) {
513 return currentContextTypes.has(targetType);
514 }
515 }
516
517 /**
518 * @param {*} type
519 * @param {?Object=} context
520 * @param {boolean=} sortByTitle
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000521 * @return {!Array.<!Extension>}
Blink Reformat4c46d092018-04-07 15:32:37 +0000522 */
523 extensions(type, context, sortByTitle) {
524 return this._extensions.filter(filter).sort(sortByTitle ? titleComparator : orderComparator);
525
526 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000527 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000528 * @return {boolean}
529 */
530 function filter(extension) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000531 if (extension._type !== type && extension._typeClass() !== type) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000532 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000533 }
534 if (!extension.enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000535 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000536 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000537 return !context || extension.isApplicable(context);
538 }
539
540 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000541 * @param {!Extension} extension1
542 * @param {!Extension} extension2
Blink Reformat4c46d092018-04-07 15:32:37 +0000543 * @return {number}
544 */
545 function orderComparator(extension1, extension2) {
546 const order1 = extension1.descriptor()['order'] || 0;
547 const order2 = extension2.descriptor()['order'] || 0;
548 return order1 - order2;
549 }
550
551 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000552 * @param {!Extension} extension1
553 * @param {!Extension} extension2
Blink Reformat4c46d092018-04-07 15:32:37 +0000554 * @return {number}
555 */
556 function titleComparator(extension1, extension2) {
557 const title1 = extension1.title() || '';
558 const title2 = extension2.title() || '';
559 return title1.localeCompare(title2);
560 }
561 }
562
563 /**
564 * @param {*} type
565 * @param {?Object=} context
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000566 * @return {?Extension}
Blink Reformat4c46d092018-04-07 15:32:37 +0000567 */
568 extension(type, context) {
569 return this.extensions(type, context)[0] || null;
570 }
571
572 /**
573 * @param {*} type
574 * @param {?Object=} context
575 * @return {!Promise.<!Array.<!Object>>}
576 */
577 allInstances(type, context) {
578 return Promise.all(this.extensions(type, context).map(extension => extension.instance()));
579 }
580
581 /**
582 * @return {?function(new:Object)}
583 */
584 _resolve(typeName) {
585 if (!this._cachedTypeClasses[typeName]) {
586 const path = typeName.split('.');
587 let object = self;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000588 for (let i = 0; object && (i < path.length); ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000589 object = object[path[i]];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000590 }
591 if (object) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000592 this._cachedTypeClasses[typeName] = /** @type function(new:Object) */ (object);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000593 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000594 }
595 return this._cachedTypeClasses[typeName] || null;
596 }
597
598 /**
Alexei Filippovf2e42e42019-04-04 21:40:45 +0000599 * @param {function(new:T)} constructorFunction
600 * @return {!T}
601 * @template T
Blink Reformat4c46d092018-04-07 15:32:37 +0000602 */
603 sharedInstance(constructorFunction) {
604 if (Runtime._instanceSymbol in constructorFunction &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000605 Object.getOwnPropertySymbols(constructorFunction).includes(Runtime._instanceSymbol)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000606 return constructorFunction[Runtime._instanceSymbol];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000607 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000608
609 const instance = new constructorFunction();
610 constructorFunction[Runtime._instanceSymbol] = instance;
611 return instance;
612 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000613}
Blink Reformat4c46d092018-04-07 15:32:37 +0000614
Ingvar Stepanyand48ae082018-11-07 04:38:32 +0000615/** @type {!URLSearchParams} */
616Runtime._queryParamsObject = new URLSearchParams(Runtime.queryParamsString());
Blink Reformat4c46d092018-04-07 15:32:37 +0000617
618Runtime._instanceSymbol = Symbol('instance');
619
620/**
621 * @type {!Object.<string, string>}
622 */
623Runtime.cachedResources = {
624 __proto__: null
625};
626
627
628Runtime._console = console;
629Runtime._originalAssert = console.assert;
630
631
632Runtime._platform = '';
633
634
635/**
636 * @unrestricted
637 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000638class ModuleDescriptor {
Blink Reformat4c46d092018-04-07 15:32:37 +0000639 constructor() {
640 /**
641 * @type {string}
642 */
643 this.name;
644
645 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000646 * @type {!Array.<!RuntimeExtensionDescriptor>}
Blink Reformat4c46d092018-04-07 15:32:37 +0000647 */
648 this.extensions;
649
650 /**
651 * @type {!Array.<string>|undefined}
652 */
653 this.dependencies;
654
655 /**
656 * @type {!Array.<string>}
657 */
658 this.scripts;
659
660 /**
661 * @type {string|undefined}
662 */
663 this.condition;
664
665 /**
666 * @type {boolean|undefined}
667 */
668 this.remote;
669 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000670}
Blink Reformat4c46d092018-04-07 15:32:37 +0000671
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000672// This class is named like this, because we already have an "ExtensionDescriptor" in the externs
673// These two do not share the same structure
Blink Reformat4c46d092018-04-07 15:32:37 +0000674/**
675 * @unrestricted
676 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000677class RuntimeExtensionDescriptor {
Blink Reformat4c46d092018-04-07 15:32:37 +0000678 constructor() {
679 /**
680 * @type {string}
681 */
682 this.type;
683
684 /**
685 * @type {string|undefined}
686 */
687 this.className;
688
689 /**
690 * @type {string|undefined}
691 */
692 this.factoryName;
693
694 /**
695 * @type {!Array.<string>|undefined}
696 */
697 this.contextTypes;
698 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000699}
Blink Reformat4c46d092018-04-07 15:32:37 +0000700
701/**
702 * @unrestricted
703 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000704class Module {
Blink Reformat4c46d092018-04-07 15:32:37 +0000705 /**
706 * @param {!Runtime} manager
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000707 * @param {!ModuleDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000708 */
709 constructor(manager, descriptor) {
710 this._manager = manager;
711 this._descriptor = descriptor;
712 this._name = descriptor.name;
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000713 /** @type {!Array<!Extension>} */
Blink Reformat4c46d092018-04-07 15:32:37 +0000714 this._extensions = [];
715
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000716 /** @type {!Map<string, !Array<!Extension>>} */
Blink Reformat4c46d092018-04-07 15:32:37 +0000717 this._extensionsByClassName = new Map();
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000718 const extensions = /** @type {?Array.<!RuntimeExtensionDescriptor>} */ (descriptor.extensions);
Blink Reformat4c46d092018-04-07 15:32:37 +0000719 for (let i = 0; extensions && i < extensions.length; ++i) {
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000720 const extension = new Extension(this, extensions[i]);
Blink Reformat4c46d092018-04-07 15:32:37 +0000721 this._manager._extensions.push(extension);
722 this._extensions.push(extension);
723 }
724 this._loadedForTest = false;
725 }
726
727 /**
728 * @return {string}
729 */
730 name() {
731 return this._name;
732 }
733
734 /**
735 * @return {boolean}
736 */
737 enabled() {
738 return Runtime._isDescriptorEnabled(this._descriptor);
739 }
740
741 /**
742 * @param {string} name
743 * @return {string}
744 */
745 resource(name) {
746 const fullName = this._name + '/' + name;
747 const content = Runtime.cachedResources[fullName];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000748 if (!content) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000749 throw new Error(fullName + ' not preloaded. Check module.json');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000750 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000751 return content;
752 }
753
754 /**
755 * @return {!Promise.<undefined>}
756 */
757 _loadPromise() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000758 if (!this.enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000759 return Promise.reject(new Error('Module ' + this._name + ' is not enabled'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000760 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000761
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000762 if (this._pendingLoadPromise) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000763 return this._pendingLoadPromise;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000764 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000765
766 const dependencies = this._descriptor.dependencies;
767 const dependencyPromises = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000768 for (let i = 0; dependencies && i < dependencies.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000769 dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPromise());
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000770 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000771
772 this._pendingLoadPromise = Promise.all(dependencyPromises)
773 .then(this._loadResources.bind(this))
774 .then(this._loadScripts.bind(this))
775 .then(() => this._loadedForTest = true);
776
777 return this._pendingLoadPromise;
778 }
779
780 /**
781 * @return {!Promise.<undefined>}
782 * @this {Runtime.Module}
783 */
784 _loadResources() {
785 const resources = this._descriptor['resources'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000786 if (!resources || !resources.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000787 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000788 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000789 const promises = [];
790 for (let i = 0; i < resources.length; ++i) {
791 const url = this._modularizeURL(resources[i]);
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000792 const isHtml = url.endsWith('.html');
793 promises.push(Runtime._loadResourceIntoCache(url, !isHtml /* appendSourceURL */));
Blink Reformat4c46d092018-04-07 15:32:37 +0000794 }
795 return Promise.all(promises).then(undefined);
796 }
797
798 /**
799 * @return {!Promise.<undefined>}
800 */
801 _loadScripts() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000802 if (!this._descriptor.scripts || !this._descriptor.scripts.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000803 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000804 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000805
806 // Module namespaces.
807 // NOTE: Update scripts/special_case_namespaces.json if you add a special cased namespace.
808 // The namespace keyword confuses clang-format.
809 // clang-format off
810 const specialCases = {
811 'sdk': 'SDK',
812 'js_sdk': 'JSSDK',
813 'browser_sdk': 'BrowserSDK',
814 'ui': 'UI',
815 'object_ui': 'ObjectUI',
Joel Einbinder3f23eb22018-05-14 23:27:51 +0000816 'javascript_metadata': 'JavaScriptMetadata',
Blink Reformat4c46d092018-04-07 15:32:37 +0000817 'perf_ui': 'PerfUI',
818 'har_importer': 'HARImporter',
819 'sdk_test_runner': 'SDKTestRunner',
820 'cpu_profiler_test_runner': 'CPUProfilerTestRunner'
821 };
822 const namespace = specialCases[this._name] || this._name.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
823 self[namespace] = self[namespace] || {};
824 // clang-format on
825 return Runtime._loadScriptsPromise(this._descriptor.scripts.map(this._modularizeURL, this), this._remoteBase());
826 }
827
828 /**
829 * @param {string} resourceName
830 */
831 _modularizeURL(resourceName) {
832 return Runtime.normalizePath(this._name + '/' + resourceName);
833 }
834
835 /**
836 * @return {string|undefined}
837 */
838 _remoteBase() {
839 return !Runtime.queryParam('debugFrontend') && this._descriptor.remote && Runtime._remoteBase || undefined;
840 }
841
842 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000843 * @param {string} resourceName
844 * @return {!Promise.<string>}
845 */
846 fetchResource(resourceName) {
847 const base = this._remoteBase();
848 const sourceURL = Runtime.getResourceURL(this._modularizeURL(resourceName), base);
849 return base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
850 }
851
852 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000853 * @param {string} value
854 * @return {string}
855 */
856 substituteURL(value) {
857 const base = this._remoteBase() || '';
858 return value.replace(/@url\(([^\)]*?)\)/g, convertURL.bind(this));
859
860 function convertURL(match, url) {
861 return base + this._modularizeURL(url);
862 }
863 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000864}
Blink Reformat4c46d092018-04-07 15:32:37 +0000865
866
867/**
868 * @unrestricted
869 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000870class Extension { /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000871 * @param {!Runtime.Module} module
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000872 * @param {!RuntimeExtensionDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000873 */
874 constructor(module, descriptor) {
875 this._module = module;
876 this._descriptor = descriptor;
877
878 this._type = descriptor.type;
879 this._hasTypeClass = this._type.charAt(0) === '@';
880
881 /**
882 * @type {?string}
883 */
884 this._className = descriptor.className || null;
885 this._factoryName = descriptor.factoryName || null;
886 }
887
888 /**
889 * @return {!Object}
890 */
891 descriptor() {
892 return this._descriptor;
893 }
894
895 /**
896 * @return {!Runtime.Module}
897 */
898 module() {
899 return this._module;
900 }
901
902 /**
903 * @return {boolean}
904 */
905 enabled() {
906 return this._module.enabled() && Runtime._isDescriptorEnabled(this.descriptor());
907 }
908
909 /**
910 * @return {?function(new:Object)}
911 */
912 _typeClass() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000913 if (!this._hasTypeClass) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000914 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000915 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000916 return this._module._manager._resolve(this._type.substring(1));
917 }
918
919 /**
920 * @param {?Object} context
921 * @return {boolean}
922 */
923 isApplicable(context) {
924 return this._module._manager.isExtensionApplicableToContext(this, context);
925 }
926
927 /**
928 * @return {!Promise.<!Object>}
929 */
930 instance() {
931 return this._module._loadPromise().then(this._createInstance.bind(this));
932 }
933
934 /**
935 * @return {boolean}
936 */
937 canInstantiate() {
938 return !!(this._className || this._factoryName);
939 }
940
941 /**
942 * @return {!Object}
943 */
944 _createInstance() {
945 const className = this._className || this._factoryName;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000946 if (!className) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000947 throw new Error('Could not instantiate extension with no class');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000948 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000949 const constructorFunction = self.eval(/** @type {string} */ (className));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000950 if (!(constructorFunction instanceof Function)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000951 throw new Error('Could not instantiate: ' + className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000952 }
953 if (this._className) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000954 return this._module._manager.sharedInstance(constructorFunction);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000955 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000956 return new constructorFunction(this);
957 }
958
959 /**
960 * @return {string}
961 */
962 title() {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000963 const title = this._descriptor['title-' + Runtime._platform] || this._descriptor['title'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000964 if (title && Runtime._l10nCallback) {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000965 return Runtime._l10nCallback(title);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000966 }
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000967 return title;
Blink Reformat4c46d092018-04-07 15:32:37 +0000968 }
969
970 /**
971 * @param {function(new:Object)} contextType
972 * @return {boolean}
973 */
974 hasContextType(contextType) {
975 const contextTypes = this.descriptor().contextTypes;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000976 if (!contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000977 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000978 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000979 for (let i = 0; i < contextTypes.length; ++i) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000980 if (contextType === this._module._manager._resolve(contextTypes[i])) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000981 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000982 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000983 }
984 return false;
985 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000986}
Blink Reformat4c46d092018-04-07 15:32:37 +0000987
988/**
989 * @unrestricted
990 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000991class ExperimentsSupport {
Blink Reformat4c46d092018-04-07 15:32:37 +0000992 constructor() {
993 this._supportEnabled = Runtime.queryParam('experiments') !== null;
994 this._experiments = [];
995 this._experimentNames = {};
996 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +0000997 /** @type {!Set<string>} */
998 this._serverEnabled = new Set();
Blink Reformat4c46d092018-04-07 15:32:37 +0000999 }
1000
1001 /**
1002 * @return {!Array.<!Runtime.Experiment>}
1003 */
1004 allConfigurableExperiments() {
1005 const result = [];
1006 for (let i = 0; i < this._experiments.length; i++) {
1007 const experiment = this._experiments[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001008 if (!this._enabledTransiently[experiment.name]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001009 result.push(experiment);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001010 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001011 }
1012 return result;
1013 }
1014
1015 /**
1016 * @return {boolean}
1017 */
1018 supportEnabled() {
1019 return this._supportEnabled;
1020 }
1021
1022 /**
1023 * @param {!Object} value
1024 */
1025 _setExperimentsSetting(value) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001026 if (!self.localStorage) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001027 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001028 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001029 self.localStorage['experiments'] = JSON.stringify(value);
1030 }
1031
1032 /**
1033 * @param {string} experimentName
1034 * @param {string} experimentTitle
1035 * @param {boolean=} hidden
1036 */
1037 register(experimentName, experimentTitle, hidden) {
1038 Runtime._assert(!this._experimentNames[experimentName], 'Duplicate registration of experiment ' + experimentName);
1039 this._experimentNames[experimentName] = true;
1040 this._experiments.push(new Runtime.Experiment(this, experimentName, experimentTitle, !!hidden));
1041 }
1042
1043 /**
1044 * @param {string} experimentName
1045 * @return {boolean}
1046 */
1047 isEnabled(experimentName) {
1048 this._checkExperiment(experimentName);
Pavel Feldmanc0395912018-08-03 02:05:00 +00001049 // Check for explicitly disabled experiments first - the code could call setEnable(false) on the experiment enabled
1050 // by default and we should respect that.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001051 if (Runtime._experimentsSetting()[experimentName] === false) {
Pavel Feldmanc0395912018-08-03 02:05:00 +00001052 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001053 }
1054 if (this._enabledTransiently[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001055 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001056 }
1057 if (this._serverEnabled.has(experimentName)) {
Jeff Fisher85b08e12019-06-13 22:24:21 +00001058 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001059 }
1060 if (!this.supportEnabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001061 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001062 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001063
1064 return !!Runtime._experimentsSetting()[experimentName];
1065 }
1066
1067 /**
1068 * @param {string} experimentName
1069 * @param {boolean} enabled
1070 */
1071 setEnabled(experimentName, enabled) {
1072 this._checkExperiment(experimentName);
1073 const experimentsSetting = Runtime._experimentsSetting();
1074 experimentsSetting[experimentName] = enabled;
1075 this._setExperimentsSetting(experimentsSetting);
1076 }
1077
1078 /**
1079 * @param {!Array.<string>} experimentNames
1080 */
1081 setDefaultExperiments(experimentNames) {
1082 for (let i = 0; i < experimentNames.length; ++i) {
1083 this._checkExperiment(experimentNames[i]);
1084 this._enabledTransiently[experimentNames[i]] = true;
1085 }
1086 }
1087
1088 /**
Jeff Fisher85b08e12019-06-13 22:24:21 +00001089 * @param {!Array.<string>} experimentNames
1090 */
1091 setServerEnabledExperiments(experimentNames) {
1092 for (const experiment of experimentNames) {
1093 this._checkExperiment(experiment);
1094 this._serverEnabled.add(experiment);
1095 }
1096 }
1097
1098 /**
Blink Reformat4c46d092018-04-07 15:32:37 +00001099 * @param {string} experimentName
1100 */
1101 enableForTest(experimentName) {
1102 this._checkExperiment(experimentName);
1103 this._enabledTransiently[experimentName] = true;
1104 }
1105
1106 clearForTest() {
1107 this._experiments = [];
1108 this._experimentNames = {};
1109 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +00001110 this._serverEnabled.clear();
Blink Reformat4c46d092018-04-07 15:32:37 +00001111 }
1112
1113 cleanUpStaleExperiments() {
1114 const experimentsSetting = Runtime._experimentsSetting();
1115 const cleanedUpExperimentSetting = {};
1116 for (let i = 0; i < this._experiments.length; ++i) {
1117 const experimentName = this._experiments[i].name;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001118 if (experimentsSetting[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001119 cleanedUpExperimentSetting[experimentName] = true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001120 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001121 }
1122 this._setExperimentsSetting(cleanedUpExperimentSetting);
1123 }
1124
1125 /**
1126 * @param {string} experimentName
1127 */
1128 _checkExperiment(experimentName) {
1129 Runtime._assert(this._experimentNames[experimentName], 'Unknown experiment ' + experimentName);
1130 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001131}
Blink Reformat4c46d092018-04-07 15:32:37 +00001132
1133/**
1134 * @unrestricted
1135 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001136class Experiment {
Blink Reformat4c46d092018-04-07 15:32:37 +00001137 /**
1138 * @param {!Runtime.ExperimentsSupport} experiments
1139 * @param {string} name
1140 * @param {string} title
1141 * @param {boolean} hidden
1142 */
1143 constructor(experiments, name, title, hidden) {
1144 this.name = name;
1145 this.title = title;
1146 this.hidden = hidden;
1147 this._experiments = experiments;
1148 }
1149
1150 /**
1151 * @return {boolean}
1152 */
1153 isEnabled() {
1154 return this._experiments.isEnabled(this.name);
1155 }
1156
1157 /**
1158 * @param {boolean} enabled
1159 */
1160 setEnabled(enabled) {
1161 this._experiments.setEnabled(this.name, enabled);
1162 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001163}
Blink Reformat4c46d092018-04-07 15:32:37 +00001164
Blink Reformat4c46d092018-04-07 15:32:37 +00001165// This must be constructed after the query parameters have been parsed.
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001166Runtime.experiments = new ExperimentsSupport();
Blink Reformat4c46d092018-04-07 15:32:37 +00001167
1168/** @type {Function} */
Pavel Feldman63e89eb2018-11-25 05:47:09 +00001169Runtime._appStartedPromiseCallback;
1170Runtime._appStartedPromise = new Promise(fulfil => Runtime._appStartedPromiseCallback = fulfil);
Vidal Diazleal0b6aff42019-04-20 01:15:43 +00001171
1172/** @type {function(string):string} */
1173Runtime._l10nCallback;
1174
Blink Reformat4c46d092018-04-07 15:32:37 +00001175/**
1176 * @type {?string}
1177 */
1178Runtime._remoteBase;
1179(function validateRemoteBase() {
James Lissiak821d2282019-05-15 15:32:04 +00001180 if (location.href.startsWith('devtools://devtools/bundled/') && Runtime.queryParam('remoteBase')) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001181 const versionMatch = /\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(Runtime.queryParam('remoteBase'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001182 if (versionMatch) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001183 Runtime._remoteBase = `${location.origin}/remote/serve_file/${versionMatch[1]}/`;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001184 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001185 }
1186})();
1187
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001188self.Root = self.Root || {};
1189Root = Root || {};
Blink Reformat4c46d092018-04-07 15:32:37 +00001190
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001191// This gets all concatenated module descriptors in the release mode.
1192Root.allDescriptors = [];
Blink Reformat4c46d092018-04-07 15:32:37 +00001193
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001194Root.applicationDescriptor = undefined;
Blink Reformat4c46d092018-04-07 15:32:37 +00001195
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001196/** @constructor */
1197Root.Runtime = Runtime;
Blink Reformat4c46d092018-04-07 15:32:37 +00001198
1199/** @type {!Runtime} */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001200Root.runtime;
1201
1202/** @constructor */
1203Root.Runtime.ModuleDescriptor = ModuleDescriptor;
1204
1205/** @constructor */
1206Root.Runtime.ExtensionDescriptor = RuntimeExtensionDescriptor;
1207
1208/** @constructor */
1209Root.Runtime.Extension = Extension;
1210
1211/** @constructor */
1212Root.Runtime.Module = Module;
1213
1214/** @constructor */
1215Root.Runtime.ExperimentsSupport = ExperimentsSupport;
1216
1217/** @constructor */
1218Root.Runtime.Experiment = Experiment;