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