| /* | |
| * 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; |