blob: 22633b29cb2a7b9cf16e53dadb04249c0f6bf5b3 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import './bookmark_bar.js';
import './content_region.js';
import './icons.html.js';
import './side_panel.js';
import '/strings.m.js';
import './tab_strip.js';
import './webview.js';
import 'chrome://resources/cr_components/searchbox/searchbox.js';
import type {Tab} from '/tab_strip_api/tab_strip_api_data_model.mojom-webui.js';
import type {SearchboxElement} from 'chrome://resources/cr_components/searchbox/searchbox.js';
import {TrackedElementManager} from 'chrome://resources/js/tracked_element/tracked_element_manager.js';
import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
import {getCss} from './app.css.js';
import {getHtml} from './app.html.js';
import type {BookmarkBar} from './bookmark_bar.js';
import {BookmarkBarController} from './bookmark_bar_controller.js';
import {BrowserProxy} from './browser_proxy.js';
import type {ContentRegion} from './content_region.js';
import type {SidePanel} from './side_panel.js';
import {TabStrip} from './tab_strip.js';
import type {TabStripControllerDelegate} from './tab_strip_controller.js';
import {TabStripController} from './tab_strip_controller.js';
export interface WebuiBrowserAppElement {
$: {
address: SearchboxElement,
appMenuButton: HTMLElement,
avatarButton: HTMLElement,
bookmarkBar: BookmarkBar,
contentRegion: ContentRegion,
sidePanel: SidePanel,
tabstrip: TabStrip,
};
}
export class WebuiBrowserAppElement extends CrLitElement implements
TabStripControllerDelegate {
static get is() {
return 'webui-browser-app';
}
static override get styles() {
return getCss();
}
override render() {
return getHtml.bind(this)();
}
static override get properties() {
return {
backButtonDisabled_: {state: true, type: Boolean},
forwardButtonDisabled_: {state: true, type: Boolean},
showingSidePanel_: {state: true, type: Boolean},
reloadOrStopIcon_: {state: true, type: String},
};
}
private bookmarkBarController_: BookmarkBarController;
private tabStripController_: TabStripController;
private trackedElementManager_: TrackedElementManager;
protected accessor backButtonDisabled_: boolean = true;
protected accessor forwardButtonDisabled_: boolean = true;
protected accessor reloadOrStopIcon_: string = 'icon-refresh';
protected accessor showingSidePanel_: boolean = false;
constructor() {
super();
this.bookmarkBarController_ = new BookmarkBarController();
this.tabStripController_ =
new TabStripController(this, this.$.tabstrip, this.$.contentRegion);
this.trackedElementManager_ = new TrackedElementManager();
const callbackRouter = BrowserProxy.getCallbackRouter();
callbackRouter.showSidePanel.addListener(this.showSidePanel_.bind(this));
callbackRouter.closeSidePanel.addListener(this.closeSidePanel_.bind(this));
}
override connectedCallback() {
// Important. Properties are not reactive without calling
// super.connectedCallback().
super.connectedCallback();
this.trackedElementManager_.startTracking(
this.$.address, 'kLocationBarElementId');
this.trackedElementManager_.startTracking(
this.$.appMenuButton, 'kToolbarAppMenuButtonElementId');
this.trackedElementManager_.startTracking(
this.$.avatarButton, 'kToolbarAvatarButtonElementId');
this.trackedElementManager_.startTracking(
this.$.contentRegion, 'kContentsContainerViewElementId');
}
// TabStripControllerDelegate:
refreshLayout() {
this.updateToolbarButtons_();
}
activeTabUpdated(tabData: Tab) {
let displayUrl = '';
const activeTabUrl = tabData.url.url;
// TODO(webium): Should match
// ChromeLocationBarModelDelegate::ShouldDisplayURL and
// LocationBarModelImpl::GetFormattedURL logic.
//
// There are also likely some subtleties about what happens when the user
// is typing and the tab navigates.
if (!activeTabUrl.startsWith('chrome://newtab')) {
displayUrl = activeTabUrl;
}
this.$.address.setInputText(displayUrl);
}
protected onLaunchDevtoolsClick_(_: Event) {
BrowserProxy.getPageHandler().launchDevToolsForBrowser();
}
protected onAppMenuClick_(_: Event) {
BrowserProxy.getPageHandler().openAppMenu();
}
protected onAvatarClick_(_: Event) {
BrowserProxy.getPageHandler().openProfileMenu();
}
protected onMinimizeClick_(_: Event) {
BrowserProxy.getPageHandler().minimize();
}
protected onMaximizeClick_(_: Event) {
BrowserProxy.getPageHandler().maximize();
}
protected onRestoreClick_(_: Event) {
BrowserProxy.getPageHandler().restore();
}
protected onCloseClick_(_: Event) {
BrowserProxy.getPageHandler().close();
}
protected onBackClick_(_: Event) {
if (this.$.contentRegion.activeWebview) {
this.$.contentRegion.activeWebview.goBack();
}
}
protected onForwardClick_(_: Event) {
if (this.$.contentRegion.activeWebview) {
this.$.contentRegion.activeWebview.goForward();
}
}
protected onReloadOrStopClick_(_: Event) {
if (this.$.contentRegion.activeWebview) {
if (this.reloadOrStopIcon_ === 'icon-refresh') {
this.$.contentRegion.activeWebview.reload();
} else {
this.$.contentRegion.activeWebview.stopLoading();
}
}
}
private async updateToolbarButtons_() {
const webview = this.$.contentRegion.activeWebview;
if (webview) {
const [canGoBack, canGoForward] =
await Promise.all([webview.canGoBack(), webview.canGoForward()]);
this.backButtonDisabled_ = !canGoBack;
this.forwardButtonDisabled_ = !canGoForward;
} else {
this.backButtonDisabled_ = true;
this.forwardButtonDisabled_ = true;
}
}
protected onTabClick_(e: CustomEvent) {
this.tabStripController_.onTabClick(e);
}
protected onTabDragOutOfBounds_(e: CustomEvent) {
this.tabStripController_.onTabDragOutOfBounds(e);
}
protected onTabClosed_(e: CustomEvent) {
const tabId = e.detail.tabId;
this.tabStripController_.removeTab(tabId);
}
protected onAddTabClick_(_: Event) {
this.tabStripController_.addNewTab();
}
protected override firstUpdated() {
this.bookmarkBarController_.init(this.$.bookmarkBar);
BrowserProxy.getCallbackRouter().setFocusToLocationBar.addListener(
this.setFocusToLocationBar.bind(this));
BrowserProxy.getCallbackRouter().setReloadStopState.addListener(
this.setReloadStopState.bind(this));
}
protected onShowBookmarkBar_() {
this.$.bookmarkBar.style.display = 'flex';
}
protected onHideBookmarkBar_() {
this.$.bookmarkBar.style.display = 'none';
}
protected onBookmarkButtonClick_(e: CustomEvent) {
const bookmarkId = e.detail.bookmarkId;
this.bookmarkBarController_.launchBookmark(bookmarkId);
}
protected onTabDragMouseDown_(e: MouseEvent) {
if (e.target instanceof TabStrip) {
this.$.tabstrip.dragMouseDown(e);
this.addEventListener('mouseup', this.onTabDragMouseUp_);
this.addEventListener('mousemove', this.onTabDragMouseMove_);
}
}
protected onTabDragMouseUp_(_: MouseEvent) {
this.$.tabstrip.closeDragElement();
this.removeEventListener('mouseup', this.onTabDragMouseUp_);
this.removeEventListener('mousemove', this.onTabDragMouseMove_);
}
protected onTabDragMouseMove_(e: MouseEvent) {
this.$.tabstrip.elementDrag(e);
}
protected setFocusToLocationBar(isUserInitiated: boolean) {
this.$.address.focusInput();
// If the user initiated the selection (e.g. by pressing Ctrl-L) we want to
// select everything in order to make it easy to replace the URL. This is
// also useful for some cases where we auto-focus (e.g. about:blank set as
// the NTP) if they're not actively using the omnibox, which we check by
// looking at the focus. See OmniBoxViewViews::SetFocus() for the
// inspiration.
if (isUserInitiated || this.shadowRoot.activeElement !== this.$.address) {
this.$.address.selectAll();
}
}
protected setReloadStopState(isLoading: boolean) {
this.reloadOrStopIcon_ = isLoading ? 'icon-clear' : 'icon-refresh';
}
protected showSidePanel_(guestContentsId: number, title: string) {
this.showingSidePanel_ = true;
this.$.sidePanel.show(guestContentsId, title);
}
protected closeSidePanel_() {
this.$.sidePanel.close();
this.showingSidePanel_ = false;
}
// This function is called when the side panel closes itself. For example,
// when user clicks the close "x" button.
protected onSidePanelClosed_() {
this.showingSidePanel_ = false;
}
}
declare global {
interface HTMLElementTagNameMap {
'webui-browser-app': WebuiBrowserAppElement;
}
}
customElements.define(WebuiBrowserAppElement.is, WebuiBrowserAppElement);