blob: dca142abfb5f7390a6a2f843afb958c8062a6140 [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.
*/
/**
* @unrestricted
*/
SDK.TargetManager = class extends Common.Object {
constructor() {
super();
/** @type {!Array.<!SDK.Target>} */
this._targets = [];
/** @type {!Array.<!SDK.TargetManager.Observer>} */
this._observers = [];
this._observerCapabiliesMaskSymbol = Symbol('observerCapabilitiesMask');
/** @type {!Map<symbol, !Array<{modelClass: !Function, thisObject: (!Object|undefined), listener: function(!Common.Event)}>>} */
this._modelListeners = new Map();
this._isSuspended = false;
}
suspendAllTargets() {
if (this._isSuspended)
return;
this._isSuspended = true;
this.dispatchEventToListeners(SDK.TargetManager.Events.SuspendStateChanged);
for (var i = 0; i < this._targets.length; ++i) {
for (var model of this._targets[i].models())
model.suspendModel();
}
}
/**
* @return {!Promise}
*/
resumeAllTargets() {
if (!this._isSuspended)
throw new Error('Not suspended');
this._isSuspended = false;
this.dispatchEventToListeners(SDK.TargetManager.Events.SuspendStateChanged);
var promises = [];
for (var i = 0; i < this._targets.length; ++i) {
for (var model of this._targets[i].models())
promises.push(model.resumeModel());
}
return Promise.all(promises);
}
suspendAndResumeAllTargets() {
this.suspendAllTargets();
this.resumeAllTargets();
}
/**
* @return {boolean}
*/
allTargetsSuspended() {
return this._isSuspended;
}
/**
* @return {string}
*/
inspectedURL() {
return this._targets[0] ? this._targets[0].inspectedURL() : '';
}
/**
* @param {!SDK.TargetManager.Events} eventName
* @param {!Common.Event} event
*/
_redispatchEvent(eventName, event) {
this.dispatchEventToListeners(eventName, event.data);
}
/**
* @param {boolean=} bypassCache
* @param {string=} injectedScript
*/
reloadPage(bypassCache, injectedScript) {
if (!this._targets.length)
return;
var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(this._targets[0]);
if (!resourceTreeModel)
return;
resourceTreeModel.reloadPage(bypassCache, injectedScript);
}
/**
* @param {!Function} modelClass
* @param {symbol} eventType
* @param {function(!Common.Event)} listener
* @param {!Object=} thisObject
*/
addModelListener(modelClass, eventType, listener, thisObject) {
for (var i = 0; i < this._targets.length; ++i) {
var model = this._targets[i].model(modelClass);
if (model)
model.addEventListener(eventType, listener, thisObject);
}
if (!this._modelListeners.has(eventType))
this._modelListeners.set(eventType, []);
this._modelListeners.get(eventType).push({modelClass: modelClass, thisObject: thisObject, listener: listener});
}
/**
* @param {!Function} modelClass
* @param {symbol} eventType
* @param {function(!Common.Event)} listener
* @param {!Object=} thisObject
*/
removeModelListener(modelClass, eventType, listener, thisObject) {
if (!this._modelListeners.has(eventType))
return;
for (var i = 0; i < this._targets.length; ++i) {
var model = this._targets[i].model(modelClass);
if (model)
model.removeEventListener(eventType, listener, thisObject);
}
var listeners = this._modelListeners.get(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)
this._modelListeners.delete(eventType);
}
/**
* @param {!SDK.TargetManager.Observer} targetObserver
* @param {number=} capabilitiesMask
*/
observeTargets(targetObserver, capabilitiesMask) {
if (this._observerCapabiliesMaskSymbol in targetObserver)
throw new Error('Observer can only be registered once');
targetObserver[this._observerCapabiliesMaskSymbol] = capabilitiesMask || 0;
this.targets(capabilitiesMask).forEach(targetObserver.targetAdded.bind(targetObserver));
this._observers.push(targetObserver);
}
/**
* @param {!SDK.TargetManager.Observer} targetObserver
*/
unobserveTargets(targetObserver) {
delete targetObserver[this._observerCapabiliesMaskSymbol];
this._observers.remove(targetObserver);
}
/**
* @param {string} name
* @param {number} capabilitiesMask
* @param {!InspectorBackendClass.Connection.Factory} connectionFactory
* @param {?SDK.Target} parentTarget
* @return {!SDK.Target}
*/
createTarget(name, capabilitiesMask, connectionFactory, parentTarget) {
var target = new SDK.Target(this, name, capabilitiesMask, connectionFactory, parentTarget);
var logAgent = target.hasLogCapability() ? target.logAgent() : null;
/** @type {!SDK.ConsoleModel} */
target.consoleModel = new SDK.ConsoleModel(target, logAgent);
var networkManager = null;
var resourceTreeModel = null;
if (target.hasNetworkCapability())
networkManager = new SDK.NetworkManager(target);
if (networkManager && target.hasDOMCapability()) {
resourceTreeModel =
new SDK.ResourceTreeModel(target, networkManager, SDK.SecurityOriginManager.fromTarget(target));
new SDK.NetworkLog(target, resourceTreeModel, networkManager);
}
/** @type {!SDK.RuntimeModel} */
target.runtimeModel = new SDK.RuntimeModel(target);
if (target.hasJSCapability())
new SDK.DebuggerModel(target);
if (resourceTreeModel) {
var domModel = new SDK.DOMModel(target);
// TODO(eostroukhov) CSSModel should not depend on RTM
new SDK.CSSModel(target, domModel);
}
/** @type {?SDK.SubTargetsManager} */
target.subTargetsManager = target.hasTargetCapability() ? new SDK.SubTargetsManager(target) : null;
/** @type {!SDK.CPUProfilerModel} */
target.cpuProfilerModel = new SDK.CPUProfilerModel(target);
/** @type {!SDK.HeapProfilerModel} */
target.heapProfilerModel = new SDK.HeapProfilerModel(target);
target.tracingManager = new SDK.TracingManager(target);
if (target.subTargetsManager && target.hasBrowserCapability())
target.serviceWorkerManager = new SDK.ServiceWorkerManager(target, target.subTargetsManager);
this.addTarget(target);
return target;
}
/**
* @param {!SDK.Target} target
* @return {!Array<!SDK.TargetManager.Observer>}
*/
_observersForTarget(target) {
return this._observers.filter(
(observer) => target.hasAllCapabilities(observer[this._observerCapabiliesMaskSymbol] || 0));
}
/**
* @param {!SDK.Target} target
*/
addTarget(target) {
this._targets.push(target);
var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target);
if (this._targets.length === 1 && resourceTreeModel) {
resourceTreeModel[SDK.TargetManager._listenersSymbol] = [
setupRedispatch.call(
this, SDK.ResourceTreeModel.Events.MainFrameNavigated, SDK.TargetManager.Events.MainFrameNavigated),
setupRedispatch.call(this, SDK.ResourceTreeModel.Events.Load, SDK.TargetManager.Events.Load),
setupRedispatch.call(
this, SDK.ResourceTreeModel.Events.PageReloadRequested, SDK.TargetManager.Events.PageReloadRequested),
setupRedispatch.call(this, SDK.ResourceTreeModel.Events.WillReloadPage, SDK.TargetManager.Events.WillReloadPage)
];
}
var copy = this._observersForTarget(target);
for (var i = 0; i < copy.length; ++i)
copy[i].targetAdded(target);
for (var pair of this._modelListeners) {
var listeners = pair[1];
for (var i = 0; i < listeners.length; ++i) {
var model = target.model(listeners[i].modelClass);
if (model)
model.addEventListener(/** @type {symbol} */ (pair[0]), listeners[i].listener, listeners[i].thisObject);
}
}
/**
* @param {!SDK.ResourceTreeModel.Events} sourceEvent
* @param {!SDK.TargetManager.Events} targetEvent
* @return {!Common.EventTarget.EventDescriptor}
* @this {SDK.TargetManager}
*/
function setupRedispatch(sourceEvent, targetEvent) {
return resourceTreeModel.addEventListener(sourceEvent, this._redispatchEvent.bind(this, targetEvent));
}
}
/**
* @param {!SDK.Target} target
*/
removeTarget(target) {
if (!this._targets.includes(target))
return;
this._targets.remove(target);
var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target);
var treeModelListeners = resourceTreeModel && resourceTreeModel[SDK.TargetManager._listenersSymbol];
if (treeModelListeners)
Common.EventTarget.removeEventListeners(treeModelListeners);
var copy = this._observersForTarget(target);
for (var i = 0; i < copy.length; ++i)
copy[i].targetRemoved(target);
for (var pair of this._modelListeners) {
var listeners = pair[1];
for (var i = 0; i < listeners.length; ++i) {
var model = target.model(listeners[i].modelClass);
if (model)
model.removeEventListener(/** @type {symbol} */ (pair[0]), listeners[i].listener, listeners[i].thisObject);
}
}
}
/**
* @param {number=} capabilitiesMask
* @return {!Array.<!SDK.Target>}
*/
targets(capabilitiesMask) {
if (!capabilitiesMask)
return this._targets.slice();
else
return this._targets.filter((target) => target.hasAllCapabilities(capabilitiesMask || 0));
}
/**
*
* @param {number} id
* @return {?SDK.Target}
*/
targetById(id) {
for (var i = 0; i < this._targets.length; ++i) {
if (this._targets[i].id() === id)
return this._targets[i];
}
return null;
}
/**
* @return {?SDK.Target}
*/
mainTarget() {
return this._targets[0] || null;
}
/**
* @param {!SDK.Target} target
*/
suspendReload(target) {
var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target);
if (resourceTreeModel)
resourceTreeModel.suspendReload();
}
/**
* @param {!SDK.Target} target
*/
resumeReload(target) {
var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target);
if (resourceTreeModel)
setImmediate(resourceTreeModel.resumeReload.bind(resourceTreeModel));
}
/**
* @param {function()} webSocketConnectionLostCallback
*/
connectToMainTarget(webSocketConnectionLostCallback) {
this._webSocketConnectionLostCallback = webSocketConnectionLostCallback;
this._connectAndCreateMainTarget();
}
_connectAndCreateMainTarget() {
var capabilities = SDK.Target.Capability.Browser | SDK.Target.Capability.DOM | SDK.Target.Capability.JS |
SDK.Target.Capability.Log | SDK.Target.Capability.Network | SDK.Target.Capability.Target;
if (Runtime.queryParam('isSharedWorker')) {
capabilities = SDK.Target.Capability.Browser | SDK.Target.Capability.Log | SDK.Target.Capability.Network |
SDK.Target.Capability.Target;
} else if (Runtime.queryParam('v8only')) {
capabilities = SDK.Target.Capability.JS;
}
var target = this.createTarget(Common.UIString('Main'), capabilities, this._createMainConnection.bind(this), null);
target.runtimeAgent().runIfWaitingForDebugger();
}
/**
* @param {!InspectorBackendClass.Connection.Params} params
* @return {!InspectorBackendClass.Connection}
*/
_createMainConnection(params) {
if (Runtime.queryParam('ws')) {
var ws = 'ws://' + Runtime.queryParam('ws');
this._mainConnection = new SDK.WebSocketConnection(ws, this._webSocketConnectionLostCallback, params);
} else if (InspectorFrontendHost.isHostedMode()) {
this._mainConnection = new SDK.StubConnection(params);
} else {
this._mainConnection = new SDK.MainConnection(params);
}
return this._mainConnection;
}
/**
* @param {function(string)} onMessage
* @return {!Promise<!InspectorBackendClass.Connection>}
*/
interceptMainConnection(onMessage) {
var params = {onMessage: onMessage, onDisconnect: this._connectAndCreateMainTarget.bind(this)};
return this._mainConnection.disconnect().then(this._createMainConnection.bind(this, params));
}
};
/** @enum {symbol} */
SDK.TargetManager.Events = {
InspectedURLChanged: Symbol('InspectedURLChanged'),
Load: Symbol('Load'),
MainFrameNavigated: Symbol('MainFrameNavigated'),
NameChanged: Symbol('NameChanged'),
PageReloadRequested: Symbol('PageReloadRequested'),
WillReloadPage: Symbol('WillReloadPage'),
TargetDisposed: Symbol('TargetDisposed'),
SuspendStateChanged: Symbol('SuspendStateChanged')
};
SDK.TargetManager._listenersSymbol = Symbol('SDK.TargetManager.Listeners');
/**
* @interface
*/
SDK.TargetManager.Observer = function() {};
SDK.TargetManager.Observer.prototype = {
/**
* @param {!SDK.Target} target
*/
targetAdded: function(target) {},
/**
* @param {!SDK.Target} target
*/
targetRemoved: function(target) {},
};
/**
* @type {!SDK.TargetManager}
*/
SDK.targetManager = new SDK.TargetManager();