Bug 1624936: Add required state caching, attribute handling for mozCheckboxAccessibles. r=eeejay
authorMorgan Reschenberg <mreschenberg@mozilla.com>
Tue, 28 Apr 2020 18:19:39 +0000
changeset 526551 160413fb8acbb4101934c8e92920b1141f8a4f36
parent 526550 d117eac2aafe1493ae3d3d5b494368d8764a7cc9
child 526552 3f6c57853e5fddddcdf86be5512b89e717a282f8
push id37358
push useropoprus@mozilla.com
push dateWed, 29 Apr 2020 03:05:14 +0000
treeherdermozilla-central@6bb8423186c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerseeejay
bugs1624936
milestone77.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 1624936: Add required state caching, attribute handling for mozCheckboxAccessibles. r=eeejay Differential Revision: https://phabricator.services.mozilla.com/D70231
accessible/generic/DocAccessible.cpp
accessible/mac/mozAccessible.mm
accessible/mac/mozActionElements.mm
accessible/tests/browser/mac/browser.ini
accessible/tests/browser/mac/browser_required.js
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1110,17 +1110,17 @@ void DocAccessible::ContentStateChanged(
   if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
     RefPtr<AccEvent> event =
         new AccStateChangeEvent(accessible, states::INVALID, true);
     FireDelayedEvent(event);
   }
 
   if (aStateMask.HasState(NS_EVENT_STATE_REQUIRED)) {
     RefPtr<AccEvent> event =
-        new AccStateChangeEvent(accessible, states::REQUIRED, true);
+        new AccStateChangeEvent(accessible, states::REQUIRED);
     FireDelayedEvent(event);
   }
 
   if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
     RefPtr<AccEvent> event =
         new AccStateChangeEvent(accessible, states::TRAVERSED, true);
     FireDelayedEvent(event);
   }
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -28,16 +28,17 @@
 #include "nsCoord.h"
 #include "nsObjCExceptions.h"
 #include "nsWhitespaceTokenizer.h"
 #include <prdtoa.h>
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
+#define NSAccessibilityRequiredAttribute @"AXRequired"
 #define NSAccessibilityARIACurrentAttribute @"AXARIACurrent"
 #define NSAccessibilityDOMIdentifierAttribute @"AXDOMIdentifier"
 #define NSAccessibilityHasPopupAttribute @"AXHasPopup"
 #define NSAccessibilityMathRootRadicandAttribute @"AXMathRootRadicand"
 #define NSAccessibilityMathRootIndexAttribute @"AXMathRootIndex"
 #define NSAccessibilityMathFractionNumeratorAttribute @"AXMathFractionNumerator"
 #define NSAccessibilityMathFractionDenominatorAttribute @"AXMathFractionDenominator"
 #define NSAccessibilityMathBaseAttribute @"AXMathBase"
@@ -206,16 +207,17 @@ static inline NSMutableArray* ConvertToN
                         NSAccessibilityRoleAttribute, NSAccessibilityTitleAttribute,
                         NSAccessibilityValueAttribute, NSAccessibilitySubroleAttribute,
                         NSAccessibilityRoleDescriptionAttribute, NSAccessibilityPositionAttribute,
                         NSAccessibilityEnabledAttribute, NSAccessibilitySizeAttribute,
                         NSAccessibilityWindowAttribute, NSAccessibilityFocusedAttribute,
                         NSAccessibilityHelpAttribute, NSAccessibilityTitleUIElementAttribute,
                         NSAccessibilityTopLevelUIElementAttribute, NSAccessibilityHasPopupAttribute,
                         NSAccessibilityARIACurrentAttribute, NSAccessibilitySelectedAttribute,
+                        NSAccessibilityRequiredAttribute,
 #if DEBUG
                         @"AXMozDescription",
 #endif
                         nil];
   }
 
   NSArray* objectAttributes = generalAttributes;
 
@@ -391,16 +393,20 @@ static const uint64_t kCacheInitialized 
     nsAutoString id;
     if (accWrap && accWrap->GetContent())
       nsCoreUtils::GetID(accWrap->GetContent(), id);
     else
       proxy->DOMNodeID(id);
     return nsCocoaUtils::ToNSString(id);
   }
 
+  if ([attribute isEqualToString:NSAccessibilityRequiredAttribute]) {
+    return [NSNumber numberWithBool:[self stateWithMask:states::REQUIRED] != 0];
+  }
+
   switch (mRole) {
     case roles::MATHML_ROOT:
       if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
         return [self childAt:0];
       if ([attribute isEqualToString:NSAccessibilityMathRootIndexAttribute])
         return [self childAt:1];
       break;
     case roles::MATHML_SQUARE_ROOT:
--- a/accessible/mac/mozActionElements.mm
+++ b/accessible/mac/mozActionElements.mm
@@ -38,16 +38,17 @@ enum CheckboxValue {
                         NSAccessibilityWindowAttribute,             // required
                         NSAccessibilityPositionAttribute,           // required
                         NSAccessibilityTopLevelUIElementAttribute,  // required
                         NSAccessibilityHelpAttribute,
                         NSAccessibilityEnabledAttribute,  // required
                         NSAccessibilityFocusedAttribute,  // required
                         NSAccessibilityTitleAttribute,    // required
                         NSAccessibilityChildrenAttribute, NSAccessibilityDescriptionAttribute,
+                        NSAccessibilityRequiredAttribute,
 #if DEBUG
                         @"AXMozDescription",
 #endif
                         nil];
   }
   return attributes;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
--- a/accessible/tests/browser/mac/browser.ini
+++ b/accessible/tests/browser/mac/browser.ini
@@ -14,8 +14,9 @@ support-files =
 [browser_details_summary.js]
 [browser_label_title.js]
 [browser_range.js]
 [browser_roles_elements.js]
 [browser_table.js]
 [browser_selectables.js]
 [browser_toggle_radio_check.js]
 [browser_link.js]
+[browser_required.js]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/mac/browser_required.js
@@ -0,0 +1,145 @@
+/* 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";
+
+/* import-globals-from ../../mochitest/role.js */
+/* import-globals-from ../../mochitest/states.js */
+loadScripts(
+  { name: "role.js", dir: MOCHITESTS_DIR },
+  { name: "states.js", dir: MOCHITESTS_DIR }
+);
+
+/**
+ * Test required and aria-required attributes on checkboxes
+ * and radio buttons.
+ */
+addAccessibleTask(
+  `
+  <form>
+    <input type="checkbox" id="checkbox" required>
+   <br>
+    <input type="radio" id="radio" required>
+   <br>
+    <input type="checkbox" id="ariaCheckbox" aria-required="true">
+   <br>
+    <input type="radio" id="ariaRadio" aria-required="true">
+  </form>
+  `,
+  async (browser, accDoc) => {
+    // Check initial AXRequired values are correct
+    let radio = getNativeInterface(accDoc, "radio");
+    is(
+      radio.getAttributeValue("AXRequired"),
+      1,
+      "Correct required val for radio"
+    );
+
+    let ariaRadio = getNativeInterface(accDoc, "ariaRadio");
+    is(
+      ariaRadio.getAttributeValue("AXRequired"),
+      1,
+      "Correct required val for ariaRadio"
+    );
+
+    let checkbox = getNativeInterface(accDoc, "checkbox");
+    is(
+      checkbox.getAttributeValue("AXRequired"),
+      1,
+      "Correct required val for checkbox"
+    );
+
+    let ariaCheckbox = getNativeInterface(accDoc, "ariaCheckbox");
+    is(
+      ariaCheckbox.getAttributeValue("AXRequired"),
+      1,
+      "Correct required val for ariaCheckbox"
+    );
+
+    // Change aria-required, verify AXRequired is updated
+    let stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaCheckbox");
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document
+        .getElementById("ariaCheckbox")
+        .setAttribute("aria-required", "false");
+    });
+    await stateChanged;
+
+    is(
+      ariaCheckbox.getAttributeValue("AXRequired"),
+      0,
+      "Correct required after false set for ariaCheckbox"
+    );
+
+    // Remove aria-required, verify AXRequired is updated
+    stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaCheckbox");
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document
+        .getElementById("ariaCheckbox")
+        .removeAttribute("aria-required");
+    });
+    await stateChanged;
+
+    is(
+      ariaCheckbox.getAttributeValue("AXRequired"),
+      0,
+      "Correct required after removal for ariaCheckbox"
+    );
+
+    // Change aria-required, verify AXRequired is updated
+    stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaRadio");
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document
+        .getElementById("ariaRadio")
+        .setAttribute("aria-required", "false");
+    });
+    await stateChanged;
+
+    is(
+      ariaRadio.getAttributeValue("AXRequired"),
+      0,
+      "Correct required after false set for ariaRadio"
+    );
+
+    // Remove aria-required, verify AXRequired is updated
+    stateChanged = waitForEvent(EVENT_STATE_CHANGE, "ariaRadio");
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document
+        .getElementById("ariaRadio")
+        .removeAttribute("aria-required");
+    });
+    await stateChanged;
+
+    is(
+      ariaRadio.getAttributeValue("AXRequired"),
+      0,
+      "Correct required after removal for ariaRadio"
+    );
+
+    // Remove required, verify AXRequired is updated
+    stateChanged = waitForEvent(EVENT_STATE_CHANGE, "checkbox");
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document.getElementById("checkbox").removeAttribute("required");
+    });
+    await stateChanged;
+
+    is(
+      checkbox.getAttributeValue("AXRequired"),
+      0,
+      "Correct required after removal for checkbox"
+    );
+
+    stateChanged = waitForEvent(EVENT_STATE_CHANGE, "radio");
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document.getElementById("radio").removeAttribute("required");
+    });
+    await stateChanged;
+
+    is(
+      checkbox.getAttributeValue("AXRequired"),
+      0,
+      "Correct required after removal for radio"
+    );
+  }
+);