Bug 1525980 - Introduce nsIAccessibleAnnouncementEvent. r=Jamie
authorEitan Isaacson <eitan@monotonous.org>
Thu, 14 Feb 2019 17:42:45 +0000
changeset 459182 31535b57ba501772b191a97ad996e5314b7c96e2
parent 459181 7aaf38e67da9291308820b9005db59f3a8dbfaa6
child 459183 7f128351ac64c04d0d26d56e2e9fb92f79bd4c28
push id35556
push userdvarga@mozilla.com
push dateFri, 15 Feb 2019 01:38:24 +0000
treeherdermozilla-central@b29c87add05f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJamie
bugs1525980
milestone67.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 1525980 - Introduce nsIAccessibleAnnouncementEvent. r=Jamie Differential Revision: https://phabricator.services.mozilla.com/D19060
accessible/base/AccEvent.cpp
accessible/base/AccEvent.h
accessible/base/nsAccessibilityService.h
accessible/generic/Accessible.cpp
accessible/generic/Accessible.h
accessible/interfaces/moz.build
accessible/interfaces/nsIAccessible.idl
accessible/interfaces/nsIAccessibleAnnouncementEvent.idl
accessible/interfaces/nsIAccessibleEvent.idl
accessible/tests/mochitest/common.js
accessible/tests/mochitest/events.js
accessible/tests/mochitest/events/a11y.ini
accessible/tests/mochitest/events/test_announcement.html
accessible/windows/msaa/nsEventMap.h
accessible/xpcom/AccEvents.conf
accessible/xpcom/xpcAccessible.cpp
accessible/xpcom/xpcAccessible.h
--- a/accessible/base/AccEvent.cpp
+++ b/accessible/base/AccEvent.cpp
@@ -257,12 +257,20 @@ already_AddRefed<nsIAccessibleEvent> a11
   if (eventGroup & (1 << AccEvent::eScrollingEvent)) {
     AccScrollingEvent* sa = downcast_accEvent(aEvent);
     xpEvent = new xpcAccScrollingEvent(
         type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, sa->ScrollX(),
         sa->ScrollY(), sa->MaxScrollX(), sa->MaxScrollY());
     return xpEvent.forget();
   }
 
+  if (eventGroup & (1 << AccEvent::eAnnouncementEvent)) {
+    AccAnnouncementEvent* aa = downcast_accEvent(aEvent);
+    xpEvent = new xpcAccAnnouncementEvent(type, ToXPC(acc), ToXPCDocument(doc),
+                                          node, fromUser, aa->Announcement(),
+                                          aa->Priority());
+    return xpEvent.forget();
+  }
+
   xpEvent =
       new xpcAccEvent(type, ToXPC(acc), ToXPCDocument(doc), node, fromUser);
   return xpEvent.forget();
 }
--- a/accessible/base/AccEvent.h
+++ b/accessible/base/AccEvent.h
@@ -99,16 +99,17 @@ class AccEvent {
     eShowEvent,
     eCaretMoveEvent,
     eTextSelChangeEvent,
     eSelectionChangeEvent,
     eTableChangeEvent,
     eVirtualCursorChangeEvent,
     eObjectAttrChangedEvent,
     eScrollingEvent,
+    eAnnouncementEvent,
   };
 
   static const EventGroup kEventGroup = eGenericEvent;
   virtual unsigned int GetEventGroups() const { return 1U << eGenericEvent; }
 
   /**
    * Reference counting and cycle collection.
    */
@@ -540,16 +541,44 @@ class AccScrollingEvent : public AccEven
  private:
   uint32_t mScrollX;
   uint32_t mScrollY;
   uint32_t mMaxScrollX;
   uint32_t mMaxScrollY;
 };
 
 /**
+ * Accessible announcement event.
+ */
+class AccAnnouncementEvent : public AccEvent {
+ public:
+  AccAnnouncementEvent(Accessible* aAccessible, const nsAString& aAnnouncement,
+                       uint16_t aPriority)
+      : AccEvent(nsIAccessibleEvent::EVENT_ANNOUNCEMENT, aAccessible),
+        mAnnouncement(aAnnouncement),
+        mPriority(aPriority) {}
+
+  virtual ~AccAnnouncementEvent() {}
+
+  // AccEvent
+  static const EventGroup kEventGroup = eAnnouncementEvent;
+  virtual unsigned int GetEventGroups() const override {
+    return AccEvent::GetEventGroups() | (1U << eAnnouncementEvent);
+  }
+
+  const nsString& Announcement() const { return mAnnouncement; }
+
+  uint16_t Priority() { return mPriority; }
+
+ private:
+  nsString mAnnouncement;
+  uint16_t mPriority;
+};
+
+/**
  * Downcast the generic accessible event object to derived type.
  */
 class downcast_accEvent {
  public:
   explicit downcast_accEvent(AccEvent* e) : mRawPtr(e) {}
 
   template <class Destination>
   operator Destination*() {
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -460,11 +460,12 @@ static const char kEventTypeNames[][40] 
     "hypertext link selected",          // EVENT_HYPERTEXT_LINK_SELECTED
     "hyperlink start index changed",    // EVENT_HYPERLINK_START_INDEX_CHANGED
     "hypertext changed",                // EVENT_HYPERTEXT_CHANGED
     "hypertext links count changed",    // EVENT_HYPERTEXT_NLINKS_CHANGED
     "object attribute changed",         // EVENT_OBJECT_ATTRIBUTE_CHANGED
     "virtual cursor changed",           // EVENT_VIRTUALCURSOR_CHANGED
     "text value change",                // EVENT_TEXT_VALUE_CHANGE
     "scrolling",                        // EVENT_SCROLLING
+    "announcement",                     // EVENT_ANNOUNCEMENT
 };
 
 #endif
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -2436,16 +2436,22 @@ Accessible* Accessible::ContainerWidget(
 
       // Don't cross DOM document boundaries.
       if (parent->IsDoc()) break;
     }
   }
   return nullptr;
 }
 
+void Accessible::Announce(const nsAString& aAnnouncement, uint16_t aPriority) {
+  RefPtr<AccAnnouncementEvent> event =
+      new AccAnnouncementEvent(this, aAnnouncement, aPriority);
+  nsEventShell::FireEvent(event);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible protected methods
 
 void Accessible::LastRelease() {
   // First cleanup if needed...
   if (mDoc) {
     Shutdown();
     NS_ASSERTION(!mDoc,
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -975,16 +975,18 @@ class Accessible : public nsISupports {
    */
   void SetShowEventTarget(bool aTarget) { mShowEventTarget = aTarget; }
 
   /**
    * Set if this accessible is a hide event target.
    */
   void SetHideEventTarget(bool aTarget) { mHideEventTarget = aTarget; }
 
+  void Announce(const nsAString& aAnnouncement, uint16_t aPriority);
+
  protected:
   virtual ~Accessible();
 
   /**
    * Return the accessible name provided by native markup. It doesn't take
    * into account ARIA markup used to specify the name.
    */
   virtual mozilla::a11y::ENameValueFlag NativeName(nsString& aName) const;
--- a/accessible/interfaces/moz.build
+++ b/accessible/interfaces/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows' and CONFIG['COMPILE_ENVIRONMENT']:
     DIRS += ['gecko', 'msaa', 'ia2']
 
 XPIDL_SOURCES += [
     'nsIAccessibilityService.idl',
     'nsIAccessible.idl',
+    'nsIAccessibleAnnouncementEvent.idl',
     'nsIAccessibleApplication.idl',
     'nsIAccessibleCaretMoveEvent.idl',
     'nsIAccessibleDocument.idl',
     'nsIAccessibleEditableText.idl',
     'nsIAccessibleEvent.idl',
     'nsIAccessibleHideEvent.idl',
     'nsIAccessibleHyperLink.idl',
     'nsIAccessibleHyperText.idl',
--- a/accessible/interfaces/nsIAccessible.idl
+++ b/accessible/interfaces/nsIAccessible.idl
@@ -293,14 +293,24 @@ interface nsIAccessible : nsISupports
    * @param coordinateType [in] - specifies whether the coordinates are relative to
    *                         the screen or the parent object (for available
    *                         constants refer to nsIAccessibleCoordinateType)
    * @param x [in] - defines the x coordinate
    * @param y [in] - defines the y coordinate
   */
   void scrollToPoint(in unsigned long coordinateType, in long x, in long y);
 
+  /**
+   * Dispatches an ANNOUNCEMENT event with this accessible as target.
+   *
+   * @param announcement [in] - string to use in announcement.
+   * @param priority [in] - priority for announcement, could be
+   *                      nsIAccessibleAnnouncementEvent.POLITE or
+   *                      nsIAccessibleAnnouncementEvent.ASSERTIVE.
+   */
+  void announce(in AString announcement, in unsigned short priority);
+
   %{C++
   virtual mozilla::a11y::Accessible* ToInternalAccessible() const = 0;
   %}
 
 };
 
new file mode 100644
--- /dev/null
+++ b/accessible/interfaces/nsIAccessibleAnnouncementEvent.idl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/* 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/. */
+
+#include "nsIAccessibleEvent.idl"
+
+/**
+ * Fired when announce() is called on the target accessible.
+ */
+[scriptable, builtinclass, uuid(8818e49c-1286-4fe6-ae82-4d1b795ec88d)]
+interface nsIAccessibleAnnouncementEvent : nsIAccessibleEvent
+{
+  const unsigned short POLITE = 0;
+  const unsigned short ASSERTIVE = 1;
+
+  // String of actual announcement
+  readonly attribute AString announcement;
+
+  // Priority for announcement, could be POLITE or ASSERTIVE, ATs
+  // will decide how to appropriately present it.
+  readonly attribute unsigned short priority;
+};
--- a/accessible/interfaces/nsIAccessibleEvent.idl
+++ b/accessible/interfaces/nsIAccessibleEvent.idl
@@ -419,19 +419,24 @@ interface nsIAccessibleEvent : nsISuppor
   const unsigned long EVENT_TEXT_VALUE_CHANGE = 0x0057;
 
   /**
    * An accessible's viewport is scrolling.
    */
   const unsigned long EVENT_SCROLLING = 0x0058;
 
   /**
+   * An accessible is making an explicit announcement.
+   */
+  const unsigned long EVENT_ANNOUNCEMENT = 0x0059;
+
+  /**
    * Help make sure event map does not get out-of-line.
    */
-  const unsigned long EVENT_LAST_ENTRY = 0x0059;
+  const unsigned long EVENT_LAST_ENTRY = 0x005A;
 
   /**
    * The type of event, based on the enumerated event values
    * defined in this interface.
    */
   readonly attribute unsigned long eventType;
   
   /**
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -11,16 +11,18 @@ const nsIAccessibleCaretMoveEvent =
 const nsIAccessibleScrollingEvent =
   Ci.nsIAccessibleScrollingEvent;
 const nsIAccessibleTextChangeEvent =
   Ci.nsIAccessibleTextChangeEvent;
 const nsIAccessibleVirtualCursorChangeEvent =
   Ci.nsIAccessibleVirtualCursorChangeEvent;
 const nsIAccessibleObjectAttributeChangedEvent =
   Ci.nsIAccessibleObjectAttributeChangedEvent;
+const nsIAccessibleAnnouncementEvent =
+  Ci.nsIAccessibleAnnouncementEvent;
 
 const nsIAccessibleStates = Ci.nsIAccessibleStates;
 const nsIAccessibleRole = Ci.nsIAccessibleRole;
 const nsIAccessibleScrollType = Ci.nsIAccessibleScrollType;
 const nsIAccessibleCoordinateType = Ci.nsIAccessibleCoordinateType;
 
 const nsIAccessibleRelation = Ci.nsIAccessibleRelation;
 const nsIAccessibleTextRange = Ci.nsIAccessibleTextRange;
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -1,15 +1,16 @@
 // XXX Bug 1425371 - enable no-redeclare and fix the issues with the tests.
 /* eslint-disable no-redeclare */
 
 // //////////////////////////////////////////////////////////////////////////////
 // Constants
 
 const EVENT_ALERT = nsIAccessibleEvent.EVENT_ALERT;
+const EVENT_ANNOUNCEMENT = nsIAccessibleEvent.EVENT_ANNOUNCEMENT;
 const EVENT_DESCRIPTION_CHANGE = nsIAccessibleEvent.EVENT_DESCRIPTION_CHANGE;
 const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
 const EVENT_DOCUMENT_RELOAD = nsIAccessibleEvent.EVENT_DOCUMENT_RELOAD;
 const EVENT_DOCUMENT_LOAD_STOPPED = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPPED;
 const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
 const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
 const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
 const EVENT_MENU_START = nsIAccessibleEvent.EVENT_MENU_START;
--- a/accessible/tests/mochitest/events/a11y.ini
+++ b/accessible/tests/mochitest/events/a11y.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 support-files =
   focus.html
   scroll.html
   !/accessible/tests/mochitest/*.js
   !/accessible/tests/mochitest/letters.gif
 
+[test_announcement.html]
 [test_aria_alert.html]
 [test_aria_menu.html]
 [test_aria_objattr.html]
 [test_aria_owns.html]
 [test_aria_statechange.html]
 [test_attrs.html]
 [test_bug1322593.html]
 [test_bug1322593-2.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_announcement.html
@@ -0,0 +1,62 @@
+<html>
+
+<head>
+  <title>Announcement event and method testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    async function doTests() {
+      let acc = getAccessible("display");
+
+      let onAnnounce = waitForEventPromise(EVENT_ANNOUNCEMENT, acc);
+      acc.announce("please", nsIAccessibleAnnouncementEvent.POLITE);
+      let evt = await onAnnounce;
+      evt.QueryInterface(nsIAccessibleAnnouncementEvent);
+      is(evt.announcement, "please", "announcement matches.");
+      is(evt.priority, nsIAccessibleAnnouncementEvent.POLITE, "priority matches");
+
+      onAnnounce = waitForEventPromise(EVENT_ANNOUNCEMENT, acc);
+      acc.announce("do it", nsIAccessibleAnnouncementEvent.ASSERTIVE);
+      evt = await onAnnounce;
+      evt.QueryInterface(nsIAccessibleAnnouncementEvent);
+      is(evt.announcement, "do it", "announcement matches.");
+      is(evt.priority, nsIAccessibleAnnouncementEvent.ASSERTIVE,
+        "priority matches");
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1525980"
+     title="Introduce announcement event and method">
+    Mozilla Bug 1525980
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+</body>
+</html>
--- a/accessible/windows/msaa/nsEventMap.h
+++ b/accessible/windows/msaa/nsEventMap.h
@@ -96,10 +96,11 @@ static const uint32_t gWinEventMap[] = {
   IA2_EVENT_HYPERTEXT_LINK_SELECTED,                 // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_SELECTED
   IA2_EVENT_HYPERLINK_START_INDEX_CHANGED,           // nsIAccessibleEvent::EVENT_HYPERLINK_START_INDEX_CHANGED
   IA2_EVENT_HYPERTEXT_CHANGED,                       // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
   IA2_EVENT_HYPERTEXT_NLINKS_CHANGED,                // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
   IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED,                // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED
   EVENT_OBJECT_VALUECHANGE,                          // nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_SCROLLING
+  kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_ANNOUNCEMENT
     // clang-format on
 };
--- a/accessible/xpcom/AccEvents.conf
+++ b/accessible/xpcom/AccEvents.conf
@@ -10,10 +10,11 @@ simple_events = [
     'Event',
     'StateChangeEvent',
     'TextChangeEvent',
     'HideEvent',
     'CaretMoveEvent',
     'ObjectAttributeChangedEvent',
     'TableChangeEvent',
     'VirtualCursorChangeEvent',
-    'ScrollingEvent'
+    'ScrollingEvent',
+    'AnnouncementEvent'
   ]
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -752,8 +752,18 @@ xpcAccessible::ScrollToPoint(uint32_t aC
     proxy->ScrollToPoint(aCoordinateType, aX, aY);
 #endif
   } else {
     Intl()->ScrollToPoint(aCoordinateType, aX, aY);
   }
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+xpcAccessible::Announce(const nsAString& aAnnouncement, uint16_t aPriority) {
+  if (IntlGeneric().IsProxy()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  } else {
+    Intl()->Announce(aAnnouncement, aPriority);
+  }
+  return NS_OK;
+}
--- a/accessible/xpcom/xpcAccessible.h
+++ b/accessible/xpcom/xpcAccessible.h
@@ -77,16 +77,18 @@ class xpcAccessible : public nsIAccessib
   NS_IMETHOD GetActionDescription(uint8_t aIndex,
                                   nsAString& aDescription) final;
   NS_IMETHOD DoAction(uint8_t aIndex) final;
 
   NS_IMETHOD ScrollTo(uint32_t aHow) final;
   NS_IMETHOD ScrollToPoint(uint32_t aCoordinateType, int32_t aX,
                            int32_t aY) final;
 
+  NS_IMETHOD Announce(const nsAString& aAnnouncement, uint16_t aPriority) final;
+
  protected:
   xpcAccessible() {}
   virtual ~xpcAccessible() {}
 
  private:
   Accessible* Intl();
   AccessibleOrProxy IntlGeneric();