blob: c399a4b78f092b7889b35dd2e45cfbac82481da6 [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
*/
export default class Target extends Protocol.TargetBase {
/**
* @param {!SDK.TargetManager} targetManager
* @param {string} id
* @param {string} name
* @param {!Type} type
* @param {?SDK.Target} parentTarget
* @param {string} sessionId
* @param {boolean} suspended
* @param {?Protocol.Connection} connection
*/
constructor(targetManager, id, name, type, parentTarget, sessionId, suspended, connection) {
const needsNodeJSPatching = type === Type.Node;
super(needsNodeJSPatching, parentTarget, sessionId, connection);
this._targetManager = targetManager;
this._name = name;
this._inspectedURL = '';
this._inspectedURLName = '';
this._capabilitiesMask = 0;
switch (type) {
case Type.Frame:
this._capabilitiesMask = Capability.Browser | Capability.Storage | Capability.DOM | Capability.JS |
Capability.Log | Capability.Network | Capability.Target | Capability.Tracing | Capability.Emulation |
Capability.Input | Capability.Inspector;
if (!parentTarget) {
// This matches backend exposing certain capabilities only for the main frame.
this._capabilitiesMask |=
Capability.DeviceEmulation | Capability.ScreenCapture | Capability.Security | Capability.ServiceWorker;
// TODO(dgozman): we report service workers for the whole frame tree on the main frame,
// while we should be able to only cover the subtree corresponding to the target.
}
break;
case Type.ServiceWorker:
this._capabilitiesMask =
Capability.JS | Capability.Log | Capability.Network | Capability.Target | Capability.Inspector;
if (!parentTarget) {
this._capabilitiesMask |= Capability.Browser;
}
break;
case Type.Worker:
this._capabilitiesMask = Capability.JS | Capability.Log | Capability.Network | Capability.Target;
break;
case Type.Node:
this._capabilitiesMask = Capability.JS;
break;
case Type.Browser:
this._capabilitiesMask = Capability.Target;
break;
}
this._type = type;
this._parentTarget = parentTarget;
this._id = id;
this._modelByConstructor = new Map();
this._isSuspended = suspended;
}
createModels(required) {
this._creatingModels = true;
// TODO(dgozman): fix this in bindings layer.
this.model(SDK.ResourceTreeModel);
const registered = Array.from(SDK.SDKModel.registeredModels.keys());
for (const modelClass of registered) {
const info = SDK.SDKModel.registeredModels.get(modelClass);
if (info.autostart || required.has(modelClass)) {
this.model(modelClass);
}
}
this._creatingModels = false;
}
/**
* @return {string}
*/
id() {
return this._id;
}
/**
* @return {string}
*/
name() {
return this._name || this._inspectedURLName;
}
/**
* @return {!Type}
*/
type() {
return this._type;
}
/**
* @override
*/
markAsNodeJSForTest() {
super.markAsNodeJSForTest();
this._type = Type.Node;
}
/**
* @return {!SDK.TargetManager}
*/
targetManager() {
return this._targetManager;
}
/**
* @param {number} capabilitiesMask
* @return {boolean}
*/
hasAllCapabilities(capabilitiesMask) {
// TODO(dgozman): get rid of this method, once we never observe targets with
// capability mask.
return (this._capabilitiesMask & capabilitiesMask) === capabilitiesMask;
}
/**
* @param {string} label
* @return {string}
*/
decorateLabel(label) {
return (this._type === Type.Worker || this._type === Type.ServiceWorker) ? '\u2699 ' + label : label;
}
/**
* @return {?SDK.Target}
*/
parentTarget() {
return this._parentTarget;
}
/**
* @override
* @param {string} reason
*/
dispose(reason) {
super.dispose(reason);
this._targetManager.removeTarget(this);
for (const model of this._modelByConstructor.valuesArray()) {
model.dispose();
}
}
/**
* @param {function(new:T, !SDK.Target)} modelClass
* @return {?T}
* @template T
*/
model(modelClass) {
if (!this._modelByConstructor.get(modelClass)) {
const info = SDK.SDKModel.registeredModels.get(modelClass);
if (info === undefined) {
throw 'Model class is not registered @' + new Error().stack;
}
if ((this._capabilitiesMask & info.capabilities) === info.capabilities) {
const model = new modelClass(this);
this._modelByConstructor.set(modelClass, model);
if (!this._creatingModels) {
this._targetManager.modelAdded(this, modelClass, model);
}
}
}
return this._modelByConstructor.get(modelClass) || null;
}
/**
* @return {!Map<function(new:SDK.SDKModel, !SDK.Target), !SDK.SDKModel>}
*/
models() {
return this._modelByConstructor;
}
/**
* @return {string}
*/
inspectedURL() {
return this._inspectedURL;
}
/**
* @param {string} inspectedURL
*/
setInspectedURL(inspectedURL) {
this._inspectedURL = inspectedURL;
const parsedURL = Common.ParsedURL.fromString(inspectedURL);
this._inspectedURLName = parsedURL ? parsedURL.lastPathComponentWithFragment() : '#' + this._id;
if (!this.parentTarget()) {
Host.InspectorFrontendHost.inspectedURLChanged(inspectedURL || '');
}
this._targetManager.dispatchEventToListeners(SDK.TargetManager.Events.InspectedURLChanged, this);
if (!this._name) {
this._targetManager.dispatchEventToListeners(SDK.TargetManager.Events.NameChanged, this);
}
}
/**
* @param {string=} reason - optionally provide a reason, so models can respond accordingly
* @return {!Promise}
*/
async suspend(reason) {
if (this._isSuspended) {
return Promise.resolve();
}
this._isSuspended = true;
await Promise.all(Array.from(this.models().values(), m => m.preSuspendModel(reason)));
await Promise.all(Array.from(this.models().values(), m => m.suspendModel(reason)));
}
/**
* @return {!Promise}
*/
async resume() {
if (!this._isSuspended) {
return Promise.resolve();
}
this._isSuspended = false;
await Promise.all(Array.from(this.models().values(), m => m.resumeModel()));
await Promise.all(Array.from(this.models().values(), m => m.postResumeModel()));
}
/**
* @return {boolean}
*/
suspended() {
return this._isSuspended;
}
}
/**
* @enum {number}
*/
export const Capability = {
Browser: 1 << 0,
DOM: 1 << 1,
JS: 1 << 2,
Log: 1 << 3,
Network: 1 << 4,
Target: 1 << 5,
ScreenCapture: 1 << 6,
Tracing: 1 << 7,
Emulation: 1 << 8,
Security: 1 << 9,
Input: 1 << 10,
Inspector: 1 << 11,
DeviceEmulation: 1 << 12,
Storage: 1 << 13,
ServiceWorker: 1 << 14,
None: 0,
};
/**
* @enum {string}
*/
export const Type = {
Frame: 'frame',
ServiceWorker: 'service-worker',
Worker: 'worker',
Node: 'node',
Browser: 'browser',
};
/* Legacy exported object */
self.SDK = self.SDK || {};
/* Legacy exported object */
SDK = SDK || {};
/** @constructor */
SDK.Target = Target;
/**
* @enum {number}
*/
SDK.Target.Capability = Capability;
/**
* @enum {string}
*/
SDK.Target.Type = Type;