blob: b9cb6919f6544fd9056051761280e9ba02c09dd4 [file] [log] [blame]
John Lee912fb9c02019-08-02 01:28:211// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
John Lee61bc1b52019-10-24 22:27:235import './strings.m.js';
6
John Lee81011102019-10-11 20:05:077import {assert} from 'chrome://resources/js/assert.m.js';
8import {getFavicon} from 'chrome://resources/js/icon.m.js';
John Lee61bc1b52019-10-24 22:27:239import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
John Lee2bafb2f2019-08-21 19:20:0310
John Leea8e39e682019-10-18 02:19:3811import {AlertIndicatorsElement} from './alert_indicators.js';
John Lee912fb9c02019-08-02 01:28:2112import {CustomElement} from './custom_element.js';
Collin Baker1ac2e432019-10-16 18:17:2113import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js';
John Lee4bcbaf12019-10-21 23:29:4614import {tabStripOptions} from './tab_strip_options.js';
John Lee99367a62019-10-10 19:03:4915import {TabData, TabNetworkState, TabsApiProxy} from './tabs_api_proxy.js';
John Lee912fb9c02019-08-02 01:28:2116
John Leea97ceab6f2019-09-04 22:26:4617export const DEFAULT_ANIMATION_DURATION = 125;
18
John Lee61bc1b52019-10-24 22:27:2319/**
20 * @param {!TabData} tab
21 * @return {string}
22 */
23function getAccessibleTitle(tab) {
24 const tabTitle = tab.title;
25
26 if (tab.crashed) {
27 return loadTimeData.getStringF('tabCrashed', tabTitle);
28 }
29
30 if (tab.networkState === TabNetworkState.ERROR) {
31 return loadTimeData.getStringF('tabNetworkError', tabTitle);
32 }
33
34 return tabTitle;
35}
36
John Lee3f8dae92019-08-09 22:37:0937export class TabElement extends CustomElement {
John Lee912fb9c02019-08-02 01:28:2138 static get template() {
39 return `{__html_template__}`;
40 }
41
John Lee3f8dae92019-08-09 22:37:0942 constructor() {
43 super();
44
John Leea8e39e682019-10-18 02:19:3845 this.alertIndicatorsEl_ = /** @type {!AlertIndicatorsElement} */
46 (this.shadowRoot.querySelector('tabstrip-alert-indicators'));
47 // Normally, custom elements will get upgraded automatically once added to
48 // the DOM, but TabElement may need to update properties on
49 // AlertIndicatorElement before this happens, so upgrade it manually.
50 customElements.upgrade(this.alertIndicatorsEl_);
51
John Lee7d416b782019-08-12 22:32:1352 /** @private {!HTMLElement} */
53 this.closeButtonEl_ =
54 /** @type {!HTMLElement} */ (this.shadowRoot.querySelector('#close'));
John Lee61bc1b52019-10-24 22:27:2355 this.closeButtonEl_.setAttribute(
56 'aria-label', loadTimeData.getString('closeTab'));
John Lee7d416b782019-08-12 22:32:1357
John Lee2bafb2f2019-08-21 19:20:0358 /** @private {!HTMLElement} */
John Lee61bc1b52019-10-24 22:27:2359 this.tabEl_ =
60 /** @type {!HTMLElement} */ (this.shadowRoot.querySelector('#tab'));
John Lee3b309d52019-09-18 19:08:0961
62 /** @private {!HTMLElement} */
John Lee2bafb2f2019-08-21 19:20:0363 this.faviconEl_ =
64 /** @type {!HTMLElement} */ (this.shadowRoot.querySelector('#favicon'));
65
Collin Bakere21f723d2019-09-05 20:05:4166 /** @private {!HTMLElement} */
67 this.thumbnailContainer_ =
68 /** @type {!HTMLElement} */ (
69 this.shadowRoot.querySelector('#thumbnail'));
70
71 /** @private {!Image} */
72 this.thumbnail_ =
73 /** @type {!Image} */ (this.shadowRoot.querySelector('#thumbnailImg'));
74
John Lee99367a62019-10-10 19:03:4975 /** @private {!TabData} */
John Lee3f8dae92019-08-09 22:37:0976 this.tab_;
77
John Lee7d416b782019-08-12 22:32:1378 /** @private {!TabsApiProxy} */
79 this.tabsApi_ = TabsApiProxy.getInstance();
80
Collin Baker1ac2e432019-10-16 18:17:2181 /** @private {!TabStripEmbedderProxy} */
82 this.embedderApi_ = TabStripEmbedderProxy.getInstance();
83
John Lee3f8dae92019-08-09 22:37:0984 /** @private {!HTMLElement} */
85 this.titleTextEl_ = /** @type {!HTMLElement} */ (
86 this.shadowRoot.querySelector('#titleText'));
John Lee3f8dae92019-08-09 22:37:0987
John Lee61bc1b52019-10-24 22:27:2388 this.tabEl_.addEventListener('click', this.onClick_.bind(this));
89 this.tabEl_.addEventListener('contextmenu', this.onContextMenu_.bind(this));
John Lee7d416b782019-08-12 22:32:1390 this.closeButtonEl_.addEventListener('click', this.onClose_.bind(this));
John Lee912fb9c02019-08-02 01:28:2191 }
John Lee3f8dae92019-08-09 22:37:0992
John Lee99367a62019-10-10 19:03:4993 /** @return {!TabData} */
John Lee3f8dae92019-08-09 22:37:0994 get tab() {
95 return this.tab_;
96 }
97
John Lee99367a62019-10-10 19:03:4998 /** @param {!TabData} tab */
John Lee3f8dae92019-08-09 22:37:0999 set tab(tab) {
John Lee81011102019-10-11 20:05:07100 assert(this.tab_ !== tab);
John Lee8e253602019-08-14 22:22:51101 this.toggleAttribute('active', tab.active);
John Lee61bc1b52019-10-24 22:27:23102 this.tabEl_.setAttribute('aria-selected', tab.active.toString());
John Lee81011102019-10-11 20:05:07103 this.toggleAttribute('hide-icon_', !tab.showIcon);
John Lee99367a62019-10-10 19:03:49104 this.toggleAttribute(
John Lee81011102019-10-11 20:05:07105 'waiting_',
John Lee99367a62019-10-10 19:03:49106 !tab.shouldHideThrobber &&
107 tab.networkState === TabNetworkState.WAITING);
108 this.toggleAttribute(
John Lee81011102019-10-11 20:05:07109 'loading_',
John Lee99367a62019-10-10 19:03:49110 !tab.shouldHideThrobber &&
111 tab.networkState === TabNetworkState.LOADING);
John Leea8e39e682019-10-18 02:19:38112 this.toggleAttribute('pinned', tab.pinned);
John Lee4734ca12019-10-14 19:34:01113 this.toggleAttribute('blocked_', tab.blocked);
John Lee4bcbaf12019-10-21 23:29:46114 this.setAttribute('draggable', true);
John Lee02b3a742019-10-15 20:00:56115 this.toggleAttribute('crashed_', tab.crashed);
John Lee8e253602019-08-14 22:22:51116
John Lee3f8dae92019-08-09 22:37:09117 if (!this.tab_ || this.tab_.title !== tab.title) {
118 this.titleTextEl_.textContent = tab.title;
119 }
John Lee61bc1b52019-10-24 22:27:23120 this.titleTextEl_.setAttribute('aria-label', getAccessibleTitle(tab));
John Lee3f8dae92019-08-09 22:37:09121
John Lee81011102019-10-11 20:05:07122 if (tab.networkState === TabNetworkState.WAITING ||
123 (tab.networkState === TabNetworkState.LOADING &&
124 tab.isDefaultFavicon)) {
125 this.faviconEl_.style.backgroundImage = 'none';
126 } else if (tab.favIconUrl) {
127 this.faviconEl_.style.backgroundImage = `url(${tab.favIconUrl})`;
John Leed14bec62019-09-23 22:41:32128 } else {
John Lee81011102019-10-11 20:05:07129 this.faviconEl_.style.backgroundImage = getFavicon('');
John Lee2bafb2f2019-08-21 19:20:03130 }
131
John Lee3f8dae92019-08-09 22:37:09132 // Expose the ID to an attribute to allow easy querySelector use
133 this.setAttribute('data-tab-id', tab.id);
134
John Leea8e39e682019-10-18 02:19:38135 this.alertIndicatorsEl_.updateAlertStates(tab.alertStates)
136 .then((alertIndicatorsCount) => {
137 this.toggleAttribute('has-alert-states_', alertIndicatorsCount > 0);
138 });
139
Collin Bakere21f723d2019-09-05 20:05:41140 if (!this.tab_ || this.tab_.id !== tab.id) {
John Lee815c2fb2019-09-19 01:25:08141 this.tabsApi_.trackThumbnailForTab(tab.id);
Collin Bakere21f723d2019-09-05 20:05:41142 }
143
John Lee3f8dae92019-08-09 22:37:09144 this.tab_ = Object.freeze(tab);
145 }
John Lee7d416b782019-08-12 22:32:13146
John Lee61bc1b52019-10-24 22:27:23147 /** @return {!HTMLElement} */
John Lee3b309d52019-09-18 19:08:09148 getDragImage() {
John Lee61bc1b52019-10-24 22:27:23149 return this.tabEl_;
John Lee3b309d52019-09-18 19:08:09150 }
151
152 /**
Collin Bakere21f723d2019-09-05 20:05:41153 * @param {string} imgData
154 */
155 updateThumbnail(imgData) {
156 this.thumbnail_.src = imgData;
157 }
158
John Lee7d416b782019-08-12 22:32:13159 /** @private */
John Lee8e253602019-08-14 22:22:51160 onClick_() {
John Lee7d416b782019-08-12 22:32:13161 if (!this.tab_) {
162 return;
163 }
164
John Lee8e253602019-08-14 22:22:51165 this.tabsApi_.activateTab(this.tab_.id);
John Lee4bcbaf12019-10-21 23:29:46166
167 if (tabStripOptions.autoCloseEnabled) {
168 this.embedderApi_.closeContainer();
169 }
John Lee8e253602019-08-14 22:22:51170 }
171
John Lee61bc1b52019-10-24 22:27:23172 /**
173 * @param {!Event} event
174 * @private
175 */
Collin Bakerdc3d2112019-10-10 18:28:20176 onContextMenu_(event) {
177 event.preventDefault();
178
179 if (!this.tab_) {
180 return;
181 }
182
Collin Baker1ac2e432019-10-16 18:17:21183 this.embedderApi_.showTabContextMenu(
Collin Bakerdc3d2112019-10-10 18:28:20184 this.tab_.id, event.clientX, event.clientY);
185 }
186
John Lee8e253602019-08-14 22:22:51187 /**
188 * @param {!Event} event
189 * @private
190 */
191 onClose_(event) {
192 if (!this.tab_) {
193 return;
194 }
195
196 event.stopPropagation();
John Lee7d416b782019-08-12 22:32:13197 this.tabsApi_.closeTab(this.tab_.id);
198 }
John Leea97ceab6f2019-09-04 22:26:46199
200 /**
John Lee3b309d52019-09-18 19:08:09201 * @param {boolean} dragging
202 */
203 setDragging(dragging) {
John Lee81011102019-10-11 20:05:07204 this.toggleAttribute('dragging_', dragging);
John Lee3b309d52019-09-18 19:08:09205 }
206
207 /**
John Leea97ceab6f2019-09-04 22:26:46208 * @return {!Promise}
209 */
210 slideIn() {
211 return new Promise(resolve => {
212 const animation = this.animate(
213 [
214 {maxWidth: 0, opacity: 0},
John Lee4df86c42019-10-25 20:00:03215 {maxWidth: 'var(--tabstrip-tab-width)', opacity: 1},
John Leea97ceab6f2019-09-04 22:26:46216 ],
217 {
218 duration: DEFAULT_ANIMATION_DURATION,
219 fill: 'forwards',
220 });
221 animation.onfinish = resolve;
222 });
223 }
224
225 /**
226 * @return {!Promise}
227 */
228 slideOut() {
229 return new Promise(resolve => {
230 const animation = this.animate(
231 [
John Lee4df86c42019-10-25 20:00:03232 {maxWidth: 'var(--tabstrip-tab-width)', opacity: 1},
John Leea97ceab6f2019-09-04 22:26:46233 {maxWidth: 0, opacity: 0},
234 ],
235 {
236 duration: DEFAULT_ANIMATION_DURATION,
237 fill: 'forwards',
238 });
239 animation.onfinish = () => {
240 this.remove();
241 resolve();
242 };
243 });
244 }
John Lee912fb9c02019-08-02 01:28:21245}
246
John Lee3f8dae92019-08-09 22:37:09247customElements.define('tabstrip-tab', TabElement);