blob: d39397359b3c8e6a840aa65f12e82742986cf79f [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2007 Matt Lilek ([email protected]).
* Copyright (C) 2009 Joseph Pecoraro
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import * as Bindings from '../bindings/bindings.js';
import * as Common from '../common/common.js';
import * as Components from '../components/components.js';
import * as Extensions from '../extensions/extensions.js';
import * as Host from '../host/host.js';
import * as i18n from '../i18n/i18n.js';
import * as Persistence from '../persistence/persistence.js';
import * as Platform from '../platform/platform.js';
import * as ProtocolClient from '../protocol_client/protocol_client.js';
import * as Root from '../root/root.js';
import * as SDK from '../sdk/sdk.js';
import * as Snippets from '../snippets/snippets.js';
import * as ThemeSupport from '../theme_support/theme_support.js';
import * as UI from '../ui/ui.js';
import * as Workspace from '../workspace/workspace.js';
import {ExecutionContextSelector} from './ExecutionContextSelector.js';
/**
* @unrestricted
*/
export class MainImpl {
constructor() {
MainImpl._instanceForTest = this;
Platform.runOnWindowLoad(() => {
this._loaded();
});
/** @type {!Promise<void>} */
this._lateInitDonePromise;
}
/**
* @param {string} label
*/
static time(label) {
if (Host.InspectorFrontendHost.isUnderTest()) {
return;
}
console.time(label);
}
/**
* @param {string} label
*/
static timeEnd(label) {
if (Host.InspectorFrontendHost.isUnderTest()) {
return;
}
console.timeEnd(label);
}
async _loaded() {
console.timeStamp('Main._loaded');
await Root.Runtime.appStarted;
Root.Runtime.Runtime.setPlatform(Host.Platform.platform());
Root.Runtime.Runtime.setL10nCallback(ls);
await this.requestAndRegisterLocaleData();
Host.InspectorFrontendHost.InspectorFrontendHostInstance.getPreferences(this._gotPreferences.bind(this));
}
async requestAndRegisterLocaleData() {
const hostLocale = navigator.language || 'en-US';
i18n.i18n.registerLocale(hostLocale);
const locale = i18n.i18n.registeredLocale;
if (locale) {
const data = await Root.Runtime.loadResourcePromise(`i18n/locales/${locale}.json`);
if (data) {
const localizedStrings = JSON.parse(data);
i18n.i18n.registerLocaleData(locale, localizedStrings);
}
}
}
/**
* @param {!Object<string, string>} prefs
*/
_gotPreferences(prefs) {
console.timeStamp('Main._gotPreferences');
this._createSettings(prefs);
this._createAppUI();
}
/**
* @param {!Object<string, string>} prefs
* Note: this function is called from testSettings in Tests.js.
*/
_createSettings(prefs) {
this._initializeExperiments();
let storagePrefix = '';
if (Host.Platform.isCustomDevtoolsFrontend()) {
storagePrefix = '__custom__';
} else if (
!Root.Runtime.Runtime.queryParam('can_dock') && !!Root.Runtime.Runtime.queryParam('debugFrontend') &&
!Host.InspectorFrontendHost.isUnderTest()) {
storagePrefix = '__bundled__';
}
let localStorage;
if (!Host.InspectorFrontendHost.isUnderTest() && window.localStorage) {
localStorage = new Common.Settings.SettingsStorage(
window.localStorage, undefined, undefined, () => window.localStorage.clear(), storagePrefix);
} else {
localStorage = new Common.Settings.SettingsStorage({}, undefined, undefined, undefined, storagePrefix);
}
const globalStorage = new Common.Settings.SettingsStorage(
prefs, Host.InspectorFrontendHost.InspectorFrontendHostInstance.setPreference,
Host.InspectorFrontendHost.InspectorFrontendHostInstance.removePreference,
Host.InspectorFrontendHost.InspectorFrontendHostInstance.clearPreferences, storagePrefix);
Common.Settings.Settings.instance({forceNew: true, globalStorage, localStorage});
// @ts-ignore layout test global
self.Common.settings = Common.Settings.Settings.instance();
if (!Host.InspectorFrontendHost.isUnderTest()) {
new Common.Settings.VersionController().updateVersion();
}
}
_initializeExperiments() {
Root.Runtime.experiments.register('applyCustomStylesheet', 'Allow custom UI themes');
Root.Runtime.experiments.register('captureNodeCreationStacks', 'Capture node creation stacks');
Root.Runtime.experiments.register('sourcesPrettyPrint', 'Automatically pretty print in the Sources Panel');
Root.Runtime.experiments.register('backgroundServices', 'Background web platform feature events', true);
Root.Runtime.experiments.register(
'backgroundServicesNotifications', 'Background services section for Notifications');
Root.Runtime.experiments.register(
'backgroundServicesPaymentHandler', 'Background services section for Payment Handler');
Root.Runtime.experiments.register(
'backgroundServicesPushMessaging', 'Background services section for Push Messaging');
Root.Runtime.experiments.register('blackboxJSFramesOnTimeline', 'Blackbox JavaScript frames on Timeline', true);
Root.Runtime.experiments.register('cssOverview', 'CSS Overview');
Root.Runtime.experiments.register('emptySourceMapAutoStepping', 'Empty sourcemap auto-stepping');
Root.Runtime.experiments.register('inputEventsOnTimelineOverview', 'Input events on Timeline overview', true);
Root.Runtime.experiments.register('liveHeapProfile', 'Live heap profile', true);
Root.Runtime.experiments.register('protocolMonitor', 'Protocol Monitor');
Root.Runtime.experiments.register('developerResourcesView', 'Show developer resources view');
Root.Runtime.experiments.register(
'recordCoverageWithPerformanceTracing', 'Record coverage while performance tracing');
Root.Runtime.experiments.register('samplingHeapProfilerTimeline', 'Sampling heap profiler timeline', true);
Root.Runtime.experiments.register(
'showOptionToNotTreatGlobalObjectsAsRoots',
'Show option to take heap snapshot where globals are not treated as root');
Root.Runtime.experiments.register('sourceDiff', 'Source diff');
Root.Runtime.experiments.register('sourceOrderViewer', 'Source order viewer');
Root.Runtime.experiments.register('spotlight', 'Spotlight', true);
Root.Runtime.experiments.register('webauthnPane', 'WebAuthn Pane');
Root.Runtime.experiments.register('keyboardShortcutEditor', 'Enable keyboard shortcut editor', true);
// Timeline
Root.Runtime.experiments.register('timelineEventInitiators', 'Timeline: event initiators');
Root.Runtime.experiments.register('timelineFlowEvents', 'Timeline: flow events', true);
Root.Runtime.experiments.register('timelineInvalidationTracking', 'Timeline: invalidation tracking', true);
Root.Runtime.experiments.register('timelineShowAllEvents', 'Timeline: show all events', true);
Root.Runtime.experiments.register(
'timelineV8RuntimeCallStats', 'Timeline: V8 Runtime Call Stats on Timeline', true);
Root.Runtime.experiments.register('timelineWebGL', 'Timeline: WebGL-based flamechart');
Root.Runtime.experiments.register('timelineReplayEvent', 'Timeline: Replay input events', true);
Root.Runtime.experiments.register('wasmDWARFDebugging', 'WebAssembly Debugging: Enable DWARF support');
// Dual-screen
Root.Runtime.experiments.register('dualScreenSupport', 'Emulation: Support dual screen mode');
// CSS Grid
Root.Runtime.experiments.register(
'cssGridFeatures',
'Enable new CSS Grid debugging features (configuration options available in Layout sidebar pane in Elements after restart)');
// CSS Flexbox
Root.Runtime.experiments.register('cssFlexboxFeatures', 'Enable new CSS Flexbox debugging features');
// Advanced Perceptual Contrast Algorithm.
Root.Runtime.experiments.register(
'APCA',
'Enable new Advanced Perceptual Contrast Algorithm (APCA) replacing previous contrast ratio and AA/AAA guidelines');
Root.Runtime.experiments.enableExperimentsByDefault([
'cssGridFeatures',
]);
Root.Runtime.experiments.cleanUpStaleExperiments();
const enabledExperiments = Root.Runtime.Runtime.queryParam('enabledExperiments');
if (enabledExperiments) {
Root.Runtime.experiments.setServerEnabledExperiments(enabledExperiments.split(';'));
}
Root.Runtime.experiments.enableExperimentsTransiently([
'backgroundServices',
'backgroundServicesNotifications',
'backgroundServicesPushMessaging',
'backgroundServicesPaymentHandler',
'webauthnPane',
]);
if (Host.InspectorFrontendHost.isUnderTest()) {
const testParam = Root.Runtime.Runtime.queryParam('test');
if (testParam && testParam.includes('live-line-level-heap-profile.js')) {
Root.Runtime.experiments.enableForTest('liveHeapProfile');
}
}
for (const experiment of Root.Runtime.experiments.enabledExperiments()) {
Host.userMetrics.experimentEnabledAtLaunch(experiment.name);
}
}
/**
* @suppressGlobalPropertiesCheck
*/
async _createAppUI() {
MainImpl.time('Main._createAppUI');
// @ts-ignore layout test global
self.UI.viewManager = UI.ViewManager.ViewManager.instance();
// Request filesystems early, we won't create connections until callback is fired. Things will happen in parallel.
// @ts-ignore layout test global
self.Persistence.isolatedFileSystemManager =
Persistence.IsolatedFileSystemManager.IsolatedFileSystemManager.instance();
const defaultThemeSetting = 'systemPreferred';
const themeSetting = Common.Settings.Settings.instance().createSetting('uiTheme', defaultThemeSetting);
UI.UIUtils.initializeUIUtils(document, themeSetting);
if (themeSetting.get() === defaultThemeSetting) {
const darkThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkThemeMediaQuery.addEventListener('change', () => {
UI.InspectorView.InspectorView.instance().displayReloadRequiredWarning(
ls`The system-preferred color scheme has changed. To apply this change to DevTools, reload.`);
});
}
UI.UIUtils.installComponentRootStyles(/** @type {!Element} */ (document.body));
this._addMainEventListeners(document);
const canDock = !!Root.Runtime.Runtime.queryParam('can_dock');
// @ts-ignore layout test global
self.UI.zoomManager = UI.ZoomManager.ZoomManager.instance(
{forceNew: true, win: window, frontendHost: Host.InspectorFrontendHost.InspectorFrontendHostInstance});
// @ts-ignore layout test global
self.UI.inspectorView = UI.InspectorView.InspectorView.instance();
UI.ContextMenu.ContextMenu.initialize();
UI.ContextMenu.ContextMenu.installHandler(document);
UI.Tooltip.Tooltip.installHandler(document);
// We need to force creation of the FrameManager early to make sure no issues are missed.
SDK.FrameManager.FrameManager.instance();
// We need to force creation of the NetworkLog early to make sure no requests are missed.
SDK.NetworkLog.NetworkLog.instance();
// @ts-ignore layout test global
self.SDK.consoleModel = SDK.ConsoleModel.ConsoleModel.instance();
// @ts-ignore layout test global
self.UI.dockController = UI.DockController.DockController.instance({forceNew: true, canDock});
// @ts-ignore layout test global
self.SDK.multitargetNetworkManager = SDK.NetworkManager.MultitargetNetworkManager.instance({forceNew: true});
// @ts-ignore layout test global
self.SDK.domDebuggerManager = SDK.DOMDebuggerModel.DOMDebuggerManager.instance({forceNew: true});
SDK.SDKModel.TargetManager.instance().addEventListener(
SDK.SDKModel.Events.SuspendStateChanged, this._onSuspendStateChanged.bind(this));
// @ts-ignore layout test global
self.Workspace.fileManager = Workspace.FileManager.FileManager.instance({forceNew: true});
// @ts-ignore layout test global
self.Workspace.workspace = Workspace.Workspace.WorkspaceImpl.instance();
// @ts-ignore layout test global
self.Bindings.networkProjectManager = Bindings.NetworkProject.NetworkProjectManager.instance();
// @ts-ignore layout test global
self.Bindings.resourceMapping = Bindings.ResourceMapping.ResourceMapping.instance({
forceNew: true,
targetManager: SDK.SDKModel.TargetManager.instance(),
workspace: Workspace.Workspace.WorkspaceImpl.instance()
});
new Bindings.PresentationConsoleMessageHelper.PresentationConsoleMessageManager();
// @ts-ignore layout test global
self.Bindings.cssWorkspaceBinding = Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding.instance({
forceNew: true,
targetManager: SDK.SDKModel.TargetManager.instance(),
workspace: Workspace.Workspace.WorkspaceImpl.instance()
});
// @ts-ignore layout test global
self.Bindings.debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
forceNew: true,
targetManager: SDK.SDKModel.TargetManager.instance(),
workspace: Workspace.Workspace.WorkspaceImpl.instance()
});
// @ts-ignore layout test global
self.Bindings.breakpointManager = Bindings.BreakpointManager.BreakpointManager.instance({
forceNew: true,
workspace: Workspace.Workspace.WorkspaceImpl.instance(),
targetManager: SDK.SDKModel.TargetManager.instance(),
debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()
});
// @ts-ignore layout test global
self.Extensions.extensionServer = Extensions.ExtensionServer.ExtensionServer.instance({forceNew: true});
new Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding(
Persistence.IsolatedFileSystemManager.IsolatedFileSystemManager.instance(),
Workspace.Workspace.WorkspaceImpl.instance());
Persistence.IsolatedFileSystemManager.IsolatedFileSystemManager.instance().addPlatformFileSystem(
// @ts-ignore https://github.com/microsoft/TypeScript/issues/41397
'snippet://', new Snippets.ScriptSnippetFileSystem.SnippetFileSystem());
// @ts-ignore layout test global
self.Persistence.persistence = Persistence.Persistence.PersistenceImpl.instance({
forceNew: true,
workspace: Workspace.Workspace.WorkspaceImpl.instance(),
breakpointManager: Bindings.BreakpointManager.BreakpointManager.instance()
});
// @ts-ignore layout test global
self.Persistence.networkPersistenceManager =
Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance(
{forceNew: true, workspace: Workspace.Workspace.WorkspaceImpl.instance()});
new ExecutionContextSelector(SDK.SDKModel.TargetManager.instance(), UI.Context.Context.instance());
// @ts-ignore layout test global
self.Bindings.blackboxManager = Bindings.BlackboxManager.BlackboxManager.instance({
forceNew: true,
debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()
});
new PauseListener();
const actionRegistryInstance = UI.ActionRegistry.ActionRegistry.instance({forceNew: true});
// Required for legacy a11y layout tests
// @ts-ignore layout test global
self.UI.actionRegistry = actionRegistryInstance;
// @ts-ignore layout test global
self.UI.shortcutRegistry =
UI.ShortcutRegistry.ShortcutRegistry.instance({forceNew: true, actionRegistry: actionRegistryInstance});
this._registerMessageSinkListener();
MainImpl.timeEnd('Main._createAppUI');
const appProvider = Root.Runtime.Runtime.instance().extension(Common.AppProvider.AppProvider);
if (!appProvider) {
throw new Error('Unable to boot DevTools, as the appprovider is missing');
}
this._showAppUI(await appProvider.instance());
}
/**
* @param {!Object} appProvider
* @suppressGlobalPropertiesCheck
*/
_showAppUI(appProvider) {
MainImpl.time('Main._showAppUI');
const app = /** @type {!Common.AppProvider.AppProvider} */ (appProvider).createApp();
// It is important to kick controller lifetime after apps are instantiated.
UI.DockController.DockController.instance().initialize();
app.presentUI(document);
const toggleSearchNodeAction = UI.ActionRegistry.ActionRegistry.instance().action('elements.toggle-element-search');
// TODO: we should not access actions from other modules.
if (toggleSearchNodeAction) {
Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.EnterInspectElementMode,
() => {
toggleSearchNodeAction.execute();
}, this);
}
Host.InspectorFrontendHost.InspectorFrontendHostInstance.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.RevealSourceLine, this._revealSourceLine, this);
UI.InspectorView.InspectorView.instance().createToolbars();
Host.InspectorFrontendHost.InspectorFrontendHostInstance.loadCompleted();
const extensions = Root.Runtime.Runtime.instance().extensions(Common.QueryParamHandler.QueryParamHandler);
for (const extension of extensions) {
const value = Root.Runtime.Runtime.queryParam(/** @type {string} */ (extension.descriptor()['name']));
if (value !== null) {
extension.instance().then(handler => {
/** @type {!Common.QueryParamHandler.QueryParamHandler} */ (handler).handleQueryParam(
/** @type {string} */ (value));
});
}
}
// Allow UI cycles to repaint prior to creating connection.
setTimeout(this._initializeTarget.bind(this), 0);
MainImpl.timeEnd('Main._showAppUI');
}
async _initializeTarget() {
MainImpl.time('Main._initializeTarget');
const instances = await Promise.all(
Root.Runtime.Runtime.instance().extensions('early-initialization').map(extension => extension.instance()));
for (const instance of instances) {
await /** @type {!Common.Runnable.Runnable} */ (instance).run();
}
// Used for browser tests.
Host.InspectorFrontendHost.InspectorFrontendHostInstance.readyForTest();
// Asynchronously run the extensions.
setTimeout(this._lateInitialization.bind(this), 100);
MainImpl.timeEnd('Main._initializeTarget');
}
_lateInitialization() {
MainImpl.time('Main._lateInitialization');
Extensions.ExtensionServer.ExtensionServer.instance().initializeExtensions();
const extensions = Root.Runtime.Runtime.instance().extensions('late-initialization');
/** @type {!Array<!Promise<void>>} */
const promises = [];
for (const extension of extensions) {
const setting = extension.descriptor()['setting'];
if (!setting || Common.Settings.Settings.instance().moduleSetting(setting).get()) {
promises.push(
extension.instance().then(instance => (/** @type {!Common.Runnable.Runnable} */ (instance)).run()));
continue;
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
const changeListener = async event => {
if (!event.data) {
return;
}
Common.Settings.Settings.instance().moduleSetting(setting).removeChangeListener(changeListener);
(/** @type {!Common.Runnable.Runnable} */ (await extension.instance())).run();
};
Common.Settings.Settings.instance().moduleSetting(setting).addChangeListener(changeListener);
}
this._lateInitDonePromise = Promise.all(promises).then(() => undefined);
MainImpl.timeEnd('Main._lateInitialization');
}
/**
* @return {?Promise<void>}
*/
lateInitDonePromiseForTest() {
return this._lateInitDonePromise;
}
_registerMessageSinkListener() {
Common.Console.Console.instance().addEventListener(Common.Console.Events.MessageAdded, messageAdded);
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
function messageAdded(event) {
const message = /** @type {!Common.Console.Message} */ (event.data);
if (message.show) {
Common.Console.Console.instance().show();
}
}
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_revealSourceLine(event) {
const url = /** @type {string} */ (event.data['url']);
const lineNumber = /** @type {number} */ (event.data['lineNumber']);
const columnNumber = /** @type {number} */ (event.data['columnNumber']);
const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(url);
if (uiSourceCode) {
Common.Revealer.reveal(uiSourceCode.uiLocation(lineNumber, columnNumber));
return;
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
function listener(event) {
const uiSourceCode = /** @type {!Workspace.UISourceCode.UISourceCode} */ (event.data);
if (uiSourceCode.url() === url) {
Common.Revealer.reveal(uiSourceCode.uiLocation(lineNumber, columnNumber));
Workspace.Workspace.WorkspaceImpl.instance().removeEventListener(
Workspace.Workspace.Events.UISourceCodeAdded, listener);
}
}
Workspace.Workspace.WorkspaceImpl.instance().addEventListener(
Workspace.Workspace.Events.UISourceCodeAdded, listener);
}
/**
* @param {!Event} event
*/
_postDocumentKeyDown(event) {
if (!event.handled) {
UI.ShortcutRegistry.ShortcutRegistry.instance().handleShortcut(/** @type {!KeyboardEvent} */ (event));
}
}
/**
* @param {!Event} event
*/
_redispatchClipboardEvent(event) {
const eventCopy = new CustomEvent('clipboard-' + event.type, {bubbles: true});
// @ts-ignore Used in ElementsTreeOutline
eventCopy['original'] = event;
const document = event.target && /** @type {!HTMLElement} */ (event.target).ownerDocument;
const target = document ? document.deepActiveElement() : null;
if (target) {
target.dispatchEvent(eventCopy);
}
if (eventCopy.handled) {
event.preventDefault();
}
}
/**
* @param {!Event} event
*/
_contextMenuEventFired(event) {
if (event.handled || /** @type {!HTMLElement} */ (event.target).classList.contains('popup-glasspane')) {
event.preventDefault();
}
}
/**
* @param {!Document} document
*/
_addMainEventListeners(document) {
document.addEventListener('keydown', this._postDocumentKeyDown.bind(this), false);
document.addEventListener('beforecopy', this._redispatchClipboardEvent.bind(this), true);
document.addEventListener('copy', this._redispatchClipboardEvent.bind(this), false);
document.addEventListener('cut', this._redispatchClipboardEvent.bind(this), false);
document.addEventListener('paste', this._redispatchClipboardEvent.bind(this), false);
document.addEventListener('contextmenu', this._contextMenuEventFired.bind(this), true);
}
_onSuspendStateChanged() {
const suspended = SDK.SDKModel.TargetManager.instance().allTargetsSuspended();
UI.InspectorView.InspectorView.instance().onSuspendStateChanged(suspended);
}
}
/** @type {?MainImpl} */
MainImpl._instanceForTest = null;
/**
* @implements {UI.ActionDelegate.ActionDelegate}
* @unrestricted
*/
export class ZoomActionDelegate {
/**
* @override
* @param {!UI.Context.Context} context
* @param {string} actionId
* @return {boolean}
*/
handleAction(context, actionId) {
if (Host.InspectorFrontendHost.InspectorFrontendHostInstance.isHostedMode()) {
return false;
}
switch (actionId) {
case 'main.zoom-in':
Host.InspectorFrontendHost.InspectorFrontendHostInstance.zoomIn();
return true;
case 'main.zoom-out':
Host.InspectorFrontendHost.InspectorFrontendHostInstance.zoomOut();
return true;
case 'main.zoom-reset':
Host.InspectorFrontendHost.InspectorFrontendHostInstance.resetZoom();
return true;
}
return false;
}
}
/**
* @implements {UI.ActionDelegate.ActionDelegate}
* @unrestricted
*/
export class SearchActionDelegate {
/**
* @override
* @param {!UI.Context.Context} context
* @param {string} actionId
* @return {boolean}
* @suppressGlobalPropertiesCheck
*/
handleAction(context, actionId) {
let searchableView = UI.SearchableView.SearchableView.fromElement(document.deepActiveElement());
if (!searchableView) {
const currentPanel = UI.InspectorView.InspectorView.instance().currentPanelDeprecated();
if (currentPanel) {
searchableView = currentPanel.searchableView();
}
if (!searchableView) {
return false;
}
}
switch (actionId) {
case 'main.search-in-panel.find':
return searchableView.handleFindShortcut();
case 'main.search-in-panel.cancel':
return searchableView.handleCancelSearchShortcut();
case 'main.search-in-panel.find-next':
return searchableView.handleFindNextShortcut();
case 'main.search-in-panel.find-previous':
return searchableView.handleFindPreviousShortcut();
}
return false;
}
}
/**
* @implements {UI.Toolbar.Provider}
*/
export class MainMenuItem {
constructor() {
this._item = new UI.Toolbar.ToolbarMenuButton(this._handleContextMenu.bind(this), true);
this._item.setTitle(Common.UIString.UIString('Customize and control DevTools'));
}
/**
* @override
* @return {?UI.Toolbar.ToolbarItem}
*/
item() {
return this._item;
}
/**
* @param {!UI.ContextMenu.ContextMenu} contextMenu
*/
_handleContextMenu(contextMenu) {
if (UI.DockController.DockController.instance().canDock()) {
const dockItemElement = document.createElement('div');
dockItemElement.classList.add('flex-centered');
dockItemElement.classList.add('flex-auto');
dockItemElement.tabIndex = -1;
const titleElement = dockItemElement.createChild('span', 'flex-auto');
titleElement.textContent = Common.UIString.UIString('Dock side');
const toggleDockSideShorcuts =
UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutsForAction('main.toggle-dock');
titleElement.title = Common.UIString.UIString(
'Placement of DevTools relative to the page. (%s to restore last position)',
toggleDockSideShorcuts[0].title());
dockItemElement.appendChild(titleElement);
const dockItemToolbar = new UI.Toolbar.Toolbar('', dockItemElement);
if (Host.Platform.isMac() && !ThemeSupport.ThemeSupport.instance().hasTheme()) {
dockItemToolbar.makeBlueOnHover();
}
const undock =
new UI.Toolbar.ToolbarToggle(Common.UIString.UIString('Undock into separate window'), 'largeicon-undock');
const bottom =
new UI.Toolbar.ToolbarToggle(Common.UIString.UIString('Dock to bottom'), 'largeicon-dock-to-bottom');
const right = new UI.Toolbar.ToolbarToggle(Common.UIString.UIString('Dock to right'), 'largeicon-dock-to-right');
const left = new UI.Toolbar.ToolbarToggle(Common.UIString.UIString('Dock to left'), 'largeicon-dock-to-left');
undock.addEventListener(UI.Toolbar.ToolbarButton.Events.MouseDown, event => event.data.consume());
bottom.addEventListener(UI.Toolbar.ToolbarButton.Events.MouseDown, event => event.data.consume());
right.addEventListener(UI.Toolbar.ToolbarButton.Events.MouseDown, event => event.data.consume());
left.addEventListener(UI.Toolbar.ToolbarButton.Events.MouseDown, event => event.data.consume());
undock.addEventListener(
UI.Toolbar.ToolbarButton.Events.Click, setDockSide.bind(null, UI.DockController.State.Undocked));
bottom.addEventListener(
UI.Toolbar.ToolbarButton.Events.Click, setDockSide.bind(null, UI.DockController.State.DockedToBottom));
right.addEventListener(
UI.Toolbar.ToolbarButton.Events.Click, setDockSide.bind(null, UI.DockController.State.DockedToRight));
left.addEventListener(
UI.Toolbar.ToolbarButton.Events.Click, setDockSide.bind(null, UI.DockController.State.DockedToLeft));
undock.setToggled(UI.DockController.DockController.instance().dockSide() === UI.DockController.State.Undocked);
bottom.setToggled(
UI.DockController.DockController.instance().dockSide() === UI.DockController.State.DockedToBottom);
right.setToggled(
UI.DockController.DockController.instance().dockSide() === UI.DockController.State.DockedToRight);
left.setToggled(UI.DockController.DockController.instance().dockSide() === UI.DockController.State.DockedToLeft);
dockItemToolbar.appendToolbarItem(undock);
dockItemToolbar.appendToolbarItem(left);
dockItemToolbar.appendToolbarItem(bottom);
dockItemToolbar.appendToolbarItem(right);
dockItemElement.addEventListener('keydown', event => {
let dir = 0;
if (event.key === 'ArrowLeft') {
dir = -1;
} else if (event.key === 'ArrowRight') {
dir = 1;
} else {
return;
}
const buttons = [undock, left, bottom, right];
let index = buttons.findIndex(button => button.element.hasFocus());
index = Platform.NumberUtilities.clamp(index + dir, 0, buttons.length - 1);
buttons[index].element.focus();
event.consume(true);
});
contextMenu.headerSection().appendCustomItem(dockItemElement);
}
const button = /** @type {!HTMLButtonElement} */ (this._item.element);
/**
* @param {string} side
*/
function setDockSide(side) {
UI.DockController.DockController.instance().once(UI.DockController.Events.AfterDockSideChanged).then(() => {
button.focus();
});
UI.DockController.DockController.instance().setDockSide(side);
contextMenu.discard();
}
if (UI.DockController.DockController.instance().dockSide() === UI.DockController.State.Undocked) {
const mainTarget = SDK.SDKModel.TargetManager.instance().mainTarget();
if (mainTarget && mainTarget.type() === SDK.SDKModel.Type.Frame) {
contextMenu.defaultSection().appendAction(
'inspector_main.focus-debuggee', Common.UIString.UIString('Focus debuggee'));
}
}
contextMenu.defaultSection().appendAction(
'main.toggle-drawer',
UI.InspectorView.InspectorView.instance().drawerVisible() ? Common.UIString.UIString('Hide console drawer') :
Common.UIString.UIString('Show console drawer'));
contextMenu.appendItemsAtLocation('mainMenu');
const moreTools = contextMenu.defaultSection().appendSubMenuItem(Common.UIString.UIString('More tools'));
const extensions = Root.Runtime.Runtime.instance().extensions('view', undefined, true);
for (const extension of extensions) {
const descriptor = extension.descriptor();
if (descriptor['id'] === 'issues-pane') {
moreTools.defaultSection().appendItem(extension.title(), () => {
Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.HamburgerMenu);
UI.ViewManager.ViewManager.instance().showView('issues-pane', /* userGesture */ true);
});
continue;
}
if (descriptor['persistence'] !== 'closeable') {
continue;
}
if (descriptor['location'] !== 'drawer-view' && descriptor['location'] !== 'panel') {
continue;
}
moreTools.defaultSection().appendItem(extension.title(), omitFocus => {
UI.ViewManager.ViewManager.instance().showView(descriptor['id'], true, /** @type {boolean} */ (omitFocus));
});
}
const helpSubMenu = contextMenu.footerSection().appendSubMenuItem(Common.UIString.UIString('Help'));
helpSubMenu.appendItemsAtLocation('mainMenuHelp');
}
}
/**
* @implements {UI.Toolbar.Provider}
*/
export class SettingsButtonProvider {
constructor() {
const settingsActionId = 'settings.show';
this._settingsButton =
UI.Toolbar.Toolbar.createActionButtonForId(settingsActionId, {showLabel: false, userActionCode: undefined});
}
/**
* @override
* @return {?UI.Toolbar.ToolbarItem}
*/
item() {
return this._settingsButton;
}
}
/**
* @unrestricted
*/
export class PauseListener {
constructor() {
SDK.SDKModel.TargetManager.instance().addModelListener(
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_debuggerPaused(event) {
SDK.SDKModel.TargetManager.instance().removeModelListener(
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
const debuggerModel = /** @type {!SDK.DebuggerModel.DebuggerModel} */ (event.data);
const debuggerPausedDetails = debuggerModel.debuggerPausedDetails();
UI.Context.Context.instance().setFlavor(SDK.SDKModel.Target, debuggerModel.target());
Common.Revealer.reveal(debuggerPausedDetails);
}
}
/**
* @param {string} method
* @param {?Object} params
* @return {!Promise<?Array<*>>}
*/
export function sendOverProtocol(method, params) {
return new Promise((resolve, reject) => {
const sendRawMessage = ProtocolClient.InspectorBackend.test.sendRawMessage;
if (!sendRawMessage) {
return reject('Unable to send message to test client');
}
sendRawMessage(method, params, (err, ...results) => {
if (err) {
return reject(err);
}
return resolve(results);
});
});
}
/**
* @implements {UI.ActionDelegate.ActionDelegate}
* @unrestricted
*/
export class ReloadActionDelegate {
/**
* @override
* @param {!UI.Context.Context} context
* @param {string} actionId
* @return {boolean}
*/
handleAction(context, actionId) {
switch (actionId) {
case 'main.debug-reload':
Components.Reload.reload();
return true;
}
return false;
}
}
new MainImpl();