chrome-devtools-frontend
Version:
Chrome DevTools UI
208 lines (174 loc) • 8.41 kB
text/typescript
// 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.
import type * as Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
import {DeviceModeWrapper} from './DeviceModeWrapper.js';
import {Events, InspectedPagePlaceholder, type Bounds} from './InspectedPagePlaceholder.js';
let appInstance: AdvancedApp;
export class AdvancedApp implements Common.App.App {
private rootSplitWidget!: UI.SplitWidget.SplitWidget;
private deviceModeView!: DeviceModeWrapper;
private inspectedPagePlaceholder!: InspectedPagePlaceholder;
private toolboxWindow?: Window|null;
private toolboxRootView?: UI.RootView.RootView;
private changingDockSide?: boolean;
constructor() {
UI.DockController.DockController.instance().addEventListener(
UI.DockController.Events.BeforeDockSideChanged, this.openToolboxWindow, this);
}
/**
* Note: it's used by toolbox.ts without real type checks.
*/
static instance(): AdvancedApp {
if (!appInstance) {
appInstance = new AdvancedApp();
}
return appInstance;
}
presentUI(document: Document): void {
const rootView = new UI.RootView.RootView();
this.rootSplitWidget = new UI.SplitWidget.SplitWidget(false, true, 'InspectorView.splitViewState', 555, 300, true);
this.rootSplitWidget.show(rootView.element);
this.rootSplitWidget.setSidebarWidget(UI.InspectorView.InspectorView.instance());
this.rootSplitWidget.setDefaultFocusedChild(UI.InspectorView.InspectorView.instance());
UI.InspectorView.InspectorView.instance().setOwnerSplit(this.rootSplitWidget);
this.inspectedPagePlaceholder = InspectedPagePlaceholder.instance();
this.inspectedPagePlaceholder.addEventListener(Events.Update, this.onSetInspectedPageBounds.bind(this), this);
this.deviceModeView =
DeviceModeWrapper.instance({inspectedPagePlaceholder: this.inspectedPagePlaceholder, forceNew: false});
UI.DockController.DockController.instance().addEventListener(
UI.DockController.Events.BeforeDockSideChanged, this.onBeforeDockSideChange, this);
UI.DockController.DockController.instance().addEventListener(
UI.DockController.Events.DockSideChanged, this.onDockSideChange, this);
UI.DockController.DockController.instance().addEventListener(
UI.DockController.Events.AfterDockSideChanged, this.onAfterDockSideChange, this);
this.onDockSideChange();
console.timeStamp('AdvancedApp.attachToBody');
rootView.attachToDocument(document);
rootView.focus();
this.inspectedPagePlaceholder.update();
}
private openToolboxWindow(event: Common.EventTarget.EventTargetEvent<UI.DockController.ChangeEvent>): void {
if (event.data.to !== UI.DockController.DockState.UNDOCKED) {
return;
}
if (this.toolboxWindow) {
return;
}
const url = window.location.href.replace('devtools_app.html', 'device_mode_emulation_frame.html');
this.toolboxWindow = window.open(url, undefined);
}
deviceModeEmulationFrameLoaded(toolboxDocument: Document): void {
ThemeSupport.ThemeSupport.instance().applyTheme(toolboxDocument);
ThemeSupport.ThemeSupport.instance().addEventListener(ThemeSupport.ThemeChangeEvent.eventName, () => {
ThemeSupport.ThemeSupport.instance().applyTheme(toolboxDocument);
});
UI.UIUtils.initializeUIUtils(toolboxDocument);
UI.UIUtils.installComponentRootStyles((toolboxDocument.body as Element));
UI.ContextMenu.ContextMenu.installHandler(toolboxDocument);
this.toolboxRootView = new UI.RootView.RootView();
this.toolboxRootView.attachToDocument(toolboxDocument);
this.updateDeviceModeView();
}
private updateDeviceModeView(): void {
if (this.isDocked()) {
this.rootSplitWidget.setMainWidget(this.deviceModeView);
} else if (this.toolboxRootView) {
this.deviceModeView.show(this.toolboxRootView.element);
}
}
private onBeforeDockSideChange(event: Common.EventTarget.EventTargetEvent<UI.DockController.ChangeEvent>): void {
if (event.data.to === UI.DockController.DockState.UNDOCKED && this.toolboxRootView) {
// Hide inspectorView and force layout to mimic the undocked state.
this.rootSplitWidget.hideSidebar();
this.inspectedPagePlaceholder.update();
}
this.changingDockSide = true;
}
private onDockSideChange(event?: Common.EventTarget.EventTargetEvent<UI.DockController.ChangeEvent>): void {
this.updateDeviceModeView();
const toDockSide = event ? event.data.to : UI.DockController.DockController.instance().dockSide();
if (toDockSide === undefined) {
throw new Error('Got onDockSideChange event with unexpected undefined for dockSide()');
}
if (toDockSide === UI.DockController.DockState.UNDOCKED) {
this.updateForUndocked();
} else if (this.toolboxRootView && event && event.data.from === UI.DockController.DockState.UNDOCKED) {
// Don't update yet for smooth transition.
this.rootSplitWidget.hideSidebar();
} else {
this.updateForDocked(toDockSide);
}
}
private onAfterDockSideChange(event: Common.EventTarget.EventTargetEvent<UI.DockController.ChangeEvent>): void {
// We may get here on the first dock side change while loading without BeforeDockSideChange.
if (!this.changingDockSide) {
return;
}
if (event.data.from && event.data.from === UI.DockController.DockState.UNDOCKED) {
this.updateForDocked(event.data.to);
}
this.changingDockSide = false;
this.inspectedPagePlaceholder.update();
}
private updateForDocked(dockSide: UI.DockController.DockState): void {
const resizerElement = (this.rootSplitWidget.resizerElement() as HTMLElement);
resizerElement.style.transform = dockSide === UI.DockController.DockState.RIGHT ?
'translateX(2px)' :
dockSide === UI.DockController.DockState.LEFT ? 'translateX(-2px)' : '';
this.rootSplitWidget.setVertical(
dockSide === UI.DockController.DockState.RIGHT || dockSide === UI.DockController.DockState.LEFT);
this.rootSplitWidget.setSecondIsSidebar(
dockSide === UI.DockController.DockState.RIGHT || dockSide === UI.DockController.DockState.BOTTOM);
this.rootSplitWidget.toggleResizer(this.rootSplitWidget.resizerElement(), true);
this.rootSplitWidget.toggleResizer(
UI.InspectorView.InspectorView.instance().topResizerElement(), dockSide === UI.DockController.DockState.BOTTOM);
this.rootSplitWidget.showBoth();
}
private updateForUndocked(): void {
this.rootSplitWidget.toggleResizer(this.rootSplitWidget.resizerElement(), false);
this.rootSplitWidget.toggleResizer(UI.InspectorView.InspectorView.instance().topResizerElement(), false);
this.rootSplitWidget.hideMain();
}
private isDocked(): boolean {
return UI.DockController.DockController.instance().dockSide() !== UI.DockController.DockState.UNDOCKED;
}
private onSetInspectedPageBounds(event: Common.EventTarget.EventTargetEvent<Bounds>): void {
if (this.changingDockSide) {
return;
}
const window = this.inspectedPagePlaceholder.element.window();
if (!window.innerWidth || !window.innerHeight) {
return;
}
if (!this.inspectedPagePlaceholder.isShowing()) {
return;
}
const bounds = event.data;
console.timeStamp('AdvancedApp.setInspectedPageBounds');
Host.InspectorFrontendHost.InspectorFrontendHostInstance.setInspectedPageBounds(bounds);
}
}
// Export required for usage in toolbox.ts
// @ts-ignore Exported for Tests.js
globalThis.Emulation = globalThis.Emulation || {};
// @ts-ignore Exported for Tests.js
globalThis.Emulation.AdvancedApp = AdvancedApp;
let advancedAppProviderInstance: AdvancedAppProvider;
export class AdvancedAppProvider implements Common.AppProvider.AppProvider {
static instance(opts: {
forceNew: boolean|null,
} = {forceNew: null}): AdvancedAppProvider {
const {forceNew} = opts;
if (!advancedAppProviderInstance || forceNew) {
advancedAppProviderInstance = new AdvancedAppProvider();
}
return advancedAppProviderInstance;
}
createApp(): Common.App.App {
return AdvancedApp.instance();
}
}