Bug 1518808 - add accessibility highlighter infobar for text label audit. When accessible object does not pass text label accessibility audit, the infobar will display a short message describing the particular labeling issue. r=nchevobbe,flod
authorYura Zenevich <yura.zenevich@gmail.com>
Thu, 04 Jul 2019 15:16:57 +0000
changeset 544139 35d33e763422611b7b63530cde0df8c266e6677e
parent 544138 6ca7c0b2e214e0bfb6e568637f961324829eb555
child 544140 234bd7dea9b0c4b4eb0ba76473e69713bf141388
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe, flod
bugs1518808
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518808 - add accessibility highlighter infobar for text label audit. When accessible object does not pass text label accessibility audit, the infobar will display a short message describing the particular labeling issue. r=nchevobbe,flod Differential Revision: https://phabricator.services.mozilla.com/D35961
devtools/server/actors/highlighters.css
devtools/server/actors/highlighters/utils/accessibility.js
devtools/server/actors/highlighters/xul-accessible.js
devtools/server/tests/browser/browser.ini
devtools/server/tests/browser/browser_accessibility_infobar_audit_text_label.js
devtools/shared/locales/en-US/accessibility.properties
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -34,16 +34,18 @@
   --highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
   --highlighter-bubble-arrow-size: 8px;
   --highlighter-font-family: message-box;
   --highlighter-font-size: 11px;
   --highlighter-infobar-color: hsl(210, 30%, 85%);
   --highlighter-marker-color: #000;
 
   --grey-40: #b1b1b3;
+  --red-40: #ff3b6b;
+  --yellow-60: #d7b600;
 }
 
 /**
  * Highlighters are asbolute positioned in the page by default.
  * A single highlighter can have fixed position in its css class if needed (see below the
  * eye dropper or rulers highlighter, for example); but if it has to handle the
  * document's scrolling (as rulers does), it would lag a bit behind due the APZ (Async
  * Pan/Zoom module), that performs asynchronously panning and zooming on the compositor
@@ -732,13 +734,41 @@
   margin-inline-end: 3px;
 }
 
 :-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio-separator:before {
   content: "-";
   margin-inline-start: 3px;
 }
 
+:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label:before {
+  display: inline-block;
+  width: 12px;
+  height: 12px;
+  content: "";
+  margin-inline-end: 4px;
+  vertical-align: -2px;
+  background-image: none;
+  background-position: center;
+  background-repeat: no-repeat;
+  -moz-context-properties: fill;
+  fill: currentColor;
+}
+
+:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label.fail:before {
+  background-image: url(chrome://devtools/skin/images/error-small.svg);
+  fill: var(--red-40);
+}
+
+:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label.WARNING:before {
+  background-image: url(chrome://devtools/skin/images/alert-small.svg);
+  fill: var(--yellow-60);
+}
+
+:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label.BEST_PRACTICES:before {
+  background-image: url(chrome://devtools/skin/images/info-small.svg);
+}
+
 :-moz-native-anonymous .accessible-infobar-name:not(:empty) {
   border-inline-start: 1px solid #5a6169;
   margin-inline-start: 6px;
   padding-inline-start: 6px;
 }
--- a/devtools/server/actors/highlighters/utils/accessibility.js
+++ b/devtools/server/actors/highlighters/utils/accessibility.js
@@ -4,23 +4,49 @@
 
 "use strict";
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { getCurrentZoom, getViewportDimensions } = require("devtools/shared/layout/utils");
 const { moveInfobar, createNode } = require("./markup");
 const { truncateString } = require("devtools/shared/inspector/utils");
 
-const { accessibility: { SCORES } } = require("devtools/shared/constants");
-
 const STRINGS_URI = "devtools/shared/locales/accessibility.properties";
 loader.lazyRequireGetter(this, "LocalizationHelper", "devtools/shared/l10n", true);
 DevToolsUtils.defineLazyGetter(this, "L10N", () => new LocalizationHelper(STRINGS_URI));
 
-const { accessibility: { AUDIT_TYPE } } = require("devtools/shared/constants");
+const {
+  accessibility: {
+    AUDIT_TYPE,
+    ISSUE_TYPE: {
+      [AUDIT_TYPE.TEXT_LABEL]: {
+        AREA_NO_NAME_FROM_ALT,
+        DIALOG_NO_NAME,
+        DOCUMENT_NO_TITLE,
+        EMBED_NO_NAME,
+        FIGURE_NO_NAME,
+        FORM_FIELDSET_NO_NAME,
+        FORM_FIELDSET_NO_NAME_FROM_LEGEND,
+        FORM_NO_NAME,
+        FORM_NO_VISIBLE_NAME,
+        FORM_OPTGROUP_NO_NAME,
+        FORM_OPTGROUP_NO_NAME_FROM_LABEL,
+        FRAME_NO_NAME,
+        HEADING_NO_CONTENT,
+        HEADING_NO_NAME,
+        IFRAME_NO_NAME_FROM_TITLE,
+        IMAGE_NO_NAME,
+        INTERACTIVE_NO_NAME,
+        MATHML_GLYPH_NO_NAME,
+        TOOLBAR_NO_NAME,
+      },
+    },
+    SCORES,
+  },
+} = require("devtools/shared/constants");
 
 // Max string length for truncating accessible name values.
 const MAX_STRING_LENGTH = 50;
 
 /**
  * The AccessibleInfobar is a class responsible for creating the markup for the
  * accessible highlighter. It is also reponsible for updating content within the
  * infobar such as role and name values.
@@ -378,16 +404,17 @@ class XULWindowInfobar extends Infobar {
 class Audit {
   constructor(infobar) {
     this.infobar = infobar;
 
     // A list of audit reports to be shown on the fly when highlighting an accessible
     // object.
     this.reports = [
       new ContrastRatio(this),
+      new TextLabel(this),
     ];
   }
 
   get prefix() {
     return this.infobar.prefix;
   }
 
   get win() {
@@ -534,36 +561,41 @@ class ContrastRatio extends AuditReport 
     el.setAttribute("style",
       `--accessibility-highlighter-contrast-ratio-color: rgba(${color});` +
       `--accessibility-highlighter-contrast-ratio-bg: rgba(${backgroundColor});`);
     el.removeAttribute("hidden");
   }
 
   /**
    * Update contrast ratio score infobar markup.
-   * @param  {Number}
-   *         Contrast ratio for an accessible object being highlighted.
+   * @param  {Object}
+   *         Audit report for a given highlighted accessible.
    * @return {Boolean}
    *         True if the contrast ratio markup was updated correctly and infobar audit
    *         block should be visible.
    */
-  update({ [AUDIT_TYPE.CONTRAST]: contrastRatio }) {
+  update(audit) {
     const els = {};
     for (const key of ["label", "min", "max", "error", "separator"]) {
       const el = els[key] = this.getElement(`contrast-ratio-${key}`);
       if (["min", "max"].includes(key)) {
         Object.values(SCORES).forEach(
           className => el.classList.remove(className));
         this.setTextContent(el, "");
       }
 
       el.setAttribute("hidden", true);
       el.removeAttribute("style");
     }
 
+    if (!audit) {
+      return false;
+    }
+
+    const contrastRatio = audit[AUDIT_TYPE.CONTRAST];
     if (!contrastRatio) {
       return false;
     }
 
     const { isLargeText, error } = contrastRatio;
     this.setTextContent(els.label,
       L10N.getStr(`accessibility.contrast.ratio.label${isLargeText ? ".large" : ""}`));
     els.label.removeAttribute("hidden");
@@ -588,16 +620,92 @@ class ContrastRatio extends AuditReport 
     this._fillAndStyleContrastValue(els.max,
       { value: max, className: scoreMax, color, backgroundColor: backgroundColorMax });
 
     return true;
   }
 }
 
 /**
+ * Text label audit report that is used to display a problem with text alternatives
+ * as part of the inforbar.
+ */
+class TextLabel extends AuditReport {
+  /**
+   * A map from text label issues to annotation component properties.
+   */
+  static get ISSUE_TO_INFOBAR_LABEL_MAP() {
+    return {
+      [AREA_NO_NAME_FROM_ALT]: "accessibility.text.label.issue.area",
+      [DIALOG_NO_NAME]: "accessibility.text.label.issue.dialog",
+      [DOCUMENT_NO_TITLE]: "accessibility.text.label.issue.document.title",
+      [EMBED_NO_NAME]: "accessibility.text.label.issue.embed",
+      [FIGURE_NO_NAME]: "accessibility.text.label.issue.figure",
+      [FORM_FIELDSET_NO_NAME]: "accessibility.text.label.issue.fieldset",
+      [FORM_FIELDSET_NO_NAME_FROM_LEGEND]:
+        "accessibility.text.label.issue.fieldset.legend",
+      [FORM_NO_NAME]: "accessibility.text.label.issue.form",
+      [FORM_NO_VISIBLE_NAME]: "accessibility.text.label.issue.form.visible",
+      [FORM_OPTGROUP_NO_NAME]: "accessibility.text.label.issue.optgroup",
+      [FORM_OPTGROUP_NO_NAME_FROM_LABEL]: "accessibility.text.label.issue.optgroup.label",
+      [FRAME_NO_NAME]: "accessibility.text.label.issue.frame",
+      [HEADING_NO_CONTENT]: "accessibility.text.label.issue.heading.content",
+      [HEADING_NO_NAME]: "accessibility.text.label.issue.heading",
+      [IFRAME_NO_NAME_FROM_TITLE]: "accessibility.text.label.issue.iframe",
+      [IMAGE_NO_NAME]: "accessibility.text.label.issue.image",
+      [INTERACTIVE_NO_NAME]: "accessibility.text.label.issue.interactive",
+      [MATHML_GLYPH_NO_NAME]: "accessibility.text.label.issue.glyph",
+      [TOOLBAR_NO_NAME]: "accessibility.text.label.issue.toolbar",
+    };
+  }
+
+  buildMarkup(root) {
+    createNode(this.win, {
+      nodeType: "span",
+      parent: root,
+      attributes: {
+        "class": "text-label",
+        "id": "text-label",
+      },
+      prefix: this.prefix,
+    });
+  }
+
+  /**
+   * Update text label audit infobar markup.
+   * @param  {Object}
+   *         Audit report for a given highlighted accessible.
+   * @return {Boolean}
+   *         True if the text label markup was updated correctly and infobar
+   *         audit block should be visible.
+   */
+  update(audit) {
+    const el = this.getElement("text-label");
+    el.setAttribute("hidden", true);
+    Object.values(SCORES).forEach(className => el.classList.remove(className));
+
+    if (!audit) {
+      return false;
+    }
+
+    const textLabelAudit = audit[AUDIT_TYPE.TEXT_LABEL];
+    if (!textLabelAudit) {
+      return false;
+    }
+
+    const { issue, score } = textLabelAudit;
+    this.setTextContent(el, L10N.getStr(TextLabel.ISSUE_TO_INFOBAR_LABEL_MAP[issue]));
+    el.classList.add(score);
+    el.removeAttribute("hidden");
+
+    return true;
+  }
+}
+
+/**
  * A helper function that calculate accessible object bounds and positioning to
  * be used for highlighting.
  *
  * @param  {Object} win
  *         window that contains accessible object.
  * @param  {Object} options
  *         Object used for passing options:
  *         - {Number} x
--- a/devtools/server/actors/highlighters/xul-accessible.js
+++ b/devtools/server/actors/highlighters/xul-accessible.js
@@ -15,16 +15,18 @@ const { TEXT_NODE } = require("devtools/
  */
 const ACCESSIBLE_BOUNDS_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
   .highlighter-container {
     --highlighter-bubble-background-color: hsl(214, 13%, 24%);
     --highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
     --highlighter-bubble-arrow-size: 8px;
 
     --grey-40: #b1b1b3;
+    --red-40: #ff3b6b;
+    --yellow-60: #d7b600;
   }
 
   .accessible-bounds {
     position: fixed;
     pointer-events: none;
     z-index: 10;
     display: block;
     background-color: #6a5acd!important;
@@ -142,16 +144,44 @@ const ACCESSIBLE_BOUNDS_SHEET = "data:te
     content: "-";
     margin-inline-start: 3px;
   }
 
   .accessible-infobar-name:not(:empty) {
     border-inline-start: 1px solid #5a6169;
     margin-inline-start: 6px;
     padding-inline-start: 6px;
+  }
+
+  .accessible-infobar-audit .accessible-text-label:before {
+    display: inline-block;
+    width: 12px;
+    height: 12px;
+    content: "";
+    margin-inline-end: 4px;
+    vertical-align: -2px;
+    background-image: none;
+    background-position: center;
+    background-repeat: no-repeat;
+    -moz-context-properties: fill;
+    fill: currentColor;
+  }
+
+  .accessible-infobar-audit .accessible-text-label.fail:before {
+    background-image: url(chrome://devtools/skin/images/error-small.svg);
+    fill: var(--red-40);
+  }
+
+  .accessible-infobar-audit .accessible-text-label.WARNING:before {
+    background-image: url(chrome://devtools/skin/images/alert-small.svg);
+    fill: var(--yellow-60);
+  }
+
+  .accessible-infobar-audit .accessible-text-label.BEST_PRACTICES:before {
+    background-image: url(chrome://devtools/skin/images/info-small.svg);
   }`);
 
 /**
  * The XULWindowAccessibleHighlighter is a class that has the same API as the
  * AccessibleHighlighter, and by extension other highlighters that implement
  * auto-refresh highlighter, but instead of drawing in canvas frame anonymous
  * content (that is not available for chrome accessible highlighting) it adds a
  * transparrent inactionable element with the same position and bounds as the
--- a/devtools/server/tests/browser/browser.ini
+++ b/devtools/server/tests/browser/browser.ini
@@ -38,16 +38,17 @@ support-files =
   storage-helpers.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/server/tests/mochitest/hello-actor.js
 
 [browser_accessibility_highlighter_infobar.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_infobar_show.js]
+[browser_accessibility_infobar_audit_text_label.js]
 [browser_accessibility_node.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_node_audit.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_node_events.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_simple.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/browser/browser_accessibility_infobar_audit_text_label.js
@@ -0,0 +1,127 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Checks for the AccessibleHighlighter's infobar component and its text label
+// audit.
+
+add_task(async function() {
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: MAIN_DOMAIN + "doc_accessibility_infobar.html",
+  }, async function(browser) {
+    await ContentTask.spawn(browser, null, async function() {
+      const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
+      const { HighlighterEnvironment } = require("devtools/server/actors/highlighters");
+      const { AccessibleHighlighter } = require("devtools/server/actors/highlighters/accessible");
+      const { LocalizationHelper } = require("devtools/shared/l10n");
+      const L10N = new LocalizationHelper(
+        "devtools/shared/locales/accessibility.properties");
+
+      const {
+        accessibility: {
+          AUDIT_TYPE,
+          ISSUE_TYPE: {
+            [AUDIT_TYPE.TEXT_LABEL]: {
+              DIALOG_NO_NAME,
+              FORM_NO_VISIBLE_NAME,
+              TOOLBAR_NO_NAME,
+            },
+          },
+          SCORES: { BEST_PRACTICES, FAIL, WARNING },
+        },
+      } = require("devtools/shared/constants");
+
+      /**
+       * Checks for updated content for an infobar.
+       *
+       * @param  {Object} infobar
+       *         Accessible highlighter's infobar component.
+       * @param  {Object} audit
+       *         Audit information that is passed on highlighter show.
+       */
+      function checkTextLabel(infobar, audit) {
+        const { issue, score } = audit || {};
+        let expected = "";
+        if (issue) {
+          const { ISSUE_TO_INFOBAR_LABEL_MAP } = infobar.audit.reports[1].constructor;
+          expected = L10N.getStr(ISSUE_TO_INFOBAR_LABEL_MAP[issue]);
+        }
+
+        is(infobar.getTextContent("text-label"), expected,
+          "infobar text label audit text content is correct");
+        if (score) {
+          ok(infobar.getElement("text-label").classList.contains(
+            score === FAIL ? "fail" : score));
+        }
+      }
+
+      // Start testing. First, create highlighter environment and initialize.
+      const env = new HighlighterEnvironment();
+      env.initFromWindow(content.window);
+
+      // Wait for loading highlighter environment content to complete before creating the
+      // highlighter.
+      await new Promise(resolve => {
+        const doc = env.document;
+
+        function onContentLoaded() {
+          if (doc.readyState === "interactive" || doc.readyState === "complete") {
+            resolve();
+          } else {
+            doc.addEventListener("DOMContentLoaded", onContentLoaded, { once: true });
+          }
+        }
+
+        onContentLoaded();
+      });
+
+      // Now, we can test the Infobar's audit content.
+      const node = content.document.createElement("div");
+      content.document.body.append(node);
+      const highlighter = new AccessibleHighlighter(env);
+      const infobar = highlighter.accessibleInfobar;
+      const bounds = {
+        x: 0,
+        y: 0,
+        w: 250,
+        h: 100,
+      };
+
+      const tests = [{
+        desc: "Infobar is shown with no text label audit content when no audit.",
+      }, {
+        desc: "Infobar is shown with no text label audit content when audit is null.",
+        audit: null,
+      }, {
+        desc: "Infobar is shown with no text label audit content when empty " +
+              "text label audit.",
+        audit: { [AUDIT_TYPE.TEXT_LABEL]: null },
+      }, {
+        desc: "Infobar is shown with text label audit content for an error.",
+        audit: { [AUDIT_TYPE.TEXT_LABEL]: { score: FAIL, issue: TOOLBAR_NO_NAME } },
+      }, {
+        desc: "Infobar is shown with text label audit content for a warning.",
+        audit: { [AUDIT_TYPE.TEXT_LABEL]: {
+          score: WARNING, issue: FORM_NO_VISIBLE_NAME,
+        }},
+      }, {
+        desc: "Infobar is shown with text label audit content for best practices.",
+        audit: { [AUDIT_TYPE.TEXT_LABEL]: {
+          score: BEST_PRACTICES, issue: DIALOG_NO_NAME,
+        }},
+      }];
+
+      for (const test of tests) {
+        const { desc, audit } = test;
+
+        info(desc);
+        highlighter.show(node, { ...bounds, audit });
+        checkTextLabel(infobar, audit && audit[AUDIT_TYPE.TEXT_LABEL]);
+        highlighter.hide();
+      }
+    });
+  });
+});
--- a/devtools/shared/locales/en-US/accessibility.properties
+++ b/devtools/shared/locales/en-US/accessibility.properties
@@ -14,8 +14,103 @@ accessibility.contrast.ratio.error=Unabl
 # LOCALIZATION NOTE (accessibility.contrast.ratio.label): A title text for the color
 # contrast ratio description, used together with the actual values.
 accessibility.contrast.ratio.label=Contrast:
 
 # LOCALIZATION NOTE (accessibility.contrast.ratio.label.large): A title text for the color
 # contrast ratio description that also specifies that the color contrast criteria used is
 # if for large text.
 accessibility.contrast.ratio.label.large=Contrast (large text):
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.area): A title text that
+# describes that currently selected accessible object for an <area> element must have
+# its name provided via the alt attribute.
+accessibility.text.label.issue.area = Use “alt” attribute to label “area” elements that have the “href” attribute.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.dialog): A title text that
+# describes that currently selected accessible object for a dialog should have a name
+# provided.
+accessibility.text.label.issue.dialog = Dialogs should be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.document.title): A title text that
+# describes that currently selected accessible object for a document must have a name
+# provided via title.
+accessibility.text.label.issue.document.title = Documents must have a title.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.embed): A title text that
+# describes that currently selected accessible object for an <embed> must have a name
+# provided.
+accessibility.text.label.issue.embed = Embedded content must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.figure): A title text that
+# describes that currently selected accessible object for a figure should have a name
+# provided.
+accessibility.text.label.issue.figure = Figures with optional captions should be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.fieldset): A title text that
+# describes that currently selected accessible object for a <fieldset> must have a name
+# provided.
+accessibility.text.label.issue.fieldset = “fieldset” elements must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.fieldset.legend): A title text that
+# describes that currently selected accessible object for a <fieldset> must have a name
+# provided via <legend> element.
+accessibility.text.label.issue.fieldset.legend = Use “legend” element to label “fieldset” elements.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.form): A title text that
+# describes that currently selected accessible object for a form element must have a name
+# provided.
+accessibility.text.label.issue.form = Form elements must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.form.visible): A title text that
+# describes that currently selected accessible object for a form element should have a name
+# provided via a visible label/element.
+accessibility.text.label.issue.form.visible = Form elements should have a visible text label.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.frame): A title text that
+# describes that currently selected accessible object for a <frame> must have a name
+# provided.
+accessibility.text.label.issue.frame = “frame” elements must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.glyph): A title text that
+# describes that currently selected accessible object for a <mglyph> must have a name
+# provided via alt attribute.
+accessibility.text.label.issue.glyph = Use “alt” attribute to label “mglyph” elements.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.heading): A title text that
+# describes that currently selected accessible object for a heading must have a name
+# provided.
+accessibility.text.label.issue.heading = Headings must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.heading.content): A title text that
+# describes that currently selected accessible object for a heading must have visible
+# content.
+accessibility.text.label.issue.heading.content = Headings should have visible text content.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.iframe): A title text that
+# describes that currently selected accessible object for an <iframe> have a name
+# provided via title attribute.
+accessibility.text.label.issue.iframe = Use “title” attribute to describe “iframe” content.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.image): A title text that
+# describes that currently selected accessible object for graphical content must have a
+# name provided.
+accessibility.text.label.issue.image = Content with images must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.interactive): A title text that
+# describes that currently selected accessible object for interactive element must have a
+# name provided.
+accessibility.text.label.issue.interactive = Interactive elements must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.optgroup): A title text that
+# describes that currently selected accessible object for an <optgroup> must have a
+# name provided.
+accessibility.text.label.issue.optgroup = “optgroup” elements must be labeled.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.optgroup.label): A title text that
+# describes that currently selected accessible object for an <optgroup> must have a
+# name provided via label attribute.
+accessibility.text.label.issue.optgroup.label = Use “label” attribute to label “optgroup” elements.
+
+# LOCALIZATION NOTE (accessibility.text.label.issue.toolbar): A title text that
+# describes that currently selected accessible object for a toolbar must have a
+# name provided when there is more than one toolbar in the document.
+accessibility.text.label.issue.toolbar = Toolbars must be labeled when there is more than one toolbar.