Commit 792c7aa8 authored by Shik Chen's avatar Shik Chen Committed by Chromium LUCI CQ

CCA: Announce detected QR code text in ChromeVox

This CL tweaks the a11y of detected QR code text by announcing the text
content and auto focus to the expand/copy button.

Bug: b/172879638
Test: Manually with Chromevox.

Change-Id: If9d219e7e397f219566e1790c17c6dfc5ad35ff6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2600428
Auto-Submit: Shik Chen <shik@chromium.org>
Reviewed-by: default avatarWei Lee <wtlee@chromium.org>
Commit-Queue: Shik Chen <shik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#839319}
parent 8a3ad768
...@@ -58,6 +58,7 @@ const struct { ...@@ -58,6 +58,7 @@ const struct {
{"barcode_copy_link_button", IDS_BARCODE_COPY_LINK_BUTTON}, {"barcode_copy_link_button", IDS_BARCODE_COPY_LINK_BUTTON},
{"barcode_copy_text_button", IDS_BARCODE_COPY_TEXT_BUTTON}, {"barcode_copy_text_button", IDS_BARCODE_COPY_TEXT_BUTTON},
{"barcode_link_detected", IDS_BARCODE_LINK_DETECTED}, {"barcode_link_detected", IDS_BARCODE_LINK_DETECTED},
{"barcode_text_detected", IDS_BARCODE_TEXT_DETECTED},
{"expert_mode_button", IDS_EXPERT_MODE_BUTTON}, {"expert_mode_button", IDS_EXPERT_MODE_BUTTON},
{"expert_preview_metadata", IDS_EXPERT_PREVIEW_METADATA}, {"expert_preview_metadata", IDS_EXPERT_PREVIEW_METADATA},
{"expert_save_metadata", IDS_EXPERT_SAVE_METADATA}, {"expert_save_metadata", IDS_EXPERT_SAVE_METADATA},
......
...@@ -6,6 +6,7 @@ import {browserProxy} from './browser_proxy/browser_proxy.js'; ...@@ -6,6 +6,7 @@ import {browserProxy} from './browser_proxy/browser_proxy.js';
import {assert} from './chrome_util.js'; import {assert} from './chrome_util.js';
import * as dom from './dom.js'; import * as dom from './dom.js';
import * as snackbar from './snackbar.js'; import * as snackbar from './snackbar.js';
import * as state from './state.js';
import {OneShotTimer} from './timer.js'; import {OneShotTimer} from './timer.js';
// TODO(b/172879638): Tune the duration according to the final motion spec. // TODO(b/172879638): Tune the duration according to the final motion spec.
...@@ -48,7 +49,13 @@ function deactivate() { ...@@ -48,7 +49,13 @@ function deactivate() {
function activate(container) { function activate(container) {
container.classList.remove('invisible'); container.classList.remove('invisible');
currentChip = container; currentChip = container;
currentTimer = new OneShotTimer(deactivate, CHIP_DURATION); currentTimer = new OneShotTimer(deactivate, CHIP_DURATION);
if (state.get(state.State.TAB_NAVIGATION)) {
// Do not auto dismiss the chip when using keyboard for a11y. Screen reader
// might need long time to read the detected content.
currentTimer.stop();
}
} }
/** /**
...@@ -75,6 +82,7 @@ function isSafeUrl(s) { ...@@ -75,6 +82,7 @@ function isSafeUrl(s) {
* @param {string} content The content to be copied. * @param {string} content The content to be copied.
* @param {string} snackbarLabel The label to be displayed on snackbar when the * @param {string} snackbarLabel The label to be displayed on snackbar when the
* content is copied. * content is copied.
* @return {!HTMLElement} The copy button element.
*/ */
function setupCopyButton(container, content, snackbarLabel) { function setupCopyButton(container, content, snackbarLabel) {
const copyButton = const copyButton =
...@@ -83,6 +91,7 @@ function setupCopyButton(container, content, snackbarLabel) { ...@@ -83,6 +91,7 @@ function setupCopyButton(container, content, snackbarLabel) {
await navigator.clipboard.writeText(content); await navigator.clipboard.writeText(content);
snackbar.show(snackbarLabel); snackbar.show(snackbarLabel);
}; };
return copyButton;
} }
/** /**
...@@ -108,7 +117,6 @@ function showUrl(url) { ...@@ -108,7 +117,6 @@ function showUrl(url) {
} }
/** /**
* TODO(b/172879638): Handle a11y.
* Shows an actionable text chip. * Shows an actionable text chip.
* @param {string} text * @param {string} text
*/ */
...@@ -125,9 +133,19 @@ function showText(text) { ...@@ -125,9 +133,19 @@ function showText(text) {
expandEl.classList.toggle('hidden', !expandable); expandEl.classList.toggle('hidden', !expandable);
expandEl.onclick = () => { expandEl.onclick = () => {
container.classList.toggle('expanded'); container.classList.toggle('expanded');
const expanded = container.classList.contains('expanded');
expandEl.setAttribute('aria-expanded', expanded.toString());
}; };
setupCopyButton(container, text, 'snackbar_text_copied'); const copyButton = setupCopyButton(container, text, 'snackbar_text_copied');
// TODO(b/172879638): There is a race in ChromeVox which will speak the
// focused element twice.
if (expandable) {
expandEl.focus();
} else {
copyButton.focus();
}
} }
/** /**
......
...@@ -34,41 +34,44 @@ export class OneShotTimer { ...@@ -34,41 +34,44 @@ export class OneShotTimer {
*/ */
this.timeoutId_ = 0; this.timeoutId_ = 0;
this.start_(); this.start();
} }
/** /**
* Resets the timer delay. * Starts the timer.
*/ */
resetTimeout() { start() {
this.clearPendingTimeout_(); assert(this.timeoutId_ === 0);
this.start_(); this.timeoutId_ = setTimeout(this.handler_, this.timeout_);
} }
/** /**
* Stops the timer and runs the scheduled handler immediately. * Stops the pending timeout.
*/ */
fireNow() { stop() {
this.clearPendingTimeout_(); assert(this.timeoutId_ !== 0);
this.handler_(); clearTimeout(this.timeoutId_);
this.timeoutId_ = 0;
} }
/** /**
* Starts the timer. * Resets the timer delay. It's a no-op if the timer is already stopped.
* @private
*/ */
start_() { resetTimeout() {
assert(this.timeoutId_ === 0); if (this.timeoutId_ === 0) {
this.timeoutId_ = setTimeout(this.handler_, this.timeout_); return;
}
this.stop();
this.start();
} }
/** /**
* Clears the pending timeout. * Stops the timer and runs the scheduled handler immediately.
* @private
*/ */
clearPendingTimeout_() { fireNow() {
assert(this.timeoutId_ !== 0); if (this.timeoutId_ !== 0) {
clearTimeout(this.timeoutId_); this.stop();
this.timeoutId_ = 0; }
this.handler_();
} }
} }
...@@ -333,9 +333,12 @@ ...@@ -333,9 +333,12 @@
<message desc="Label for the copy button of the barcode text chip." name="IDS_BARCODE_COPY_TEXT_BUTTON"> <message desc="Label for the copy button of the barcode text chip." name="IDS_BARCODE_COPY_TEXT_BUTTON">
Copy text Copy text
</message> </message>
<message desc="Label for the copy button of the barcode text chip." name="IDS_BARCODE_LINK_DETECTED"> <message desc="Label for detected link on the barcode chip." name="IDS_BARCODE_LINK_DETECTED">
Link detected. <ph name="hostname">$1<ex>www.google.com</ex></ph> Link detected. <ph name="hostname">$1<ex>www.google.com</ex></ph>
</message> </message>
<message desc="Label for detected text on the barcode chip." name="IDS_BARCODE_TEXT_DETECTED">
Text detected.
</message>
<message desc="Label for the button of expert mode options." name="IDS_EXPERT_MODE_BUTTON"> <message desc="Label for the button of expert mode options." name="IDS_EXPERT_MODE_BUTTON">
Expert mode Expert mode
</message> </message>
......
...@@ -417,10 +417,12 @@ ...@@ -417,10 +417,12 @@
</div> </div>
</div> </div>
<div id="barcode-chip-text-container" <div id="barcode-chip-text-container"
class="invisible barcode-chip-container"> class="invisible barcode-chip-container"
role="dialog" i18n-label="barcode_text_detected"
aria-live="polite" aria-describedby="barcode-chip-text-content">
<div class="barcode-chip-text"> <div class="barcode-chip-text">
<div id="barcode-chip-text-content"></div> <div id="barcode-chip-text-content"></div>
<button id="barcode-chip-text-expand"></button> <button id="barcode-chip-text-expand" aria-expanded="false"></button>
</div> </div>
<div class="circle"> <div class="circle">
<button class="barcode-copy-button" tabindex="0" <button class="barcode-copy-button" tabindex="0"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment