blob: c48107e252f3da422e09d872cc4392c6303aaa56 [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 /**
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +000064 * @private
Blink Reformat4c46d092018-04-07 15:32:37 +000065 * @param {string} url
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +000066 * @param {boolean} asBinary
67 * @template T
68 * @return {!Promise.<T>}
Blink Reformat4c46d092018-04-07 15:32:37 +000069 */
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +000070 static _loadResourcePromise(url, asBinary) {
Blink Reformat4c46d092018-04-07 15:32:37 +000071 return new Promise(load);
72
73 /**
74 * @param {function(?)} fulfill
75 * @param {function(*)} reject
76 */
77 function load(fulfill, reject) {
78 const xhr = new XMLHttpRequest();
79 xhr.open('GET', url, true);
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +000080 if (asBinary) {
81 xhr.responseType = 'arraybuffer';
82 }
Blink Reformat4c46d092018-04-07 15:32:37 +000083 xhr.onreadystatechange = onreadystatechange;
84
85 /**
86 * @param {Event} e
87 */
88 function onreadystatechange(e) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000089 if (xhr.readyState !== XMLHttpRequest.DONE) {
Blink Reformat4c46d092018-04-07 15:32:37 +000090 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +000091 }
Blink Reformat4c46d092018-04-07 15:32:37 +000092
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +000093 const {response} = e.target;
94
95 const text = asBinary ? new TextDecoder().decode(response) : response;
96
Patrick Hulceb8c09f32018-06-11 21:28:22 +000097 // DevTools Proxy server can mask 404s as 200s, check the body to be sure
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +000098 const status = /^HTTP\/1.1 404/.test(text) ? 404 : xhr.status;
Patrick Hulceb8c09f32018-06-11 21:28:22 +000099
100 if ([0, 200, 304].indexOf(status) === -1) // Testing harness file:/// results in 0.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000101 {
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000102 reject(new Error('While loading from url ' + url + ' server responded with a status of ' + status));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000103 } else {
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +0000104 fulfill(response);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000105 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000106 }
107 xhr.send(null);
108 }
109 }
110
111 /**
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000112 * @param {string} url
113 * @return {!Promise.<string>}
114 */
Ingvar Stepanyan1a8762b2019-10-29 18:24:39 +0000115 static loadResourcePromise(url) {
116 return Runtime._loadResourcePromise(url, false);
117 }
118
119 /**
120 * @param {string} url
121 * @return {!Promise.<!ArrayBuffer>}
122 */
123 static loadBinaryResourcePromise(url) {
124 return Runtime._loadResourcePromise(url, true);
125 }
126
127 /**
128 * @param {string} url
129 * @return {!Promise.<string>}
130 */
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000131 static loadResourcePromiseWithFallback(url) {
132 return Runtime.loadResourcePromise(url).catch(err => {
133 const urlWithFallbackVersion = url.replace(/@[0-9a-f]{40}/, REMOTE_MODULE_FALLBACK_REVISION);
134 // TODO(phulce): mark fallbacks in module.json and modify build script instead
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000135 if (urlWithFallbackVersion === url || !url.includes('audits_worker_module')) {
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000136 throw err;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000137 }
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000138 return Runtime.loadResourcePromise(urlWithFallbackVersion);
139 });
140 }
141
142 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000143 * http://tools.ietf.org/html/rfc3986#section-5.2.4
144 * @param {string} path
145 * @return {string}
146 */
147 static normalizePath(path) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000148 if (path.indexOf('..') === -1 && path.indexOf('.') === -1) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000149 return path;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000150 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000151
152 const normalizedSegments = [];
153 const segments = path.split('/');
154 for (let i = 0; i < segments.length; i++) {
155 const segment = segments[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000156 if (segment === '.') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000157 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000158 } else if (segment === '..') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000159 normalizedSegments.pop();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000160 } else if (segment) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000161 normalizedSegments.push(segment);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000162 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000163 }
164 let normalizedPath = normalizedSegments.join('/');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000165 if (normalizedPath[normalizedPath.length - 1] === '/') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000166 return normalizedPath;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000167 }
168 if (path[0] === '/' && normalizedPath) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000169 normalizedPath = '/' + normalizedPath;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000170 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000171 if ((path[path.length - 1] === '/') || (segments[segments.length - 1] === '.') ||
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000172 (segments[segments.length - 1] === '..')) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000173 normalizedPath = normalizedPath + '/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000174 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000175
176 return normalizedPath;
177 }
178
179 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000180 * @param {string} scriptName
181 * @param {string=} base
182 * @return {string}
183 */
184 static getResourceURL(scriptName, base) {
185 const sourceURL = (base || self._importScriptPathPrefix) + scriptName;
186 const schemaIndex = sourceURL.indexOf('://') + 3;
187 let pathIndex = sourceURL.indexOf('/', schemaIndex);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000188 if (pathIndex === -1) {
Mandy Chen20b48f62019-09-18 19:28:22 +0000189 pathIndex = sourceURL.length;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000190 }
Mandy Chen20b48f62019-09-18 19:28:22 +0000191 return sourceURL.substring(0, pathIndex) + Runtime.normalizePath(sourceURL.substring(pathIndex));
192 }
193
194 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000195 * @param {!Array.<string>} scriptNames
196 * @param {string=} base
197 * @return {!Promise.<undefined>}
198 */
199 static _loadScriptsPromise(scriptNames, base) {
200 /** @type {!Array<!Promise<undefined>>} */
201 const promises = [];
202 /** @type {!Array<string>} */
203 const urls = [];
204 const sources = new Array(scriptNames.length);
205 let scriptToEval = 0;
206 for (let i = 0; i < scriptNames.length; ++i) {
207 const scriptName = scriptNames[i];
Mandy Chen20b48f62019-09-18 19:28:22 +0000208 const sourceURL = Runtime.getResourceURL(scriptName, base);
Blink Reformat4c46d092018-04-07 15:32:37 +0000209
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000210 if (_loadedScripts[sourceURL]) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000211 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000212 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000213 urls.push(sourceURL);
Patrick Hulceb8c09f32018-06-11 21:28:22 +0000214 const loadResourcePromise =
215 base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
216 promises.push(
217 loadResourcePromise.then(scriptSourceLoaded.bind(null, i), scriptSourceLoaded.bind(null, i, undefined)));
Blink Reformat4c46d092018-04-07 15:32:37 +0000218 }
219 return Promise.all(promises).then(undefined);
220
221 /**
222 * @param {number} scriptNumber
223 * @param {string=} scriptSource
224 */
225 function scriptSourceLoaded(scriptNumber, scriptSource) {
226 sources[scriptNumber] = scriptSource || '';
227 // Eval scripts as fast as possible.
228 while (typeof sources[scriptToEval] !== 'undefined') {
229 evaluateScript(urls[scriptToEval], sources[scriptToEval]);
230 ++scriptToEval;
231 }
232 }
233
234 /**
235 * @param {string} sourceURL
236 * @param {string=} scriptSource
237 */
238 function evaluateScript(sourceURL, scriptSource) {
239 _loadedScripts[sourceURL] = true;
240 if (!scriptSource) {
241 // Do not reject, as this is normal in the hosted mode.
242 console.error('Empty response arrived for script \'' + sourceURL + '\'');
243 return;
244 }
245 self.eval(scriptSource + '\n//# sourceURL=' + sourceURL);
246 }
247 }
248
249 /**
250 * @param {string} url
251 * @param {boolean} appendSourceURL
252 * @return {!Promise<undefined>}
253 */
254 static _loadResourceIntoCache(url, appendSourceURL) {
255 return Runtime.loadResourcePromise(url).then(
256 cacheResource.bind(this, url), cacheResource.bind(this, url, undefined));
257
258 /**
259 * @param {string} path
260 * @param {string=} content
261 */
262 function cacheResource(path, content) {
263 if (!content) {
264 console.error('Failed to load resource: ' + path);
265 return;
266 }
267 const sourceURL = appendSourceURL ? Runtime.resolveSourceURL(path) : '';
268 Runtime.cachedResources[path] = content + sourceURL;
269 }
270 }
271
272 /**
273 * @return {!Promise}
274 */
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000275 static async appStarted() {
276 return Runtime._appStartedPromise;
Blink Reformat4c46d092018-04-07 15:32:37 +0000277 }
278
279 /**
280 * @param {string} appName
281 * @return {!Promise.<undefined>}
282 */
283 static async startApplication(appName) {
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000284 console.timeStamp('Root.Runtime.startApplication');
Blink Reformat4c46d092018-04-07 15:32:37 +0000285
286 const allDescriptorsByName = {};
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000287 for (let i = 0; i < Root.allDescriptors.length; ++i) {
288 const d = Root.allDescriptors[i];
Blink Reformat4c46d092018-04-07 15:32:37 +0000289 allDescriptorsByName[d['name']] = d;
290 }
291
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000292 if (!Root.applicationDescriptor) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000293 let data = await Runtime.loadResourcePromise(appName + '.json');
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000294 Root.applicationDescriptor = JSON.parse(data);
295 let descriptor = Root.applicationDescriptor;
Blink Reformat4c46d092018-04-07 15:32:37 +0000296 while (descriptor.extends) {
297 data = await Runtime.loadResourcePromise(descriptor.extends + '.json');
298 descriptor = JSON.parse(data);
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000299 Root.applicationDescriptor.modules = descriptor.modules.concat(Root.applicationDescriptor.modules);
Blink Reformat4c46d092018-04-07 15:32:37 +0000300 }
301 }
302
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000303 const configuration = Root.applicationDescriptor.modules;
Blink Reformat4c46d092018-04-07 15:32:37 +0000304 const moduleJSONPromises = [];
305 const coreModuleNames = [];
306 for (let i = 0; i < configuration.length; ++i) {
307 const descriptor = configuration[i];
308 const name = descriptor['name'];
309 const moduleJSON = allDescriptorsByName[name];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000310 if (moduleJSON) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000311 moduleJSONPromises.push(Promise.resolve(moduleJSON));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000312 } else {
Blink Reformat4c46d092018-04-07 15:32:37 +0000313 moduleJSONPromises.push(Runtime.loadResourcePromise(name + '/module.json').then(JSON.parse.bind(JSON)));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000314 }
315 if (descriptor['type'] === 'autostart') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000316 coreModuleNames.push(name);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000317 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000318 }
319
320 const moduleDescriptors = await Promise.all(moduleJSONPromises);
321
322 for (let i = 0; i < moduleDescriptors.length; ++i) {
323 moduleDescriptors[i].name = configuration[i]['name'];
324 moduleDescriptors[i].condition = configuration[i]['condition'];
325 moduleDescriptors[i].remote = configuration[i]['type'] === 'remote';
326 }
327 self.runtime = new Runtime(moduleDescriptors);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000328 if (coreModuleNames) {
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000329 await self.runtime._loadAutoStartModules(coreModuleNames);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000330 }
Pavel Feldman63e89eb2018-11-25 05:47:09 +0000331 Runtime._appStartedPromiseCallback();
Blink Reformat4c46d092018-04-07 15:32:37 +0000332 }
333
334 /**
335 * @param {string} appName
336 * @return {!Promise.<undefined>}
337 */
338 static startWorker(appName) {
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000339 return Root.Runtime.startApplication(appName).then(sendWorkerReady);
Blink Reformat4c46d092018-04-07 15:32:37 +0000340
341 function sendWorkerReady() {
342 self.postMessage('workerReady');
343 }
344 }
345
346 /**
347 * @param {string} name
348 * @return {?string}
349 */
350 static queryParam(name) {
Ingvar Stepanyand48ae082018-11-07 04:38:32 +0000351 return Runtime._queryParamsObject.get(name);
Blink Reformat4c46d092018-04-07 15:32:37 +0000352 }
353
354 /**
355 * @return {string}
356 */
357 static queryParamsString() {
358 return location.search;
359 }
360
361 /**
362 * @return {!Object}
363 */
364 static _experimentsSetting() {
365 try {
366 return /** @type {!Object} */ (
367 JSON.parse(self.localStorage && self.localStorage['experiments'] ? self.localStorage['experiments'] : '{}'));
368 } catch (e) {
369 console.error('Failed to parse localStorage[\'experiments\']');
370 return {};
371 }
372 }
373
374 static _assert(value, message) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000375 if (value) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000376 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000377 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000378 Runtime._originalAssert.call(Runtime._console, value, message + ' ' + new Error().stack);
379 }
380
381 /**
382 * @param {string} platform
383 */
384 static setPlatform(platform) {
385 Runtime._platform = platform;
386 }
387
388 /**
389 * @param {!Object} descriptor
390 * @return {boolean}
391 */
392 static _isDescriptorEnabled(descriptor) {
393 const activatorExperiment = descriptor['experiment'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000394 if (activatorExperiment === '*') {
Blink Reformat4c46d092018-04-07 15:32:37 +0000395 return Runtime.experiments.supportEnabled();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000396 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000397 if (activatorExperiment && activatorExperiment.startsWith('!') &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000398 Runtime.experiments.isEnabled(activatorExperiment.substring(1))) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000399 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000400 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000401 if (activatorExperiment && !activatorExperiment.startsWith('!') &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000402 !Runtime.experiments.isEnabled(activatorExperiment)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000403 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000404 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000405 const condition = descriptor['condition'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000406 if (condition && !condition.startsWith('!') && !Runtime.queryParam(condition)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000407 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000408 }
409 if (condition && condition.startsWith('!') && Runtime.queryParam(condition.substring(1))) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000410 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000411 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000412 return true;
413 }
414
415 /**
416 * @param {string} path
417 * @return {string}
418 */
419 static resolveSourceURL(path) {
420 let sourceURL = self.location.href;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000421 if (self.location.search) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000422 sourceURL = sourceURL.replace(self.location.search, '');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000423 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000424 sourceURL = sourceURL.substring(0, sourceURL.lastIndexOf('/') + 1) + path;
425 return '\n/*# sourceURL=' + sourceURL + ' */';
426 }
427
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000428 /**
429 * @param {function(string):string} localizationFunction
430 */
431 static setL10nCallback(localizationFunction) {
432 Runtime._l10nCallback = localizationFunction;
433 }
434
Blink Reformat4c46d092018-04-07 15:32:37 +0000435 useTestBase() {
436 Runtime._remoteBase = 'http://localhost:8000/inspector-sources/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000437 if (Runtime.queryParam('debugFrontend')) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000438 Runtime._remoteBase += 'debug/';
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000439 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000440 }
441
442 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000443 * @param {string} moduleName
444 * @return {!Runtime.Module}
445 */
446 module(moduleName) {
447 return this._modulesMap[moduleName];
448 }
449
450 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000451 * @param {!ModuleDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000452 */
453 _registerModule(descriptor) {
454 const module = new Runtime.Module(this, descriptor);
455 this._modules.push(module);
456 this._modulesMap[descriptor['name']] = module;
457 }
458
459 /**
460 * @param {string} moduleName
461 * @return {!Promise.<undefined>}
462 */
463 loadModulePromise(moduleName) {
464 return this._modulesMap[moduleName]._loadPromise();
465 }
466
467 /**
468 * @param {!Array.<string>} moduleNames
469 * @return {!Promise.<!Array.<*>>}
470 */
471 _loadAutoStartModules(moduleNames) {
472 const promises = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000473 for (let i = 0; i < moduleNames.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000474 promises.push(this.loadModulePromise(moduleNames[i]));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000475 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000476 return Promise.all(promises);
477 }
478
479 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000480 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000481 * @param {?function(function(new:Object)):boolean} predicate
482 * @return {boolean}
483 */
484 _checkExtensionApplicability(extension, predicate) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000485 if (!predicate) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000486 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000487 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000488 const contextTypes = extension.descriptor().contextTypes;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000489 if (!contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000490 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000491 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000492 for (let i = 0; i < contextTypes.length; ++i) {
493 const contextType = this._resolve(contextTypes[i]);
494 const isMatching = !!contextType && predicate(contextType);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000495 if (isMatching) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000496 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000497 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000498 }
499 return false;
500 }
501
502 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000503 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000504 * @param {?Object} context
505 * @return {boolean}
506 */
507 isExtensionApplicableToContext(extension, context) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000508 if (!context) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000509 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000510 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000511 return this._checkExtensionApplicability(extension, isInstanceOf);
512
513 /**
514 * @param {!Function} targetType
515 * @return {boolean}
516 */
517 function isInstanceOf(targetType) {
518 return context instanceof targetType;
519 }
520 }
521
522 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000523 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000524 * @param {!Set.<!Function>=} currentContextTypes
525 * @return {boolean}
526 */
527 isExtensionApplicableToContextTypes(extension, currentContextTypes) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000528 if (!extension.descriptor().contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000529 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000530 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000531
532 return this._checkExtensionApplicability(extension, currentContextTypes ? isContextTypeKnown : null);
533
534 /**
535 * @param {!Function} targetType
536 * @return {boolean}
537 */
538 function isContextTypeKnown(targetType) {
539 return currentContextTypes.has(targetType);
540 }
541 }
542
543 /**
544 * @param {*} type
545 * @param {?Object=} context
546 * @param {boolean=} sortByTitle
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000547 * @return {!Array.<!Extension>}
Blink Reformat4c46d092018-04-07 15:32:37 +0000548 */
549 extensions(type, context, sortByTitle) {
550 return this._extensions.filter(filter).sort(sortByTitle ? titleComparator : orderComparator);
551
552 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000553 * @param {!Extension} extension
Blink Reformat4c46d092018-04-07 15:32:37 +0000554 * @return {boolean}
555 */
556 function filter(extension) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000557 if (extension._type !== type && extension._typeClass() !== type) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000558 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000559 }
560 if (!extension.enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000561 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000562 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000563 return !context || extension.isApplicable(context);
564 }
565
566 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000567 * @param {!Extension} extension1
568 * @param {!Extension} extension2
Blink Reformat4c46d092018-04-07 15:32:37 +0000569 * @return {number}
570 */
571 function orderComparator(extension1, extension2) {
572 const order1 = extension1.descriptor()['order'] || 0;
573 const order2 = extension2.descriptor()['order'] || 0;
574 return order1 - order2;
575 }
576
577 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000578 * @param {!Extension} extension1
579 * @param {!Extension} extension2
Blink Reformat4c46d092018-04-07 15:32:37 +0000580 * @return {number}
581 */
582 function titleComparator(extension1, extension2) {
583 const title1 = extension1.title() || '';
584 const title2 = extension2.title() || '';
585 return title1.localeCompare(title2);
586 }
587 }
588
589 /**
590 * @param {*} type
591 * @param {?Object=} context
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000592 * @return {?Extension}
Blink Reformat4c46d092018-04-07 15:32:37 +0000593 */
594 extension(type, context) {
595 return this.extensions(type, context)[0] || null;
596 }
597
598 /**
599 * @param {*} type
600 * @param {?Object=} context
601 * @return {!Promise.<!Array.<!Object>>}
602 */
603 allInstances(type, context) {
604 return Promise.all(this.extensions(type, context).map(extension => extension.instance()));
605 }
606
607 /**
608 * @return {?function(new:Object)}
609 */
610 _resolve(typeName) {
611 if (!this._cachedTypeClasses[typeName]) {
612 const path = typeName.split('.');
613 let object = self;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000614 for (let i = 0; object && (i < path.length); ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000615 object = object[path[i]];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000616 }
617 if (object) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000618 this._cachedTypeClasses[typeName] = /** @type function(new:Object) */ (object);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000619 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000620 }
621 return this._cachedTypeClasses[typeName] || null;
622 }
623
624 /**
Alexei Filippovf2e42e42019-04-04 21:40:45 +0000625 * @param {function(new:T)} constructorFunction
626 * @return {!T}
627 * @template T
Blink Reformat4c46d092018-04-07 15:32:37 +0000628 */
629 sharedInstance(constructorFunction) {
630 if (Runtime._instanceSymbol in constructorFunction &&
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000631 Object.getOwnPropertySymbols(constructorFunction).includes(Runtime._instanceSymbol)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000632 return constructorFunction[Runtime._instanceSymbol];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000633 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000634
635 const instance = new constructorFunction();
636 constructorFunction[Runtime._instanceSymbol] = instance;
637 return instance;
638 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000639}
Blink Reformat4c46d092018-04-07 15:32:37 +0000640
Ingvar Stepanyand48ae082018-11-07 04:38:32 +0000641/** @type {!URLSearchParams} */
642Runtime._queryParamsObject = new URLSearchParams(Runtime.queryParamsString());
Blink Reformat4c46d092018-04-07 15:32:37 +0000643
644Runtime._instanceSymbol = Symbol('instance');
645
646/**
647 * @type {!Object.<string, string>}
648 */
649Runtime.cachedResources = {
650 __proto__: null
651};
652
653
654Runtime._console = console;
655Runtime._originalAssert = console.assert;
656
657
658Runtime._platform = '';
659
660
661/**
662 * @unrestricted
663 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000664class ModuleDescriptor {
Blink Reformat4c46d092018-04-07 15:32:37 +0000665 constructor() {
666 /**
667 * @type {string}
668 */
669 this.name;
670
671 /**
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000672 * @type {!Array.<!RuntimeExtensionDescriptor>}
Blink Reformat4c46d092018-04-07 15:32:37 +0000673 */
674 this.extensions;
675
676 /**
677 * @type {!Array.<string>|undefined}
678 */
679 this.dependencies;
680
681 /**
682 * @type {!Array.<string>}
683 */
684 this.scripts;
685
686 /**
687 * @type {string|undefined}
688 */
689 this.condition;
690
691 /**
692 * @type {boolean|undefined}
693 */
694 this.remote;
695 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000696}
Blink Reformat4c46d092018-04-07 15:32:37 +0000697
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000698// This class is named like this, because we already have an "ExtensionDescriptor" in the externs
699// These two do not share the same structure
Blink Reformat4c46d092018-04-07 15:32:37 +0000700/**
701 * @unrestricted
702 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000703class RuntimeExtensionDescriptor {
Blink Reformat4c46d092018-04-07 15:32:37 +0000704 constructor() {
705 /**
706 * @type {string}
707 */
708 this.type;
709
710 /**
711 * @type {string|undefined}
712 */
713 this.className;
714
715 /**
716 * @type {string|undefined}
717 */
718 this.factoryName;
719
720 /**
721 * @type {!Array.<string>|undefined}
722 */
723 this.contextTypes;
724 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000725}
Blink Reformat4c46d092018-04-07 15:32:37 +0000726
727/**
728 * @unrestricted
729 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000730class Module {
Blink Reformat4c46d092018-04-07 15:32:37 +0000731 /**
732 * @param {!Runtime} manager
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000733 * @param {!ModuleDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000734 */
735 constructor(manager, descriptor) {
736 this._manager = manager;
737 this._descriptor = descriptor;
738 this._name = descriptor.name;
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000739 /** @type {!Array<!Extension>} */
Blink Reformat4c46d092018-04-07 15:32:37 +0000740 this._extensions = [];
741
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000742 /** @type {!Map<string, !Array<!Extension>>} */
Blink Reformat4c46d092018-04-07 15:32:37 +0000743 this._extensionsByClassName = new Map();
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000744 const extensions = /** @type {?Array.<!RuntimeExtensionDescriptor>} */ (descriptor.extensions);
Blink Reformat4c46d092018-04-07 15:32:37 +0000745 for (let i = 0; extensions && i < extensions.length; ++i) {
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000746 const extension = new Extension(this, extensions[i]);
Blink Reformat4c46d092018-04-07 15:32:37 +0000747 this._manager._extensions.push(extension);
748 this._extensions.push(extension);
749 }
750 this._loadedForTest = false;
751 }
752
753 /**
754 * @return {string}
755 */
756 name() {
757 return this._name;
758 }
759
760 /**
761 * @return {boolean}
762 */
763 enabled() {
764 return Runtime._isDescriptorEnabled(this._descriptor);
765 }
766
767 /**
768 * @param {string} name
769 * @return {string}
770 */
771 resource(name) {
772 const fullName = this._name + '/' + name;
773 const content = Runtime.cachedResources[fullName];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000774 if (!content) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000775 throw new Error(fullName + ' not preloaded. Check module.json');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000776 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000777 return content;
778 }
779
780 /**
781 * @return {!Promise.<undefined>}
782 */
783 _loadPromise() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000784 if (!this.enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000785 return Promise.reject(new Error('Module ' + this._name + ' is not enabled'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000786 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000787
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000788 if (this._pendingLoadPromise) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000789 return this._pendingLoadPromise;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000790 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000791
792 const dependencies = this._descriptor.dependencies;
793 const dependencyPromises = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000794 for (let i = 0; dependencies && i < dependencies.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000795 dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPromise());
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000796 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000797
798 this._pendingLoadPromise = Promise.all(dependencyPromises)
799 .then(this._loadResources.bind(this))
800 .then(this._loadScripts.bind(this))
801 .then(() => this._loadedForTest = true);
802
803 return this._pendingLoadPromise;
804 }
805
806 /**
807 * @return {!Promise.<undefined>}
808 * @this {Runtime.Module}
809 */
810 _loadResources() {
811 const resources = this._descriptor['resources'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000812 if (!resources || !resources.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000813 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000814 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000815 const promises = [];
816 for (let i = 0; i < resources.length; ++i) {
817 const url = this._modularizeURL(resources[i]);
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000818 const isHtml = url.endsWith('.html');
819 promises.push(Runtime._loadResourceIntoCache(url, !isHtml /* appendSourceURL */));
Blink Reformat4c46d092018-04-07 15:32:37 +0000820 }
821 return Promise.all(promises).then(undefined);
822 }
823
824 /**
825 * @return {!Promise.<undefined>}
826 */
827 _loadScripts() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000828 if (!this._descriptor.scripts || !this._descriptor.scripts.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000829 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000830 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000831
832 // Module namespaces.
Yang Guo75beda92019-10-28 08:29:25 +0100833 // NOTE: Update scripts/build/special_case_namespaces.json if you add a special cased namespace.
Blink Reformat4c46d092018-04-07 15:32:37 +0000834 // The namespace keyword confuses clang-format.
835 // clang-format off
836 const specialCases = {
837 'sdk': 'SDK',
838 'js_sdk': 'JSSDK',
839 'browser_sdk': 'BrowserSDK',
840 'ui': 'UI',
841 'object_ui': 'ObjectUI',
Joel Einbinder3f23eb22018-05-14 23:27:51 +0000842 'javascript_metadata': 'JavaScriptMetadata',
Blink Reformat4c46d092018-04-07 15:32:37 +0000843 'perf_ui': 'PerfUI',
844 'har_importer': 'HARImporter',
845 'sdk_test_runner': 'SDKTestRunner',
846 'cpu_profiler_test_runner': 'CPUProfilerTestRunner'
847 };
848 const namespace = specialCases[this._name] || this._name.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
849 self[namespace] = self[namespace] || {};
850 // clang-format on
851 return Runtime._loadScriptsPromise(this._descriptor.scripts.map(this._modularizeURL, this), this._remoteBase());
852 }
853
854 /**
855 * @param {string} resourceName
856 */
857 _modularizeURL(resourceName) {
858 return Runtime.normalizePath(this._name + '/' + resourceName);
859 }
860
861 /**
862 * @return {string|undefined}
863 */
864 _remoteBase() {
865 return !Runtime.queryParam('debugFrontend') && this._descriptor.remote && Runtime._remoteBase || undefined;
866 }
867
868 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000869 * @param {string} resourceName
870 * @return {!Promise.<string>}
871 */
872 fetchResource(resourceName) {
873 const base = this._remoteBase();
874 const sourceURL = Runtime.getResourceURL(this._modularizeURL(resourceName), base);
875 return base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
876 }
877
878 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000879 * @param {string} value
880 * @return {string}
881 */
882 substituteURL(value) {
883 const base = this._remoteBase() || '';
884 return value.replace(/@url\(([^\)]*?)\)/g, convertURL.bind(this));
885
886 function convertURL(match, url) {
887 return base + this._modularizeURL(url);
888 }
889 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000890}
Blink Reformat4c46d092018-04-07 15:32:37 +0000891
892
893/**
894 * @unrestricted
895 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000896class Extension { /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000897 * @param {!Runtime.Module} module
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000898 * @param {!RuntimeExtensionDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000899 */
900 constructor(module, descriptor) {
901 this._module = module;
902 this._descriptor = descriptor;
903
904 this._type = descriptor.type;
905 this._hasTypeClass = this._type.charAt(0) === '@';
906
907 /**
908 * @type {?string}
909 */
910 this._className = descriptor.className || null;
911 this._factoryName = descriptor.factoryName || null;
912 }
913
914 /**
915 * @return {!Object}
916 */
917 descriptor() {
918 return this._descriptor;
919 }
920
921 /**
922 * @return {!Runtime.Module}
923 */
924 module() {
925 return this._module;
926 }
927
928 /**
929 * @return {boolean}
930 */
931 enabled() {
932 return this._module.enabled() && Runtime._isDescriptorEnabled(this.descriptor());
933 }
934
935 /**
936 * @return {?function(new:Object)}
937 */
938 _typeClass() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000939 if (!this._hasTypeClass) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000940 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000941 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000942 return this._module._manager._resolve(this._type.substring(1));
943 }
944
945 /**
946 * @param {?Object} context
947 * @return {boolean}
948 */
949 isApplicable(context) {
950 return this._module._manager.isExtensionApplicableToContext(this, context);
951 }
952
953 /**
954 * @return {!Promise.<!Object>}
955 */
956 instance() {
957 return this._module._loadPromise().then(this._createInstance.bind(this));
958 }
959
960 /**
961 * @return {boolean}
962 */
963 canInstantiate() {
964 return !!(this._className || this._factoryName);
965 }
966
967 /**
968 * @return {!Object}
969 */
970 _createInstance() {
971 const className = this._className || this._factoryName;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000972 if (!className) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000973 throw new Error('Could not instantiate extension with no class');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000974 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000975 const constructorFunction = self.eval(/** @type {string} */ (className));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000976 if (!(constructorFunction instanceof Function)) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000977 throw new Error('Could not instantiate: ' + className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000978 }
979 if (this._className) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000980 return this._module._manager.sharedInstance(constructorFunction);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000981 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000982 return new constructorFunction(this);
983 }
984
985 /**
986 * @return {string}
987 */
988 title() {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000989 const title = this._descriptor['title-' + Runtime._platform] || this._descriptor['title'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000990 if (title && Runtime._l10nCallback) {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000991 return Runtime._l10nCallback(title);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000992 }
Vidal Diazleal0b6aff42019-04-20 01:15:43 +0000993 return title;
Blink Reformat4c46d092018-04-07 15:32:37 +0000994 }
995
996 /**
997 * @param {function(new:Object)} contextType
998 * @return {boolean}
999 */
1000 hasContextType(contextType) {
1001 const contextTypes = this.descriptor().contextTypes;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001002 if (!contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001003 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001004 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001005 for (let i = 0; i < contextTypes.length; ++i) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001006 if (contextType === this._module._manager._resolve(contextTypes[i])) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001007 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001008 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001009 }
1010 return false;
1011 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001012}
Blink Reformat4c46d092018-04-07 15:32:37 +00001013
1014/**
1015 * @unrestricted
1016 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001017class ExperimentsSupport {
Blink Reformat4c46d092018-04-07 15:32:37 +00001018 constructor() {
1019 this._supportEnabled = Runtime.queryParam('experiments') !== null;
1020 this._experiments = [];
1021 this._experimentNames = {};
1022 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +00001023 /** @type {!Set<string>} */
1024 this._serverEnabled = new Set();
Blink Reformat4c46d092018-04-07 15:32:37 +00001025 }
1026
1027 /**
1028 * @return {!Array.<!Runtime.Experiment>}
1029 */
1030 allConfigurableExperiments() {
1031 const result = [];
1032 for (let i = 0; i < this._experiments.length; i++) {
1033 const experiment = this._experiments[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001034 if (!this._enabledTransiently[experiment.name]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001035 result.push(experiment);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001036 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001037 }
1038 return result;
1039 }
1040
1041 /**
1042 * @return {boolean}
1043 */
1044 supportEnabled() {
1045 return this._supportEnabled;
1046 }
1047
1048 /**
1049 * @param {!Object} value
1050 */
1051 _setExperimentsSetting(value) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001052 if (!self.localStorage) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001053 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001054 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001055 self.localStorage['experiments'] = JSON.stringify(value);
1056 }
1057
1058 /**
1059 * @param {string} experimentName
1060 * @param {string} experimentTitle
1061 * @param {boolean=} hidden
1062 */
1063 register(experimentName, experimentTitle, hidden) {
1064 Runtime._assert(!this._experimentNames[experimentName], 'Duplicate registration of experiment ' + experimentName);
1065 this._experimentNames[experimentName] = true;
1066 this._experiments.push(new Runtime.Experiment(this, experimentName, experimentTitle, !!hidden));
1067 }
1068
1069 /**
1070 * @param {string} experimentName
1071 * @return {boolean}
1072 */
1073 isEnabled(experimentName) {
1074 this._checkExperiment(experimentName);
Pavel Feldmanc0395912018-08-03 02:05:00 +00001075 // Check for explicitly disabled experiments first - the code could call setEnable(false) on the experiment enabled
1076 // by default and we should respect that.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001077 if (Runtime._experimentsSetting()[experimentName] === false) {
Pavel Feldmanc0395912018-08-03 02:05:00 +00001078 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001079 }
1080 if (this._enabledTransiently[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001081 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001082 }
1083 if (this._serverEnabled.has(experimentName)) {
Jeff Fisher85b08e12019-06-13 22:24:21 +00001084 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001085 }
1086 if (!this.supportEnabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001087 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001088 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001089
1090 return !!Runtime._experimentsSetting()[experimentName];
1091 }
1092
1093 /**
1094 * @param {string} experimentName
1095 * @param {boolean} enabled
1096 */
1097 setEnabled(experimentName, enabled) {
1098 this._checkExperiment(experimentName);
1099 const experimentsSetting = Runtime._experimentsSetting();
1100 experimentsSetting[experimentName] = enabled;
1101 this._setExperimentsSetting(experimentsSetting);
1102 }
1103
1104 /**
1105 * @param {!Array.<string>} experimentNames
1106 */
1107 setDefaultExperiments(experimentNames) {
1108 for (let i = 0; i < experimentNames.length; ++i) {
1109 this._checkExperiment(experimentNames[i]);
1110 this._enabledTransiently[experimentNames[i]] = true;
1111 }
1112 }
1113
1114 /**
Jeff Fisher85b08e12019-06-13 22:24:21 +00001115 * @param {!Array.<string>} experimentNames
1116 */
1117 setServerEnabledExperiments(experimentNames) {
1118 for (const experiment of experimentNames) {
1119 this._checkExperiment(experiment);
1120 this._serverEnabled.add(experiment);
1121 }
1122 }
1123
1124 /**
Blink Reformat4c46d092018-04-07 15:32:37 +00001125 * @param {string} experimentName
1126 */
1127 enableForTest(experimentName) {
1128 this._checkExperiment(experimentName);
1129 this._enabledTransiently[experimentName] = true;
1130 }
1131
1132 clearForTest() {
1133 this._experiments = [];
1134 this._experimentNames = {};
1135 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +00001136 this._serverEnabled.clear();
Blink Reformat4c46d092018-04-07 15:32:37 +00001137 }
1138
1139 cleanUpStaleExperiments() {
1140 const experimentsSetting = Runtime._experimentsSetting();
1141 const cleanedUpExperimentSetting = {};
1142 for (let i = 0; i < this._experiments.length; ++i) {
1143 const experimentName = this._experiments[i].name;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001144 if (experimentsSetting[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001145 cleanedUpExperimentSetting[experimentName] = true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001146 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001147 }
1148 this._setExperimentsSetting(cleanedUpExperimentSetting);
1149 }
1150
1151 /**
1152 * @param {string} experimentName
1153 */
1154 _checkExperiment(experimentName) {
1155 Runtime._assert(this._experimentNames[experimentName], 'Unknown experiment ' + experimentName);
1156 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001157}
Blink Reformat4c46d092018-04-07 15:32:37 +00001158
1159/**
1160 * @unrestricted
1161 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001162class Experiment {
Blink Reformat4c46d092018-04-07 15:32:37 +00001163 /**
1164 * @param {!Runtime.ExperimentsSupport} experiments
1165 * @param {string} name
1166 * @param {string} title
1167 * @param {boolean} hidden
1168 */
1169 constructor(experiments, name, title, hidden) {
1170 this.name = name;
1171 this.title = title;
1172 this.hidden = hidden;
1173 this._experiments = experiments;
1174 }
1175
1176 /**
1177 * @return {boolean}
1178 */
1179 isEnabled() {
1180 return this._experiments.isEnabled(this.name);
1181 }
1182
1183 /**
1184 * @param {boolean} enabled
1185 */
1186 setEnabled(enabled) {
1187 this._experiments.setEnabled(this.name, enabled);
1188 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001189}
Blink Reformat4c46d092018-04-07 15:32:37 +00001190
Blink Reformat4c46d092018-04-07 15:32:37 +00001191// This must be constructed after the query parameters have been parsed.
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001192Runtime.experiments = new ExperimentsSupport();
Blink Reformat4c46d092018-04-07 15:32:37 +00001193
1194/** @type {Function} */
Pavel Feldman63e89eb2018-11-25 05:47:09 +00001195Runtime._appStartedPromiseCallback;
1196Runtime._appStartedPromise = new Promise(fulfil => Runtime._appStartedPromiseCallback = fulfil);
Vidal Diazleal0b6aff42019-04-20 01:15:43 +00001197
1198/** @type {function(string):string} */
1199Runtime._l10nCallback;
1200
Blink Reformat4c46d092018-04-07 15:32:37 +00001201/**
1202 * @type {?string}
1203 */
1204Runtime._remoteBase;
1205(function validateRemoteBase() {
James Lissiak821d2282019-05-15 15:32:04 +00001206 if (location.href.startsWith('devtools://devtools/bundled/') && Runtime.queryParam('remoteBase')) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001207 const versionMatch = /\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(Runtime.queryParam('remoteBase'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001208 if (versionMatch) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001209 Runtime._remoteBase = `${location.origin}/remote/serve_file/${versionMatch[1]}/`;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001210 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001211 }
1212})();
1213
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001214self.Root = self.Root || {};
1215Root = Root || {};
Blink Reformat4c46d092018-04-07 15:32:37 +00001216
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001217// This gets all concatenated module descriptors in the release mode.
1218Root.allDescriptors = [];
Blink Reformat4c46d092018-04-07 15:32:37 +00001219
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001220Root.applicationDescriptor = undefined;
Blink Reformat4c46d092018-04-07 15:32:37 +00001221
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001222/** @constructor */
1223Root.Runtime = Runtime;
Blink Reformat4c46d092018-04-07 15:32:37 +00001224
1225/** @type {!Runtime} */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001226Root.runtime;
1227
1228/** @constructor */
1229Root.Runtime.ModuleDescriptor = ModuleDescriptor;
1230
1231/** @constructor */
1232Root.Runtime.ExtensionDescriptor = RuntimeExtensionDescriptor;
1233
1234/** @constructor */
1235Root.Runtime.Extension = Extension;
1236
1237/** @constructor */
1238Root.Runtime.Module = Module;
1239
1240/** @constructor */
1241Root.Runtime.ExperimentsSupport = ExperimentsSupport;
1242
1243/** @constructor */
Yang Guo75beda92019-10-28 08:29:25 +01001244Root.Runtime.Experiment = Experiment;