| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2014 Google Inc. All rights reserved. | 2 * Copyright (C) 2014 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| 11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. | 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
| 17 * | 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | |
| 31 // This gets all concatenated module descriptors in the release mode. | 30 // This gets all concatenated module descriptors in the release mode. |
| 32 var allDescriptors = []; | 31 var allDescriptors = []; |
| 33 var applicationDescriptor; | 32 var applicationDescriptor; |
| 34 var _loadedScripts = {}; | 33 var _loadedScripts = {}; |
| 35 | 34 |
| 36 // FIXME: This is a workaround to force Closure compiler provide | 35 // FIXME: This is a workaround to force Closure compiler provide |
| 37 // the standard ES6 runtime for all modules. This should be removed | 36 // the standard ES6 runtime for all modules. This should be removed |
| 38 // once Closure provides standard externs for Map et al. | 37 // once Closure provides standard externs for Map et al. |
| 39 for (var k of []) {} | 38 for (var k of []) { |
| 39 } |
| 40 | 40 |
| 41 (function() { | 41 (function() { |
| 42 var baseUrl = self.location ? self.location.origin + self.location.pathname
: ""; | 42 var baseUrl = self.location ? self.location.origin + self.location.pathname :
''; |
| 43 self._importScriptPathPrefix = baseUrl.substring(0, baseUrl.lastIndexOf("/")
+ 1); | 43 self._importScriptPathPrefix = baseUrl.substring(0, baseUrl.lastIndexOf('/') +
1); |
| 44 })(); | 44 })(); |
| 45 | 45 |
| 46 /** | 46 /** |
| 47 * @constructor | 47 * @unrestricted |
| 48 * @param {!Array.<!Runtime.ModuleDescriptor>} descriptors | |
| 49 */ | 48 */ |
| 50 function Runtime(descriptors) | 49 var Runtime = class { |
| 51 { | 50 /** |
| 51 * @param {!Array.<!Runtime.ModuleDescriptor>} descriptors |
| 52 */ |
| 53 constructor(descriptors) { |
| 52 /** @type {!Array<!Runtime.Module>} */ | 54 /** @type {!Array<!Runtime.Module>} */ |
| 53 this._modules = []; | 55 this._modules = []; |
| 54 /** @type {!Object<string, !Runtime.Module>} */ | 56 /** @type {!Object<string, !Runtime.Module>} */ |
| 55 this._modulesMap = {}; | 57 this._modulesMap = {}; |
| 56 /** @type {!Array<!Runtime.Extension>} */ | 58 /** @type {!Array<!Runtime.Extension>} */ |
| 57 this._extensions = []; | 59 this._extensions = []; |
| 58 /** @type {!Object<string, !function(new:Object)>} */ | 60 /** @type {!Object<string, !function(new:Object)>} */ |
| 59 this._cachedTypeClasses = {}; | 61 this._cachedTypeClasses = {}; |
| 60 /** @type {!Object<string, !Runtime.ModuleDescriptor>} */ | 62 /** @type {!Object<string, !Runtime.ModuleDescriptor>} */ |
| 61 this._descriptorsMap = {}; | 63 this._descriptorsMap = {}; |
| 62 | 64 |
| 63 for (var i = 0; i < descriptors.length; ++i) | 65 for (var i = 0; i < descriptors.length; ++i) |
| 64 this._registerModule(descriptors[i]); | 66 this._registerModule(descriptors[i]); |
| 65 } | 67 } |
| 66 | 68 |
| 67 /** | 69 /** |
| 68 * @param {string} url | 70 * @param {string} url |
| 69 * @return {!Promise.<string>} | 71 * @return {!Promise.<string>} |
| 70 */ | 72 */ |
| 71 Runtime.loadResourcePromise = function(url) | 73 static loadResourcePromise(url) { |
| 72 { | |
| 73 return new Promise(load); | 74 return new Promise(load); |
| 74 | 75 |
| 75 /** | 76 /** |
| 76 * @param {function(?)} fulfill | 77 * @param {function(?)} fulfill |
| 77 * @param {function(*)} reject | 78 * @param {function(*)} reject |
| 78 */ | 79 */ |
| 79 function load(fulfill, reject) | 80 function load(fulfill, reject) { |
| 80 { | 81 var xhr = new XMLHttpRequest(); |
| 81 var xhr = new XMLHttpRequest(); | 82 xhr.open('GET', url, true); |
| 82 xhr.open("GET", url, true); | 83 xhr.onreadystatechange = onreadystatechange; |
| 83 xhr.onreadystatechange = onreadystatechange; | |
| 84 | 84 |
| 85 /** | 85 /** |
| 86 * @param {Event} e | 86 * @param {Event} e |
| 87 */ | 87 */ |
| 88 function onreadystatechange(e) | 88 function onreadystatechange(e) { |
| 89 { | 89 if (xhr.readyState !== XMLHttpRequest.DONE) |
| 90 if (xhr.readyState !== XMLHttpRequest.DONE) | 90 return; |
| 91 return; | |
| 92 | 91 |
| 93 if ([0, 200, 304].indexOf(xhr.status) === -1) // Testing harness fi
le:/// results in 0. | 92 if ([0, 200, 304].indexOf(xhr.status) === -1) // Testing harness file:/
// results in 0. |
| 94 reject(new Error("While loading from url " + url + " server resp
onded with a status of " + xhr.status)); | 93 reject(new Error('While loading from url ' + url + ' server responded
with a status of ' + xhr.status)); |
| 95 else | 94 else |
| 96 fulfill(e.target.response); | 95 fulfill(e.target.response); |
| 97 } | 96 } |
| 98 xhr.send(null); | 97 xhr.send(null); |
| 99 } | 98 } |
| 100 }; | 99 } |
| 101 | 100 |
| 102 /** | 101 /** |
| 103 * http://tools.ietf.org/html/rfc3986#section-5.2.4 | 102 * http://tools.ietf.org/html/rfc3986#section-5.2.4 |
| 104 * @param {string} path | 103 * @param {string} path |
| 105 * @return {string} | 104 * @return {string} |
| 106 */ | 105 */ |
| 107 Runtime.normalizePath = function(path) | 106 static normalizePath(path) { |
| 108 { | 107 if (path.indexOf('..') === -1 && path.indexOf('.') === -1) |
| 109 if (path.indexOf("..") === -1 && path.indexOf(".") === -1) | 108 return path; |
| 110 return path; | |
| 111 | 109 |
| 112 var normalizedSegments = []; | 110 var normalizedSegments = []; |
| 113 var segments = path.split("/"); | 111 var segments = path.split('/'); |
| 114 for (var i = 0; i < segments.length; i++) { | 112 for (var i = 0; i < segments.length; i++) { |
| 115 var segment = segments[i]; | 113 var segment = segments[i]; |
| 116 if (segment === ".") | 114 if (segment === '.') |
| 117 continue; | 115 continue; |
| 118 else if (segment === "..") | 116 else if (segment === '..') |
| 119 normalizedSegments.pop(); | 117 normalizedSegments.pop(); |
| 120 else if (segment) | 118 else if (segment) |
| 121 normalizedSegments.push(segment); | 119 normalizedSegments.push(segment); |
| 122 } | 120 } |
| 123 var normalizedPath = normalizedSegments.join("/"); | 121 var normalizedPath = normalizedSegments.join('/'); |
| 124 if (normalizedPath[normalizedPath.length - 1] === "/") | 122 if (normalizedPath[normalizedPath.length - 1] === '/') |
| 125 return normalizedPath; | 123 return normalizedPath; |
| 126 if (path[0] === "/" && normalizedPath) | 124 if (path[0] === '/' && normalizedPath) |
| 127 normalizedPath = "/" + normalizedPath; | 125 normalizedPath = '/' + normalizedPath; |
| 128 if ((path[path.length - 1] === "/") || (segments[segments.length - 1] === ".
") || (segments[segments.length - 1] === "..")) | 126 if ((path[path.length - 1] === '/') || (segments[segments.length - 1] === '.
') || |
| 129 normalizedPath = normalizedPath + "/"; | 127 (segments[segments.length - 1] === '..')) |
| 128 normalizedPath = normalizedPath + '/'; |
| 130 | 129 |
| 131 return normalizedPath; | 130 return normalizedPath; |
| 132 }; | 131 } |
| 133 | 132 |
| 134 /** | 133 /** |
| 135 * @param {!Array.<string>} scriptNames | 134 * @param {!Array.<string>} scriptNames |
| 136 * @param {string=} base | 135 * @param {string=} base |
| 137 * @return {!Promise.<undefined>} | 136 * @return {!Promise.<undefined>} |
| 138 */ | 137 */ |
| 139 Runtime._loadScriptsPromise = function(scriptNames, base) | 138 static _loadScriptsPromise(scriptNames, base) { |
| 140 { | |
| 141 /** @type {!Array<!Promise<undefined>>} */ | 139 /** @type {!Array<!Promise<undefined>>} */ |
| 142 var promises = []; | 140 var promises = []; |
| 143 /** @type {!Array<string>} */ | 141 /** @type {!Array<string>} */ |
| 144 var urls = []; | 142 var urls = []; |
| 145 var sources = new Array(scriptNames.length); | 143 var sources = new Array(scriptNames.length); |
| 146 var scriptToEval = 0; | 144 var scriptToEval = 0; |
| 147 for (var i = 0; i < scriptNames.length; ++i) { | 145 for (var i = 0; i < scriptNames.length; ++i) { |
| 148 var scriptName = scriptNames[i]; | 146 var scriptName = scriptNames[i]; |
| 149 var sourceURL = (base || self._importScriptPathPrefix) + scriptName; | 147 var sourceURL = (base || self._importScriptPathPrefix) + scriptName; |
| 150 | 148 |
| 151 var schemaIndex = sourceURL.indexOf("://") + 3; | 149 var schemaIndex = sourceURL.indexOf('://') + 3; |
| 152 var pathIndex = sourceURL.indexOf("/", schemaIndex); | 150 var pathIndex = sourceURL.indexOf('/', schemaIndex); |
| 153 if (pathIndex === -1) | 151 if (pathIndex === -1) |
| 154 pathIndex = sourceURL.length; | 152 pathIndex = sourceURL.length; |
| 155 sourceURL = sourceURL.substring(0, pathIndex) + Runtime.normalizePath(so
urceURL.substring(pathIndex)); | 153 sourceURL = sourceURL.substring(0, pathIndex) + Runtime.normalizePath(sour
ceURL.substring(pathIndex)); |
| 156 | 154 |
| 157 if (_loadedScripts[sourceURL]) | 155 if (_loadedScripts[sourceURL]) |
| 158 continue; | 156 continue; |
| 159 urls.push(sourceURL); | 157 urls.push(sourceURL); |
| 160 promises.push(Runtime.loadResourcePromise(sourceURL).then(scriptSourceLo
aded.bind(null, i), scriptSourceLoaded.bind(null, i, undefined))); | 158 promises.push(Runtime.loadResourcePromise(sourceURL).then( |
| 159 scriptSourceLoaded.bind(null, i), scriptSourceLoaded.bind(null, i, und
efined))); |
| 161 } | 160 } |
| 162 return Promise.all(promises).then(undefined); | 161 return Promise.all(promises).then(undefined); |
| 163 | 162 |
| 164 /** | 163 /** |
| 165 * @param {number} scriptNumber | 164 * @param {number} scriptNumber |
| 166 * @param {string=} scriptSource | 165 * @param {string=} scriptSource |
| 167 */ | 166 */ |
| 168 function scriptSourceLoaded(scriptNumber, scriptSource) | 167 function scriptSourceLoaded(scriptNumber, scriptSource) { |
| 169 { | 168 sources[scriptNumber] = scriptSource || ''; |
| 170 sources[scriptNumber] = scriptSource || ""; | 169 // Eval scripts as fast as possible. |
| 171 // Eval scripts as fast as possible. | 170 while (typeof sources[scriptToEval] !== 'undefined') { |
| 172 while (typeof sources[scriptToEval] !== "undefined") { | 171 evaluateScript(urls[scriptToEval], sources[scriptToEval]); |
| 173 evaluateScript(urls[scriptToEval], sources[scriptToEval]); | 172 ++scriptToEval; |
| 174 ++scriptToEval; | 173 } |
| 175 } | |
| 176 } | 174 } |
| 177 | 175 |
| 178 /** | 176 /** |
| 179 * @param {string} sourceURL | 177 * @param {string} sourceURL |
| 180 * @param {string=} scriptSource | 178 * @param {string=} scriptSource |
| 181 */ | 179 */ |
| 182 function evaluateScript(sourceURL, scriptSource) | 180 function evaluateScript(sourceURL, scriptSource) { |
| 183 { | 181 _loadedScripts[sourceURL] = true; |
| 184 _loadedScripts[sourceURL] = true; | 182 if (!scriptSource) { |
| 185 if (!scriptSource) { | 183 // Do not reject, as this is normal in the hosted mode. |
| 186 // Do not reject, as this is normal in the hosted mode. | 184 console.error('Empty response arrived for script \'' + sourceURL + '\'')
; |
| 187 console.error("Empty response arrived for script '" + sourceURL + "'
"); | 185 return; |
| 188 return; | 186 } |
| 189 } | 187 self.eval(scriptSource + '\n//# sourceURL=' + sourceURL); |
| 190 self.eval(scriptSource + "\n//# sourceURL=" + sourceURL); | |
| 191 } | 188 } |
| 192 }; | 189 } |
| 193 | 190 |
| 194 /** | 191 /** |
| 195 * @type {!Object.<string, string>} | 192 * @param {string} url |
| 196 */ | 193 * @param {boolean} appendSourceURL |
| 197 Runtime._queryParamsObject = { __proto__: null }; | 194 * @return {!Promise<undefined>} |
| 198 | 195 */ |
| 199 Runtime._instanceSymbol = Symbol("instance"); | 196 static loadResourceIntoCache(url, appendSourceURL) { |
| 200 Runtime._extensionSymbol = Symbol("extension"); | 197 return Runtime.loadResourcePromise(url).then( |
| 201 | 198 cacheResource.bind(this, url), cacheResource.bind(this, url, undefined))
; |
| 202 /** | |
| 203 * @type {!Object.<string, string>} | |
| 204 */ | |
| 205 Runtime.cachedResources = { __proto__: null }; | |
| 206 | |
| 207 /** | |
| 208 * @param {string} url | |
| 209 * @param {boolean} appendSourceURL | |
| 210 * @return {!Promise<undefined>} | |
| 211 */ | |
| 212 Runtime.loadResourceIntoCache = function(url, appendSourceURL) | |
| 213 { | |
| 214 return Runtime.loadResourcePromise(url).then(cacheResource.bind(this, url),
cacheResource.bind(this, url, undefined)); | |
| 215 | 199 |
| 216 /** | 200 /** |
| 217 * @param {string} path | 201 * @param {string} path |
| 218 * @param {string=} content | 202 * @param {string=} content |
| 219 */ | 203 */ |
| 220 function cacheResource(path, content) | 204 function cacheResource(path, content) { |
| 221 { | 205 if (!content) { |
| 222 if (!content) { | 206 console.error('Failed to load resource: ' + path); |
| 223 console.error("Failed to load resource: " + path); | 207 return; |
| 224 return; | 208 } |
| 225 } | 209 var sourceURL = appendSourceURL ? Runtime.resolveSourceURL(path) : ''; |
| 226 var sourceURL = appendSourceURL ? Runtime.resolveSourceURL(path) : ""; | 210 Runtime.cachedResources[path] = content + sourceURL; |
| 227 Runtime.cachedResources[path] = content + sourceURL; | |
| 228 } | 211 } |
| 229 }; | 212 } |
| 230 | 213 |
| 231 /** | 214 /** |
| 232 * @param {string} appName | 215 * @param {string} appName |
| 233 * @return {!Promise.<undefined>} | 216 * @return {!Promise.<undefined>} |
| 234 */ | 217 */ |
| 235 Runtime.startApplication = function(appName) | 218 static startApplication(appName) { |
| 236 { | 219 console.timeStamp('Runtime.startApplication'); |
| 237 console.timeStamp("Runtime.startApplication"); | |
| 238 | 220 |
| 239 var allDescriptorsByName = {}; | 221 var allDescriptorsByName = {}; |
| 240 for (var i = 0; i < allDescriptors.length; ++i) { | 222 for (var i = 0; i < allDescriptors.length; ++i) { |
| 241 var d = allDescriptors[i]; | 223 var d = allDescriptors[i]; |
| 242 allDescriptorsByName[d["name"]] = d; | 224 allDescriptorsByName[d['name']] = d; |
| 243 } | 225 } |
| 244 | 226 |
| 245 var applicationPromise; | 227 var applicationPromise; |
| 246 if (applicationDescriptor) | 228 if (applicationDescriptor) |
| 247 applicationPromise = Promise.resolve(applicationDescriptor); | 229 applicationPromise = Promise.resolve(applicationDescriptor); |
| 248 else | 230 else |
| 249 applicationPromise = Runtime.loadResourcePromise(appName + ".json").then
(JSON.parse.bind(JSON)); | 231 applicationPromise = Runtime.loadResourcePromise(appName + '.json').then(J
SON.parse.bind(JSON)); |
| 250 | 232 |
| 251 return applicationPromise.then(parseModuleDescriptors); | 233 return applicationPromise.then(parseModuleDescriptors); |
| 252 | 234 |
| 253 /** | 235 /** |
| 254 * @param {!{modules: !Array.<!Object>, has_html: boolean}} appDescriptor | 236 * @param {!{modules: !Array.<!Object>, has_html: boolean}} appDescriptor |
| 255 * @return {!Promise.<undefined>} | 237 * @return {!Promise.<undefined>} |
| 256 */ | 238 */ |
| 257 function parseModuleDescriptors(appDescriptor) | 239 function parseModuleDescriptors(appDescriptor) { |
| 258 { | 240 var configuration = appDescriptor.modules; |
| 259 var configuration = appDescriptor.modules; | 241 var moduleJSONPromises = []; |
| 260 var moduleJSONPromises = []; | 242 var coreModuleNames = []; |
| 261 var coreModuleNames = []; | 243 for (var i = 0; i < configuration.length; ++i) { |
| 262 for (var i = 0; i < configuration.length; ++i) { | 244 var descriptor = configuration[i]; |
| 263 var descriptor = configuration[i]; | 245 var name = descriptor['name']; |
| 264 var name = descriptor["name"]; | 246 var moduleJSON = allDescriptorsByName[name]; |
| 265 var moduleJSON = allDescriptorsByName[name]; | 247 if (moduleJSON) |
| 266 if (moduleJSON) | 248 moduleJSONPromises.push(Promise.resolve(moduleJSON)); |
| 267 moduleJSONPromises.push(Promise.resolve(moduleJSON)); | 249 else |
| 268 else | 250 moduleJSONPromises.push(Runtime.loadResourcePromise(name + '/module.js
on').then(JSON.parse.bind(JSON))); |
| 269 moduleJSONPromises.push(Runtime.loadResourcePromise(name + "/mod
ule.json").then(JSON.parse.bind(JSON))); | 251 if (descriptor['type'] === 'autostart') |
| 270 if (descriptor["type"] === "autostart") | 252 coreModuleNames.push(name); |
| 271 coreModuleNames.push(name); | 253 } |
| 254 |
| 255 return Promise.all(moduleJSONPromises).then(instantiateRuntime); |
| 256 |
| 257 /** |
| 258 * @param {!Array.<!Object>} moduleDescriptors |
| 259 * @return {!Promise.<undefined>} |
| 260 */ |
| 261 function instantiateRuntime(moduleDescriptors) { |
| 262 for (var i = 0; i < moduleDescriptors.length; ++i) { |
| 263 moduleDescriptors[i].name = configuration[i]['name']; |
| 264 moduleDescriptors[i].condition = configuration[i]['condition']; |
| 265 moduleDescriptors[i].remote = configuration[i]['type'] === 'remote'; |
| 272 } | 266 } |
| 273 | 267 self.runtime = new Runtime(moduleDescriptors); |
| 274 return Promise.all(moduleJSONPromises).then(instantiateRuntime); | 268 if (coreModuleNames) |
| 275 | 269 return /** @type {!Promise<undefined>} */ (self.runtime._loadAutoStart
Modules(coreModuleNames)); |
| 276 /** | 270 return Promise.resolve(); |
| 277 * @param {!Array.<!Object>} moduleDescriptors | 271 } |
| 278 * @return {!Promise.<undefined>} | 272 } |
| 279 */ | 273 } |
| 280 function instantiateRuntime(moduleDescriptors) | 274 |
| 281 { | 275 /** |
| 282 for (var i = 0; i < moduleDescriptors.length; ++i) { | 276 * @param {string} appName |
| 283 moduleDescriptors[i].name = configuration[i]["name"]; | 277 * @return {!Promise.<undefined>} |
| 284 moduleDescriptors[i].condition = configuration[i]["condition"]; | 278 */ |
| 285 moduleDescriptors[i].remote = configuration[i]["type"] === "remo
te"; | 279 static startWorker(appName) { |
| 286 } | 280 return Runtime.startApplication(appName).then(sendWorkerReady); |
| 287 self.runtime = new Runtime(moduleDescriptors); | 281 |
| 288 if (coreModuleNames) | 282 function sendWorkerReady() { |
| 289 return /** @type {!Promise<undefined>} */ (self.runtime._loadAut
oStartModules(coreModuleNames)); | 283 self.postMessage('workerReady'); |
| 290 return Promise.resolve(); | 284 } |
| 291 } | 285 } |
| 292 } | 286 |
| 287 /** |
| 288 * @param {string} appName |
| 289 */ |
| 290 static startSharedWorker(appName) { |
| 291 var startPromise = Runtime.startApplication(appName); |
| 292 |
| 293 /** |
| 294 * @param {!MessageEvent} event |
| 295 */ |
| 296 self.onconnect = function(event) { |
| 297 var newPort = /** @type {!MessagePort} */ (event.ports[0]); |
| 298 startPromise.then(sendWorkerReadyAndContinue); |
| 299 |
| 300 function sendWorkerReadyAndContinue() { |
| 301 newPort.postMessage('workerReady'); |
| 302 if (Runtime._sharedWorkerNewPortCallback) |
| 303 Runtime._sharedWorkerNewPortCallback.call(null, newPort); |
| 304 else |
| 305 Runtime._sharedWorkerConnectedPorts.push(newPort); |
| 306 } |
| 307 }; |
| 308 } |
| 309 |
| 310 /** |
| 311 * @param {function(!MessagePort)} callback |
| 312 */ |
| 313 static setSharedWorkerNewPortCallback(callback) { |
| 314 Runtime._sharedWorkerNewPortCallback = callback; |
| 315 while (Runtime._sharedWorkerConnectedPorts.length) { |
| 316 var port = Runtime._sharedWorkerConnectedPorts.shift(); |
| 317 callback.call(null, port); |
| 318 } |
| 319 } |
| 320 |
| 321 /** |
| 322 * @param {string} name |
| 323 * @return {?string} |
| 324 */ |
| 325 static queryParam(name) { |
| 326 return Runtime._queryParamsObject[name] || null; |
| 327 } |
| 328 |
| 329 /** |
| 330 * @return {!Object} |
| 331 */ |
| 332 static _experimentsSetting() { |
| 333 try { |
| 334 return /** @type {!Object} */ ( |
| 335 JSON.parse(self.localStorage && self.localStorage['experiments'] ? sel
f.localStorage['experiments'] : '{}')); |
| 336 } catch (e) { |
| 337 console.error('Failed to parse localStorage[\'experiments\']'); |
| 338 return {}; |
| 339 } |
| 340 } |
| 341 |
| 342 static _assert(value, message) { |
| 343 if (value) |
| 344 return; |
| 345 Runtime._originalAssert.call(Runtime._console, value, message + ' ' + new Er
ror().stack); |
| 346 } |
| 347 |
| 348 /** |
| 349 * @param {string} platform |
| 350 */ |
| 351 static setPlatform(platform) { |
| 352 Runtime._platform = platform; |
| 353 } |
| 354 |
| 355 /** |
| 356 * @param {!Object} descriptor |
| 357 * @return {boolean} |
| 358 */ |
| 359 static _isDescriptorEnabled(descriptor) { |
| 360 var activatorExperiment = descriptor['experiment']; |
| 361 if (activatorExperiment === '*') |
| 362 return Runtime.experiments.supportEnabled(); |
| 363 if (activatorExperiment && activatorExperiment.startsWith('!') && |
| 364 Runtime.experiments.isEnabled(activatorExperiment.substring(1))) |
| 365 return false; |
| 366 if (activatorExperiment && !activatorExperiment.startsWith('!') && |
| 367 !Runtime.experiments.isEnabled(activatorExperiment)) |
| 368 return false; |
| 369 var condition = descriptor['condition']; |
| 370 if (condition && !condition.startsWith('!') && !Runtime.queryParam(condition
)) |
| 371 return false; |
| 372 if (condition && condition.startsWith('!') && Runtime.queryParam(condition.s
ubstring(1))) |
| 373 return false; |
| 374 return true; |
| 375 } |
| 376 |
| 377 /** |
| 378 * @param {string} path |
| 379 * @return {string} |
| 380 */ |
| 381 static resolveSourceURL(path) { |
| 382 var sourceURL = self.location.href; |
| 383 if (self.location.search) |
| 384 sourceURL = sourceURL.replace(self.location.search, ''); |
| 385 sourceURL = sourceURL.substring(0, sourceURL.lastIndexOf('/') + 1) + path; |
| 386 return '\n/*# sourceURL=' + sourceURL + ' */'; |
| 387 } |
| 388 |
| 389 useTestBase() { |
| 390 Runtime._remoteBase = 'http://localhost:8000/inspector-sources/'; |
| 391 if (Runtime.queryParam('debugFrontend')) |
| 392 Runtime._remoteBase += 'debug/'; |
| 393 } |
| 394 |
| 395 /** |
| 396 * @param {!Runtime.ModuleDescriptor} descriptor |
| 397 */ |
| 398 _registerModule(descriptor) { |
| 399 var module = new Runtime.Module(this, descriptor); |
| 400 this._modules.push(module); |
| 401 this._modulesMap[descriptor['name']] = module; |
| 402 } |
| 403 |
| 404 /** |
| 405 * @param {string} moduleName |
| 406 * @return {!Promise.<undefined>} |
| 407 */ |
| 408 loadModulePromise(moduleName) { |
| 409 return this._modulesMap[moduleName]._loadPromise(); |
| 410 } |
| 411 |
| 412 /** |
| 413 * @param {!Array.<string>} moduleNames |
| 414 * @return {!Promise.<!Array.<*>>} |
| 415 */ |
| 416 _loadAutoStartModules(moduleNames) { |
| 417 var promises = []; |
| 418 for (var i = 0; i < moduleNames.length; ++i) |
| 419 promises.push(this.loadModulePromise(moduleNames[i])); |
| 420 return Promise.all(promises); |
| 421 } |
| 422 |
| 423 /** |
| 424 * @param {!Runtime.Extension} extension |
| 425 * @param {?function(function(new:Object)):boolean} predicate |
| 426 * @return {boolean} |
| 427 */ |
| 428 _checkExtensionApplicability(extension, predicate) { |
| 429 if (!predicate) |
| 430 return false; |
| 431 var contextTypes = extension.descriptor().contextTypes; |
| 432 if (!contextTypes) |
| 433 return true; |
| 434 for (var i = 0; i < contextTypes.length; ++i) { |
| 435 var contextType = this._resolve(contextTypes[i]); |
| 436 var isMatching = !!contextType && predicate(contextType); |
| 437 if (isMatching) |
| 438 return true; |
| 439 } |
| 440 return false; |
| 441 } |
| 442 |
| 443 /** |
| 444 * @param {!Runtime.Extension} extension |
| 445 * @param {?Object} context |
| 446 * @return {boolean} |
| 447 */ |
| 448 isExtensionApplicableToContext(extension, context) { |
| 449 if (!context) |
| 450 return true; |
| 451 return this._checkExtensionApplicability(extension, isInstanceOf); |
| 452 |
| 453 /** |
| 454 * @param {!Function} targetType |
| 455 * @return {boolean} |
| 456 */ |
| 457 function isInstanceOf(targetType) { |
| 458 return context instanceof targetType; |
| 459 } |
| 460 } |
| 461 |
| 462 /** |
| 463 * @param {!Runtime.Extension} extension |
| 464 * @param {!Set.<!Function>=} currentContextTypes |
| 465 * @return {boolean} |
| 466 */ |
| 467 isExtensionApplicableToContextTypes(extension, currentContextTypes) { |
| 468 if (!extension.descriptor().contextTypes) |
| 469 return true; |
| 470 |
| 471 return this._checkExtensionApplicability(extension, currentContextTypes ? is
ContextTypeKnown : null); |
| 472 |
| 473 /** |
| 474 * @param {!Function} targetType |
| 475 * @return {boolean} |
| 476 */ |
| 477 function isContextTypeKnown(targetType) { |
| 478 return currentContextTypes.has(targetType); |
| 479 } |
| 480 } |
| 481 |
| 482 /** |
| 483 * @param {*} type |
| 484 * @param {?Object=} context |
| 485 * @param {boolean=} sortByTitle |
| 486 * @return {!Array.<!Runtime.Extension>} |
| 487 */ |
| 488 extensions(type, context, sortByTitle) { |
| 489 return this._extensions.filter(filter).sort(sortByTitle ? titleComparator :
orderComparator); |
| 490 |
| 491 /** |
| 492 * @param {!Runtime.Extension} extension |
| 493 * @return {boolean} |
| 494 */ |
| 495 function filter(extension) { |
| 496 if (extension._type !== type && extension._typeClass() !== type) |
| 497 return false; |
| 498 if (!extension.enabled()) |
| 499 return false; |
| 500 return !context || extension.isApplicable(context); |
| 501 } |
| 502 |
| 503 /** |
| 504 * @param {!Runtime.Extension} extension1 |
| 505 * @param {!Runtime.Extension} extension2 |
| 506 * @return {number} |
| 507 */ |
| 508 function orderComparator(extension1, extension2) { |
| 509 var order1 = extension1.descriptor()['order'] || 0; |
| 510 var order2 = extension2.descriptor()['order'] || 0; |
| 511 return order1 - order2; |
| 512 } |
| 513 |
| 514 /** |
| 515 * @param {!Runtime.Extension} extension1 |
| 516 * @param {!Runtime.Extension} extension2 |
| 517 * @return {number} |
| 518 */ |
| 519 function titleComparator(extension1, extension2) { |
| 520 var title1 = extension1.title() || ''; |
| 521 var title2 = extension2.title() || ''; |
| 522 return title1.localeCompare(title2); |
| 523 } |
| 524 } |
| 525 |
| 526 /** |
| 527 * @param {*} type |
| 528 * @param {?Object=} context |
| 529 * @return {?Runtime.Extension} |
| 530 */ |
| 531 extension(type, context) { |
| 532 return this.extensions(type, context)[0] || null; |
| 533 } |
| 534 |
| 535 /** |
| 536 * @param {*} type |
| 537 * @param {?Object=} context |
| 538 * @return {!Promise.<!Array.<!Object>>} |
| 539 */ |
| 540 allInstances(type, context) { |
| 541 return Promise.all(this.extensions(type, context).map(extension => extension
.instance())); |
| 542 } |
| 543 |
| 544 /** |
| 545 * @return {?function(new:Object)} |
| 546 */ |
| 547 _resolve(typeName) { |
| 548 if (!this._cachedTypeClasses[typeName]) { |
| 549 var path = typeName.split('.'); |
| 550 var object = self; |
| 551 for (var i = 0; object && (i < path.length); ++i) |
| 552 object = object[path[i]]; |
| 553 if (object) |
| 554 this._cachedTypeClasses[typeName] = /** @type function(new:Object) */ (o
bject); |
| 555 } |
| 556 return this._cachedTypeClasses[typeName] || null; |
| 557 } |
| 558 |
| 559 /** |
| 560 * @param {!Function} constructorFunction |
| 561 * @return {!Object} |
| 562 */ |
| 563 sharedInstance(constructorFunction) { |
| 564 if (Runtime._instanceSymbol in constructorFunction) |
| 565 return constructorFunction[Runtime._instanceSymbol]; |
| 566 var instance = new constructorFunction(); |
| 567 constructorFunction[Runtime._instanceSymbol] = instance; |
| 568 return instance; |
| 569 } |
| 570 } |
| 571 |
| 572 ; |
| 573 |
| 574 |
| 575 /** |
| 576 * @type {!Object.<string, string>} |
| 577 */ |
| 578 Runtime._queryParamsObject = { |
| 579 __proto__: null |
| 293 }; | 580 }; |
| 294 | 581 |
| 582 Runtime._instanceSymbol = Symbol('instance'); |
| 583 Runtime._extensionSymbol = Symbol('extension'); |
| 584 |
| 295 /** | 585 /** |
| 296 * @param {string} appName | 586 * @type {!Object.<string, string>} |
| 297 * @return {!Promise.<undefined>} | |
| 298 */ | 587 */ |
| 299 Runtime.startWorker = function(appName) | 588 Runtime.cachedResources = { |
| 300 { | 589 __proto__: null |
| 301 return Runtime.startApplication(appName).then(sendWorkerReady); | |
| 302 | |
| 303 function sendWorkerReady() | |
| 304 { | |
| 305 self.postMessage("workerReady"); | |
| 306 } | |
| 307 }; | 590 }; |
| 308 | 591 |
| 592 |
| 309 /** @type {?function(!MessagePort)} */ | 593 /** @type {?function(!MessagePort)} */ |
| 310 Runtime._sharedWorkerNewPortCallback = null; | 594 Runtime._sharedWorkerNewPortCallback = null; |
| 311 /** @type {!Array<!MessagePort>} */ | 595 /** @type {!Array<!MessagePort>} */ |
| 312 Runtime._sharedWorkerConnectedPorts = []; | 596 Runtime._sharedWorkerConnectedPorts = []; |
| 313 | 597 |
| 314 /** | |
| 315 * @param {string} appName | |
| 316 */ | |
| 317 Runtime.startSharedWorker = function(appName) | |
| 318 { | |
| 319 var startPromise = Runtime.startApplication(appName); | |
| 320 | |
| 321 /** | |
| 322 * @param {!MessageEvent} event | |
| 323 */ | |
| 324 self.onconnect = function(event) | |
| 325 { | |
| 326 var newPort = /** @type {!MessagePort} */ (event.ports[0]); | |
| 327 startPromise.then(sendWorkerReadyAndContinue); | |
| 328 | |
| 329 function sendWorkerReadyAndContinue() | |
| 330 { | |
| 331 newPort.postMessage("workerReady"); | |
| 332 if (Runtime._sharedWorkerNewPortCallback) | |
| 333 Runtime._sharedWorkerNewPortCallback.call(null, newPort); | |
| 334 else | |
| 335 Runtime._sharedWorkerConnectedPorts.push(newPort); | |
| 336 } | |
| 337 }; | |
| 338 }; | |
| 339 | |
| 340 /** | |
| 341 * @param {function(!MessagePort)} callback | |
| 342 */ | |
| 343 Runtime.setSharedWorkerNewPortCallback = function(callback) | |
| 344 { | |
| 345 Runtime._sharedWorkerNewPortCallback = callback; | |
| 346 while (Runtime._sharedWorkerConnectedPorts.length) { | |
| 347 var port = Runtime._sharedWorkerConnectedPorts.shift(); | |
| 348 callback.call(null, port); | |
| 349 } | |
| 350 }; | |
| 351 | |
| 352 /** | |
| 353 * @param {string} name | |
| 354 * @return {?string} | |
| 355 */ | |
| 356 Runtime.queryParam = function(name) | |
| 357 { | |
| 358 return Runtime._queryParamsObject[name] || null; | |
| 359 }; | |
| 360 | |
| 361 /** | |
| 362 * @return {!Object} | |
| 363 */ | |
| 364 Runtime._experimentsSetting = function() | |
| 365 { | |
| 366 try { | |
| 367 return /** @type {!Object} */ (JSON.parse(self.localStorage && self.loca
lStorage["experiments"] ? self.localStorage["experiments"] : "{}")); | |
| 368 } catch (e) { | |
| 369 console.error("Failed to parse localStorage['experiments']"); | |
| 370 return {}; | |
| 371 } | |
| 372 }; | |
| 373 | 598 |
| 374 Runtime._console = console; | 599 Runtime._console = console; |
| 375 Runtime._originalAssert = console.assert; | 600 Runtime._originalAssert = console.assert; |
| 376 Runtime._assert = function(value, message) | 601 |
| 377 { | 602 |
| 378 if (value) | 603 Runtime._platform = ''; |
| 379 return; | 604 |
| 380 Runtime._originalAssert.call(Runtime._console, value, message + " " + new Er
ror().stack); | |
| 381 }; | |
| 382 | |
| 383 Runtime._platform = ""; | |
| 384 | 605 |
| 385 /** | 606 /** |
| 386 * @param {string} platform | 607 * @unrestricted |
| 387 */ | 608 */ |
| 388 Runtime.setPlatform = function(platform) | 609 Runtime.ModuleDescriptor = class { |
| 389 { | 610 constructor() { |
| 390 Runtime._platform = platform; | |
| 391 }; | |
| 392 | |
| 393 Runtime.prototype = { | |
| 394 useTestBase: function() | |
| 395 { | |
| 396 Runtime._remoteBase = "http://localhost:8000/inspector-sources/"; | |
| 397 if (Runtime.queryParam("debugFrontend")) | |
| 398 Runtime._remoteBase += "debug/"; | |
| 399 }, | |
| 400 | |
| 401 /** | |
| 402 * @param {!Runtime.ModuleDescriptor} descriptor | |
| 403 */ | |
| 404 _registerModule: function(descriptor) | |
| 405 { | |
| 406 var module = new Runtime.Module(this, descriptor); | |
| 407 this._modules.push(module); | |
| 408 this._modulesMap[descriptor["name"]] = module; | |
| 409 }, | |
| 410 | |
| 411 /** | |
| 412 * @param {string} moduleName | |
| 413 * @return {!Promise.<undefined>} | |
| 414 */ | |
| 415 loadModulePromise: function(moduleName) | |
| 416 { | |
| 417 return this._modulesMap[moduleName]._loadPromise(); | |
| 418 }, | |
| 419 | |
| 420 /** | |
| 421 * @param {!Array.<string>} moduleNames | |
| 422 * @return {!Promise.<!Array.<*>>} | |
| 423 */ | |
| 424 _loadAutoStartModules: function(moduleNames) | |
| 425 { | |
| 426 var promises = []; | |
| 427 for (var i = 0; i < moduleNames.length; ++i) | |
| 428 promises.push(this.loadModulePromise(moduleNames[i])); | |
| 429 return Promise.all(promises); | |
| 430 }, | |
| 431 | |
| 432 /** | |
| 433 * @param {!Runtime.Extension} extension | |
| 434 * @param {?function(function(new:Object)):boolean} predicate | |
| 435 * @return {boolean} | |
| 436 */ | |
| 437 _checkExtensionApplicability: function(extension, predicate) | |
| 438 { | |
| 439 if (!predicate) | |
| 440 return false; | |
| 441 var contextTypes = extension.descriptor().contextTypes; | |
| 442 if (!contextTypes) | |
| 443 return true; | |
| 444 for (var i = 0; i < contextTypes.length; ++i) { | |
| 445 var contextType = this._resolve(contextTypes[i]); | |
| 446 var isMatching = !!contextType && predicate(contextType); | |
| 447 if (isMatching) | |
| 448 return true; | |
| 449 } | |
| 450 return false; | |
| 451 }, | |
| 452 | |
| 453 /** | |
| 454 * @param {!Runtime.Extension} extension | |
| 455 * @param {?Object} context | |
| 456 * @return {boolean} | |
| 457 */ | |
| 458 isExtensionApplicableToContext: function(extension, context) | |
| 459 { | |
| 460 if (!context) | |
| 461 return true; | |
| 462 return this._checkExtensionApplicability(extension, isInstanceOf); | |
| 463 | |
| 464 /** | |
| 465 * @param {!Function} targetType | |
| 466 * @return {boolean} | |
| 467 */ | |
| 468 function isInstanceOf(targetType) | |
| 469 { | |
| 470 return context instanceof targetType; | |
| 471 } | |
| 472 }, | |
| 473 | |
| 474 /** | |
| 475 * @param {!Runtime.Extension} extension | |
| 476 * @param {!Set.<!Function>=} currentContextTypes | |
| 477 * @return {boolean} | |
| 478 */ | |
| 479 isExtensionApplicableToContextTypes: function(extension, currentContextTypes
) | |
| 480 { | |
| 481 if (!extension.descriptor().contextTypes) | |
| 482 return true; | |
| 483 | |
| 484 return this._checkExtensionApplicability(extension, currentContextTypes
? isContextTypeKnown : null); | |
| 485 | |
| 486 /** | |
| 487 * @param {!Function} targetType | |
| 488 * @return {boolean} | |
| 489 */ | |
| 490 function isContextTypeKnown(targetType) | |
| 491 { | |
| 492 return currentContextTypes.has(targetType); | |
| 493 } | |
| 494 }, | |
| 495 | |
| 496 /** | |
| 497 * @param {*} type | |
| 498 * @param {?Object=} context | |
| 499 * @param {boolean=} sortByTitle | |
| 500 * @return {!Array.<!Runtime.Extension>} | |
| 501 */ | |
| 502 extensions: function(type, context, sortByTitle) | |
| 503 { | |
| 504 return this._extensions.filter(filter).sort(sortByTitle ? titleComparato
r : orderComparator); | |
| 505 | |
| 506 /** | |
| 507 * @param {!Runtime.Extension} extension | |
| 508 * @return {boolean} | |
| 509 */ | |
| 510 function filter(extension) | |
| 511 { | |
| 512 if (extension._type !== type && extension._typeClass() !== type) | |
| 513 return false; | |
| 514 if (!extension.enabled()) | |
| 515 return false; | |
| 516 return !context || extension.isApplicable(context); | |
| 517 } | |
| 518 | |
| 519 /** | |
| 520 * @param {!Runtime.Extension} extension1 | |
| 521 * @param {!Runtime.Extension} extension2 | |
| 522 * @return {number} | |
| 523 */ | |
| 524 function orderComparator(extension1, extension2) | |
| 525 { | |
| 526 var order1 = extension1.descriptor()["order"] || 0; | |
| 527 var order2 = extension2.descriptor()["order"] || 0; | |
| 528 return order1 - order2; | |
| 529 } | |
| 530 | |
| 531 /** | |
| 532 * @param {!Runtime.Extension} extension1 | |
| 533 * @param {!Runtime.Extension} extension2 | |
| 534 * @return {number} | |
| 535 */ | |
| 536 function titleComparator(extension1, extension2) | |
| 537 { | |
| 538 var title1 = extension1.title() || ""; | |
| 539 var title2 = extension2.title() || ""; | |
| 540 return title1.localeCompare(title2); | |
| 541 } | |
| 542 }, | |
| 543 | |
| 544 /** | |
| 545 * @param {*} type | |
| 546 * @param {?Object=} context | |
| 547 * @return {?Runtime.Extension} | |
| 548 */ | |
| 549 extension: function(type, context) | |
| 550 { | |
| 551 return this.extensions(type, context)[0] || null; | |
| 552 }, | |
| 553 | |
| 554 /** | |
| 555 * @param {*} type | |
| 556 * @param {?Object=} context | |
| 557 * @return {!Promise.<!Array.<!Object>>} | |
| 558 */ | |
| 559 allInstances: function(type, context) | |
| 560 { | |
| 561 return Promise.all(this.extensions(type, context).map(extension => exten
sion.instance())); | |
| 562 }, | |
| 563 | |
| 564 /** | |
| 565 * @return {?function(new:Object)} | |
| 566 */ | |
| 567 _resolve: function(typeName) | |
| 568 { | |
| 569 if (!this._cachedTypeClasses[typeName]) { | |
| 570 var path = typeName.split("."); | |
| 571 var object = self; | |
| 572 for (var i = 0; object && (i < path.length); ++i) | |
| 573 object = object[path[i]]; | |
| 574 if (object) | |
| 575 this._cachedTypeClasses[typeName] = /** @type function(new:Objec
t) */(object); | |
| 576 } | |
| 577 return this._cachedTypeClasses[typeName] || null; | |
| 578 }, | |
| 579 | |
| 580 /** | |
| 581 * @param {!Function} constructorFunction | |
| 582 * @return {!Object} | |
| 583 */ | |
| 584 sharedInstance: function(constructorFunction) | |
| 585 { | |
| 586 if (Runtime._instanceSymbol in constructorFunction) | |
| 587 return constructorFunction[Runtime._instanceSymbol]; | |
| 588 var instance = new constructorFunction(); | |
| 589 constructorFunction[Runtime._instanceSymbol] = instance; | |
| 590 return instance; | |
| 591 } | |
| 592 }; | |
| 593 | |
| 594 /** | |
| 595 * @constructor | |
| 596 */ | |
| 597 Runtime.ModuleDescriptor = function() | |
| 598 { | |
| 599 /** | 611 /** |
| 600 * @type {string} | 612 * @type {string} |
| 601 */ | 613 */ |
| 602 this.name; | 614 this.name; |
| 603 | 615 |
| 604 /** | 616 /** |
| 605 * @type {!Array.<!Runtime.ExtensionDescriptor>} | 617 * @type {!Array.<!Runtime.ExtensionDescriptor>} |
| 606 */ | 618 */ |
| 607 this.extensions; | 619 this.extensions; |
| 608 | 620 |
| 609 /** | 621 /** |
| 610 * @type {!Array.<string>|undefined} | 622 * @type {!Array.<string>|undefined} |
| 611 */ | 623 */ |
| 612 this.dependencies; | 624 this.dependencies; |
| 613 | 625 |
| 614 /** | 626 /** |
| 615 * @type {!Array.<string>} | 627 * @type {!Array.<string>} |
| 616 */ | 628 */ |
| 617 this.scripts; | 629 this.scripts; |
| 618 | 630 |
| 619 /** | 631 /** |
| 620 * @type {string|undefined} | 632 * @type {string|undefined} |
| 621 */ | 633 */ |
| 622 this.condition; | 634 this.condition; |
| 623 | 635 |
| 624 /** | 636 /** |
| 625 * @type {boolean|undefined} | 637 * @type {boolean|undefined} |
| 626 */ | 638 */ |
| 627 this.remote; | 639 this.remote; |
| 640 } |
| 628 }; | 641 }; |
| 629 | 642 |
| 630 /** | 643 /** |
| 631 * @constructor | 644 * @unrestricted |
| 632 */ | 645 */ |
| 633 Runtime.ExtensionDescriptor = function() | 646 Runtime.ExtensionDescriptor = class { |
| 634 { | 647 constructor() { |
| 635 /** | 648 /** |
| 636 * @type {string} | 649 * @type {string} |
| 637 */ | 650 */ |
| 638 this.type; | 651 this.type; |
| 639 | 652 |
| 640 /** | 653 /** |
| 641 * @type {string|undefined} | 654 * @type {string|undefined} |
| 642 */ | 655 */ |
| 643 this.className; | 656 this.className; |
| 644 | 657 |
| 645 /** | 658 /** |
| 646 * @type {string|undefined} | 659 * @type {string|undefined} |
| 647 */ | 660 */ |
| 648 this.factoryName; | 661 this.factoryName; |
| 649 | 662 |
| 650 /** | 663 /** |
| 651 * @type {!Array.<string>|undefined} | 664 * @type {!Array.<string>|undefined} |
| 652 */ | 665 */ |
| 653 this.contextTypes; | 666 this.contextTypes; |
| 667 } |
| 654 }; | 668 }; |
| 655 | 669 |
| 656 /** | 670 /** |
| 657 * @constructor | 671 * @unrestricted |
| 658 * @param {!Runtime} manager | |
| 659 * @param {!Runtime.ModuleDescriptor} descriptor | |
| 660 */ | 672 */ |
| 661 Runtime.Module = function(manager, descriptor) | 673 Runtime.Module = class { |
| 662 { | 674 /** |
| 675 * @param {!Runtime} manager |
| 676 * @param {!Runtime.ModuleDescriptor} descriptor |
| 677 */ |
| 678 constructor(manager, descriptor) { |
| 663 this._manager = manager; | 679 this._manager = manager; |
| 664 this._descriptor = descriptor; | 680 this._descriptor = descriptor; |
| 665 this._name = descriptor.name; | 681 this._name = descriptor.name; |
| 666 /** @type {!Array<!Runtime.Extension>} */ | 682 /** @type {!Array<!Runtime.Extension>} */ |
| 667 this._extensions = []; | 683 this._extensions = []; |
| 668 | 684 |
| 669 /** @type {!Map<string, !Array<!Runtime.Extension>>} */ | 685 /** @type {!Map<string, !Array<!Runtime.Extension>>} */ |
| 670 this._extensionsByClassName = new Map(); | 686 this._extensionsByClassName = new Map(); |
| 671 var extensions = /** @type {?Array.<!Runtime.ExtensionDescriptor>} */ (descr
iptor.extensions); | 687 var extensions = /** @type {?Array.<!Runtime.ExtensionDescriptor>} */ (descr
iptor.extensions); |
| 672 for (var i = 0; extensions && i < extensions.length; ++i) { | 688 for (var i = 0; extensions && i < extensions.length; ++i) { |
| 673 var extension = new Runtime.Extension(this, extensions[i]); | 689 var extension = new Runtime.Extension(this, extensions[i]); |
| 674 this._manager._extensions.push(extension); | 690 this._manager._extensions.push(extension); |
| 675 this._extensions.push(extension); | 691 this._extensions.push(extension); |
| 676 } | 692 } |
| 677 this._loadedForTest = false; | 693 this._loadedForTest = false; |
| 694 } |
| 695 |
| 696 /** |
| 697 * @return {string} |
| 698 */ |
| 699 name() { |
| 700 return this._name; |
| 701 } |
| 702 |
| 703 /** |
| 704 * @return {boolean} |
| 705 */ |
| 706 enabled() { |
| 707 return Runtime._isDescriptorEnabled(this._descriptor); |
| 708 } |
| 709 |
| 710 /** |
| 711 * @param {string} name |
| 712 * @return {string} |
| 713 */ |
| 714 resource(name) { |
| 715 var fullName = this._name + '/' + name; |
| 716 var content = Runtime.cachedResources[fullName]; |
| 717 if (!content) |
| 718 throw new Error(fullName + ' not preloaded. Check module.json'); |
| 719 return content; |
| 720 } |
| 721 |
| 722 /** |
| 723 * @return {!Promise.<undefined>} |
| 724 */ |
| 725 _loadPromise() { |
| 726 if (!this.enabled()) |
| 727 return Promise.reject(new Error('Module ' + this._name + ' is not enabled'
)); |
| 728 |
| 729 if (this._pendingLoadPromise) |
| 730 return this._pendingLoadPromise; |
| 731 |
| 732 var dependencies = this._descriptor.dependencies; |
| 733 var dependencyPromises = []; |
| 734 for (var i = 0; dependencies && i < dependencies.length; ++i) |
| 735 dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPr
omise()); |
| 736 |
| 737 this._pendingLoadPromise = Promise.all(dependencyPromises) |
| 738 .then(this._loadResources.bind(this)) |
| 739 .then(this._loadScripts.bind(this)) |
| 740 .then(() => this._loadedForTest = true); |
| 741 |
| 742 return this._pendingLoadPromise; |
| 743 } |
| 744 |
| 745 /** |
| 746 * @return {!Promise.<undefined>} |
| 747 * @this {Runtime.Module} |
| 748 */ |
| 749 _loadResources() { |
| 750 var resources = this._descriptor['resources']; |
| 751 if (!resources || !resources.length) |
| 752 return Promise.resolve(); |
| 753 var promises = []; |
| 754 for (var i = 0; i < resources.length; ++i) { |
| 755 var url = this._modularizeURL(resources[i]); |
| 756 promises.push(Runtime.loadResourceIntoCache(url, true)); |
| 757 } |
| 758 return Promise.all(promises).then(undefined); |
| 759 } |
| 760 |
| 761 /** |
| 762 * @return {!Promise.<undefined>} |
| 763 */ |
| 764 _loadScripts() { |
| 765 if (!this._descriptor.scripts || !this._descriptor.scripts.length) |
| 766 return Promise.resolve(); |
| 767 return Runtime._loadScriptsPromise(this._descriptor.scripts.map(this._modula
rizeURL, this), this._remoteBase()); |
| 768 } |
| 769 |
| 770 /** |
| 771 * @param {string} resourceName |
| 772 */ |
| 773 _modularizeURL(resourceName) { |
| 774 return Runtime.normalizePath(this._name + '/' + resourceName); |
| 775 } |
| 776 |
| 777 /** |
| 778 * @return {string|undefined} |
| 779 */ |
| 780 _remoteBase() { |
| 781 return !Runtime.queryParam('debugFrontend') && this._descriptor.remote && Ru
ntime._remoteBase || undefined; |
| 782 } |
| 783 |
| 784 /** |
| 785 * @param {string} value |
| 786 * @return {string} |
| 787 */ |
| 788 substituteURL(value) { |
| 789 var base = this._remoteBase() || ''; |
| 790 return value.replace(/@url\(([^\)]*?)\)/g, convertURL.bind(this)); |
| 791 |
| 792 function convertURL(match, url) { |
| 793 return base + this._modularizeURL(url); |
| 794 } |
| 795 } |
| 678 }; | 796 }; |
| 679 | 797 |
| 680 Runtime.Module.prototype = { | |
| 681 /** | |
| 682 * @return {string} | |
| 683 */ | |
| 684 name: function() | |
| 685 { | |
| 686 return this._name; | |
| 687 }, | |
| 688 | |
| 689 /** | |
| 690 * @return {boolean} | |
| 691 */ | |
| 692 enabled: function() | |
| 693 { | |
| 694 return Runtime._isDescriptorEnabled(this._descriptor); | |
| 695 }, | |
| 696 | |
| 697 /** | |
| 698 * @param {string} name | |
| 699 * @return {string} | |
| 700 */ | |
| 701 resource: function(name) | |
| 702 { | |
| 703 var fullName = this._name + "/" + name; | |
| 704 var content = Runtime.cachedResources[fullName]; | |
| 705 if (!content) | |
| 706 throw new Error(fullName + " not preloaded. Check module.json"); | |
| 707 return content; | |
| 708 }, | |
| 709 | |
| 710 /** | |
| 711 * @return {!Promise.<undefined>} | |
| 712 */ | |
| 713 _loadPromise: function() | |
| 714 { | |
| 715 if (!this.enabled()) | |
| 716 return Promise.reject(new Error("Module " + this._name + " is not en
abled")); | |
| 717 | |
| 718 if (this._pendingLoadPromise) | |
| 719 return this._pendingLoadPromise; | |
| 720 | |
| 721 var dependencies = this._descriptor.dependencies; | |
| 722 var dependencyPromises = []; | |
| 723 for (var i = 0; dependencies && i < dependencies.length; ++i) | |
| 724 dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._
loadPromise()); | |
| 725 | |
| 726 this._pendingLoadPromise = Promise.all(dependencyPromises) | |
| 727 .then(this._loadResources.bind(this)) | |
| 728 .then(this._loadScripts.bind(this)) | |
| 729 .then(() => this._loadedForTest = true); | |
| 730 | |
| 731 return this._pendingLoadPromise; | |
| 732 }, | |
| 733 | |
| 734 /** | |
| 735 * @return {!Promise.<undefined>} | |
| 736 * @this {Runtime.Module} | |
| 737 */ | |
| 738 _loadResources: function() | |
| 739 { | |
| 740 var resources = this._descriptor["resources"]; | |
| 741 if (!resources || !resources.length) | |
| 742 return Promise.resolve(); | |
| 743 var promises = []; | |
| 744 for (var i = 0; i < resources.length; ++i) { | |
| 745 var url = this._modularizeURL(resources[i]); | |
| 746 promises.push(Runtime.loadResourceIntoCache(url, true)); | |
| 747 } | |
| 748 return Promise.all(promises).then(undefined); | |
| 749 }, | |
| 750 | |
| 751 /** | |
| 752 * @return {!Promise.<undefined>} | |
| 753 */ | |
| 754 _loadScripts: function() | |
| 755 { | |
| 756 if (!this._descriptor.scripts || !this._descriptor.scripts.length) | |
| 757 return Promise.resolve(); | |
| 758 return Runtime._loadScriptsPromise(this._descriptor.scripts.map(this._mo
dularizeURL, this), this._remoteBase()); | |
| 759 }, | |
| 760 | |
| 761 /** | |
| 762 * @param {string} resourceName | |
| 763 */ | |
| 764 _modularizeURL: function(resourceName) | |
| 765 { | |
| 766 return Runtime.normalizePath(this._name + "/" + resourceName); | |
| 767 }, | |
| 768 | |
| 769 /** | |
| 770 * @return {string|undefined} | |
| 771 */ | |
| 772 _remoteBase: function() | |
| 773 { | |
| 774 return !Runtime.queryParam("debugFrontend") && this._descriptor.remote &
& Runtime._remoteBase || undefined; | |
| 775 }, | |
| 776 | |
| 777 /** | |
| 778 * @param {string} value | |
| 779 * @return {string} | |
| 780 */ | |
| 781 substituteURL: function(value) | |
| 782 { | |
| 783 var base = this._remoteBase() || ""; | |
| 784 return value.replace(/@url\(([^\)]*?)\)/g, convertURL.bind(this)); | |
| 785 | |
| 786 function convertURL(match, url) | |
| 787 { | |
| 788 return base + this._modularizeURL(url); | |
| 789 } | |
| 790 } | |
| 791 }; | |
| 792 | 798 |
| 793 /** | 799 /** |
| 794 * @param {!Object} descriptor | 800 * @unrestricted |
| 795 * @return {boolean} | |
| 796 */ | 801 */ |
| 797 Runtime._isDescriptorEnabled = function(descriptor) | 802 Runtime.Extension = class { |
| 798 { | 803 /** |
| 799 var activatorExperiment = descriptor["experiment"]; | 804 * @param {!Runtime.Module} module |
| 800 if (activatorExperiment === "*") | 805 * @param {!Runtime.ExtensionDescriptor} descriptor |
| 801 return Runtime.experiments.supportEnabled(); | 806 */ |
| 802 if (activatorExperiment && activatorExperiment.startsWith("!") && Runtime.ex
periments.isEnabled(activatorExperiment.substring(1))) | 807 constructor(module, descriptor) { |
| 803 return false; | |
| 804 if (activatorExperiment && !activatorExperiment.startsWith("!") && !Runtime.
experiments.isEnabled(activatorExperiment)) | |
| 805 return false; | |
| 806 var condition = descriptor["condition"]; | |
| 807 if (condition && !condition.startsWith("!") && !Runtime.queryParam(condition
)) | |
| 808 return false; | |
| 809 if (condition && condition.startsWith("!") && Runtime.queryParam(condition.s
ubstring(1))) | |
| 810 return false; | |
| 811 return true; | |
| 812 }; | |
| 813 | |
| 814 /** | |
| 815 * @constructor | |
| 816 * @param {!Runtime.Module} module | |
| 817 * @param {!Runtime.ExtensionDescriptor} descriptor | |
| 818 */ | |
| 819 Runtime.Extension = function(module, descriptor) | |
| 820 { | |
| 821 this._module = module; | 808 this._module = module; |
| 822 this._descriptor = descriptor; | 809 this._descriptor = descriptor; |
| 823 | 810 |
| 824 this._type = descriptor.type; | 811 this._type = descriptor.type; |
| 825 this._hasTypeClass = this._type.charAt(0) === "@"; | 812 this._hasTypeClass = this._type.charAt(0) === '@'; |
| 826 | 813 |
| 827 /** | 814 /** |
| 828 * @type {?string} | 815 * @type {?string} |
| 829 */ | 816 */ |
| 830 this._className = descriptor.className || null; | 817 this._className = descriptor.className || null; |
| 831 this._factoryName = descriptor.factoryName || null; | 818 this._factoryName = descriptor.factoryName || null; |
| 819 } |
| 820 |
| 821 /** |
| 822 * @return {!Object} |
| 823 */ |
| 824 descriptor() { |
| 825 return this._descriptor; |
| 826 } |
| 827 |
| 828 /** |
| 829 * @return {!Runtime.Module} |
| 830 */ |
| 831 module() { |
| 832 return this._module; |
| 833 } |
| 834 |
| 835 /** |
| 836 * @return {boolean} |
| 837 */ |
| 838 enabled() { |
| 839 return this._module.enabled() && Runtime._isDescriptorEnabled(this.descripto
r()); |
| 840 } |
| 841 |
| 842 /** |
| 843 * @return {?function(new:Object)} |
| 844 */ |
| 845 _typeClass() { |
| 846 if (!this._hasTypeClass) |
| 847 return null; |
| 848 return this._module._manager._resolve(this._type.substring(1)); |
| 849 } |
| 850 |
| 851 /** |
| 852 * @param {?Object} context |
| 853 * @return {boolean} |
| 854 */ |
| 855 isApplicable(context) { |
| 856 return this._module._manager.isExtensionApplicableToContext(this, context); |
| 857 } |
| 858 |
| 859 /** |
| 860 * @return {!Promise.<!Object>} |
| 861 */ |
| 862 instance() { |
| 863 return this._module._loadPromise().then(this._createInstance.bind(this)); |
| 864 } |
| 865 |
| 866 /** |
| 867 * @return {!Object} |
| 868 */ |
| 869 _createInstance() { |
| 870 var className = this._className || this._factoryName; |
| 871 if (!className) |
| 872 throw new Error('Could not instantiate extension with no class'); |
| 873 var constructorFunction = self.eval(/** @type {string} */ (className)); |
| 874 if (!(constructorFunction instanceof Function)) |
| 875 throw new Error('Could not instantiate: ' + className); |
| 876 if (this._className) |
| 877 return this._module._manager.sharedInstance(constructorFunction); |
| 878 return new constructorFunction(this); |
| 879 } |
| 880 |
| 881 /** |
| 882 * @return {string} |
| 883 */ |
| 884 title() { |
| 885 // FIXME: should be WebInspector.UIString() but runtime is not l10n aware ye
t. |
| 886 return this._descriptor['title-' + Runtime._platform] || this._descriptor['t
itle']; |
| 887 } |
| 888 |
| 889 /** |
| 890 * @param {function(new:Object)} contextType |
| 891 * @return {boolean} |
| 892 */ |
| 893 hasContextType(contextType) { |
| 894 var contextTypes = this.descriptor().contextTypes; |
| 895 if (!contextTypes) |
| 896 return false; |
| 897 for (var i = 0; i < contextTypes.length; ++i) { |
| 898 if (contextType === this._module._manager._resolve(contextTypes[i])) |
| 899 return true; |
| 900 } |
| 901 return false; |
| 902 } |
| 832 }; | 903 }; |
| 833 | 904 |
| 834 Runtime.Extension.prototype = { | |
| 835 /** | |
| 836 * @return {!Object} | |
| 837 */ | |
| 838 descriptor: function() | |
| 839 { | |
| 840 return this._descriptor; | |
| 841 }, | |
| 842 | |
| 843 /** | |
| 844 * @return {!Runtime.Module} | |
| 845 */ | |
| 846 module: function() | |
| 847 { | |
| 848 return this._module; | |
| 849 }, | |
| 850 | |
| 851 /** | |
| 852 * @return {boolean} | |
| 853 */ | |
| 854 enabled: function() | |
| 855 { | |
| 856 return this._module.enabled() && Runtime._isDescriptorEnabled(this.descr
iptor()); | |
| 857 }, | |
| 858 | |
| 859 /** | |
| 860 * @return {?function(new:Object)} | |
| 861 */ | |
| 862 _typeClass: function() | |
| 863 { | |
| 864 if (!this._hasTypeClass) | |
| 865 return null; | |
| 866 return this._module._manager._resolve(this._type.substring(1)); | |
| 867 }, | |
| 868 | |
| 869 /** | |
| 870 * @param {?Object} context | |
| 871 * @return {boolean} | |
| 872 */ | |
| 873 isApplicable: function(context) | |
| 874 { | |
| 875 return this._module._manager.isExtensionApplicableToContext(this, contex
t); | |
| 876 }, | |
| 877 | |
| 878 /** | |
| 879 * @return {!Promise.<!Object>} | |
| 880 */ | |
| 881 instance: function() | |
| 882 { | |
| 883 return this._module._loadPromise().then(this._createInstance.bind(this))
; | |
| 884 }, | |
| 885 | |
| 886 /** | |
| 887 * @return {!Object} | |
| 888 */ | |
| 889 _createInstance: function() | |
| 890 { | |
| 891 var className = this._className || this._factoryName; | |
| 892 if (!className) | |
| 893 throw new Error("Could not instantiate extension with no class"); | |
| 894 var constructorFunction = self.eval(/** @type {string} */(className)); | |
| 895 if (!(constructorFunction instanceof Function)) | |
| 896 throw new Error("Could not instantiate: " + className); | |
| 897 if (this._className) | |
| 898 return this._module._manager.sharedInstance(constructorFunction); | |
| 899 return new constructorFunction(this); | |
| 900 }, | |
| 901 | |
| 902 /** | |
| 903 * @return {string} | |
| 904 */ | |
| 905 title: function() | |
| 906 { | |
| 907 // FIXME: should be WebInspector.UIString() but runtime is not l10n awar
e yet. | |
| 908 return this._descriptor["title-" + Runtime._platform] || this._descripto
r["title"]; | |
| 909 }, | |
| 910 | |
| 911 /** | |
| 912 * @param {function(new:Object)} contextType | |
| 913 * @return {boolean} | |
| 914 */ | |
| 915 hasContextType: function(contextType) | |
| 916 { | |
| 917 var contextTypes = this.descriptor().contextTypes; | |
| 918 if (!contextTypes) | |
| 919 return false; | |
| 920 for (var i = 0; i < contextTypes.length; ++i) { | |
| 921 if (contextType === this._module._manager._resolve(contextTypes[i])) | |
| 922 return true; | |
| 923 } | |
| 924 return false; | |
| 925 } | |
| 926 }; | |
| 927 | |
| 928 /** | 905 /** |
| 929 * @constructor | 906 * @unrestricted |
| 930 */ | 907 */ |
| 931 Runtime.ExperimentsSupport = function() | 908 Runtime.ExperimentsSupport = class { |
| 932 { | 909 constructor() { |
| 933 this._supportEnabled = Runtime.queryParam("experiments") !== null; | 910 this._supportEnabled = Runtime.queryParam('experiments') !== null; |
| 934 this._experiments = []; | 911 this._experiments = []; |
| 935 this._experimentNames = {}; | 912 this._experimentNames = {}; |
| 936 this._enabledTransiently = {}; | 913 this._enabledTransiently = {}; |
| 914 } |
| 915 |
| 916 /** |
| 917 * @return {!Array.<!Runtime.Experiment>} |
| 918 */ |
| 919 allConfigurableExperiments() { |
| 920 var result = []; |
| 921 for (var i = 0; i < this._experiments.length; i++) { |
| 922 var experiment = this._experiments[i]; |
| 923 if (!this._enabledTransiently[experiment.name]) |
| 924 result.push(experiment); |
| 925 } |
| 926 return result; |
| 927 } |
| 928 |
| 929 /** |
| 930 * @return {boolean} |
| 931 */ |
| 932 supportEnabled() { |
| 933 return this._supportEnabled; |
| 934 } |
| 935 |
| 936 /** |
| 937 * @param {!Object} value |
| 938 */ |
| 939 _setExperimentsSetting(value) { |
| 940 if (!self.localStorage) |
| 941 return; |
| 942 self.localStorage['experiments'] = JSON.stringify(value); |
| 943 } |
| 944 |
| 945 /** |
| 946 * @param {string} experimentName |
| 947 * @param {string} experimentTitle |
| 948 * @param {boolean=} hidden |
| 949 */ |
| 950 register(experimentName, experimentTitle, hidden) { |
| 951 Runtime._assert(!this._experimentNames[experimentName], 'Duplicate registrat
ion of experiment ' + experimentName); |
| 952 this._experimentNames[experimentName] = true; |
| 953 this._experiments.push(new Runtime.Experiment(this, experimentName, experime
ntTitle, !!hidden)); |
| 954 } |
| 955 |
| 956 /** |
| 957 * @param {string} experimentName |
| 958 * @return {boolean} |
| 959 */ |
| 960 isEnabled(experimentName) { |
| 961 this._checkExperiment(experimentName); |
| 962 |
| 963 if (this._enabledTransiently[experimentName]) |
| 964 return true; |
| 965 if (!this.supportEnabled()) |
| 966 return false; |
| 967 |
| 968 return !!Runtime._experimentsSetting()[experimentName]; |
| 969 } |
| 970 |
| 971 /** |
| 972 * @param {string} experimentName |
| 973 * @param {boolean} enabled |
| 974 */ |
| 975 setEnabled(experimentName, enabled) { |
| 976 this._checkExperiment(experimentName); |
| 977 var experimentsSetting = Runtime._experimentsSetting(); |
| 978 experimentsSetting[experimentName] = enabled; |
| 979 this._setExperimentsSetting(experimentsSetting); |
| 980 } |
| 981 |
| 982 /** |
| 983 * @param {!Array.<string>} experimentNames |
| 984 */ |
| 985 setDefaultExperiments(experimentNames) { |
| 986 for (var i = 0; i < experimentNames.length; ++i) { |
| 987 this._checkExperiment(experimentNames[i]); |
| 988 this._enabledTransiently[experimentNames[i]] = true; |
| 989 } |
| 990 } |
| 991 |
| 992 /** |
| 993 * @param {string} experimentName |
| 994 */ |
| 995 enableForTest(experimentName) { |
| 996 this._checkExperiment(experimentName); |
| 997 this._enabledTransiently[experimentName] = true; |
| 998 } |
| 999 |
| 1000 clearForTest() { |
| 1001 this._experiments = []; |
| 1002 this._experimentNames = {}; |
| 1003 this._enabledTransiently = {}; |
| 1004 } |
| 1005 |
| 1006 cleanUpStaleExperiments() { |
| 1007 var experimentsSetting = Runtime._experimentsSetting(); |
| 1008 var cleanedUpExperimentSetting = {}; |
| 1009 for (var i = 0; i < this._experiments.length; ++i) { |
| 1010 var experimentName = this._experiments[i].name; |
| 1011 if (experimentsSetting[experimentName]) |
| 1012 cleanedUpExperimentSetting[experimentName] = true; |
| 1013 } |
| 1014 this._setExperimentsSetting(cleanedUpExperimentSetting); |
| 1015 } |
| 1016 |
| 1017 /** |
| 1018 * @param {string} experimentName |
| 1019 */ |
| 1020 _checkExperiment(experimentName) { |
| 1021 Runtime._assert(this._experimentNames[experimentName], 'Unknown experiment '
+ experimentName); |
| 1022 } |
| 937 }; | 1023 }; |
| 938 | 1024 |
| 939 Runtime.ExperimentsSupport.prototype = { | |
| 940 /** | |
| 941 * @return {!Array.<!Runtime.Experiment>} | |
| 942 */ | |
| 943 allConfigurableExperiments: function() | |
| 944 { | |
| 945 var result = []; | |
| 946 for (var i = 0; i < this._experiments.length; i++) { | |
| 947 var experiment = this._experiments[i]; | |
| 948 if (!this._enabledTransiently[experiment.name]) | |
| 949 result.push(experiment); | |
| 950 } | |
| 951 return result; | |
| 952 }, | |
| 953 | |
| 954 /** | |
| 955 * @return {boolean} | |
| 956 */ | |
| 957 supportEnabled: function() | |
| 958 { | |
| 959 return this._supportEnabled; | |
| 960 }, | |
| 961 | |
| 962 /** | |
| 963 * @param {!Object} value | |
| 964 */ | |
| 965 _setExperimentsSetting: function(value) | |
| 966 { | |
| 967 if (!self.localStorage) | |
| 968 return; | |
| 969 self.localStorage["experiments"] = JSON.stringify(value); | |
| 970 }, | |
| 971 | |
| 972 /** | |
| 973 * @param {string} experimentName | |
| 974 * @param {string} experimentTitle | |
| 975 * @param {boolean=} hidden | |
| 976 */ | |
| 977 register: function(experimentName, experimentTitle, hidden) | |
| 978 { | |
| 979 Runtime._assert(!this._experimentNames[experimentName], "Duplicate regis
tration of experiment " + experimentName); | |
| 980 this._experimentNames[experimentName] = true; | |
| 981 this._experiments.push(new Runtime.Experiment(this, experimentName, expe
rimentTitle, !!hidden)); | |
| 982 }, | |
| 983 | |
| 984 /** | |
| 985 * @param {string} experimentName | |
| 986 * @return {boolean} | |
| 987 */ | |
| 988 isEnabled: function(experimentName) | |
| 989 { | |
| 990 this._checkExperiment(experimentName); | |
| 991 | |
| 992 if (this._enabledTransiently[experimentName]) | |
| 993 return true; | |
| 994 if (!this.supportEnabled()) | |
| 995 return false; | |
| 996 | |
| 997 return !!Runtime._experimentsSetting()[experimentName]; | |
| 998 }, | |
| 999 | |
| 1000 /** | |
| 1001 * @param {string} experimentName | |
| 1002 * @param {boolean} enabled | |
| 1003 */ | |
| 1004 setEnabled: function(experimentName, enabled) | |
| 1005 { | |
| 1006 this._checkExperiment(experimentName); | |
| 1007 var experimentsSetting = Runtime._experimentsSetting(); | |
| 1008 experimentsSetting[experimentName] = enabled; | |
| 1009 this._setExperimentsSetting(experimentsSetting); | |
| 1010 }, | |
| 1011 | |
| 1012 /** | |
| 1013 * @param {!Array.<string>} experimentNames | |
| 1014 */ | |
| 1015 setDefaultExperiments: function(experimentNames) | |
| 1016 { | |
| 1017 for (var i = 0; i < experimentNames.length; ++i) { | |
| 1018 this._checkExperiment(experimentNames[i]); | |
| 1019 this._enabledTransiently[experimentNames[i]] = true; | |
| 1020 } | |
| 1021 }, | |
| 1022 | |
| 1023 /** | |
| 1024 * @param {string} experimentName | |
| 1025 */ | |
| 1026 enableForTest: function(experimentName) | |
| 1027 { | |
| 1028 this._checkExperiment(experimentName); | |
| 1029 this._enabledTransiently[experimentName] = true; | |
| 1030 }, | |
| 1031 | |
| 1032 clearForTest: function() | |
| 1033 { | |
| 1034 this._experiments = []; | |
| 1035 this._experimentNames = {}; | |
| 1036 this._enabledTransiently = {}; | |
| 1037 }, | |
| 1038 | |
| 1039 cleanUpStaleExperiments: function() | |
| 1040 { | |
| 1041 var experimentsSetting = Runtime._experimentsSetting(); | |
| 1042 var cleanedUpExperimentSetting = {}; | |
| 1043 for (var i = 0; i < this._experiments.length; ++i) { | |
| 1044 var experimentName = this._experiments[i].name; | |
| 1045 if (experimentsSetting[experimentName]) | |
| 1046 cleanedUpExperimentSetting[experimentName] = true; | |
| 1047 } | |
| 1048 this._setExperimentsSetting(cleanedUpExperimentSetting); | |
| 1049 }, | |
| 1050 | |
| 1051 /** | |
| 1052 * @param {string} experimentName | |
| 1053 */ | |
| 1054 _checkExperiment: function(experimentName) | |
| 1055 { | |
| 1056 Runtime._assert(this._experimentNames[experimentName], "Unknown experime
nt " + experimentName); | |
| 1057 } | |
| 1058 }; | |
| 1059 | |
| 1060 /** | 1025 /** |
| 1061 * @constructor | 1026 * @unrestricted |
| 1062 * @param {!Runtime.ExperimentsSupport} experiments | |
| 1063 * @param {string} name | |
| 1064 * @param {string} title | |
| 1065 * @param {boolean} hidden | |
| 1066 */ | 1027 */ |
| 1067 Runtime.Experiment = function(experiments, name, title, hidden) | 1028 Runtime.Experiment = class { |
| 1068 { | 1029 /** |
| 1030 * @param {!Runtime.ExperimentsSupport} experiments |
| 1031 * @param {string} name |
| 1032 * @param {string} title |
| 1033 * @param {boolean} hidden |
| 1034 */ |
| 1035 constructor(experiments, name, title, hidden) { |
| 1069 this.name = name; | 1036 this.name = name; |
| 1070 this.title = title; | 1037 this.title = title; |
| 1071 this.hidden = hidden; | 1038 this.hidden = hidden; |
| 1072 this._experiments = experiments; | 1039 this._experiments = experiments; |
| 1040 } |
| 1041 |
| 1042 /** |
| 1043 * @return {boolean} |
| 1044 */ |
| 1045 isEnabled() { |
| 1046 return this._experiments.isEnabled(this.name); |
| 1047 } |
| 1048 |
| 1049 /** |
| 1050 * @param {boolean} enabled |
| 1051 */ |
| 1052 setEnabled(enabled) { |
| 1053 this._experiments.setEnabled(this.name, enabled); |
| 1054 } |
| 1073 }; | 1055 }; |
| 1074 | 1056 |
| 1075 Runtime.Experiment.prototype = { | |
| 1076 /** | |
| 1077 * @return {boolean} | |
| 1078 */ | |
| 1079 isEnabled: function() | |
| 1080 { | |
| 1081 return this._experiments.isEnabled(this.name); | |
| 1082 }, | |
| 1083 | |
| 1084 /** | |
| 1085 * @param {boolean} enabled | |
| 1086 */ | |
| 1087 setEnabled: function(enabled) | |
| 1088 { | |
| 1089 this._experiments.setEnabled(this.name, enabled); | |
| 1090 } | |
| 1091 }; | |
| 1092 | |
| 1093 {(function parseQueryParameters() | |
| 1094 { | 1057 { |
| 1058 (function parseQueryParameters() { |
| 1095 var queryParams = location.search; | 1059 var queryParams = location.search; |
| 1096 if (!queryParams) | 1060 if (!queryParams) |
| 1097 return; | 1061 return; |
| 1098 var params = queryParams.substring(1).split("&"); | 1062 var params = queryParams.substring(1).split('&'); |
| 1099 for (var i = 0; i < params.length; ++i) { | 1063 for (var i = 0; i < params.length; ++i) { |
| 1100 var pair = params[i].split("="); | 1064 var pair = params[i].split('='); |
| 1101 var name = pair.shift(); | 1065 var name = pair.shift(); |
| 1102 Runtime._queryParamsObject[name] = pair.join("="); | 1066 Runtime._queryParamsObject[name] = pair.join('='); |
| 1103 } | 1067 } |
| 1104 })();} | 1068 })(); |
| 1105 | 1069 } |
| 1106 | 1070 |
| 1107 // This must be constructed after the query parameters have been parsed. | 1071 // This must be constructed after the query parameters have been parsed. |
| 1108 Runtime.experiments = new Runtime.ExperimentsSupport(); | 1072 Runtime.experiments = new Runtime.ExperimentsSupport(); |
| 1109 | 1073 |
| 1110 /** | 1074 /** |
| 1111 * @type {?string} | 1075 * @type {?string} |
| 1112 */ | 1076 */ |
| 1113 Runtime._remoteBase = Runtime.queryParam("remoteBase"); | 1077 Runtime._remoteBase = Runtime.queryParam('remoteBase'); |
| 1114 {(function validateRemoteBase() | |
| 1115 { | 1078 { |
| 1079 (function validateRemoteBase() { |
| 1116 var remoteBaseRegexp = /^https:\/\/chrome-devtools-frontend\.appspot\.com\/s
erve_file\/@[0-9a-zA-Z]+\/?$/; | 1080 var remoteBaseRegexp = /^https:\/\/chrome-devtools-frontend\.appspot\.com\/s
erve_file\/@[0-9a-zA-Z]+\/?$/; |
| 1117 if (Runtime._remoteBase && !remoteBaseRegexp.test(Runtime._remoteBase)) | 1081 if (Runtime._remoteBase && !remoteBaseRegexp.test(Runtime._remoteBase)) |
| 1118 Runtime._remoteBase = null; | 1082 Runtime._remoteBase = null; |
| 1119 })();} | 1083 })(); |
| 1084 } |
| 1120 | 1085 |
| 1121 /** | |
| 1122 * @param {string} path | |
| 1123 * @return {string} | |
| 1124 */ | |
| 1125 Runtime.resolveSourceURL = function(path) | |
| 1126 { | |
| 1127 var sourceURL = self.location.href; | |
| 1128 if (self.location.search) | |
| 1129 sourceURL = sourceURL.replace(self.location.search, ""); | |
| 1130 sourceURL = sourceURL.substring(0, sourceURL.lastIndexOf("/") + 1) + path; | |
| 1131 return "\n/*# sourceURL=" + sourceURL + " */"; | |
| 1132 }; | |
| 1133 | 1086 |
| 1134 /** | 1087 /** |
| 1135 * @interface | 1088 * @interface |
| 1136 */ | 1089 */ |
| 1137 function ServicePort() { } | 1090 function ServicePort() { |
| 1091 } |
| 1138 | 1092 |
| 1139 ServicePort.prototype = { | 1093 ServicePort.prototype = { |
| 1140 /** | 1094 /** |
| 1141 * @param {function(string)} messageHandler | 1095 * @param {function(string)} messageHandler |
| 1142 * @param {function(string)} closeHandler | 1096 * @param {function(string)} closeHandler |
| 1143 */ | 1097 */ |
| 1144 setHandlers: function(messageHandler, closeHandler) { }, | 1098 setHandlers: function(messageHandler, closeHandler) {}, |
| 1145 | 1099 |
| 1146 /** | 1100 /** |
| 1147 * @param {string} message | 1101 * @param {string} message |
| 1148 * @return {!Promise<boolean>} | 1102 * @return {!Promise<boolean>} |
| 1149 */ | 1103 */ |
| 1150 send: function(message) { }, | 1104 send: function(message) {}, |
| 1151 | 1105 |
| 1152 /** | 1106 /** |
| 1153 * @return {!Promise<boolean>} | 1107 * @return {!Promise<boolean>} |
| 1154 */ | 1108 */ |
| 1155 close: function() { } | 1109 close: function() {} |
| 1156 }; | 1110 }; |
| 1157 | 1111 |
| 1158 /** @type {!Runtime} */ | 1112 /** @type {!Runtime} */ |
| 1159 var runtime; | 1113 var runtime; |
| OLD | NEW |