Bug 493683 part 5: When aria-labell/describedby is set, set HasName/DescriptionDependent on the target subtree. r=eeejay
authorJames Teh <jteh@mozilla.com>
Mon, 25 Jan 2021 17:31:19 +0000
changeset 564811 2c9004c95a7a7d39c2c1417b66fda37892398263
parent 564810 2a3f24bdb8e2adc88494f03e331abe0f81460f12
child 564812 0d0bce9329de0f3eea56921594233ba72089dbf2
push id135063
push userjteh@mozilla.com
push dateWed, 27 Jan 2021 05:35:39 +0000
treeherderautoland@2c9004c95a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerseeejay
bugs493683
milestone87.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 493683 part 5: When aria-labell/describedby is set, set HasName/DescriptionDependent on the target subtree. r=eeejay Differential Revision: https://phabricator.services.mozilla.com/D102680
accessible/generic/DocAccessible.cpp
accessible/tests/mochitest/events/test_descrchange.html
accessible/tests/mochitest/events/test_namechange.html
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -11,16 +11,17 @@
 #include "HTMLImageMapAccessible.h"
 #include "nsAccCache.h"
 #include "nsAccessiblePivot.h"
 #include "nsAccUtils.h"
 #include "nsDeckFrame.h"
 #include "nsEventShell.h"
 #include "nsLayoutUtils.h"
 #include "nsTextEquivUtils.h"
+#include "Pivot.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "TreeWalker.h"
 #include "xpcAccessibleDocument.h"
 
 #include "nsCommandManager.h"
 #include "nsContentUtils.h"
 #include "nsIDocShell.h"
@@ -846,25 +847,60 @@ void DocAccessible::AttributeChangedImpl
   // Fire name change and description change events. XXX: it's not complete and
   // dupes the code logic of accessible name and description calculation, we do
   // that for performance reasons.
   if (aAttribute == nsGkAtoms::aria_label) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
     return;
   }
 
+  dom::Element* elm = aAccessible->GetContent()->AsElement();
   if (aAttribute == nsGkAtoms::aria_describedby) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
+    if (aModType == dom::MutationEvent_Binding::MODIFICATION ||
+        aModType == dom::MutationEvent_Binding::ADDITION) {
+      // The subtrees of the new aria-describedby targets might be used to
+      // compute the description for aAccessible. Therefore, we need to set
+      // the eHasDescriptionDependent flag on all Accessibles in these subtrees.
+      IDRefsIterator iter(this, aAccessible->Elm(),
+                          nsGkAtoms::aria_describedby);
+      while (Accessible* target = iter.Next()) {
+        Pivot pivot(target);
+        LocalAccInSameDocRule rule;
+        for (AccessibleOrProxy anchor(target); !anchor.IsNull();
+             anchor = pivot.Next(anchor, rule)) {
+          Accessible* acc = anchor.AsAccessible();
+          MOZ_ASSERT(acc);
+          acc->mContextFlags |= eHasDescriptionDependent;
+        }
+      }
+    }
     return;
   }
 
-  dom::Element* elm = aAccessible->GetContent()->AsElement();
   if (aAttribute == nsGkAtoms::aria_labelledby &&
       !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label)) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
+    if (aModType == dom::MutationEvent_Binding::MODIFICATION ||
+        aModType == dom::MutationEvent_Binding::ADDITION) {
+      // The subtrees of the new aria-labelledby targets might be used to
+      // compute the name for aAccessible. Therefore, we need to set
+      // the eHasNameDependent flag on all Accessibles in these subtrees.
+      IDRefsIterator iter(this, aAccessible->Elm(), nsGkAtoms::aria_labelledby);
+      while (Accessible* target = iter.Next()) {
+        Pivot pivot(target);
+        LocalAccInSameDocRule rule;
+        for (AccessibleOrProxy anchor(target); !anchor.IsNull();
+             anchor = pivot.Next(anchor, rule)) {
+          Accessible* acc = anchor.AsAccessible();
+          MOZ_ASSERT(acc);
+          acc->mContextFlags |= eHasNameDependent;
+        }
+      }
+    }
     return;
   }
 
   if (aAttribute == nsGkAtoms::alt &&
       !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
       !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby)) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
     return;
--- a/accessible/tests/mochitest/events/test_descrchange.html
+++ b/accessible/tests/mochitest/events/test_descrchange.html
@@ -85,16 +85,32 @@
       descChanged = PromEvents.waitForEvent(
         EVENT_DESCRIPTION_CHANGE,
         describedBy
       );
       info("Changing text of aria-describedby target's child");
       getNode("descriptionChild").textContent = "d4";
       await descChanged;
 
+      const lateDescribedBy = getNode("lateDescribedBy");
+      descChanged = PromEvents.waitForEvent(
+        EVENT_DESCRIPTION_CHANGE,
+        lateDescribedBy
+      );
+      info("Setting aria-describedby");
+      lateDescribedBy.setAttribute("aria-describedby", "lateDescription");
+      await descChanged;
+      descChanged = PromEvents.waitForEvent(
+        EVENT_DESCRIPTION_CHANGE,
+        lateDescribedBy
+      );
+      info("Changing text of late aria-describedby target's child");
+      getNode("lateDescriptionChild").textContent = "d2";
+      await descChanged;
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
@@ -112,11 +128,14 @@
   </pre>
 
   <button id="tst1">btn1</button>
   <button id="tst2">btn2</button>
 
   <div id="describedBy" aria-describedby="description"></div>
   <div id="description">d1</div>
 
+  <div id="lateDescribedBy"></div>
+  <div id="lateDescription"><p id="lateDescriptionChild">d1</p></div>
+
   <div id="eventdump"></div>
 </body>
 </html>
--- a/accessible/tests/mochitest/events/test_namechange.html
+++ b/accessible/tests/mochitest/events/test_namechange.html
@@ -114,16 +114,26 @@
       info("Adding node to aria-labelledby target");
       label.innerHTML = '<p id="labelChild">l3</p>';
       await nameChanged;
       nameChanged = PromEvents.waitForEvent(EVENT_NAME_CHANGE, labelledBy);
       info("Changing text of aria-labelledby target's child");
       getNode("labelChild").textContent = "l4";
       await nameChanged;
 
+      const lateLabelledBy = getNode("lateLabelledBy");
+      nameChanged = PromEvents.waitForEvent(EVENT_NAME_CHANGE, lateLabelledBy);
+      info("Setting aria-labelledby");
+      lateLabelledBy.setAttribute("aria-labelledby", "lateLabel");
+      await nameChanged;
+      nameChanged = PromEvents.waitForEvent(EVENT_NAME_CHANGE, lateLabelledBy);
+      info("Changing text of late aria-labelledby target's child");
+      getNode("lateLabelChild").textContent = "l2";
+      await nameChanged;
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
@@ -143,11 +153,14 @@
   <img id="tst1" alt="initial" src="../moz.png">
   <img id="tst2" src="../moz.png">
   <img id="tst3">
   <img id="tst4" src="../moz.png">
 
   <div id="labelledBy" aria-labelledby="label"></div>
   <div id="label">l1</div>
 
+  <div id="lateLabelledBy"></div>
+  <div id="lateLabel"><p id="lateLabelChild">l1</p></div>
+
   <div id="eventdump"></div>
 </body>
 </html>