1 /* 2 * Copyright 2014 The Chromium Authors. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6 7 /** 8 * @constructor 9 * @extends {Protocol.Agents} 10 * @param {string} name 11 * @param {!InspectorBackendClass.Connection} connection 12 * @param {function(?WebInspector.Target)=} callback 13 */ 14 WebInspector.Target = function(name, connection, callback) 15 { 16 Protocol.Agents.call(this, connection.agentsMap()); 17 this._name = name; 18 this._connection = connection; 19 connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, this._onDisconnect, this); 20 this._id = WebInspector.Target._nextId++; 21 22 /** @type {!Map.<!Function, !WebInspector.SDKModel>} */ 23 this._modelByConstructor = new Map(); 24 25 /** @type {!Object.<string, boolean>} */ 26 this._capabilities = {}; 27 this.pageAgent().canScreencast(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanScreencast, null)); 28 this.pageAgent().canEmulate(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanEmulate, null)); 29 if (Runtime.experiments.isEnabled("timelinePowerProfiler")) 30 this.powerAgent().canProfilePower(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanProfilePower, null)); 31 this.workerAgent().canInspectWorkers(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanInspectWorkers, this._loadedWithCapabilities.bind(this, callback))); 32 if (Runtime.experiments.isEnabled("timelineOnTraceEvents")) 33 this.consoleAgent().setTracingBasedTimeline(true); 34 } 35 36 /** 37 * @enum {string} 38 */ 39 WebInspector.Target.Capabilities = { 40 CanScreencast: "CanScreencast", 41 HasTouchInputs: "HasTouchInputs", 42 CanProfilePower: "CanProfilePower", 43 CanInspectWorkers: "CanInspectWorkers", 44 CanEmulate: "CanEmulate" 45 } 46 47 WebInspector.Target._nextId = 1; 48 49 WebInspector.Target.prototype = { 50 51 /** 52 * @return {number} 53 */ 54 id: function() 55 { 56 return this._id; 57 }, 58 59 /** 60 * 61 * @return {string} 62 */ 63 name: function() 64 { 65 return this._name; 66 }, 67 68 /** 69 * @param {string} name 70 * @param {function()|null} callback 71 * @param {?Protocol.Error} error 72 * @param {boolean} result 73 */ 74 _initializeCapability: function(name, callback, error, result) 75 { 76 this._capabilities[name] = result; 77 if (callback) 78 callback(); 79 }, 80 81 /** 82 * @param {string} capability 83 * @return {boolean} 84 */ 85 hasCapability: function(capability) 86 { 87 return !!this._capabilities[capability]; 88 }, 89 90 /** 91 * @param {function(?WebInspector.Target)=} callback 92 */ 93 _loadedWithCapabilities: function(callback) 94 { 95 if (this._connection.isClosed()) { 96 callback(null); 97 return; 98 } 99 100 /** @type {!WebInspector.ConsoleModel} */ 101 this.consoleModel = new WebInspector.ConsoleModel(this); 102 103 /** @type {!WebInspector.NetworkManager} */ 104 this.networkManager = new WebInspector.NetworkManager(this); 105 106 /** @type {!WebInspector.ResourceTreeModel} */ 107 this.resourceTreeModel = new WebInspector.ResourceTreeModel(this); 108 if (!WebInspector.resourceTreeModel) 109 WebInspector.resourceTreeModel = this.resourceTreeModel; 110 111 /** @type {!WebInspector.NetworkLog} */ 112 this.networkLog = new WebInspector.NetworkLog(this); 113 if (!WebInspector.networkLog) 114 WebInspector.networkLog = this.networkLog; 115 116 /** @type {!WebInspector.DebuggerModel} */ 117 this.debuggerModel = new WebInspector.DebuggerModel(this); 118 if (!WebInspector.debuggerModel) 119 WebInspector.debuggerModel = this.debuggerModel; 120 121 /** @type {!WebInspector.RuntimeModel} */ 122 this.runtimeModel = new WebInspector.RuntimeModel(this); 123 if (!WebInspector.runtimeModel) 124 WebInspector.runtimeModel = this.runtimeModel; 125 126 /** @type {!WebInspector.DOMModel} */ 127 this.domModel = new WebInspector.DOMModel(this); 128 129 /** @type {!WebInspector.CSSStyleModel} */ 130 this.cssModel = new WebInspector.CSSStyleModel(this); 131 if (!WebInspector.cssModel) 132 WebInspector.cssModel = this.cssModel; 133 134 /** @type {!WebInspector.WorkerManager} */ 135 this.workerManager = new WebInspector.WorkerManager(this, this.hasCapability(WebInspector.Target.Capabilities.CanInspectWorkers)); 136 if (!WebInspector.workerManager) 137 WebInspector.workerManager = this.workerManager; 138 139 if (this.hasCapability(WebInspector.Target.Capabilities.CanProfilePower)) 140 WebInspector.powerProfiler = new WebInspector.PowerProfiler(this); 141 142 /** @type {!WebInspector.TimelineManager} */ 143 this.timelineManager = new WebInspector.TimelineManager(this); 144 145 /** @type {!WebInspector.DatabaseModel} */ 146 this.databaseModel = new WebInspector.DatabaseModel(this); 147 if (!WebInspector.databaseModel) 148 WebInspector.databaseModel = this.databaseModel; 149 150 /** @type {!WebInspector.DOMStorageModel} */ 151 this.domStorageModel = new WebInspector.DOMStorageModel(this); 152 if (!WebInspector.domStorageModel) 153 WebInspector.domStorageModel = this.domStorageModel; 154 155 /** @type {!WebInspector.CPUProfilerModel} */ 156 this.cpuProfilerModel = new WebInspector.CPUProfilerModel(this); 157 if (!WebInspector.cpuProfilerModel) 158 WebInspector.cpuProfilerModel = this.cpuProfilerModel; 159 160 /** @type {!WebInspector.HeapProfilerModel} */ 161 this.heapProfilerModel = new WebInspector.HeapProfilerModel(this); 162 163 /** @type {!WebInspector.IndexedDBModel} */ 164 this.indexedDBModel = new WebInspector.IndexedDBModel(this); 165 166 /** @type {!WebInspector.LayerTreeModel} */ 167 this.layerTreeModel = new WebInspector.LayerTreeModel(this); 168 169 if (callback) 170 callback(this); 171 }, 172 173 /** 174 * @override 175 * @param {string} domain 176 * @param {!Object} dispatcher 177 */ 178 registerDispatcher: function(domain, dispatcher) 179 { 180 this._connection.registerDispatcher(domain, dispatcher); 181 }, 182 183 /** 184 * @return {boolean} 185 */ 186 isWorkerTarget: function() 187 { 188 return !this.hasCapability(WebInspector.Target.Capabilities.CanInspectWorkers); 189 }, 190 191 /** 192 * @return {boolean} 193 */ 194 canEmulate: function() 195 { 196 return this.hasCapability(WebInspector.Target.Capabilities.CanEmulate); 197 }, 198 199 _onDisconnect: function() 200 { 201 WebInspector.targetManager.removeTarget(this); 202 this._dispose(); 203 }, 204 205 _dispose: function() 206 { 207 this.debuggerModel.dispose(); 208 this.networkManager.dispose(); 209 this.cpuProfilerModel.dispose(); 210 }, 211 212 /** 213 * @return {boolean} 214 */ 215 isDetached: function() 216 { 217 return this._connection.isClosed(); 218 }, 219 220 __proto__: Protocol.Agents.prototype 221 } 222 223 /** 224 * @constructor 225 * @extends {WebInspector.Object} 226 * @param {!WebInspector.Target} target 227 */ 228 WebInspector.SDKObject = function(target) 229 { 230 WebInspector.Object.call(this); 231 this._target = target; 232 } 233 234 WebInspector.SDKObject.prototype = { 235 /** 236 * @return {!WebInspector.Target} 237 */ 238 target: function() 239 { 240 return this._target; 241 }, 242 243 __proto__: WebInspector.Object.prototype 244 } 245 246 /** 247 * @constructor 248 * @extends {WebInspector.SDKObject} 249 * @param {!Function} modelClass 250 * @param {!WebInspector.Target} target 251 */ 252 WebInspector.SDKModel = function(modelClass, target) 253 { 254 WebInspector.SDKObject.call(this, target); 255 target._modelByConstructor.set(modelClass, this); 256 } 257 258 WebInspector.SDKModel.prototype = { 259 __proto__: WebInspector.SDKObject.prototype 260 } 261 262 /** 263 * @constructor 264 * @extends {WebInspector.Object} 265 */ 266 WebInspector.TargetManager = function() 267 { 268 WebInspector.Object.call(this); 269 /** @type {!Array.<!WebInspector.Target>} */ 270 this._targets = []; 271 /** @type {!Array.<!WebInspector.TargetManager.Observer>} */ 272 this._observers = []; 273 /** @type {!Object.<string, !Array.<{modelClass: !Function, thisObject: (!Object|undefined), listener: function(!WebInspector.Event)}>>} */ 274 this._modelListeners = {}; 275 } 276 277 WebInspector.TargetManager.Events = { 278 InspectedURLChanged: "InspectedURLChanged", 279 MainFrameNavigated: "MainFrameNavigated", 280 Load: "Load", 281 WillReloadPage: "WillReloadPage" 282 } 283 284 WebInspector.TargetManager.prototype = { 285 /** 286 * @return {string} 287 */ 288 inspectedPageURL: function() 289 { 290 if (!this._targets.length) 291 return ""; 292 293 return this._targets[0].resourceTreeModel.inspectedPageURL(); 294 }, 295 296 /** 297 * @return {string} 298 */ 299 inspectedPageDomain: function() 300 { 301 if (!this._targets.length) 302 return ""; 303 304 return this._targets[0].resourceTreeModel.inspectedPageDomain(); 305 }, 306 307 /** 308 * @param {!WebInspector.Event} event 309 */ 310 _redispatchEvent: function(event) 311 { 312 this.dispatchEventToListeners(event.type, event.data); 313 }, 314 315 /** 316 * @param {boolean=} ignoreCache 317 */ 318 reloadPage: function(ignoreCache) 319 { 320 if (this._targets.length) 321 this._targets[0].resourceTreeModel.reloadPage(ignoreCache); 322 }, 323 324 /** 325 * @param {!Function} modelClass 326 * @param {string} eventType 327 * @param {function(!WebInspector.Event)} listener 328 * @param {!Object=} thisObject 329 */ 330 addModelListener: function(modelClass, eventType, listener, thisObject) 331 { 332 for (var i = 0; i < this._targets.length; ++i) { 333 var model = this._targets[i]._modelByConstructor.get(modelClass); 334 model.addEventListener(eventType, listener, thisObject); 335 } 336 if (!this._modelListeners[eventType]) 337 this._modelListeners[eventType] = []; 338 this._modelListeners[eventType].push({ modelClass: modelClass, thisObject: thisObject, listener: listener }); 339 }, 340 341 /** 342 * @param {!Function} modelClass 343 * @param {string} eventType 344 * @param {function(!WebInspector.Event)} listener 345 * @param {!Object=} thisObject 346 */ 347 removeModelListener: function(modelClass, eventType, listener, thisObject) 348 { 349 if (!this._modelListeners[eventType]) 350 return; 351 352 for (var i = 0; i < this._targets.length; ++i) { 353 var model = this._targets[i]._modelByConstructor.get(modelClass); 354 model.removeEventListener(eventType, listener, thisObject); 355 } 356 357 var listeners = this._modelListeners[eventType]; 358 for (var i = 0; i < listeners.length; ++i) { 359 if (listeners[i].modelClass === modelClass && listeners[i].listener === listener && listeners[i].thisObject === thisObject) 360 listeners.splice(i--, 1); 361 } 362 if (!listeners.length) 363 delete this._modelListeners[eventType]; 364 }, 365 366 /** 367 * @param {!WebInspector.TargetManager.Observer} targetObserver 368 */ 369 observeTargets: function(targetObserver) 370 { 371 this.targets().forEach(targetObserver.targetAdded.bind(targetObserver)); 372 this._observers.push(targetObserver); 373 }, 374 375 /** 376 * @param {!WebInspector.TargetManager.Observer} targetObserver 377 */ 378 unobserveTargets: function(targetObserver) 379 { 380 this._observers.remove(targetObserver); 381 }, 382 383 /** 384 * @param {string} name 385 * @param {!InspectorBackendClass.Connection} connection 386 * @param {function(?WebInspector.Target)=} callback 387 */ 388 createTarget: function(name, connection, callback) 389 { 390 var target = new WebInspector.Target(name, connection, callbackWrapper.bind(this)); 391 392 /** 393 * @this {WebInspector.TargetManager} 394 * @param {?WebInspector.Target} newTarget 395 */ 396 function callbackWrapper(newTarget) 397 { 398 if (newTarget) 399 this.addTarget(newTarget); 400 if (callback) 401 callback(newTarget); 402 } 403 }, 404 405 /** 406 * @param {!WebInspector.Target} target 407 */ 408 addTarget: function(target) 409 { 410 this._targets.push(target); 411 if (this._targets.length === 1) { 412 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this); 413 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this); 414 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this); 415 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this); 416 } 417 var copy = this._observers.slice(); 418 for (var i = 0; i < copy.length; ++i) 419 copy[i].targetAdded(target); 420 421 for (var eventType in this._modelListeners) { 422 var listeners = this._modelListeners[eventType]; 423 for (var i = 0; i < listeners.length; ++i) { 424 var model = target._modelByConstructor.get(listeners[i].modelClass); 425 model.addEventListener(eventType, listeners[i].listener, listeners[i].thisObject); 426 } 427 } 428 }, 429 430 /** 431 * @param {!WebInspector.Target} target 432 */ 433 removeTarget: function(target) 434 { 435 this._targets.remove(target); 436 if (this._targets.length === 0) { 437 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this); 438 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this); 439 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this); 440 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this); 441 } 442 var copy = this._observers.slice(); 443 for (var i = 0; i < copy.length; ++i) 444 copy[i].targetRemoved(target); 445 446 for (var eventType in this._modelListeners) { 447 var listeners = this._modelListeners[eventType]; 448 for (var i = 0; i < listeners.length; ++i) { 449 var model = target._modelByConstructor.get(listeners[i].modelClass); 450 model.removeEventListener(eventType, listeners[i].listener, listeners[i].thisObject); 451 } 452 } 453 }, 454 455 /** 456 * @return {boolean} 457 */ 458 hasTargets: function() 459 { 460 return !!this._targets.length; 461 }, 462 463 /** 464 * @return {!Array.<!WebInspector.Target>} 465 */ 466 targets: function() 467 { 468 return this._targets.slice(); 469 }, 470 471 /** 472 * @return {?WebInspector.Target} 473 */ 474 mainTarget: function() 475 { 476 return this._targets[0]; 477 }, 478 479 __proto__: WebInspector.Object.prototype 480 } 481 482 /** 483 * @interface 484 */ 485 WebInspector.TargetManager.Observer = function() 486 { 487 } 488 489 WebInspector.TargetManager.Observer.prototype = { 490 /** 491 * @param {!WebInspector.Target} target 492 */ 493 targetAdded: function(target) { }, 494 495 /** 496 * @param {!WebInspector.Target} target 497 */ 498 targetRemoved: function(target) { }, 499 } 500 501 /** 502 * @type {!WebInspector.TargetManager} 503 */ 504 WebInspector.targetManager = new WebInspector.TargetManager(); 505