blob: 7886aa62a02475db9b14df585a18c520f55053f6 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/icons.html.js';
import './searched_label.js';
import '/strings.m.js';
import type {CrCollapseElement} from 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
import {FocusRow} from 'chrome://resources/js/focus_row.js';
import {getFaviconForPageURL} from 'chrome://resources/js/icon.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
import {BrowserServiceImpl} from './browser_service.js';
import {SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from './constants.js';
import type {ForeignSessionTab} from './externs.js';
import {getCss} from './synced_device_card.css.js';
import {getHtml} from './synced_device_card.html.js';
declare global {
interface HTMLElementTagNameMap {
'history-synced-device-card': HistorySyncedDeviceCardElement;
}
}
export interface HistorySyncedDeviceCardElement {
$: {
'card-heading': HTMLElement,
'collapse': CrCollapseElement,
'collapse-button': HTMLElement,
'menu-button': HTMLElement,
};
}
export class HistorySyncedDeviceCardElement extends CrLitElement {
static get is() {
return 'history-synced-device-card';
}
static override get styles() {
return getCss();
}
override render() {
return getHtml.bind(this)();
}
static override get properties() {
return {
/**
* The list of tabs open for this device.
*/
tabs: {type: Array},
// Name of the synced device.
device: {type: String},
// When the device information was last updated.
lastUpdateTime: {type: String},
// Whether the card is open.
opened: {
type: Boolean,
notify: true,
},
searchTerm: {type: String},
/**
* The indexes where a window separator should be shown. The use of a
* separate array here is necessary for window separators to appear
* correctly in search. See http://crrev.com/2022003002 for more details.
*/
separatorIndexes: {type: Array},
// Internal identifier for the device.
sessionTag: {type: String},
};
}
accessor device: string = '';
accessor lastUpdateTime: string = '';
accessor tabs: ForeignSessionTab[] = [];
accessor opened: boolean = true;
accessor searchTerm: string;
accessor separatorIndexes: number[] = [];
accessor sessionTag: string = '';
override updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties);
if (changedProperties.has('tabs')) {
this.notifyFocusUpdate_();
this.updateIcons_();
}
}
/**
* Create FocusRows for this card. One is always made for the card heading and
* one for each result if the card is open.
*/
createFocusRows(): FocusRow[] {
const titleRow = new FocusRow(this.$['card-heading'], null);
titleRow.addItem('menu', '#menu-button');
titleRow.addItem('collapse', '#collapse-button');
const rows = [titleRow];
if (this.opened) {
this.shadowRoot.querySelectorAll<HTMLElement>('.item-container')
.forEach(function(el) {
const row = new FocusRow(el, null);
row.addItem('link', '.website-link');
rows.push(row);
});
}
return rows;
}
/** Open a single synced tab. */
protected openTab_(e: MouseEvent) {
const browserService = BrowserServiceImpl.getInstance();
browserService.recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.LINK_CLICKED,
SyncedTabsHistogram.LIMIT);
browserService.openForeignSessionTab(
this.sessionTag,
Number((e.currentTarget as HTMLElement).dataset['sessionId']), e);
e.preventDefault();
}
/**
* Toggles the dropdown display of synced tabs for each device card.
*/
async toggleTabCard() {
const histogramValue = this.opened ? SyncedTabsHistogram.COLLAPSE_SESSION :
SyncedTabsHistogram.EXPAND_SESSION;
BrowserServiceImpl.getInstance().recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, histogramValue, SyncedTabsHistogram.LIMIT);
this.opened = !this.opened;
await this.updateComplete; // Wait until focusable elements are updated.
this.fire('update-focus-grid');
}
private notifyFocusUpdate_() {
// Refresh focus after all rows are rendered.
this.fire('update-focus-grid');
}
/**
* When the synced tab information is set, the icon associated with the tab
* website is also set.
*/
private updateIcons_() {
setTimeout(() => {
const icons =
this.shadowRoot.querySelectorAll<HTMLElement>('.website-icon');
for (let i = 0; i < this.tabs.length; i++) {
// Entries on this UI are coming strictly from sync, so we can set
// |isSyncedUrlForHistoryUi| to true on the getFavicon call below.
icons[i].style.backgroundImage = getFaviconForPageURL(
this.tabs[i].url, true, this.tabs[i].remoteIconUrlForUma);
}
}, 0);
}
protected isWindowSeparatorIndex_(index: number): boolean {
return this.separatorIndexes.indexOf(index) !== -1;
}
protected getCollapseIcon_(): string {
return this.opened ? 'cr:expand-less' : 'cr:expand-more';
}
protected getCollapseTitle_(): string {
return this.opened ? loadTimeData.getString('collapseSessionButton') :
loadTimeData.getString('expandSessionButton');
}
protected onMenuButtonClick_(e: Event) {
this.fire('synced-device-card-open-menu', {
target: e.target,
tag: this.sessionTag,
});
e.stopPropagation(); // Prevent cr-collapse.
}
protected onLinkRightClick_() {
BrowserServiceImpl.getInstance().recordHistogram(
SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.LINK_RIGHT_CLICKED,
SyncedTabsHistogram.LIMIT);
}
protected onOpenedChanged_(e: CustomEvent<{value: boolean}>) {
this.opened = e.detail.value;
}
}
// Exported to be used in the autogenerated Lit template file
export type SyncedDeviceCardElement = HistorySyncedDeviceCardElement;
declare global {
interface HTMLElementTagNameMap {
'history-synced-device-card': HistorySyncedDeviceCardElement;
}
}
customElements.define(
HistorySyncedDeviceCardElement.is, HistorySyncedDeviceCardElement);