| // 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. |
| /** |
| * @implements {SDK.SDKModelObserver<!SDK.RuntimeModel>} |
| * @unrestricted |
| */ |
| export default class ExecutionContextSelector { |
| /** |
| * @param {!SDK.TargetManager} targetManager |
| * @param {!UI.Context} context |
| */ |
| constructor(targetManager, context) { |
| context.addFlavorChangeListener(SDK.ExecutionContext, this._executionContextChanged, this); |
| context.addFlavorChangeListener(SDK.Target, this._targetChanged, this); |
| |
| targetManager.addModelListener( |
| SDK.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this); |
| targetManager.addModelListener( |
| SDK.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this); |
| targetManager.addModelListener( |
| SDK.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextOrderChanged, this._onExecutionContextOrderChanged, |
| this); |
| this._targetManager = targetManager; |
| this._context = context; |
| targetManager.observeModels(SDK.RuntimeModel, this); |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.RuntimeModel} runtimeModel |
| */ |
| modelAdded(runtimeModel) { |
| // Defer selecting default target since we need all clients to get their |
| // targetAdded notifications first. |
| setImmediate(deferred.bind(this)); |
| |
| /** |
| * @this {Main.ExecutionContextSelector} |
| */ |
| function deferred() { |
| // We always want the second context for the service worker targets. |
| if (!this._context.flavor(SDK.Target)) { |
| this._context.setFlavor(SDK.Target, runtimeModel.target()); |
| } |
| } |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.RuntimeModel} runtimeModel |
| */ |
| modelRemoved(runtimeModel) { |
| const currentExecutionContext = this._context.flavor(SDK.ExecutionContext); |
| if (currentExecutionContext && currentExecutionContext.runtimeModel === runtimeModel) { |
| this._currentExecutionContextGone(); |
| } |
| |
| const models = this._targetManager.models(SDK.RuntimeModel); |
| if (this._context.flavor(SDK.Target) === runtimeModel.target() && models.length) { |
| this._context.setFlavor(SDK.Target, models[0].target()); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _executionContextChanged(event) { |
| const newContext = /** @type {?SDK.ExecutionContext} */ (event.data); |
| if (newContext) { |
| this._context.setFlavor(SDK.Target, newContext.target()); |
| if (!this._ignoreContextChanged) { |
| this._lastSelectedContextId = this._contextPersistentId(newContext); |
| } |
| } |
| } |
| |
| /** |
| * @param {!SDK.ExecutionContext} executionContext |
| * @return {string} |
| */ |
| _contextPersistentId(executionContext) { |
| return executionContext.isDefault ? executionContext.target().name() + ':' + executionContext.frameId : ''; |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _targetChanged(event) { |
| const newTarget = /** @type {?SDK.Target} */ (event.data); |
| const currentContext = this._context.flavor(SDK.ExecutionContext); |
| |
| if (!newTarget || (currentContext && currentContext.target() === newTarget)) { |
| return; |
| } |
| |
| const runtimeModel = newTarget.model(SDK.RuntimeModel); |
| const executionContexts = runtimeModel ? runtimeModel.executionContexts() : []; |
| if (!executionContexts.length) { |
| return; |
| } |
| |
| let newContext = null; |
| for (let i = 0; i < executionContexts.length && !newContext; ++i) { |
| if (this._shouldSwitchToContext(executionContexts[i])) { |
| newContext = executionContexts[i]; |
| } |
| } |
| for (let i = 0; i < executionContexts.length && !newContext; ++i) { |
| if (this._isDefaultContext(executionContexts[i])) { |
| newContext = executionContexts[i]; |
| } |
| } |
| this._ignoreContextChanged = true; |
| this._context.setFlavor(SDK.ExecutionContext, newContext || executionContexts[0]); |
| this._ignoreContextChanged = false; |
| } |
| |
| /** |
| * @param {!SDK.ExecutionContext} executionContext |
| * @return {boolean} |
| */ |
| _shouldSwitchToContext(executionContext) { |
| if (this._lastSelectedContextId && this._lastSelectedContextId === this._contextPersistentId(executionContext)) { |
| return true; |
| } |
| if (!this._lastSelectedContextId && this._isDefaultContext(executionContext)) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @param {!SDK.ExecutionContext} executionContext |
| * @return {boolean} |
| */ |
| _isDefaultContext(executionContext) { |
| if (!executionContext.isDefault || !executionContext.frameId) { |
| return false; |
| } |
| if (executionContext.target().parentTarget()) { |
| return false; |
| } |
| const resourceTreeModel = executionContext.target().model(SDK.ResourceTreeModel); |
| const frame = resourceTreeModel && resourceTreeModel.frameForId(executionContext.frameId); |
| if (frame && frame.isTopFrame()) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onExecutionContextCreated(event) { |
| this._switchContextIfNecessary(/** @type {!SDK.ExecutionContext} */ (event.data)); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onExecutionContextDestroyed(event) { |
| const executionContext = /** @type {!SDK.ExecutionContext}*/ (event.data); |
| if (this._context.flavor(SDK.ExecutionContext) === executionContext) { |
| this._currentExecutionContextGone(); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onExecutionContextOrderChanged(event) { |
| const runtimeModel = /** @type {!SDK.RuntimeModel} */ (event.data); |
| const executionContexts = runtimeModel.executionContexts(); |
| for (let i = 0; i < executionContexts.length; i++) { |
| if (this._switchContextIfNecessary(executionContexts[i])) { |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @param {!SDK.ExecutionContext} executionContext |
| * @return {boolean} |
| */ |
| _switchContextIfNecessary(executionContext) { |
| if (!this._context.flavor(SDK.ExecutionContext) || this._shouldSwitchToContext(executionContext)) { |
| this._ignoreContextChanged = true; |
| this._context.setFlavor(SDK.ExecutionContext, executionContext); |
| this._ignoreContextChanged = false; |
| return true; |
| } |
| return false; |
| } |
| |
| _currentExecutionContextGone() { |
| const runtimeModels = this._targetManager.models(SDK.RuntimeModel); |
| let newContext = null; |
| for (let i = 0; i < runtimeModels.length && !newContext; ++i) { |
| const executionContexts = runtimeModels[i].executionContexts(); |
| for (const executionContext of executionContexts) { |
| if (this._isDefaultContext(executionContext)) { |
| newContext = executionContext; |
| break; |
| } |
| } |
| } |
| if (!newContext) { |
| for (let i = 0; i < runtimeModels.length && !newContext; ++i) { |
| const executionContexts = runtimeModels[i].executionContexts(); |
| if (executionContexts.length) { |
| newContext = executionContexts[0]; |
| break; |
| } |
| } |
| } |
| this._ignoreContextChanged = true; |
| this._context.setFlavor(SDK.ExecutionContext, newContext); |
| this._ignoreContextChanged = false; |
| } |
| } |
| |
| /* Legacy exported object */ |
| self.Main = self.Main || {}; |
| |
| /* Legacy exported object */ |
| Main = Main || {}; |
| |
| /** |
| * @constructor |
| */ |
| Main.ExecutionContextSelector = ExecutionContextSelector; |