blob: a7cee2be3cb7160cd390359067c9ac76982f46ab [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 {assert} from '//resources/js/assert.js';
import {CrLitElement, html} from '//resources/lit/v3_0/lit.rollup.js';
import type {SecurityIcon} from './browser.mojom-webui.js';
import {GuestHandlerRemote} from './browser.mojom-webui.js';
import {BrowserProxy} from './browser_proxy.js';
import {getCss} from './webview.css.js';
export interface WebviewElement {
$: {
iframe: HTMLIFrameElement,
};
}
export class WebviewElement extends CrLitElement {
static get is() {
return 'cr-webview';
}
static override get styles() {
return getCss();
}
override render() {
return html`<iframe id="iframe"></iframe>`;
}
static override get properties() {
return {
guestId: {type: Number},
};
}
accessor guestId: number = -1;
private attached: boolean = false;
override async connectedCallback() {
super.connectedCallback();
await this.tryToAttach();
}
protected async tryToAttach() {
if (this.attached || this.guestId === -1) {
return;
}
this.attached = true;
// Wait until iframe.contentWindow becomes available.
if (!this.$.iframe.contentWindow) {
await this.whenIframeContentWindowAvailable_(this.$.iframe);
}
this.attachGuestToIframe(this.guestId, this.$.iframe);
}
private attachGuestToIframe(guestId: number, iframe: HTMLIFrameElement) {
const iframeContentWindow = iframe.contentWindow;
assert(iframeContentWindow);
chrome.browser.attachIframeGuest(guestId, iframeContentWindow);
}
private async whenIframeContentWindowAvailable_(iframe: HTMLIFrameElement):
Promise<void> {
return new Promise(resolve => {
// TODO(webium): find a way to get notified when the contentWindow is
// ready. This is a workaround to poll every 100ms.
const intervalId = setInterval(() => {
if (!iframe.contentWindow) {
return;
}
clearInterval(intervalId);
resolve();
}, 100);
});
}
}
export class TabWebviewElement extends WebviewElement {
static override get is() {
return 'cr-tab-webview';
}
tabId: string;
private guestHandler: GuestHandlerRemote = new GuestHandlerRemote();
constructor(tabId: string) {
super();
this.tabId = tabId;
this.attachTabContents();
}
setActive(active: boolean) {
if (active) {
this.classList.add('active');
this.$.iframe.focus();
} else {
this.classList.remove('active');
}
}
openPageInfoMenu() {
this.guestHandler.openPageInfoMenu();
}
async getSecurityIcon(): Promise<SecurityIcon> {
const {securityIcon} = await this.guestHandler.getSecurityIcon();
return securityIcon;
}
private attachTabContents() {
BrowserProxy.getPageHandler()
.getGuestIdForTabId(
this.tabId, this.guestHandler.$.bindNewPipeAndPassReceiver())
.then(({guestId}) => {
this.guestId = guestId;
this.tryToAttach();
});
}
goBack() {
this.guestHandler.goBack();
}
goForward() {
this.guestHandler.goForward();
}
reload() {
this.guestHandler.reload();
}
stopLoading() {
this.guestHandler.stopLoading();
}
async canGoBack(): Promise<boolean> {
const {canGoBack} = await this.guestHandler.canGoBack();
return canGoBack;
}
async canGoForward(): Promise<boolean> {
const {canGoForward} = await this.guestHandler.canGoForward();
return canGoForward;
}
}
declare global {
interface HTMLElementTagNameMap {
'cr-webview': WebviewElement;
'cr-tab-webview': TabWebviewElement;
}
}
customElements.define(WebviewElement.is, WebviewElement);
customElements.define(TabWebviewElement.is, TabWebviewElement);