1/* 2 * Copyright (C) 2014 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/** 32 * @constructor 33 * @param {!Array.<!WebInspector.ModuleManager.ModuleDescriptor>} descriptors 34 */ 35WebInspector.ModuleManager = function(descriptors) 36{ 37 /** 38 * @type {!Array.<!WebInspector.ModuleManager.Module>} 39 */ 40 this._modules = []; 41 /** 42 * @type {!Object.<string, !WebInspector.ModuleManager.Module>} 43 */ 44 this._modulesMap = {}; 45 /** 46 * @type {!Array.<!WebInspector.ModuleManager.Extension>} 47 */ 48 this._extensions = []; 49 50 /** 51 * @type {!Object.<string, !function(new:Object)>} 52 */ 53 this._cachedTypeClasses = {}; 54 55 /** 56 * @type {!Object.<string, !WebInspector.ModuleManager.ModuleDescriptor>} 57 */ 58 this._descriptorsMap = {}; 59 for (var i = 0; i < descriptors.length; ++i) 60 this._descriptorsMap[descriptors[i]["name"]] = descriptors[i]; 61} 62 63WebInspector.ModuleManager.prototype = { 64 /** 65 * @param {!Array.<string>} configuration 66 */ 67 registerModules: function(configuration) 68 { 69 for (var i = 0; i < configuration.length; ++i) 70 this.registerModule(configuration[i]); 71 }, 72 73 /** 74 * @param {string} moduleName 75 */ 76 registerModule: function(moduleName) 77 { 78 if (!this._descriptorsMap[moduleName]) { 79 var content = loadResource(moduleName + "/module.json"); 80 if (!content) 81 throw new Error("Module is not defined: " + moduleName + " " + new Error().stack); 82 var module = /** @type {!WebInspector.ModuleManager.ModuleDescriptor} */ (self.eval("(" + content + ")")); 83 module["name"] = moduleName; 84 this._descriptorsMap[moduleName] = module; 85 } 86 var module = new WebInspector.ModuleManager.Module(this, this._descriptorsMap[moduleName]); 87 this._modules.push(module); 88 this._modulesMap[moduleName] = module; 89 }, 90 91 /** 92 * @param {string} moduleName 93 */ 94 loadModule: function(moduleName) 95 { 96 this._modulesMap[moduleName]._load(); 97 }, 98 99 /** 100 * @param {!WebInspector.ModuleManager.Extension} extension 101 * @param {?function(function(new:Object)):boolean} predicate 102 * @return {boolean} 103 */ 104 _checkExtensionApplicability: function(extension, predicate) 105 { 106 if (!predicate) 107 return false; 108 var contextTypes = /** @type {!Array.<string>|undefined} */ (extension.descriptor().contextTypes); 109 if (!contextTypes) 110 return true; 111 for (var i = 0; i < contextTypes.length; ++i) { 112 var contextType = this._resolve(contextTypes[i]); 113 var isMatching = !!contextType && predicate(contextType); 114 if (isMatching) 115 return true; 116 } 117 return false; 118 }, 119 120 /** 121 * @param {!WebInspector.ModuleManager.Extension} extension 122 * @param {?Object} context 123 * @return {boolean} 124 */ 125 isExtensionApplicableToContext: function(extension, context) 126 { 127 if (!context) 128 return true; 129 return this._checkExtensionApplicability(extension, isInstanceOf); 130 131 /** 132 * @param {!Function} targetType 133 * @return {boolean} 134 */ 135 function isInstanceOf(targetType) 136 { 137 return context instanceof targetType; 138 } 139 }, 140 141 /** 142 * @param {!WebInspector.ModuleManager.Extension} extension 143 * @param {!Set.<!Function>=} currentContextTypes 144 * @return {boolean} 145 */ 146 isExtensionApplicableToContextTypes: function(extension, currentContextTypes) 147 { 148 if (!extension.descriptor().contextTypes) 149 return true; 150 151 return this._checkExtensionApplicability(extension, currentContextTypes ? isContextTypeKnown : null); 152 153 /** 154 * @param {!Function} targetType 155 * @return {boolean} 156 */ 157 function isContextTypeKnown(targetType) 158 { 159 return currentContextTypes.contains(targetType); 160 } 161 }, 162 163 /** 164 * @param {string|!Function} type 165 * @param {?Object=} context 166 * @return {!Array.<!WebInspector.ModuleManager.Extension>} 167 */ 168 extensions: function(type, context) 169 { 170 /** 171 * @param {!WebInspector.ModuleManager.Extension} extension 172 * @return {boolean} 173 */ 174 function filter(extension) 175 { 176 if (extension._type !== type && extension._typeClass() !== type) 177 return false; 178 return !context || extension.isApplicable(context); 179 } 180 return this._extensions.filter(filter); 181 }, 182 183 /** 184 * @param {string|!Function} type 185 * @param {?Object=} context 186 * @return {?WebInspector.ModuleManager.Extension} 187 */ 188 extension: function(type, context) 189 { 190 return this.extensions(type, context)[0] || null; 191 }, 192 193 /** 194 * @param {string|!Function} type 195 * @param {?Object=} context 196 * @return {!Array.<!Object>} 197 */ 198 instances: function(type, context) 199 { 200 /** 201 * @param {!WebInspector.ModuleManager.Extension} extension 202 * @return {?Object} 203 */ 204 function instantiate(extension) 205 { 206 return extension.instance(); 207 } 208 return this.extensions(type, context).filter(instantiate).map(instantiate); 209 }, 210 211 /** 212 * @param {string|!Function} type 213 * @param {?Object=} context 214 * @return {?Object} 215 */ 216 instance: function(type, context) 217 { 218 var extension = this.extension(type, context); 219 return extension ? extension.instance() : null; 220 }, 221 222 /** 223 * @param {string|!Function} type 224 * @param {string} nameProperty 225 * @param {string} orderProperty 226 * @return {function(string, string):number} 227 */ 228 orderComparator: function(type, nameProperty, orderProperty) 229 { 230 var extensions = this.extensions(type); 231 var orderForName = {}; 232 for (var i = 0; i < extensions.length; ++i) { 233 var descriptor = extensions[i].descriptor(); 234 orderForName[descriptor[nameProperty]] = descriptor[orderProperty]; 235 } 236 237 /** 238 * @param {string} name1 239 * @param {string} name2 240 * @return {number} 241 */ 242 function result(name1, name2) 243 { 244 if (name1 in orderForName && name2 in orderForName) 245 return orderForName[name1] - orderForName[name2]; 246 if (name1 in orderForName) 247 return -1; 248 if (name2 in orderForName) 249 return 1; 250 return name1.compareTo(name2); 251 } 252 return result; 253 }, 254 255 /** 256 * @return {?function(new:Object)} 257 */ 258 _resolve: function(typeName) 259 { 260 if (!this._cachedTypeClasses[typeName]) { 261 var path = typeName.split("."); 262 var object = window; 263 for (var i = 0; object && (i < path.length); ++i) 264 object = object[path[i]]; 265 if (object) 266 this._cachedTypeClasses[typeName] = /** @type function(new:Object) */(object); 267 } 268 return this._cachedTypeClasses[typeName]; 269 } 270} 271 272/** 273 * @constructor 274 */ 275WebInspector.ModuleManager.ModuleDescriptor = function() 276{ 277 /** 278 * @type {string} 279 */ 280 this.name; 281 282 /** 283 * @type {!Array.<!WebInspector.ModuleManager.ExtensionDescriptor>} 284 */ 285 this.extensions; 286 287 /** 288 * @type {!Array.<string>|undefined} 289 */ 290 this.dependencies; 291 292 /** 293 * @type {!Array.<string>} 294 */ 295 this.scripts; 296} 297 298/** 299 * @constructor 300 */ 301WebInspector.ModuleManager.ExtensionDescriptor = function() 302{ 303 /** 304 * @type {string} 305 */ 306 this.type; 307 308 /** 309 * @type {string|undefined} 310 */ 311 this.className; 312 313 /** 314 * @type {!Array.<string>|undefined} 315 */ 316 this.contextTypes; 317} 318 319/** 320 * @constructor 321 * @param {!WebInspector.ModuleManager} manager 322 * @param {!WebInspector.ModuleManager.ModuleDescriptor} descriptor 323 */ 324WebInspector.ModuleManager.Module = function(manager, descriptor) 325{ 326 this._manager = manager; 327 this._descriptor = descriptor; 328 this._name = descriptor.name; 329 var extensions = /** @type {?Array.<!WebInspector.ModuleManager.ExtensionDescriptor>}*/ (descriptor.extensions); 330 for (var i = 0; extensions && i < extensions.length; ++i) 331 this._manager._extensions.push(new WebInspector.ModuleManager.Extension(this, extensions[i])); 332 this._loaded = false; 333} 334 335WebInspector.ModuleManager.Module.prototype = { 336 /** 337 * @return {string} 338 */ 339 name: function() 340 { 341 return this._name; 342 }, 343 344 _load: function() 345 { 346 if (this._loaded) 347 return; 348 349 if (this._isLoading) { 350 var oldStackTraceLimit = Error.stackTraceLimit; 351 Error.stackTraceLimit = 50; 352 console.assert(false, "Module " + this._name + " is loaded from itself: " + new Error().stack); 353 Error.stackTraceLimit = oldStackTraceLimit; 354 return; 355 } 356 357 this._isLoading = true; 358 var dependencies = this._descriptor.dependencies; 359 for (var i = 0; dependencies && i < dependencies.length; ++i) 360 this._manager.loadModule(dependencies[i]); 361 var scripts = this._descriptor.scripts; 362 for (var i = 0; scripts && i < scripts.length; ++i) 363 loadScript(this._name + "/" + scripts[i]); 364 this._isLoading = false; 365 this._loaded = true; 366 } 367} 368 369/** 370 * @constructor 371 * @param {!WebInspector.ModuleManager.Module} module 372 * @param {!WebInspector.ModuleManager.ExtensionDescriptor} descriptor 373 */ 374WebInspector.ModuleManager.Extension = function(module, descriptor) 375{ 376 this._module = module; 377 this._descriptor = descriptor; 378 379 this._type = descriptor.type; 380 this._hasTypeClass = !!this._type.startsWith("@"); 381 382 /** 383 * @type {?string} 384 */ 385 this._className = descriptor.className || null; 386} 387 388WebInspector.ModuleManager.Extension.prototype = { 389 /** 390 * @return {!Object} 391 */ 392 descriptor: function() 393 { 394 return this._descriptor; 395 }, 396 397 /** 398 * @return {!WebInspector.ModuleManager.Module} 399 */ 400 module: function() 401 { 402 return this._module; 403 }, 404 405 /** 406 * @return {?function(new:Object)} 407 */ 408 _typeClass: function() 409 { 410 if (!this._hasTypeClass) 411 return null; 412 return this._module._manager._resolve(this._type.substring(1)); 413 }, 414 415 /** 416 * @param {?Object} context 417 * @return {boolean} 418 */ 419 isApplicable: function(context) 420 { 421 return this._module._manager.isExtensionApplicableToContext(this, context); 422 }, 423 424 /** 425 * @return {?Object} 426 */ 427 instance: function() 428 { 429 if (!this._className) 430 return null; 431 432 if (!this._instance) { 433 this._module._load(); 434 435 var constructorFunction = window.eval(this._className); 436 if (!(constructorFunction instanceof Function)) 437 return null; 438 439 this._instance = new constructorFunction(); 440 } 441 return this._instance; 442 } 443} 444 445/** 446 * @interface 447 */ 448WebInspector.Renderer = function() 449{ 450} 451 452WebInspector.Renderer.prototype = { 453 /** 454 * @param {!Object} object 455 * @return {?Element} 456 */ 457 render: function(object) {} 458} 459 460/** 461 * @interface 462 */ 463WebInspector.Revealer = function() 464{ 465} 466 467/** 468 * @param {?Object} revealable 469 * @param {number=} lineNumber 470 */ 471WebInspector.Revealer.reveal = function(revealable, lineNumber) 472{ 473 if (!revealable) 474 return; 475 var revealer = WebInspector.moduleManager.instance(WebInspector.Revealer, revealable); 476 if (revealer) 477 revealer.reveal(revealable, lineNumber); 478} 479 480WebInspector.Revealer.prototype = { 481 /** 482 * @param {!Object} object 483 */ 484 reveal: function(object) {} 485} 486 487WebInspector.moduleManager = new WebInspector.ModuleManager(allDescriptors); 488