| /* | |
| * 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. | |
| */ | |
| export default class TargetManager extends Common.Object { | |
| constructor() { | |
| super(); | |
| /** @type {!Array.<!SDK.Target>} */ | |
| this._targets = []; | |
| /** @type {!Array.<!Observer>} */ | |
| this._observers = []; | |
| /** @type {!Platform.Multimap<symbol, !{modelClass: !Function, thisObject: (!Object|undefined), listener: function(!Common.Event)}>} */ | |
| this._modelListeners = new Platform.Multimap(); | |
| /** @type {!Platform.Multimap<function(new:SDK.SDKModel, !SDK.Target), !SDKModelObserver>} */ | |
| this._modelObservers = new Platform.Multimap(); | |
| this._isSuspended = false; | |
| } | |
| /** | |
| * @param {string=} reason - optionally provide a reason, so targets can respond accordingly | |
| * @return {!Promise} | |
| */ | |
| suspendAllTargets(reason) { | |
| if (this._isSuspended) { | |
| return Promise.resolve(); | |
| } | |
| this._isSuspended = true; | |
| this.dispatchEventToListeners(Events.SuspendStateChanged); | |
| return Promise.all(this._targets.map(target => target.suspend(reason))); | |
| } | |
| /** | |
| * @return {!Promise} | |
| */ | |
| resumeAllTargets() { | |
| if (!this._isSuspended) { | |
| return Promise.resolve(); | |
| } | |
| this._isSuspended = false; | |
| this.dispatchEventToListeners(Events.SuspendStateChanged); | |
| return Promise.all(this._targets.map(target => target.resume())); | |
| } | |
| /** | |
| * @return {boolean} | |
| */ | |
| allTargetsSuspended() { | |
| return this._isSuspended; | |
| } | |
| /** | |
| * @param {function(new:T,!SDK.Target)} modelClass | |
| * @return {!Array<!T>} | |
| * @template T | |
| */ | |
| models(modelClass) { | |
| const result = []; | |
| for (let i = 0; i < this._targets.length; ++i) { | |
| const model = this._targets[i].model(modelClass); | |
| if (model) { | |
| result.push(model); | |
| } | |
| } | |
| return result; | |
| } | |
| /** | |
| * @return {string} | |
| */ | |
| inspectedURL() { | |
| return this._targets[0] ? this._targets[0].inspectedURL() : ''; | |
| } | |
| /** | |
| * @param {function(new:T,!SDK.Target)} modelClass | |
| * @param {!SDKModelObserver<T>} observer | |
| * @template T | |
| */ | |
| observeModels(modelClass, observer) { | |
| const models = this.models(modelClass); | |
| this._modelObservers.set(modelClass, observer); | |
| for (const model of models) { | |
| observer.modelAdded(model); | |
| } | |
| } | |
| /** | |
| * @param {function(new:T,!SDK.Target)} modelClass | |
| * @param {!SDKModelObserver<T>} observer | |
| * @template T | |
| */ | |
| unobserveModels(modelClass, observer) { | |
| this._modelObservers.delete(modelClass, observer); | |
| } | |
| /** | |
| * @param {!SDK.Target} target | |
| * @param {function(new:SDK.SDKModel,!SDK.Target)} modelClass | |
| * @param {!SDK.SDKModel} model | |
| */ | |
| modelAdded(target, modelClass, model) { | |
| for (const observer of this._modelObservers.get(modelClass).valuesArray()) { | |
| observer.modelAdded(model); | |
| } | |
| } | |
| /** | |
| * @param {!SDK.Target} target | |
| * @param {function(new:SDK.SDKModel,!SDK.Target)} modelClass | |
| * @param {!SDK.SDKModel} model | |
| */ | |
| _modelRemoved(target, modelClass, model) { | |
| for (const observer of this._modelObservers.get(modelClass).valuesArray()) { | |
| observer.modelRemoved(model); | |
| } | |
| } | |
| /** | |
| * @param {!Function} modelClass | |
| * @param {symbol} eventType | |
| * @param {function(!Common.Event)} listener | |
| * @param {!Object=} thisObject | |
| */ | |
| addModelListener(modelClass, eventType, listener, thisObject) { | |
| for (let i = 0; i < this._targets.length; ++i) { | |
| const model = this._targets[i].model(modelClass); | |
| if (model) { | |
| model.addEventListener(eventType, listener, thisObject); | |
| } | |
| } | |
| this._modelListeners.set(eventType, {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 (let i = 0; i < this._targets.length; ++i) { | |
| const model = this._targets[i].model(modelClass); | |
| if (model) { | |
| model.removeEventListener(eventType, listener, thisObject); | |
| } | |
| } | |
| for (const info of this._modelListeners.get(eventType)) { | |
| if (info.modelClass === modelClass && info.listener === listener && info.thisObject === thisObject) { | |
| this._modelListeners.delete(eventType, info); | |
| } | |
| } | |
| } | |
| /** | |
| * @param {!Observer} targetObserver | |
| */ | |
| observeTargets(targetObserver) { | |
| if (this._observers.indexOf(targetObserver) !== -1) { | |
| throw new Error('Observer can only be registered once'); | |
| } | |
| for (const target of this._targets) { | |
| targetObserver.targetAdded(target); | |
| } | |
| this._observers.push(targetObserver); | |
| } | |
| /** | |
| * @param {!Observer} targetObserver | |
| */ | |
| unobserveTargets(targetObserver) { | |
| this._observers.remove(targetObserver); | |
| } | |
| /** | |
| * @param {string} id | |
| * @param {string} name | |
| * @param {!SDK.Target.Type} type | |
| * @param {?SDK.Target} parentTarget | |
| * @param {string=} sessionId | |
| * @param {boolean=} waitForDebuggerInPage | |
| * @param {!Protocol.Connection=} connection | |
| * @return {!SDK.Target} | |
| */ | |
| createTarget(id, name, type, parentTarget, sessionId, waitForDebuggerInPage, connection) { | |
| const target = | |
| new SDK.Target(this, id, name, type, parentTarget, sessionId || '', this._isSuspended, connection || null); | |
| if (waitForDebuggerInPage) { | |
| target.pageAgent().waitForDebugger(); | |
| } | |
| target.createModels(new Set(this._modelObservers.keysArray())); | |
| this._targets.push(target); | |
| const copy = this._observers.slice(0); | |
| for (const observer of copy) { | |
| observer.targetAdded(target); | |
| } | |
| for (const modelClass of target.models().keys()) { | |
| this.modelAdded(target, modelClass, target.models().get(modelClass)); | |
| } | |
| for (const key of this._modelListeners.keysArray()) { | |
| for (const info of this._modelListeners.get(key)) { | |
| const model = target.model(info.modelClass); | |
| if (model) { | |
| model.addEventListener(key, info.listener, info.thisObject); | |
| } | |
| } | |
| } | |
| return target; | |
| } | |
| /** | |
| * @param {!SDK.Target} target | |
| */ | |
| removeTarget(target) { | |
| if (!this._targets.includes(target)) { | |
| return; | |
| } | |
| this._targets.remove(target); | |
| for (const modelClass of target.models().keys()) { | |
| this._modelRemoved(target, modelClass, target.models().get(modelClass)); | |
| } | |
| const copy = this._observers.slice(0); | |
| for (const observer of copy) { | |
| observer.targetRemoved(target); | |
| } | |
| for (const key of this._modelListeners.keysArray()) { | |
| for (const info of this._modelListeners.get(key)) { | |
| const model = target.model(info.modelClass); | |
| if (model) { | |
| model.removeEventListener(key, info.listener, info.thisObject); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * @return {!Array.<!SDK.Target>} | |
| */ | |
| targets() { | |
| return this._targets.slice(); | |
| } | |
| /** | |
| * @param {string} id | |
| * @return {?SDK.Target} | |
| */ | |
| targetById(id) { | |
| // TODO(dgozman): add a map id -> target. | |
| for (let 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; | |
| } | |
| } | |
| /** @enum {symbol} */ | |
| export const Events = { | |
| AvailableTargetsChanged: Symbol('AvailableTargetsChanged'), | |
| InspectedURLChanged: Symbol('InspectedURLChanged'), | |
| NameChanged: Symbol('NameChanged'), | |
| SuspendStateChanged: Symbol('SuspendStateChanged') | |
| }; | |
| /** | |
| * @interface | |
| */ | |
| export class Observer { | |
| /** | |
| * @param {!SDK.Target} target | |
| */ | |
| targetAdded(target) { | |
| } | |
| /** | |
| * @param {!SDK.Target} target | |
| */ | |
| targetRemoved(target) { | |
| } | |
| } | |
| /** | |
| * @interface | |
| * @template T | |
| */ | |
| export class SDKModelObserver { | |
| /** | |
| * @param {!T} model | |
| */ | |
| modelAdded(model) { | |
| } | |
| /** | |
| * @param {!T} model | |
| */ | |
| modelRemoved(model) { | |
| } | |
| } | |
| /* Legacy exported object */ | |
| self.SDK = self.SDK || {}; | |
| /* Legacy exported object */ | |
| SDK = SDK || {}; | |
| /** @constructor */ | |
| SDK.TargetManager = TargetManager; | |
| /** @enum {symbol} */ | |
| SDK.TargetManager.Events = Events; | |
| /** @interface */ | |
| SDK.TargetManager.Observer = Observer; | |
| /** @interface */ | |
| SDK.SDKModelObserver = SDKModelObserver; | |
| /** | |
| * @type {!TargetManager} | |
| */ | |
| SDK.targetManager = new TargetManager(); |