blob: 605acf35ff81000ab279b855010953ee1bdad94f [file] [log] [blame]
// Copyright 2016 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.SubTargetsManager = class extends SDK.SDKModel {
/**
* @param {!SDK.Target} target
*/
constructor(target) {
super(SDK.SubTargetsManager, target);
target.registerTargetDispatcher(new SDK.SubTargetsDispatcher(this));
this._lastAnonymousTargetId = 0;
this._agent = target.targetAgent();
/** @type {!Map<string, !SDK.Target>} */
this._attachedTargets = new Map();
/** @type {!Map<string, !SDK.SubTargetConnection>} */
this._connections = new Map();
/** @type {!Map<string, !SDK.PendingTarget>} */
this._pendingTargets = new Map();
this._agent.setAutoAttach(true /* autoAttach */, true /* waitForDebuggerOnStart */);
if (Runtime.experiments.isEnabled('autoAttachToCrossProcessSubframes'))
this._agent.setAttachToFrames(true);
if (Runtime.experiments.isEnabled('nodeDebugging') && !target.parentTarget()) {
var defaultLocations = [{host: 'localhost', port: 9229}];
this._agent.setRemoteLocations(defaultLocations);
this._agent.setDiscoverTargets(true);
}
SDK.targetManager.addEventListener(SDK.TargetManager.Events.MainFrameNavigated, this._mainFrameNavigated, this);
}
/**
* @param {!SDK.Target} target
* @return {?SDK.SubTargetsManager}
*/
static fromTarget(target) {
return /** @type {?SDK.SubTargetsManager} */ (target.model(SDK.SubTargetsManager));
}
/**
* @override
* @return {!Promise}
*/
suspendModel() {
var fulfill;
var promise = new Promise(f => fulfill = f);
this._agent.setAutoAttach(true /* autoAttach */, false /* waitForDebuggerOnStart */, fulfill);
return promise;
}
/**
* @override
* @return {!Promise}
*/
resumeModel() {
var fulfill;
var promise = new Promise(f => fulfill = f);
this._agent.setAutoAttach(true /* autoAttach */, true /* waitForDebuggerOnStart */, fulfill);
return promise;
}
/**
* @override
*/
dispose() {
for (var attachedTargetId of this._attachedTargets.keys())
this._detachedFromTarget(attachedTargetId);
for (var pendingConnectionId of this._pendingTargets.keys())
this._targetDestroyed(pendingConnectionId);
}
/**
* @param {!Protocol.Target.TargetID} targetId
*/
activateTarget(targetId) {
this._agent.activateTarget(targetId);
}
/**
* @param {!Protocol.Target.TargetID} targetId
* @param {function(?SDK.TargetInfo)=} callback
*/
getTargetInfo(targetId, callback) {
/**
* @param {?Protocol.Error} error
* @param {?Protocol.Target.TargetInfo} targetInfo
*/
function innerCallback(error, targetInfo) {
if (error) {
console.error(error);
callback(null);
return;
}
if (targetInfo)
callback(new SDK.TargetInfo(targetInfo));
else
callback(null);
}
this._agent.getTargetInfo(targetId, innerCallback);
}
/**
* @param {string} targetId
* @return {?SDK.Target}
*/
targetForId(targetId) {
return this._attachedTargets.get(targetId) || null;
}
/**
* @param {!SDK.Target} target
* @return {?SDK.TargetInfo}
*/
targetInfo(target) {
return target[SDK.SubTargetsManager._InfoSymbol] || null;
}
/**
* @param {string} type
* @return {number}
*/
_capabilitiesForType(type) {
if (type === 'worker')
return SDK.Target.Capability.JS | SDK.Target.Capability.Log;
if (type === 'service_worker')
return SDK.Target.Capability.Log | SDK.Target.Capability.Network | SDK.Target.Capability.Target;
if (type === 'iframe') {
return 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 (type === 'node')
return SDK.Target.Capability.JS;
return 0;
}
/**
* @param {!SDK.TargetInfo} targetInfo
* @param {boolean} waitingForDebugger
*/
_attachedToTarget(targetInfo, waitingForDebugger) {
var targetName = '';
if (targetInfo.type === 'node') {
targetName = targetInfo.title;
} else if (targetInfo.type !== 'iframe') {
var parsedURL = targetInfo.url.asParsedURL();
targetName = parsedURL ? parsedURL.lastPathComponentWithFragment() : '#' + (++this._lastAnonymousTargetId);
}
var target = SDK.targetManager.createTarget(
targetName, this._capabilitiesForType(targetInfo.type), this._createConnection.bind(this, targetInfo.id),
this.target());
target[SDK.SubTargetsManager._InfoSymbol] = targetInfo;
this._attachedTargets.set(targetInfo.id, target);
// Only pause new worker if debugging SW - we are going through the pause on start checkbox.
var mainIsServiceWorker =
!this.target().parentTarget() && this.target().hasTargetCapability() && !this.target().hasBrowserCapability();
if (mainIsServiceWorker && waitingForDebugger)
target.debuggerAgent().pause();
target.runtimeAgent().runIfWaitingForDebugger();
var pendingTarget = this._pendingTargets.get(targetInfo.id);
if (!pendingTarget) {
this._targetCreated(targetInfo);
pendingTarget = this._pendingTargets.get(targetInfo.id);
}
pendingTarget.notifyAttached(target);
this.dispatchEventToListeners(SDK.SubTargetsManager.Events.PendingTargetAttached, pendingTarget);
}
/**
* @param {string} targetId
* @param {!InspectorBackendClass.Connection.Params} params
* @return {!InspectorBackendClass.Connection}
*/
_createConnection(targetId, params) {
var connection = new SDK.SubTargetConnection(this._agent, targetId, params);
this._connections.set(targetId, connection);
return connection;
}
/**
* @param {string} targetId
*/
_detachedFromTarget(targetId) {
var target = this._attachedTargets.get(targetId);
this._attachedTargets.delete(targetId);
var connection = this._connections.get(targetId);
connection._onDisconnect.call(null, 'target terminated');
this._connections.delete(targetId);
this.dispatchEventToListeners(
SDK.SubTargetsManager.Events.PendingTargetDetached, this._pendingTargets.get(targetId));
}
/**
* @param {string} targetId
* @param {string} message
*/
_receivedMessageFromTarget(targetId, message) {
var connection = this._connections.get(targetId);
if (connection)
connection._onMessage.call(null, message);
}
/**
* @param {!SDK.TargetInfo} targetInfo
*/
_targetCreated(targetInfo) {
var pendingTarget = this._pendingTargets.get(targetInfo.id);
if (pendingTarget)
return;
pendingTarget = new SDK.PendingTarget(targetInfo.id, targetInfo.title, targetInfo.type === 'node', this);
this._pendingTargets.set(targetInfo.id, pendingTarget);
this.dispatchEventToListeners(SDK.SubTargetsManager.Events.PendingTargetAdded, pendingTarget);
}
/**
* @param {string} targetId
*/
_targetDestroyed(targetId) {
var pendingTarget = this._pendingTargets.get(targetId);
if (!pendingTarget)
return;
this._pendingTargets.delete(targetId);
this.dispatchEventToListeners(SDK.SubTargetsManager.Events.PendingTargetRemoved, pendingTarget);
}
/**
* @return {!Array<!SDK.PendingTarget>}
*/
pendingTargets() {
return this._pendingTargets.valuesArray();
}
/**
* @param {!Common.Event} event
*/
_mainFrameNavigated(event) {
if (event.data.target() !== this.target())
return;
var idsToDetach = [];
for (var targetId of this._attachedTargets.keys()) {
var target = this._attachedTargets.get(targetId);
var targetInfo = this.targetInfo(target);
if (targetInfo.type === 'worker')
idsToDetach.push(targetId);
}
idsToDetach.forEach(id => this._detachedFromTarget(id));
}
};
/** @enum {symbol} */
SDK.SubTargetsManager.Events = {
PendingTargetAdded: Symbol('PendingTargetAdded'),
PendingTargetRemoved: Symbol('PendingTargetRemoved'),
PendingTargetAttached: Symbol('PendingTargetAttached'),
PendingTargetDetached: Symbol('PendingTargetDetached'),
};
SDK.SubTargetsManager._InfoSymbol = Symbol('SubTargetInfo');
/**
* @implements {Protocol.TargetDispatcher}
* @unrestricted
*/
SDK.SubTargetsDispatcher = class {
/**
* @param {!SDK.SubTargetsManager} manager
*/
constructor(manager) {
this._manager = manager;
}
/**
* @override
* @param {!Protocol.Target.TargetInfo} targetInfo
*/
targetCreated(targetInfo) {
this._manager._targetCreated(new SDK.TargetInfo(targetInfo));
}
/**
* @override
* @param {string} targetId
*/
targetDestroyed(targetId) {
this._manager._targetDestroyed(targetId);
}
/**
* @override
* @param {!Protocol.Target.TargetInfo} targetInfo
* @param {boolean} waitingForDebugger
*/
attachedToTarget(targetInfo, waitingForDebugger) {
this._manager._attachedToTarget(new SDK.TargetInfo(targetInfo), waitingForDebugger);
}
/**
* @override
* @param {string} targetId
*/
detachedFromTarget(targetId) {
this._manager._detachedFromTarget(targetId);
}
/**
* @override
* @param {string} targetId
* @param {string} message
*/
receivedMessageFromTarget(targetId, message) {
this._manager._receivedMessageFromTarget(targetId, message);
}
};
/**
* @implements {InspectorBackendClass.Connection}
* @unrestricted
*/
SDK.SubTargetConnection = class {
/**
* @param {!Protocol.TargetAgent} agent
* @param {string} targetId
* @param {!InspectorBackendClass.Connection.Params} params
*/
constructor(agent, targetId, params) {
this._agent = agent;
this._targetId = targetId;
this._onMessage = params.onMessage;
this._onDisconnect = params.onDisconnect;
}
/**
* @override
* @param {string} message
*/
sendMessage(message) {
this._agent.sendMessageToTarget(this._targetId, message);
}
/**
* @override
* @return {!Promise}
*/
disconnect() {
throw new Error('Not implemented');
}
};
/**
* @unrestricted
*/
SDK.TargetInfo = class {
/**
* @param {!Protocol.Target.TargetInfo} payload
*/
constructor(payload) {
this.id = payload.targetId;
this.url = payload.url;
this.type = payload.type;
this.canActivate = this.type === 'page' || this.type === 'iframe';
if (this.type === 'node')
this.title = Common.UIString('Node: %s', this.url);
else if (this.type === 'page' || this.type === 'iframe')
this.title = payload.title;
else
this.title = Common.UIString('Worker: %s', this.url);
}
};
/**
* @unrestricted
*/
SDK.PendingTarget = class {
/**
* @param {string} id
* @param {string} title
* @param {boolean} canConnect
* @param {?SDK.SubTargetsManager} manager
*/
constructor(id, title, canConnect, manager) {
this._id = id;
this._title = title;
this._isRemote = canConnect;
this._manager = manager;
/** @type {?Promise} */
this._connectPromise = null;
/** @type {?Function} */
this._attachedCallback = null;
}
/**
* @return {string}
*/
id() {
return this._id;
}
/**
* @return {?SDK.Target}
*/
target() {
if (!this._manager)
return null;
return this._manager.targetForId(this.id());
}
/**
* @return {string}
*/
name() {
return this._title;
}
/**
* @return {!Promise}
*/
attach() {
if (!this._manager)
return Promise.reject();
if (this._connectPromise)
return this._connectPromise;
if (this.target())
return Promise.resolve(this.target());
this._connectPromise = new Promise(resolve => {
this._attachedCallback = resolve;
this._manager._agent.attachToTarget(this.id());
});
return this._connectPromise;
}
/**
* @return {!Promise}
*/
detach() {
if (this._manager)
this._manager._agent.detachFromTarget(this.id());
return Promise.resolve();
}
/**
* @param {!SDK.Target} target
*/
notifyAttached(target) {
if (this._attachedCallback)
this._attachedCallback(target);
this._connectPromise = null;
this._attachedCallback = null;
}
/**
* @return {boolean}
*/
canConnect() {
return this._isRemote;
}
};