blob: acfaef59437297f44ec762ce2c5b584436d19f72 [file] [log] [blame]
/*
* Copyright 2014 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/**
* @constructor
* @extends {Protocol.Agents}
* @param {!WebInspector.TargetManager} targetManager
* @param {string} name
* @param {number} type
* @param {!InspectorBackendClass.Connection} connection
* @param {?WebInspector.Target} parentTarget
*/
WebInspector.Target = function(targetManager, name, type, connection, parentTarget)
{
Protocol.Agents.call(this, connection.agentsMap());
this._targetManager = targetManager;
this._name = name;
this._type = type;
this._connection = connection;
this._parentTarget = parentTarget;
connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, this._onDisconnect, this);
this._id = WebInspector.Target._nextId++;
/** @type {!Map.<!Function, !WebInspector.SDKModel>} */
this._modelByConstructor = new Map();
/** @type {!WebInspector.ConsoleModel} */
this.consoleModel = new WebInspector.ConsoleModel(this);
/** @type {!WebInspector.NetworkManager} */
this.networkManager = new WebInspector.NetworkManager(this);
/** @type {!WebInspector.ResourceTreeModel} */
this.resourceTreeModel = new WebInspector.ResourceTreeModel(this);
/** @type {!WebInspector.NetworkLog} */
this.networkLog = new WebInspector.NetworkLog(this);
if (this.hasJSContext())
new WebInspector.DebuggerModel(this);
/** @type {!WebInspector.RuntimeModel} */
this.runtimeModel = new WebInspector.RuntimeModel(this);
if (this._type === WebInspector.Target.Type.Page) {
new WebInspector.DOMModel(this);
new WebInspector.CSSStyleModel(this);
}
/** @type {?WebInspector.WorkerManager} */
this.workerManager = !this.isDedicatedWorker() ? new WebInspector.WorkerManager(this) : null;
/** @type {!WebInspector.CPUProfilerModel} */
this.cpuProfilerModel = new WebInspector.CPUProfilerModel(this);
/** @type {!WebInspector.HeapProfilerModel} */
this.heapProfilerModel = new WebInspector.HeapProfilerModel(this);
this.tracingManager = new WebInspector.TracingManager(this);
if (this.isPage())
this.serviceWorkerManager = new WebInspector.ServiceWorkerManager(this);
}
/**
* @enum {number}
*/
WebInspector.Target.Type = {
Page: 1,
DedicatedWorker: 2,
ServiceWorker: 4
}
WebInspector.Target._nextId = 1;
WebInspector.Target.prototype = {
/**
* @return {number}
*/
id: function()
{
return this._id;
},
/**
*
* @return {string}
*/
name: function()
{
return this._name;
},
/**
*
* @return {!WebInspector.TargetManager}
*/
targetManager: function()
{
return this._targetManager;
},
/**
* @param {string} label
* @return {string}
*/
decorateLabel: function(label)
{
return this.isWorker() ? "\u2699 " + label : label;
},
/**
* @override
* @param {string} domain
* @param {!Object} dispatcher
*/
registerDispatcher: function(domain, dispatcher)
{
this._connection.registerDispatcher(domain, dispatcher);
},
/**
* @return {boolean}
*/
isPage: function()
{
return this._type === WebInspector.Target.Type.Page;
},
/**
* @return {boolean}
*/
isWorker: function()
{
return this.isDedicatedWorker() || this.isServiceWorker();
},
/**
* @return {boolean}
*/
isDedicatedWorker: function()
{
return this._type === WebInspector.Target.Type.DedicatedWorker;
},
/**
* @return {boolean}
*/
isServiceWorker: function()
{
return this._type === WebInspector.Target.Type.ServiceWorker;
},
/**
* @return {boolean}
*/
hasJSContext: function()
{
return !this.isServiceWorker();
},
/**
* @return {?WebInspector.Target}
*/
parentTarget: function()
{
return this._parentTarget;
},
_onDisconnect: function()
{
this._targetManager.removeTarget(this);
this._dispose();
},
_dispose: function()
{
this._targetManager.dispatchEventToListeners(WebInspector.TargetManager.Events.TargetDisposed, this);
this.networkManager.dispose();
this.cpuProfilerModel.dispose();
WebInspector.ServiceWorkerCacheModel.fromTarget(this).dispose();
if (this.workerManager)
this.workerManager.dispose();
},
/**
* @return {boolean}
*/
isDetached: function()
{
return this._connection.isClosed();
},
/**
* @param {!Function} modelClass
* @return {?WebInspector.SDKModel}
*/
model: function(modelClass)
{
return this._modelByConstructor.get(modelClass) || null;
},
__proto__: Protocol.Agents.prototype
}
/**
* @constructor
* @extends {WebInspector.Object}
* @param {!WebInspector.Target} target
*/
WebInspector.SDKObject = function(target)
{
WebInspector.Object.call(this);
this._target = target;
}
WebInspector.SDKObject.prototype = {
/**
* @return {!WebInspector.Target}
*/
target: function()
{
return this._target;
},
__proto__: WebInspector.Object.prototype
}
/**
* @constructor
* @extends {WebInspector.SDKObject}
* @param {!Function} modelClass
* @param {!WebInspector.Target} target
*/
WebInspector.SDKModel = function(modelClass, target)
{
WebInspector.SDKObject.call(this, target);
target._modelByConstructor.set(modelClass, this);
}
WebInspector.SDKModel.prototype = {
/**
* @return {!Promise}
*/
suspendModel: function()
{
return Promise.resolve();
},
/**
* @return {!Promise}
*/
resumeModel: function()
{
return Promise.resolve();
},
__proto__: WebInspector.SDKObject.prototype
}
/**
* @constructor
* @extends {WebInspector.Object}
*/
WebInspector.TargetManager = function()
{
WebInspector.Object.call(this);
/** @type {!Array.<!WebInspector.Target>} */
this._targets = [];
/** @type {!Array.<!WebInspector.TargetManager.Observer>} */
this._observers = [];
this._observerTypeSymbol = Symbol("observerType");
/** @type {!Object.<string, !Array.<{modelClass: !Function, thisObject: (!Object|undefined), listener: function(!WebInspector.Event)}>>} */
this._modelListeners = {};
this._isSuspended = false;
}
WebInspector.TargetManager.Events = {
InspectedURLChanged: "InspectedURLChanged",
MainFrameNavigated: "MainFrameNavigated",
Load: "Load",
PageReloadRequested: "PageReloadRequested",
WillReloadPage: "WillReloadPage",
TargetDisposed: "TargetDisposed",
SuspendStateChanged: "SuspendStateChanged"
}
WebInspector.TargetManager.prototype = {
suspendAllTargets: function()
{
if (this._isSuspended)
return;
this._isSuspended = true;
this.dispatchEventToListeners(WebInspector.TargetManager.Events.SuspendStateChanged);
for (var i = 0; i < this._targets.length; ++i) {
for (var model of this._targets[i]._modelByConstructor.values())
model.suspendModel();
}
},
/**
* @return {!Promise}
*/
resumeAllTargets: function()
{
if (!this._isSuspended)
throw new Error("Not suspended");
this._isSuspended = false;
this.dispatchEventToListeners(WebInspector.TargetManager.Events.SuspendStateChanged);
var promises = [];
for (var i = 0; i < this._targets.length; ++i) {
for (var model of this._targets[i]._modelByConstructor.values())
promises.push(model.resumeModel());
}
return Promise.all(promises);
},
suspendAndResumeAllTargets: function()
{
this.suspendAllTargets();
this.resumeAllTargets();
},
/**
* @return {boolean}
*/
allTargetsSuspended: function()
{
return this._isSuspended;
},
/**
* @return {string}
*/
inspectedPageURL: function()
{
if (!this._targets.length)
return "";
return this._targets[0].resourceTreeModel.inspectedPageURL();
},
/**
* @return {string}
*/
inspectedPageDomain: function()
{
if (!this._targets.length)
return "";
return this._targets[0].resourceTreeModel.inspectedPageDomain();
},
/**
* @param {!WebInspector.Event} event
*/
_redispatchEvent: function(event)
{
this.dispatchEventToListeners(event.type, event.data);
},
/**
* @param {boolean=} ignoreCache
* @param {string=} injectedScript
*/
reloadPage: function(ignoreCache, injectedScript)
{
if (this._targets.length)
this._targets[0].resourceTreeModel.reloadPage(ignoreCache, injectedScript);
},
/**
* @param {!Function} modelClass
* @param {string} eventType
* @param {function(!WebInspector.Event)} listener
* @param {!Object=} thisObject
*/
addModelListener: function(modelClass, eventType, listener, thisObject)
{
for (var i = 0; i < this._targets.length; ++i) {
var model = this._targets[i]._modelByConstructor.get(modelClass);
if (model)
model.addEventListener(eventType, listener, thisObject);
}
if (!this._modelListeners[eventType])
this._modelListeners[eventType] = [];
this._modelListeners[eventType].push({ modelClass: modelClass, thisObject: thisObject, listener: listener });
},
/**
* @param {!Function} modelClass
* @param {string} eventType
* @param {function(!WebInspector.Event)} listener
* @param {!Object=} thisObject
*/
removeModelListener: function(modelClass, eventType, listener, thisObject)
{
if (!this._modelListeners[eventType])
return;
for (var i = 0; i < this._targets.length; ++i) {
var model = this._targets[i]._modelByConstructor.get(modelClass);
if (model)
model.removeEventListener(eventType, listener, thisObject);
}
var listeners = this._modelListeners[eventType];
for (var i = 0; i < listeners.length; ++i) {
if (listeners[i].modelClass === modelClass && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
listeners.splice(i--, 1);
}
if (!listeners.length)
delete this._modelListeners[eventType];
},
/**
* @param {!WebInspector.TargetManager.Observer} targetObserver
* @param {number=} type
*/
observeTargets: function(targetObserver, type)
{
if (this._observerTypeSymbol in targetObserver)
throw new Error("Observer can only be registered once");
targetObserver[this._observerTypeSymbol] = type || 0x7fff;
this.targets(type).forEach(targetObserver.targetAdded.bind(targetObserver));
this._observers.push(targetObserver);
},
/**
* @param {!WebInspector.TargetManager.Observer} targetObserver
*/
unobserveTargets: function(targetObserver)
{
delete targetObserver[this._observerTypeSymbol];
this._observers.remove(targetObserver);
},
/**
* @param {string} name
* @param {number} type
* @param {!InspectorBackendClass.Connection} connection
* @param {?WebInspector.Target} parentTarget
* @return {!WebInspector.Target}
*/
createTarget: function(name, type, connection, parentTarget)
{
var target = new WebInspector.Target(this, name, type, connection, parentTarget);
this.addTarget(target);
return target;
},
/**
* @param {number} type
* @return {!Array<!WebInspector.TargetManager.Observer>}
*/
_observersByType: function(type)
{
var result = [];
for (var observer of this._observers) {
if (observer[this._observerTypeSymbol] & type)
result.push(observer);
}
return result;
},
/**
* @param {!WebInspector.Target} target
*/
addTarget: function(target)
{
this._targets.push(target);
if (this._targets.length === 1) {
target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this);
target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this);
target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this);
target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.PageReloadRequested, this._redispatchEvent, this);
target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this);
}
var copy = this._observersByType(target._type);
for (var i = 0; i < copy.length; ++i)
copy[i].targetAdded(target);
for (var eventType in this._modelListeners) {
var listeners = this._modelListeners[eventType];
for (var i = 0; i < listeners.length; ++i) {
var model = target._modelByConstructor.get(listeners[i].modelClass);
if (model)
model.addEventListener(eventType, listeners[i].listener, listeners[i].thisObject);
}
}
},
/**
* @param {!WebInspector.Target} target
*/
removeTarget: function(target)
{
this._targets.remove(target);
if (this._targets.length === 0) {
target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this);
target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this);
target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this);
target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this);
}
var copy = this._observersByType(target._type);
for (var i = 0; i < copy.length; ++i)
copy[i].targetRemoved(target);
for (var eventType in this._modelListeners) {
var listeners = this._modelListeners[eventType];
for (var i = 0; i < listeners.length; ++i) {
var model = target._modelByConstructor.get(listeners[i].modelClass);
if (model)
model.removeEventListener(eventType, listeners[i].listener, listeners[i].thisObject);
}
}
},
/**
* @param {number=} type
* @return {boolean}
*/
hasTargets: function(type)
{
return !!this.targets(type).length;
},
/**
* @param {number=} type
* @return {!Array.<!WebInspector.Target>}
*/
targets: function(type)
{
if (!type)
return this._targets.slice();
var result = [];
for (var target of this._targets) {
if (target._type & type)
result.push(target);
}
return result;
},
/**
* @return {!Array.<!WebInspector.Target>}
*/
targetsWithJSContext: function()
{
var result = [];
for (var target of this._targets) {
if (target.hasJSContext())
result.push(target);
}
return result;
},
/**
*
* @param {number} id
* @return {?WebInspector.Target}
*/
targetById: function(id)
{
for (var i = 0; i < this._targets.length; ++i) {
if (this._targets[i].id() === id)
return this._targets[i];
}
return null;
},
/**
* @return {?WebInspector.Target}
*/
mainTarget: function()
{
return this._targets[0] || null;
},
__proto__: WebInspector.Object.prototype
}
/**
* @interface
*/
WebInspector.TargetManager.Observer = function()
{
}
WebInspector.TargetManager.Observer.prototype = {
/**
* @param {!WebInspector.Target} target
*/
targetAdded: function(target) { },
/**
* @param {!WebInspector.Target} target
*/
targetRemoved: function(target) { },
}
/**
* @type {!WebInspector.TargetManager}
*/
WebInspector.targetManager = new WebInspector.TargetManager();