Bug 1506514 - Don't create accessibles for unselected deck panel descendants. r=Jamie
authorEitan Isaacson <eitan@monotonous.org>
Mon, 20 May 2019 15:54:56 +0000
changeset 474573 f0d9b81a5aedaad55a49c61a951989f1e385c2cc
parent 474572 5cc7612688c546542c3b1c654063e1bb3eb64ca3
child 474574 eb1763edca85fb806e76a65a7a7a9ea80bf19a65
push id36042
push userdvarga@mozilla.com
push dateTue, 21 May 2019 04:19:40 +0000
treeherdermozilla-central@ca560ff55451 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJamie
bugs1506514
milestone69.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 1506514 - Don't create accessibles for unselected deck panel descendants. r=Jamie Differential Revision: https://phabricator.services.mozilla.com/D31656
accessible/base/NotificationController.cpp
accessible/generic/DocAccessible-inl.h
accessible/generic/DocAccessible.cpp
accessible/generic/DocAccessible.h
accessible/tests/mochitest/treeupdate/test_deck.xul
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -746,18 +746,18 @@ void NotificationController::WillRefresh
         logging::Node("content", textNode);
         logging::MsgEnd();
       }
 #endif
 
       MOZ_ASSERT(mDocument->AccessibleOrTrueContainer(containerNode),
                  "Text node having rendered text hasn't accessible document!");
 
-      Accessible* container = mDocument->AccessibleOrTrueContainer(
-          containerNode, DocAccessible::eNoContainerIfARIAHidden);
+      Accessible* container =
+          mDocument->AccessibleOrTrueContainer(containerNode, true);
       if (container) {
         nsTArray<nsCOMPtr<nsIContent>>* list =
             mContentInsertions.LookupOrAdd(container);
         list->AppendElement(textNode);
       }
     }
   }
   mTextHash.Clear();
--- a/accessible/generic/DocAccessible-inl.h
+++ b/accessible/generic/DocAccessible-inl.h
@@ -18,20 +18,20 @@
 #ifdef A11Y_LOG
 #  include "Logging.h"
 #endif
 
 namespace mozilla {
 namespace a11y {
 
 inline Accessible* DocAccessible::AccessibleOrTrueContainer(
-    nsINode* aNode, int aIgnoreARIAHidden) const {
+    nsINode* aNode, bool aNoContainerIfPruned) const {
   // HTML comboboxes have no-content list accessible as an intermediate
   // containing all options.
-  Accessible* container = GetAccessibleOrContainer(aNode, aIgnoreARIAHidden);
+  Accessible* container = GetAccessibleOrContainer(aNode, aNoContainerIfPruned);
   if (container && container->IsHTMLCombobox()) {
     return container->FirstChild();
   }
   return container;
 }
 
 inline nsIAccessiblePivot* DocAccessible::VirtualCursor() {
   if (!mVirtualCursor) {
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1127,18 +1127,18 @@ Accessible* DocAccessible::GetAccessible
     DocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
     child = childDocument->GetAccessibleByUniqueIDInSubtree(aUniqueID);
     if (child) return child;
   }
 
   return nullptr;
 }
 
-Accessible* DocAccessible::GetAccessibleOrContainer(nsINode* aNode,
-                                                    int aARIAHiddenFlag) const {
+Accessible* DocAccessible::GetAccessibleOrContainer(
+    nsINode* aNode, bool aNoContainerIfPruned) const {
   if (!aNode || !aNode->GetComposedDoc()) {
     return nullptr;
   }
 
   nsINode* currNode = nullptr;
   if (aNode->IsShadowRoot()) {
     // This can happen, for example, when called within
     // SelectionManager::ProcessSelectionChanged due to focusing a direct
@@ -1152,21 +1152,36 @@ Accessible* DocAccessible::GetAccessible
     }
   } else {
     currNode = aNode;
   }
 
   MOZ_ASSERT(currNode);
   for (; currNode; currNode = currNode->GetFlattenedTreeParentNode()) {
     // No container if is inside of aria-hidden subtree.
-    if (aARIAHiddenFlag == eNoContainerIfARIAHidden && currNode->IsElement() &&
+    if (aNoContainerIfPruned && currNode->IsElement() &&
         aria::HasDefinedARIAHidden(currNode->AsElement())) {
       return nullptr;
     }
 
+    // Check if node is in an unselected deck panel
+    if (aNoContainerIfPruned && currNode->IsXULElement()) {
+      if (nsIFrame* frame = currNode->AsContent()->GetPrimaryFrame()) {
+        nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent());
+        if (deckFrame && deckFrame->GetSelectedBox() != frame) {
+          // If deck is not a <tabpanels>, return null
+          nsIContent* parentFrameContent = deckFrame->GetContent();
+          if (!parentFrameContent ||
+              !parentFrameContent->IsXULElement(nsGkAtoms::tabpanels)) {
+            return nullptr;
+          }
+        }
+      }
+    }
+
     if (Accessible* accessible = GetAccessible(currNode)) {
       return accessible;
     }
   }
 
   return nullptr;
 }
 
@@ -1661,18 +1676,17 @@ bool InsertIterator::Next() {
     // overlapping content insertion (i.e. other content was inserted between
     // this inserted content and its container or the content was reinserted
     // into different container of unrelated part of tree). To avoid a double
     // processing of the content insertion ignore this insertion notification.
     // Note, the inserted content might be not in tree at all at this point
     // what means there's no container. Ignore the insertion too.
     nsIContent* prevNode = mNodes->SafeElementAt(mNodesIdx - 1);
     nsIContent* node = mNodes->ElementAt(mNodesIdx++);
-    Accessible* container = Document()->AccessibleOrTrueContainer(
-        node, DocAccessible::eNoContainerIfARIAHidden);
+    Accessible* container = Document()->AccessibleOrTrueContainer(node, true);
     if (container != Context()) {
       continue;
     }
 
     // HTML comboboxes have no-content list accessible as an intermediate
     // containing all options.
     if (container->IsHTMLCombobox()) {
       container = container->FirstChild();
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -279,35 +279,36 @@ class DocAccessible : public HyperTextAc
   /**
    * Return the cached accessible by the given unique ID looking through
    * this and nested documents.
    */
   Accessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID);
 
   /**
    * Return an accessible for the given DOM node or container accessible if
-   * the node is not accessible.
+   * the node is not accessible. If aNoContainerIfPruned is true it will return
+   * null if the node is in a pruned subtree (eg. aria-hidden or unselected deck
+   * panel)
    */
-  enum { eIgnoreARIAHidden = 0, eNoContainerIfARIAHidden = 1 };
-  Accessible* GetAccessibleOrContainer(
-      nsINode* aNode, int aARIAHiddenFlag = eIgnoreARIAHidden) const;
+  Accessible* GetAccessibleOrContainer(nsINode* aNode,
+                                       bool aNoContainerIfPruned = false) const;
 
   /**
    * Return a container accessible for the given DOM node.
    */
   Accessible* GetContainerAccessible(nsINode* aNode) const {
     return aNode ? GetAccessibleOrContainer(aNode->GetParentNode()) : nullptr;
   }
 
   /**
    * Return an accessible for the given node if any, or an immediate accessible
    * container for it.
    */
   Accessible* AccessibleOrTrueContainer(
-      nsINode* aNode, int aARIAHiddenFlag = eIgnoreARIAHidden) const;
+      nsINode* aNode, bool aNoContainerIfPruned = false) const;
 
   /**
    * Return an accessible for the given node or its first accessible descendant.
    */
   Accessible* GetAccessibleOrDescendant(nsINode* aNode) const;
 
   /**
    * Returns aria-owns seized child at the given index.
--- a/accessible/tests/mochitest/treeupdate/test_deck.xul
+++ b/accessible/tests/mochitest/treeupdate/test_deck.xul
@@ -59,20 +59,65 @@
       }
 
       this.getID = function switchDeckPanel_getID()
       {
         return "switch deck panel";
       }
     }
 
+    function showDeckPanel(aContainerID, aPanelID)
+    {
+      this.container = getAccessible(aContainerID);
+      this.deckNode = getNode(aPanelID);
+      var tree =
+        { GROUPING: [ // role="group"
+          { GROUPING: [ // grouping of panel 2
+            { PUSHBUTTON: []  } // push button in panel 2
+          ] }
+        ] };
+
+
+      this.unexpectedEventSeq = [
+        new invokerChecker(EVENT_REORDER, this.container)
+      ];
+
+      this.invoke = function showDeckPanel_invoke()
+      {
+        // This stops the refreh driver from doing its regular ticks, and leaves
+        // us in control. 100 is an arbitrary positive number to advance the clock
+        // it is not checked or used anywhere.
+        window.windowUtils.advanceTimeAndRefresh(100);
+
+        testAccessibleTree(this.container, tree);
+        this.deckNode.style.display = "-moz-box";
+
+        // This flushes our DOM mutations and forces any pending mutation events.
+        window.windowUtils.advanceTimeAndRefresh(100);
+      }
+
+      this.finalCheck = function showDeckPanel_finalCheck()
+      {
+        testAccessibleTree(this.container, tree);
+
+        // Return to regular refresh driver ticks.
+        window.windowUtils.restoreNormalRefresh();
+      }
+
+      this.getID = function showDeckPanel_getID()
+      {
+        return "show deck panel";
+      }
+    }
+
     var gQueue = null;
     function doTest()
     {
       gQueue = new eventQueue();
+      gQueue.push(new showDeckPanel("container", "hidden"));
       gQueue.push(new switchDeckPanel("container", "deck"));
       gQueue.invoke(); // will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
@@ -94,15 +139,16 @@
 
     <vbox flex="1" id="container" role="group">
 
       <deck id="deck" selectedIndex="1">
         <description>This is the first page</description>
         <groupbox>
           <button label="This is the second page"/>
         </groupbox>
+        <hbox id="hidden" style="display: none;"><label>This is the third page</label></hbox>
       </deck>
 
     </vbox>
   </hbox>
 
 </window>