06ed9f914500ac1c3b0bccbc1058fdc3e8cfc383438cbe39801c652f186f0e3f777b950fe90cdb1da5d1a3273905283fee91f4950f22ba1957636699626d28 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import * as dom from '../../dom.js';
  6. import { isMacintosh } from '../../../common/platform.js';
  7. import './aria.css';
  8. // Use a max length since we are inserting the whole msg in the DOM and that can cause browsers to freeze for long messages #94233
  9. const MAX_MESSAGE_LENGTH = 20000;
  10. let ariaContainer;
  11. let alertContainer;
  12. let alertContainer2;
  13. let statusContainer;
  14. let statusContainer2;
  15. export function setARIAContainer(parent) {
  16. ariaContainer = document.createElement('div');
  17. ariaContainer.className = 'monaco-aria-container';
  18. const createAlertContainer = () => {
  19. const element = document.createElement('div');
  20. element.className = 'monaco-alert';
  21. element.setAttribute('role', 'alert');
  22. element.setAttribute('aria-atomic', 'true');
  23. ariaContainer.appendChild(element);
  24. return element;
  25. };
  26. alertContainer = createAlertContainer();
  27. alertContainer2 = createAlertContainer();
  28. const createStatusContainer = () => {
  29. const element = document.createElement('div');
  30. element.className = 'monaco-status';
  31. element.setAttribute('role', 'complementary');
  32. element.setAttribute('aria-live', 'polite');
  33. element.setAttribute('aria-atomic', 'true');
  34. ariaContainer.appendChild(element);
  35. return element;
  36. };
  37. statusContainer = createStatusContainer();
  38. statusContainer2 = createStatusContainer();
  39. parent.appendChild(ariaContainer);
  40. }
  41. /**
  42. * Given the provided message, will make sure that it is read as alert to screen readers.
  43. */
  44. export function alert(msg) {
  45. if (!ariaContainer) {
  46. return;
  47. }
  48. // Use alternate containers such that duplicated messages get read out by screen readers #99466
  49. if (alertContainer.textContent !== msg) {
  50. dom.clearNode(alertContainer2);
  51. insertMessage(alertContainer, msg);
  52. }
  53. else {
  54. dom.clearNode(alertContainer);
  55. insertMessage(alertContainer2, msg);
  56. }
  57. }
  58. /**
  59. * Given the provided message, will make sure that it is read as status to screen readers.
  60. */
  61. export function status(msg) {
  62. if (!ariaContainer) {
  63. return;
  64. }
  65. if (isMacintosh) {
  66. alert(msg); // VoiceOver does not seem to support status role
  67. }
  68. else {
  69. if (statusContainer.textContent !== msg) {
  70. dom.clearNode(statusContainer2);
  71. insertMessage(statusContainer, msg);
  72. }
  73. else {
  74. dom.clearNode(statusContainer);
  75. insertMessage(statusContainer2, msg);
  76. }
  77. }
  78. }
  79. function insertMessage(target, msg) {
  80. dom.clearNode(target);
  81. if (msg.length > MAX_MESSAGE_LENGTH) {
  82. msg = msg.substr(0, MAX_MESSAGE_LENGTH);
  83. }
  84. target.textContent = msg;
  85. // See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/
  86. target.style.visibility = 'hidden';
  87. target.style.visibility = 'visible';
  88. }