blob: eb460f5cac76cc9e759d7b4cffa17bec7e827d82 [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 /**
Tim van der Lippe647e33b2019-11-04 19:02:31 +0000687 * @type {!Array.<string>}
688 */
689 this.modules;
690
691 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000692 * @type {string|undefined}
693 */
694 this.condition;
695
696 /**
697 * @type {boolean|undefined}
698 */
699 this.remote;
700 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000701}
Blink Reformat4c46d092018-04-07 15:32:37 +0000702
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000703// This class is named like this, because we already have an "ExtensionDescriptor" in the externs
704// These two do not share the same structure
Blink Reformat4c46d092018-04-07 15:32:37 +0000705/**
706 * @unrestricted
707 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000708class RuntimeExtensionDescriptor {
Blink Reformat4c46d092018-04-07 15:32:37 +0000709 constructor() {
710 /**
711 * @type {string}
712 */
713 this.type;
714
715 /**
716 * @type {string|undefined}
717 */
718 this.className;
719
720 /**
721 * @type {string|undefined}
722 */
723 this.factoryName;
724
725 /**
726 * @type {!Array.<string>|undefined}
727 */
728 this.contextTypes;
729 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000730}
Blink Reformat4c46d092018-04-07 15:32:37 +0000731
Tim van der Lippe647e33b2019-11-04 19:02:31 +0000732// Module namespaces.
733// NOTE: Update scripts/build/special_case_namespaces.json if you add a special cased namespace.
734const specialCases = {
735 'sdk': 'SDK',
736 'js_sdk': 'JSSDK',
737 'browser_sdk': 'BrowserSDK',
738 'ui': 'UI',
739 'object_ui': 'ObjectUI',
740 'javascript_metadata': 'JavaScriptMetadata',
741 'perf_ui': 'PerfUI',
742 'har_importer': 'HARImporter',
743 'sdk_test_runner': 'SDKTestRunner',
744 'cpu_profiler_test_runner': 'CPUProfilerTestRunner'
745};
746
Blink Reformat4c46d092018-04-07 15:32:37 +0000747/**
748 * @unrestricted
749 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000750class Module {
Blink Reformat4c46d092018-04-07 15:32:37 +0000751 /**
752 * @param {!Runtime} manager
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000753 * @param {!ModuleDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000754 */
755 constructor(manager, descriptor) {
756 this._manager = manager;
757 this._descriptor = descriptor;
758 this._name = descriptor.name;
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000759 /** @type {!Array<!Extension>} */
Blink Reformat4c46d092018-04-07 15:32:37 +0000760 this._extensions = [];
761
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000762 /** @type {!Map<string, !Array<!Extension>>} */
Blink Reformat4c46d092018-04-07 15:32:37 +0000763 this._extensionsByClassName = new Map();
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000764 const extensions = /** @type {?Array.<!RuntimeExtensionDescriptor>} */ (descriptor.extensions);
Blink Reformat4c46d092018-04-07 15:32:37 +0000765 for (let i = 0; extensions && i < extensions.length; ++i) {
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000766 const extension = new Extension(this, extensions[i]);
Blink Reformat4c46d092018-04-07 15:32:37 +0000767 this._manager._extensions.push(extension);
768 this._extensions.push(extension);
769 }
770 this._loadedForTest = false;
771 }
772
773 /**
774 * @return {string}
775 */
776 name() {
777 return this._name;
778 }
779
780 /**
781 * @return {boolean}
782 */
783 enabled() {
784 return Runtime._isDescriptorEnabled(this._descriptor);
785 }
786
787 /**
788 * @param {string} name
789 * @return {string}
790 */
791 resource(name) {
792 const fullName = this._name + '/' + name;
793 const content = Runtime.cachedResources[fullName];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000794 if (!content) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000795 throw new Error(fullName + ' not preloaded. Check module.json');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000796 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000797 return content;
798 }
799
800 /**
801 * @return {!Promise.<undefined>}
802 */
803 _loadPromise() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000804 if (!this.enabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000805 return Promise.reject(new Error('Module ' + this._name + ' is not enabled'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000806 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000807
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000808 if (this._pendingLoadPromise) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000809 return this._pendingLoadPromise;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000810 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000811
812 const dependencies = this._descriptor.dependencies;
813 const dependencyPromises = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000814 for (let i = 0; dependencies && i < dependencies.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000815 dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPromise());
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000816 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000817
818 this._pendingLoadPromise = Promise.all(dependencyPromises)
819 .then(this._loadResources.bind(this))
Tim van der Lippe647e33b2019-11-04 19:02:31 +0000820 .then(this._loadModules.bind(this))
Blink Reformat4c46d092018-04-07 15:32:37 +0000821 .then(this._loadScripts.bind(this))
822 .then(() => this._loadedForTest = true);
823
824 return this._pendingLoadPromise;
825 }
826
827 /**
828 * @return {!Promise.<undefined>}
829 * @this {Runtime.Module}
830 */
831 _loadResources() {
832 const resources = this._descriptor['resources'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000833 if (!resources || !resources.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000834 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000835 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000836 const promises = [];
837 for (let i = 0; i < resources.length; ++i) {
838 const url = this._modularizeURL(resources[i]);
cjamcl@google.comc5214af2019-06-25 20:31:21 +0000839 const isHtml = url.endsWith('.html');
840 promises.push(Runtime._loadResourceIntoCache(url, !isHtml /* appendSourceURL */));
Blink Reformat4c46d092018-04-07 15:32:37 +0000841 }
842 return Promise.all(promises).then(undefined);
843 }
844
Tim van der Lippe647e33b2019-11-04 19:02:31 +0000845 _loadModules() {
846 if (!this._descriptor.modules || !this._descriptor.modules.length) {
847 return Promise.resolve();
848 }
849
850 const namespace = this._computeNamespace();
851 self[namespace] = self[namespace] || {};
852
853 // TODO(crbug.com/680046): We are in a worker and we dont support modules yet
854 if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
855 return Promise.resolve();
856 }
857
858 // TODO(crbug.com/1011811): Remove eval when we use TypeScript which does support dynamic imports
859 return eval(`import('./${this._name}/${this._name}.js')`);
860 }
861
Blink Reformat4c46d092018-04-07 15:32:37 +0000862 /**
863 * @return {!Promise.<undefined>}
864 */
865 _loadScripts() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000866 if (!this._descriptor.scripts || !this._descriptor.scripts.length) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000867 return Promise.resolve();
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000868 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000869
Tim van der Lippe647e33b2019-11-04 19:02:31 +0000870 const namespace = this._computeNamespace();
Blink Reformat4c46d092018-04-07 15:32:37 +0000871 self[namespace] = self[namespace] || {};
Blink Reformat4c46d092018-04-07 15:32:37 +0000872 return Runtime._loadScriptsPromise(this._descriptor.scripts.map(this._modularizeURL, this), this._remoteBase());
873 }
874
875 /**
Tim van der Lippe647e33b2019-11-04 19:02:31 +0000876 * @return {string}
877 */
878 _computeNamespace() {
879 return specialCases[this._name] ||
880 this._name.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
881 }
882
883 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000884 * @param {string} resourceName
885 */
886 _modularizeURL(resourceName) {
887 return Runtime.normalizePath(this._name + '/' + resourceName);
888 }
889
890 /**
891 * @return {string|undefined}
892 */
893 _remoteBase() {
894 return !Runtime.queryParam('debugFrontend') && this._descriptor.remote && Runtime._remoteBase || undefined;
895 }
896
897 /**
Mandy Chen20b48f62019-09-18 19:28:22 +0000898 * @param {string} resourceName
899 * @return {!Promise.<string>}
900 */
901 fetchResource(resourceName) {
902 const base = this._remoteBase();
903 const sourceURL = Runtime.getResourceURL(this._modularizeURL(resourceName), base);
904 return base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
905 }
906
907 /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000908 * @param {string} value
909 * @return {string}
910 */
911 substituteURL(value) {
912 const base = this._remoteBase() || '';
913 return value.replace(/@url\(([^\)]*?)\)/g, convertURL.bind(this));
914
915 function convertURL(match, url) {
916 return base + this._modularizeURL(url);
917 }
918 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000919}
Blink Reformat4c46d092018-04-07 15:32:37 +0000920
921
922/**
923 * @unrestricted
924 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000925class Extension { /**
Blink Reformat4c46d092018-04-07 15:32:37 +0000926 * @param {!Runtime.Module} module
Tim van der Lippe99e59b82019-09-30 20:00:59 +0000927 * @param {!RuntimeExtensionDescriptor} descriptor
Blink Reformat4c46d092018-04-07 15:32:37 +0000928 */
929 constructor(module, descriptor) {
930 this._module = module;
931 this._descriptor = descriptor;
932
933 this._type = descriptor.type;
934 this._hasTypeClass = this._type.charAt(0) === '@';
935
936 /**
937 * @type {?string}
938 */
939 this._className = descriptor.className || null;
940 this._factoryName = descriptor.factoryName || null;
941 }
942
943 /**
944 * @return {!Object}
945 */
946 descriptor() {
947 return this._descriptor;
948 }
949
950 /**
951 * @return {!Runtime.Module}
952 */
953 module() {
954 return this._module;
955 }
956
957 /**
958 * @return {boolean}
959 */
960 enabled() {
961 return this._module.enabled() && Runtime._isDescriptorEnabled(this.descriptor());
962 }
963
964 /**
965 * @return {?function(new:Object)}
966 */
967 _typeClass() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000968 if (!this._hasTypeClass) {
Blink Reformat4c46d092018-04-07 15:32:37 +0000969 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +0000970 }
Blink Reformat4c46d092018-04-07 15:32:37 +0000971 return this._module._manager._resolve(this._type.substring(1));
972 }
973
974 /**
975 * @param {?Object} context
976 * @return {boolean}
977 */
978 isApplicable(context) {
979 return this._module._manager.isExtensionApplicableToContext(this, context);
980 }
981
982 /**
983 * @return {!Promise.<!Object>}
984 */
985 instance() {
986 return this._module._loadPromise().then(this._createInstance.bind(this));
987 }
988
989 /**
990 * @return {boolean}
991 */
992 canInstantiate() {
993 return !!(this._className || this._factoryName);
994 }
995
996 /**
997 * @return {!Object}
998 */
999 _createInstance() {
1000 const className = this._className || this._factoryName;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001001 if (!className) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001002 throw new Error('Could not instantiate extension with no class');
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001003 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001004 const constructorFunction = self.eval(/** @type {string} */ (className));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001005 if (!(constructorFunction instanceof Function)) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001006 throw new Error('Could not instantiate: ' + className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001007 }
1008 if (this._className) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001009 return this._module._manager.sharedInstance(constructorFunction);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001010 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001011 return new constructorFunction(this);
1012 }
1013
1014 /**
1015 * @return {string}
1016 */
1017 title() {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +00001018 const title = this._descriptor['title-' + Runtime._platform] || this._descriptor['title'];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001019 if (title && Runtime._l10nCallback) {
Vidal Diazleal0b6aff42019-04-20 01:15:43 +00001020 return Runtime._l10nCallback(title);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001021 }
Vidal Diazleal0b6aff42019-04-20 01:15:43 +00001022 return title;
Blink Reformat4c46d092018-04-07 15:32:37 +00001023 }
1024
1025 /**
1026 * @param {function(new:Object)} contextType
1027 * @return {boolean}
1028 */
1029 hasContextType(contextType) {
1030 const contextTypes = this.descriptor().contextTypes;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001031 if (!contextTypes) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001032 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001033 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001034 for (let i = 0; i < contextTypes.length; ++i) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001035 if (contextType === this._module._manager._resolve(contextTypes[i])) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001036 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001037 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001038 }
1039 return false;
1040 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001041}
Blink Reformat4c46d092018-04-07 15:32:37 +00001042
1043/**
1044 * @unrestricted
1045 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001046class ExperimentsSupport {
Blink Reformat4c46d092018-04-07 15:32:37 +00001047 constructor() {
1048 this._supportEnabled = Runtime.queryParam('experiments') !== null;
1049 this._experiments = [];
1050 this._experimentNames = {};
1051 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +00001052 /** @type {!Set<string>} */
1053 this._serverEnabled = new Set();
Blink Reformat4c46d092018-04-07 15:32:37 +00001054 }
1055
1056 /**
1057 * @return {!Array.<!Runtime.Experiment>}
1058 */
1059 allConfigurableExperiments() {
1060 const result = [];
1061 for (let i = 0; i < this._experiments.length; i++) {
1062 const experiment = this._experiments[i];
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001063 if (!this._enabledTransiently[experiment.name]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001064 result.push(experiment);
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001065 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001066 }
1067 return result;
1068 }
1069
1070 /**
1071 * @return {boolean}
1072 */
1073 supportEnabled() {
1074 return this._supportEnabled;
1075 }
1076
1077 /**
1078 * @param {!Object} value
1079 */
1080 _setExperimentsSetting(value) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001081 if (!self.localStorage) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001082 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001083 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001084 self.localStorage['experiments'] = JSON.stringify(value);
1085 }
1086
1087 /**
1088 * @param {string} experimentName
1089 * @param {string} experimentTitle
1090 * @param {boolean=} hidden
1091 */
1092 register(experimentName, experimentTitle, hidden) {
1093 Runtime._assert(!this._experimentNames[experimentName], 'Duplicate registration of experiment ' + experimentName);
1094 this._experimentNames[experimentName] = true;
1095 this._experiments.push(new Runtime.Experiment(this, experimentName, experimentTitle, !!hidden));
1096 }
1097
1098 /**
1099 * @param {string} experimentName
1100 * @return {boolean}
1101 */
1102 isEnabled(experimentName) {
1103 this._checkExperiment(experimentName);
Pavel Feldmanc0395912018-08-03 02:05:00 +00001104 // Check for explicitly disabled experiments first - the code could call setEnable(false) on the experiment enabled
1105 // by default and we should respect that.
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001106 if (Runtime._experimentsSetting()[experimentName] === false) {
Pavel Feldmanc0395912018-08-03 02:05:00 +00001107 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001108 }
1109 if (this._enabledTransiently[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001110 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001111 }
1112 if (this._serverEnabled.has(experimentName)) {
Jeff Fisher85b08e12019-06-13 22:24:21 +00001113 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001114 }
1115 if (!this.supportEnabled()) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001116 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001117 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001118
1119 return !!Runtime._experimentsSetting()[experimentName];
1120 }
1121
1122 /**
1123 * @param {string} experimentName
1124 * @param {boolean} enabled
1125 */
1126 setEnabled(experimentName, enabled) {
1127 this._checkExperiment(experimentName);
1128 const experimentsSetting = Runtime._experimentsSetting();
1129 experimentsSetting[experimentName] = enabled;
1130 this._setExperimentsSetting(experimentsSetting);
1131 }
1132
1133 /**
1134 * @param {!Array.<string>} experimentNames
1135 */
1136 setDefaultExperiments(experimentNames) {
1137 for (let i = 0; i < experimentNames.length; ++i) {
1138 this._checkExperiment(experimentNames[i]);
1139 this._enabledTransiently[experimentNames[i]] = true;
1140 }
1141 }
1142
1143 /**
Jeff Fisher85b08e12019-06-13 22:24:21 +00001144 * @param {!Array.<string>} experimentNames
1145 */
1146 setServerEnabledExperiments(experimentNames) {
1147 for (const experiment of experimentNames) {
1148 this._checkExperiment(experiment);
1149 this._serverEnabled.add(experiment);
1150 }
1151 }
1152
1153 /**
Blink Reformat4c46d092018-04-07 15:32:37 +00001154 * @param {string} experimentName
1155 */
1156 enableForTest(experimentName) {
1157 this._checkExperiment(experimentName);
1158 this._enabledTransiently[experimentName] = true;
1159 }
1160
1161 clearForTest() {
1162 this._experiments = [];
1163 this._experimentNames = {};
1164 this._enabledTransiently = {};
Jeff Fisher85b08e12019-06-13 22:24:21 +00001165 this._serverEnabled.clear();
Blink Reformat4c46d092018-04-07 15:32:37 +00001166 }
1167
1168 cleanUpStaleExperiments() {
1169 const experimentsSetting = Runtime._experimentsSetting();
1170 const cleanedUpExperimentSetting = {};
1171 for (let i = 0; i < this._experiments.length; ++i) {
1172 const experimentName = this._experiments[i].name;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001173 if (experimentsSetting[experimentName]) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001174 cleanedUpExperimentSetting[experimentName] = true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001175 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001176 }
1177 this._setExperimentsSetting(cleanedUpExperimentSetting);
1178 }
1179
1180 /**
1181 * @param {string} experimentName
1182 */
1183 _checkExperiment(experimentName) {
1184 Runtime._assert(this._experimentNames[experimentName], 'Unknown experiment ' + experimentName);
1185 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001186}
Blink Reformat4c46d092018-04-07 15:32:37 +00001187
1188/**
1189 * @unrestricted
1190 */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001191class Experiment {
Blink Reformat4c46d092018-04-07 15:32:37 +00001192 /**
1193 * @param {!Runtime.ExperimentsSupport} experiments
1194 * @param {string} name
1195 * @param {string} title
1196 * @param {boolean} hidden
1197 */
1198 constructor(experiments, name, title, hidden) {
1199 this.name = name;
1200 this.title = title;
1201 this.hidden = hidden;
1202 this._experiments = experiments;
1203 }
1204
1205 /**
1206 * @return {boolean}
1207 */
1208 isEnabled() {
1209 return this._experiments.isEnabled(this.name);
1210 }
1211
1212 /**
1213 * @param {boolean} enabled
1214 */
1215 setEnabled(enabled) {
1216 this._experiments.setEnabled(this.name, enabled);
1217 }
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001218}
Blink Reformat4c46d092018-04-07 15:32:37 +00001219
Blink Reformat4c46d092018-04-07 15:32:37 +00001220// This must be constructed after the query parameters have been parsed.
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001221Runtime.experiments = new ExperimentsSupport();
Blink Reformat4c46d092018-04-07 15:32:37 +00001222
1223/** @type {Function} */
Pavel Feldman63e89eb2018-11-25 05:47:09 +00001224Runtime._appStartedPromiseCallback;
1225Runtime._appStartedPromise = new Promise(fulfil => Runtime._appStartedPromiseCallback = fulfil);
Vidal Diazleal0b6aff42019-04-20 01:15:43 +00001226
1227/** @type {function(string):string} */
1228Runtime._l10nCallback;
1229
Blink Reformat4c46d092018-04-07 15:32:37 +00001230/**
1231 * @type {?string}
1232 */
1233Runtime._remoteBase;
1234(function validateRemoteBase() {
James Lissiak821d2282019-05-15 15:32:04 +00001235 if (location.href.startsWith('devtools://devtools/bundled/') && Runtime.queryParam('remoteBase')) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001236 const versionMatch = /\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(Runtime.queryParam('remoteBase'));
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001237 if (versionMatch) {
Blink Reformat4c46d092018-04-07 15:32:37 +00001238 Runtime._remoteBase = `${location.origin}/remote/serve_file/${versionMatch[1]}/`;
Tim van der Lippe1d6e57a2019-09-30 11:55:34 +00001239 }
Blink Reformat4c46d092018-04-07 15:32:37 +00001240 }
1241})();
1242
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001243self.Root = self.Root || {};
1244Root = Root || {};
Blink Reformat4c46d092018-04-07 15:32:37 +00001245
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001246// This gets all concatenated module descriptors in the release mode.
1247Root.allDescriptors = [];
Blink Reformat4c46d092018-04-07 15:32:37 +00001248
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001249Root.applicationDescriptor = undefined;
Blink Reformat4c46d092018-04-07 15:32:37 +00001250
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001251/** @constructor */
1252Root.Runtime = Runtime;
Blink Reformat4c46d092018-04-07 15:32:37 +00001253
1254/** @type {!Runtime} */
Tim van der Lippe99e59b82019-09-30 20:00:59 +00001255Root.runtime;
1256
1257/** @constructor */
1258Root.Runtime.ModuleDescriptor = ModuleDescriptor;
1259
1260/** @constructor */
1261Root.Runtime.ExtensionDescriptor = RuntimeExtensionDescriptor;
1262
1263/** @constructor */
1264Root.Runtime.Extension = Extension;
1265
1266/** @constructor */
1267Root.Runtime.Module = Module;
1268
1269/** @constructor */
1270Root.Runtime.ExperimentsSupport = ExperimentsSupport;
1271
1272/** @constructor */
Yang Guo75beda92019-10-28 08:29:25 +01001273Root.Runtime.Experiment = Experiment;