WebUI Tab Strip: Add aria-labels and roles to elements

- Add more accessible labels for title and alert states.
- Move the focusable target from TabElement's host to inner #tab
element to allow aria-labelledby to work.
- Add role and tabindex to appropriate elements.
- Does not update focus outline UI.

Screenshot examples of what is read out: https://imgur.com/a/o0Hmu3a

Bug: 1017472
Change-Id: I230e810fadf92749c2ecf824bb92eb4d8c45774d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1877194
Commit-Queue: John Lee <[email protected]>
Reviewed-by: Demetrios Papadopoulos <[email protected]>
Cr-Commit-Position: refs/heads/master@{#709246}
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index a9e4bc0..cafce9b 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './strings.m.js';
+
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {getFavicon} from 'chrome://resources/js/icon.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {AlertIndicatorsElement} from './alert_indicators.js';
 import {CustomElement} from './custom_element.js';
@@ -13,6 +16,24 @@
 
 export const DEFAULT_ANIMATION_DURATION = 125;
 
+/**
+ * @param {!TabData} tab
+ * @return {string}
+ */
+function getAccessibleTitle(tab) {
+  const tabTitle = tab.title;
+
+  if (tab.crashed) {
+    return loadTimeData.getStringF('tabCrashed', tabTitle);
+  }
+
+  if (tab.networkState === TabNetworkState.ERROR) {
+    return loadTimeData.getStringF('tabNetworkError', tabTitle);
+  }
+
+  return tabTitle;
+}
+
 export class TabElement extends CustomElement {
   static get template() {
     return `{__html_template__}`;
@@ -31,11 +52,12 @@
     /** @private {!HTMLElement} */
     this.closeButtonEl_ =
         /** @type {!HTMLElement} */ (this.shadowRoot.querySelector('#close'));
+    this.closeButtonEl_.setAttribute(
+        'aria-label', loadTimeData.getString('closeTab'));
 
     /** @private {!HTMLElement} */
-    this.dragImage_ =
-        /** @type {!HTMLElement} */ (
-            this.shadowRoot.querySelector('#dragImage'));
+    this.tabEl_ =
+        /** @type {!HTMLElement} */ (this.shadowRoot.querySelector('#tab'));
 
     /** @private {!HTMLElement} */
     this.faviconEl_ =
@@ -63,8 +85,8 @@
     this.titleTextEl_ = /** @type {!HTMLElement} */ (
         this.shadowRoot.querySelector('#titleText'));
 
-    this.addEventListener('click', this.onClick_.bind(this));
-    this.addEventListener('contextmenu', this.onContextMenu_.bind(this));
+    this.tabEl_.addEventListener('click', this.onClick_.bind(this));
+    this.tabEl_.addEventListener('contextmenu', this.onContextMenu_.bind(this));
     this.closeButtonEl_.addEventListener('click', this.onClose_.bind(this));
   }
 
@@ -77,6 +99,7 @@
   set tab(tab) {
     assert(this.tab_ !== tab);
     this.toggleAttribute('active', tab.active);
+    this.tabEl_.setAttribute('aria-selected', tab.active.toString());
     this.toggleAttribute('hide-icon_', !tab.showIcon);
     this.toggleAttribute(
         'waiting_',
@@ -94,6 +117,7 @@
     if (!this.tab_ || this.tab_.title !== tab.title) {
       this.titleTextEl_.textContent = tab.title;
     }
+    this.titleTextEl_.setAttribute('aria-label', getAccessibleTitle(tab));
 
     if (tab.networkState === TabNetworkState.WAITING ||
         (tab.networkState === TabNetworkState.LOADING &&
@@ -120,11 +144,9 @@
     this.tab_ = Object.freeze(tab);
   }
 
-  /**
-   * @return {!HTMLElement}
-   */
+  /** @return {!HTMLElement} */
   getDragImage() {
-    return this.dragImage_;
+    return this.tabEl_;
   }
 
   /**
@@ -147,7 +169,10 @@
     }
   }
 
-  /** @private */
+  /**
+   * @param {!Event} event
+   * @private
+   */
   onContextMenu_(event) {
     event.preventDefault();