Bug 1349223 - cut aria-hidden tree, r=yzen
☠☠ backed out by d5905334d10d ☠ ☠
authorAlexander Surkov <surkov.alexander@gmail.com>
Mon, 25 Jun 2018 14:17:01 -0400
changeset 481366 b2df5de436bd21c0271fc0ede5f8a2b21232eaa4
parent 481365 7b27e3b7aa9c2629744eba48da51dc85cae1ed90
child 481367 3eab1911d04a8bc42602f4b938e154a11da9d60a
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1349223
milestone62.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 1349223 - cut aria-hidden tree, r=yzen
accessible/base/ARIAMap.cpp
accessible/base/NotificationController.cpp
accessible/base/nsAccessibilityService.cpp
accessible/base/nsAccessiblePivot.cpp
accessible/generic/Accessible.cpp
accessible/generic/Accessible.h
accessible/generic/DocAccessible-inl.h
accessible/generic/DocAccessible.cpp
accessible/generic/DocAccessible.h
accessible/interfaces/nsIAccessiblePivot.idl
accessible/jsat/EventManager.jsm
accessible/jsat/Traversal.jsm
accessible/jsat/Utils.jsm
accessible/tests/mochitest/attributes/test_obj.html
accessible/tests/mochitest/events.js
accessible/tests/mochitest/events/test_aria_objattr.html
accessible/tests/mochitest/jsat/test_content_integration.html
accessible/tests/mochitest/pivot.js
accessible/tests/mochitest/pivot/test_virtualcursor.html
accessible/tests/mochitest/tree/test_aria_globals.html
accessible/tests/mochitest/tree/test_media.html
accessible/tests/mochitest/treeupdate/a11y.ini
accessible/tests/mochitest/treeupdate/test_ariahidden.html
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -1426,22 +1426,20 @@ aria::AttrCharacteristicsFor(nsAtom* aAt
       return gWAIUnivAttrMap[i].characteristics;
 
   return 0;
 }
 
 bool
 aria::HasDefinedARIAHidden(nsIContent* aContent)
 {
-  return aContent &&
-    nsAccUtils::HasDefinedARIAToken(aContent, nsGkAtoms::aria_hidden) &&
-    !aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-                                        nsGkAtoms::aria_hidden,
-                                        nsGkAtoms::_false,
-                                        eCaseMatters);
+  return aContent && aContent->IsElement() &&
+    aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+                                       nsGkAtoms::aria_hidden,
+                                       nsGkAtoms::_true, eCaseMatters);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AttrIterator class
 
 bool
 AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue)
 {
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -745,19 +745,21 @@ NotificationController::WillRefresh(mozi
       if (logging::IsEnabled(logging::eTree | logging::eText)) {
         logging::MsgBegin("TREE", "text node gains new content; doc: %p", mDocument);
         logging::Node("container", containerElm);
         logging::Node("content", textNode);
         logging::MsgEnd();
       }
   #endif
 
-      Accessible* container = mDocument->AccessibleOrTrueContainer(containerNode);
-      MOZ_ASSERT(container,
+      MOZ_ASSERT(mDocument->AccessibleOrTrueContainer(containerNode),
                  "Text node having rendered text hasn't accessible document!");
+
+      Accessible* container = mDocument->AccessibleOrTrueContainer(
+        containerNode, DocAccessible::eNoContainerIfARIAHidden);
       if (container) {
         nsTArray<nsCOMPtr<nsIContent>>* list =
           mContentInsertions.LookupOrAdd(container);
         list->AppendElement(textNode);
       }
     }
   }
   mTextHash.Clear();
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -1045,20 +1045,26 @@ nsAccessibilityService::CreateAccessible
     NS_ERROR("Creating accessible for wrong document");
     return nullptr;
   }
 
   if (!aNode->IsContent())
     return nullptr;
 
   nsIContent* content = aNode->AsContent();
-  nsIFrame* frame = content->GetPrimaryFrame();
+  if (aria::HasDefinedARIAHidden(content)) {
+    if (aIsSubtreeHidden) {
+      *aIsSubtreeHidden = true;
+    }
+    return nullptr;
+  }
 
   // Check frame and its visibility. Note, hidden frame allows visible
   // elements in subtree.
+  nsIFrame* frame = content->GetPrimaryFrame();
   if (!frame || !frame->StyleVisibility()->IsVisible()) {
     // display:contents element doesn't have a frame, but retains the semantics.
     // All its children are unaffected.
     if (content->IsElement() && content->AsElement()->IsDisplayContents()) {
       const HTMLMarkupMapInfo* markupMap =
         mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
       if (markupMap && markupMap->new_func) {
         RefPtr<Accessible> newAcc =
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -890,23 +890,16 @@ RuleCache::ApplyFilter(Accessible* aAcce
     if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
         (state & states::OFFSCREEN))
       return NS_OK;
 
     if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
         !(state & states::FOCUSABLE))
       return NS_OK;
 
-    if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) {
-      if (aAccessible->IsARIAHidden()) {
-        *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
-        return NS_OK;
-      }
-    }
-
     if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) &&
         !(state & states::OPAQUE1)) {
       nsIFrame* frame = aAccessible->GetFrame();
       if (frame->StyleEffects()->mOpacity == 0.0f) {
         *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
         return NS_OK;
       }
     }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -977,21 +977,16 @@ Accessible::Attributes()
 
   // Expose object attributes from ARIA attributes.
   nsAutoString unused;
   aria::AttrIterator attribIter(mContent);
   nsAutoString name, value;
   while(attribIter.Next(name, value))
     attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
 
-  if (IsARIAHidden()) {
-    nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden,
-                           NS_LITERAL_STRING("true"));
-  }
-
   // If there is no aria-live attribute then expose default value of 'live'
   // object attribute used for ARIA role of this accessible.
   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
   if (roleMapEntry) {
     if (roleMapEntry->Is(nsGkAtoms::searchbox)) {
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType,
                              NS_LITERAL_STRING("search"));
     }
@@ -2108,19 +2103,16 @@ Accessible::BindToParent(Accessible* aPa
   mIndexInParent = aIndexInParent;
 
   // Note: this is currently only used for richlistitems and their children.
   if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
     mContextFlags |= eHasNameDependentParent;
   else
     mContextFlags &= ~eHasNameDependentParent;
 
-  if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent))
-    SetARIAHidden(true);
-
   mContextFlags |=
     static_cast<uint32_t>((mParent->IsAlert() ||
                            mParent->IsInsideAlert())) & eInsideAlert;
 }
 
 // Accessible protected
 void
 Accessible::UnbindFromParent()
@@ -2637,30 +2629,16 @@ Accessible::ContainerWidget() const
       // Don't cross DOM document boundaries.
       if (parent->IsDoc())
         break;
     }
   }
   return nullptr;
 }
 
-void
-Accessible::SetARIAHidden(bool aIsDefined)
-{
-  if (aIsDefined)
-    mContextFlags |= eARIAHidden;
-  else
-    mContextFlags &= ~eARIAHidden;
-
-  uint32_t length = mChildren.Length();
-  for (uint32_t i = 0; i < length; i++) {
-    mChildren[i]->SetARIAHidden(aIsDefined);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible protected methods
 
 void
 Accessible::LastRelease()
 {
   // First cleanup if needed...
   if (mDoc) {
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -936,23 +936,16 @@ public:
   /**
    * Return true if this accessible has a parent whose name depends on this
    * accessible.
    */
   bool HasNameDependentParent() const
     { return mContextFlags & eHasNameDependentParent; }
 
   /**
-   * Return true if aria-hidden="true" is applied to the accessible or inherited
-   * from the parent.
-   */
-  bool IsARIAHidden() const { return mContextFlags & eARIAHidden; }
-  void SetARIAHidden(bool aIsDefined);
-
-  /**
    * Return true if the element is inside an alert.
    */
   bool IsInsideAlert() const { return mContextFlags & eInsideAlert; }
 
   /**
    * Return true if there is a pending reorder event for this accessible.
    */
   bool ReorderEventTarget() const { return mReorderEventTarget; }
@@ -1043,18 +1036,17 @@ protected:
     eLastStateFlag = eNoKidsFromDOM
   };
 
   /**
    * Flags used for contextual information about the accessible.
    */
   enum ContextFlags {
     eHasNameDependentParent = 1 << 0, // Parent's name depends on this accessible.
-    eARIAHidden = 1 << 1,
-    eInsideAlert = 1 << 2,
+    eInsideAlert = 1 << 1,
 
     eLastContextFlag = eInsideAlert
   };
 
 protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
@@ -1137,17 +1129,17 @@ protected:
   nsCOMPtr<nsIContent> mContent;
   RefPtr<DocAccessible> mDoc;
 
   Accessible* mParent;
   nsTArray<Accessible*> mChildren;
   int32_t mIndexInParent;
 
   static const uint8_t kStateFlagsBits = 12;
-  static const uint8_t kContextFlagsBits = 3;
+  static const uint8_t kContextFlagsBits = 2;
   static const uint8_t kTypeBits = 6;
   static const uint8_t kGenericTypesBits = 16;
 
   /**
    * Non-NO_ROLE_MAP_ENTRY_INDEX indicates author-supplied role;
    * possibly state & value as well
    */
   uint8_t mRoleMapEntryIndex;
--- a/accessible/generic/DocAccessible-inl.h
+++ b/accessible/generic/DocAccessible-inl.h
@@ -18,21 +18,21 @@
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
 namespace mozilla {
 namespace a11y {
 
 inline Accessible*
-DocAccessible::AccessibleOrTrueContainer(nsINode* aNode) const
+DocAccessible::AccessibleOrTrueContainer(nsINode* aNode, int aIgnoreARIAHidden) const
 {
   // HTML comboboxes have no-content list accessible as an intermediate
   // containing all options.
-  Accessible* container = GetAccessibleOrContainer(aNode);
+  Accessible* container = GetAccessibleOrContainer(aNode, aIgnoreARIAHidden);
   if (container && container->IsHTMLCombobox()) {
     return container->FirstChild();
   }
   return container;
 }
 
 inline nsIAccessiblePivot*
 DocAccessible::VirtualCursor()
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -764,16 +764,29 @@ DocAccessible::AttributeChanged(dom::Ele
   NS_ASSERTION(!IsDefunct(),
                "Attribute changed called on defunct document accessible!");
 
   // Proceed even if the element is not accessible because element may become
   // accessible if it gets certain attribute.
   if (UpdateAccessibleOnAttrChange(aElement, aAttribute))
     return;
 
+  // Update the accessible tree on aria-hidden change. Make sure to not create
+  // a tree under aria-hidden='true'.
+  if (aAttribute == nsGkAtoms::aria_hidden) {
+    if (aria::HasDefinedARIAHidden(aElement)) {
+      ContentRemoved(aElement);
+    }
+    else {
+      ContentInserted(aElement->GetFlattenedTreeParent(),
+                      aElement, aElement->GetNextSibling());
+    }
+    return;
+  }
+
   // Ignore attribute change if the element doesn't have an accessible (at all
   // or still) iff the element is not a root content of this document accessible
   // (which is treated as attribute change on this document accessible).
   // Note: we don't bail if all the content hasn't finished loading because
   // these attributes are changing for a loaded part of the content.
   Accessible* accessible = GetAccessible(aElement);
   if (!accessible) {
     if (mContent != aElement)
@@ -987,32 +1000,16 @@ DocAccessible::ARIAAttributeChanged(Acce
   if (!(attrFlags & ATTR_BYPASSOBJ)) {
     RefPtr<AccEvent> event =
       new AccObjectAttrChangedEvent(aAccessible, aAttribute);
     FireDelayedEvent(event);
   }
 
   dom::Element* elm = aAccessible->GetContent()->AsElement();
 
-  // Update aria-hidden flag for the whole subtree iff aria-hidden is changed
-  // on the root, i.e. ignore any affiliated aria-hidden changes in the subtree
-  // of top aria-hidden.
-  if (aAttribute == nsGkAtoms::aria_hidden) {
-    bool isDefined = aria::HasDefinedARIAHidden(elm);
-    if (isDefined != aAccessible->IsARIAHidden() &&
-        (!aAccessible->Parent() || !aAccessible->Parent()->IsARIAHidden())) {
-      aAccessible->SetARIAHidden(isDefined);
-
-      RefPtr<AccEvent> event =
-        new AccObjectAttrChangedEvent(aAccessible, aAttribute);
-      FireDelayedEvent(event);
-    }
-    return;
-  }
-
   if (aAttribute == nsGkAtoms::aria_checked ||
       (aAccessible->IsButton() &&
        aAttribute == nsGkAtoms::aria_pressed)) {
     const uint64_t kState = (aAttribute == nsGkAtoms::aria_checked) ?
                             states::CHECKED : states::PRESSED;
     RefPtr<AccEvent> event = new AccStateChangeEvent(aAccessible, kState);
     FireDelayedEvent(event);
 
@@ -1228,23 +1225,31 @@ DocAccessible::GetAccessibleByUniqueIDIn
     if (child)
       return child;
   }
 
   return nullptr;
 }
 
 Accessible*
-DocAccessible::GetAccessibleOrContainer(nsINode* aNode) const
+DocAccessible::GetAccessibleOrContainer(nsINode* aNode,
+                                        int aARIAHiddenFlag) const
 {
   if (!aNode || !aNode->GetComposedDoc())
     return nullptr;
 
   for (nsINode* currNode = aNode; currNode;
        currNode = currNode->GetFlattenedTreeParentNode()) {
+
+    // No container if is inside of aria-hidden subtree.
+    if (aARIAHiddenFlag == eNoContainerIfARIAHidden && currNode->IsElement() &&
+        aria::HasDefinedARIAHidden(currNode->AsElement())) {
+      return nullptr;
+    }
+
     if (Accessible* accessible = GetAccessible(currNode)) {
       return accessible;
     }
   }
 
   return nullptr;
 }
 
@@ -1792,17 +1797,18 @@ 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);
+    Accessible* container = Document()->
+      AccessibleOrTrueContainer(node, DocAccessible::eNoContainerIfARIAHidden);
     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
@@ -273,31 +273,37 @@ public:
    * 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.
    */
-  Accessible* GetAccessibleOrContainer(nsINode* aNode) const;
+  enum {
+    eIgnoreARIAHidden = 0,
+    eNoContainerIfARIAHidden = 1
+  };
+  Accessible* GetAccessibleOrContainer(nsINode* aNode,
+                                       int aARIAHiddenFlag = eIgnoreARIAHidden) 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) const;
+  Accessible* AccessibleOrTrueContainer(nsINode* aNode,
+                                        int aARIAHiddenFlag = eIgnoreARIAHidden) 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/interfaces/nsIAccessiblePivot.idl
+++ b/accessible/interfaces/nsIAccessiblePivot.idl
@@ -224,18 +224,17 @@ interface nsIAccessibleTraversalRule : n
   const unsigned short FILTER_MATCH = 0x1;
   /* Don't traverse accessibles children */
   const unsigned short FILTER_IGNORE_SUBTREE = 0x2;
 
   /* Pre-filters */
   const unsigned long PREFILTER_INVISIBLE     = 0x00000001;
   const unsigned long PREFILTER_OFFSCREEN     = 0x00000002;
   const unsigned long PREFILTER_NOT_FOCUSABLE = 0x00000004;
-  const unsigned long PREFILTER_ARIA_HIDDEN   = 0x00000008;
-  const unsigned long PREFILTER_TRANSPARENT   = 0x00000010;
+  const unsigned long PREFILTER_TRANSPARENT   = 0x00000008;
 
   /**
    * Pre-filter bitfield to filter out obviously ignorable nodes and lighten
    * the load on match().
    */
   readonly attribute unsigned long preFilter;
 
   /**
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -210,31 +210,16 @@ this.EventManager.prototype = {
         // on this one..
         let state = Utils.getState(acc);
         if (state.contains(States.FOCUSED) && state.contains(States.EDITABLE)) {
           this.present(Presentation.textSelectionChanged(acc.getText(0, -1),
             caretOffset, caretOffset, 0, 0, aEvent.isFromUserInput));
         }
         break;
       }
-      case Events.OBJECT_ATTRIBUTE_CHANGED:
-      {
-        let evt = aEvent.QueryInterface(
-          Ci.nsIAccessibleObjectAttributeChangedEvent);
-        if (evt.changedAttribute !== "aria-hidden") {
-          // Only handle aria-hidden attribute change.
-          break;
-        }
-        let hidden = Utils.isHidden(aEvent.accessible);
-        this[hidden ? "_handleHide" : "_handleShow"](evt);
-        if (this.inTest) {
-          this.sendMsgFunc("AccessFu:AriaHidden", { hidden });
-        }
-        break;
-      }
       case Events.SHOW:
       {
         this._handleShow(aEvent);
         break;
       }
       case Events.HIDE:
       {
         let evt = aEvent.QueryInterface(Ci.nsIAccessibleHideEvent);
@@ -624,17 +609,26 @@ const AccessibilityEventObserver = {
     let event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
     if (!event.accessibleDocument) {
       Logger.warning(
         "AccessibilityEventObserver.observe: no accessible document:",
         Logger.eventToString(event), "accessible:",
         Logger.accessibleToString(event.accessible));
       return;
     }
-    let content = event.accessibleDocument.window;
+    let content;
+    try {
+      content = event.accessibleDocument.window;
+    } catch (e) {
+      Logger.warning(
+        "AccessibilityEventObserver.observe: no window for accessible document:",
+        Logger.eventToString(event), "accessible:",
+        Logger.accessibleToString(event.accessible));
+      return;
+    }
     // Match the content window to its EventManager.
     let eventManager = this.getListener(content);
     if (!eventManager || !eventManager._started) {
       if (Utils.MozBuildApp === "browser" && !content.isChromeWindow) {
         Logger.warning(
           "AccessibilityEventObserver.observe: ignored event:",
           Logger.eventToString(event), "accessible:",
           Logger.accessibleToString(event.accessible), "document:",
--- a/accessible/jsat/Traversal.jsm
+++ b/accessible/jsat/Traversal.jsm
@@ -172,25 +172,24 @@ var gSimpleMatchFunc = function gSimpleM
     // the same content that was already presented by its parent.
     return Filters.MATCH |
       Filters.IGNORE_SUBTREE;
   }
 };
 
 var gSimplePreFilter = Prefilters.DEFUNCT |
   Prefilters.INVISIBLE |
-  Prefilters.ARIA_HIDDEN |
   Prefilters.TRANSPARENT;
 
 var TraversalRules = { // jshint ignore:line
   Simple: new BaseTraversalRule(gSimpleTraversalRoles, gSimpleMatchFunc),
 
   SimpleOnScreen: new BaseTraversalRule(
     gSimpleTraversalRoles, gSimpleMatchFunc,
-    Prefilters.DEFUNCT | Prefilters.INVISIBLE | Prefilters.ARIA_HIDDEN |
+    Prefilters.DEFUNCT | Prefilters.INVISIBLE |
     Prefilters.TRANSPARENT | Prefilters.OFFSCREEN),
 
   Anchor: new BaseTraversalRule(
     [Roles.LINK],
     function Anchor_match(aAccessible) {
       // We want to ignore links, only focus named anchors.
       if (Utils.getState(aAccessible).contains(States.LINKED)) {
         return Filters.IGNORE;
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -272,52 +272,33 @@ var Utils = { // jshint ignore:line
         Logger.debug("Failed to get parent:", x);
         acc = null;
       }
     }
 
     return false;
   },
 
-  isHidden: function isHidden(aAccessible) {
-    // Need to account for aria-hidden, so can't just check for INVISIBLE
-    // state.
-    let hidden = Utils.getAttributes(aAccessible).hidden;
-    return hidden && hidden === "true";
-  },
-
   visibleChildCount: function visibleChildCount(aAccessible) {
     let count = 0;
     for (let child = aAccessible.firstChild; child; child = child.nextSibling) {
-      if (!this.isHidden(child)) {
-        ++count;
-      }
+      ++count;
     }
     return count;
   },
 
-  inHiddenSubtree: function inHiddenSubtree(aAccessible) {
-    for (let acc = aAccessible; acc; acc = acc.parent) {
-      if (this.isHidden(acc)) {
-        return true;
-      }
-    }
-    return false;
-  },
-
   isAliveAndVisible: function isAliveAndVisible(aAccessible, aIsOnScreen) {
     if (!aAccessible) {
       return false;
     }
 
     try {
       let state = this.getState(aAccessible);
       if (state.contains(States.DEFUNCT) || state.contains(States.INVISIBLE) ||
-          (aIsOnScreen && state.contains(States.OFFSCREEN)) ||
-          Utils.inHiddenSubtree(aAccessible)) {
+          (aIsOnScreen && state.contains(States.OFFSCREEN))) {
         return false;
       }
     } catch (x) {
       return false;
     }
 
     return true;
   },
@@ -726,22 +707,17 @@ PivotContext.prototype = {
    * whether aAccessible's subtree is required.
    */
   _traverse: function* _traverse(aAccessible, aPreorder, aStop) {
     if (aStop && aStop(aAccessible)) {
       return;
     }
     let child = aAccessible.firstChild;
     while (child) {
-      let include;
-      if (this._includeInvisible) {
-        include = true;
-      } else {
-        include = !Utils.isHidden(child);
-      }
+      let include = true;
       if (include) {
         if (aPreorder) {
           yield child;
           for (let node of this._traverse(child, aPreorder, aStop)) {
             yield node;
           }
         } else {
           for (let node of this._traverse(child, aPreorder, aStop)) {
--- a/accessible/tests/mochitest/attributes/test_obj.html
+++ b/accessible/tests/mochitest/attributes/test_obj.html
@@ -39,18 +39,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       testAttrs("grabbed", {"grabbed": "true"}, true);
       testAttrs("haspopupTrue", { "haspopup": "true" }, true);
       testAbsentAttrs("haspopupFalse", { "haspopup": "false" });
       testAbsentAttrs("haspopupEmpty", { "haspopup": "" });
       testAttrs("haspopupDialog", { "haspopup": "dialog" }, true);
       testAttrs("haspopupListbox", { "haspopup": "listbox" }, true);
       testAttrs("haspopupMenu", { "haspopup": "menu" }, true);
       testAttrs("haspopupTree", { "haspopup": "tree" }, true);
-      testAttrs("hidden", {"hidden": "true"}, true);
-      testAbsentAttrs("hidden_false", { "hidden": "false" });
       testAbsentAttrs("modal", {"modal": "true"});
       testAttrs("sortAscending", {"sort": "ascending"}, true);
       testAttrs("sortDescending", {"sort": "descending"}, true);
       testAttrs("sortNone", {"sort": "none"}, true);
       testAttrs("sortOther", {"sort": "other"}, true);
       testAttrs("roledescr", {"roledescription": "spreadshit"}, true);
       testAttrs("currentPage", {"current": "page"}, true);
 
@@ -150,61 +148,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=475006"
-     title="Extend nsARIAMap to capture ARIA attribute characteristics">
-    Mozilla Bug 475006
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=391829"
-     title="Add support for container-live-role to object attributes">
-    Mozilla Bug 391829
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=581952"
-     title="Make explicit that aria-label is not an object attribute">
-    Mozilla Bug 475006
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
-     title="make HTML <output> accessible">
-    Mozilla Bug 558036
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=896400"
-     title="Tablist should no longer be an implicit live region">
-    Mozilla Bug 896400
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=563862"
-     title="Expand support for nsIAccessibleEvent::OBJECT_ATTRIBUTE_CHANGE">
-    Mozilla Bug 563862
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=819303"
-     title="crash in nsTextEquivUtils::AppendTextEquivFromTextContent">
-    Mozilla Bug 819303
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=838407"
-     title="aria-hidden false value shouldn't be exposed via object attributes">
-    Mozilla Bug 838407
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121518"
-     title="ARIA 1.1: Support role 'searchbox'">
-    Mozilla Bug 1121518
-  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- aria -->
   <div id="atomic" aria-atomic="true">live region</div>
   <div id="atomic_false" aria-atomic="false">live region</div>
@@ -221,18 +174,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div id="grabbed" aria-grabbed="true"></div>
   <div id="haspopupTrue" aria-haspopup="true"></div>
   <div id="haspopupFalse" aria-haspopup="false"></div>
   <div id="haspopupEmpty" aria-haspopup=""></div>
   <div id="haspopupDialog" aria-haspopup="dialog"></div>
   <div id="haspopupListbox" aria-haspopup="listbox"></div>
   <div id="haspopupMenu" aria-haspopup="menu"></div>
   <div id="haspopupTree" aria-haspopup="tree"></div>
-  <div id="hidden" aria-hidden="true"></div>
-  <div id="hidden_false" aria-hidden="false"></div>
   <div id="modal" aria-modal="true"></div>
   <div id="sortAscending" role="columnheader" aria-sort="ascending"></div>
   <div id="sortDescending" role="columnheader" aria-sort="descending"></div>
   <div id="sortNone" role="columnheader" aria-sort="none"></div>
   <div id="sortOther" role="columnheader" aria-sort="other"></div>
   <div id="roledescr" aria-roledescription="spreadshit"></div>
   <div id="currentPage" aria-current="page"></div>
 
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -109,16 +109,38 @@ function waitForEvent(aEventType, aTarge
       );
     }
   };
 
   registerA11yEventListener(aEventType, handler);
 }
 
 /**
+ * A promise based version of waitForEvent function.
+ */
+function waitForEventPromise(eventType, target) {
+  return new Promise(resolve => {
+    let eventObserver = {
+      observe(subject, topic, data) {
+        let event = subject.QueryInterface(nsIAccessibleEvent);
+        if (event.eventType !== eventType) {
+          return;
+        }
+
+        if (event.accessible == getAccessible(target)) {
+          Services.obs.removeObserver(this, "accessible-event");
+          resolve(event);
+        }
+      }
+    };
+    Services.obs.addObserver(eventObserver, "accessible-event");
+  });
+}
+
+/**
  * Generate mouse move over image map what creates image map accessible (async).
  * See waitForImageMap() function.
  */
 function waveOverImageMap(aImageMapID) {
   var imageMapNode = getNode(aImageMapID);
   synthesizeMouse(imageMapNode, 10, 10, { type: "mousemove" },
                   imageMapNode.ownerGlobal);
 }
--- a/accessible/tests/mochitest/events/test_aria_objattr.html
+++ b/accessible/tests/mochitest/events/test_aria_objattr.html
@@ -34,79 +34,36 @@
         this.node.setAttribute(aAttr, aValue);
       };
 
       this.getID = function updateAttribute_getID() {
         return aAttr + " for " + aID + " " + aValue;
       };
     }
 
-    function updateARIAHidden(aID, aIsDefined, aChildId) {
-      this.__proto__ = new updateAttribute(aID, "aria-hidden",
-                                           aIsDefined ? "true" : "false");
-
-      this.finalCheck = function updateARIAHidden_finalCheck() {
-        if (aIsDefined) {
-          testAttrs(aID, {"hidden": "true"}, true);
-          testAttrs(aChildId, {"hidden": "true"}, true);
-        } else {
-          testAbsentAttrs(aID, { "hidden": "true"});
-          testAbsentAttrs(aChildId, { "hidden": "true"});
-        }
-      };
-    }
-
-    // Debug stuff.
-    // gA11yEventDumpID = "eventdump";
     // gA11yEventDumpToConsole = true;
-
     function doTests() {
       gQueue = new eventQueue();
 
-      gQueue.push(new updateARIAHidden("hideable", true, "hideable_child"));
-      gQueue.push(new updateARIAHidden("hideable", false, "hideable_child"));
-
       gQueue.push(new updateAttribute("sortable", "aria-sort", "ascending"));
 
       // For experimental ARIA extensions
       gQueue.push(new updateAttribute("custom", "aria-blah", "true"));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
-
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=581096"
-     title="Add support for aria-hidden">
-    Mozilla Bug 581096
-  </a>
-
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=640707"
-     title="Add event support for aria-sort">
-    Mozilla Bug 640707
-  </a>
-
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=640707"
-     title="Expand support for aria attribute change events">
-    Mozilla Bug 563862
-  </a>
-
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
-  <div id="eventdump"></div>
-
-  <div id="hideable"><div id="hideable_child">Hi</div><div>there</div></div>
 
   <div id="sortable" role="columnheader" aria-sort="none">aria-sort</div>
 
   <div id="custom" role="custom" aria-blah="false">Fat free cheese</div>
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -289,53 +289,59 @@
       evt = await runner.expectAndroidEvents(() => {
         doc.getElementById("back").setAttribute("aria-hidden", true);
       }, AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["such app", "wow", "heading level 1"]);
 
       // Changing aria-hidden attribute twice and making sure that the event
       // is fired only once when the actual change happens.
       doc.getElementById("back").setAttribute("aria-hidden", true);
+      let onShow = waitForEventPromise(EVENT_SHOW, doc.getElementById("back"));
       doc.getElementById("back").setAttribute("aria-hidden", false);
+      await onShow;
+
       evt = await runner.movePrevious("Simple",
         AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["Back", "button"]);
       await runner.clearCursor();
 
       // aria-hidden on the iframe that has the vc.
       evt = await runner.moveNext("Simple",
         AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["Traversal Rule test document", "Phone status bar"]);
       evt = await runner.moveNext("Simple",
         AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["Back", "button"]);
       evt = await runner.moveNext("Simple",
         AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["such app", "wow", "heading level 1"]);
 
-
       evt = await runner.expectAndroidEvents(() => {
         doc.getElementById("iframe").setAttribute("aria-hidden", true);
       }, AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
-      runner.eventTextMatches(evt, ["Home", "button"]);
+      runner.eventTextMatches(evt, ["Traversal Rule test document", "Home", "button"]);
 
+      onShow = waitForEventPromise(EVENT_SHOW, doc.getElementById("iframe"));
       doc.getElementById("iframe").setAttribute("aria-hidden", false);
+      await onShow;
       await runner.clearCursor();
 
       // aria-hidden element and auto Move
       evt = await runner.moveNext("Simple",
         AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["Traversal Rule test document", "Phone status bar"]);
 
       doc.getElementById("back").setAttribute("aria-hidden", true);
       evt = await runner.focusSelector("button#back",
         AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["such app", "wow", "heading level 1"]);
 
+      onShow = waitForEventPromise(EVENT_SHOW, doc.getElementById("back"));
       doc.getElementById("back").setAttribute("aria-hidden", false);
+      await onShow;
       runner.blur();
       await runner.clearCursor();
 
       // Open dialog in outer doc, while cursor is also in outer doc
       evt = await runner.moveLast("Simple",
         AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
       runner.eventTextMatches(evt, ["Traversal Rule test document", "mover", "medium", "slider", "live"]);
 
--- a/accessible/tests/mochitest/pivot.js
+++ b/accessible/tests/mochitest/pivot.js
@@ -1,15 +1,14 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 // //////////////////////////////////////////////////////////////////////////////
 // Constants
 
 const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
-const PREFILTER_ARIA_HIDDEN = nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN;
 const PREFILTER_TRANSPARENT = nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT;
 const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
 const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
 const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
 const CHAR_BOUNDARY = nsIAccessiblePivot.CHAR_BOUNDARY;
 const WORD_BOUNDARY = nsIAccessiblePivot.WORD_BOUNDARY;
 
 const NS_ERROR_NOT_IN_TREE = 0x80780026;
@@ -42,17 +41,17 @@ var HeadersTraversalRule =
  */
 var ObjectTraversalRule =
 {
   getMatchRoles(aRules) {
     aRules.value = [];
     return 0;
   },
 
-  preFilter: PREFILTER_INVISIBLE | PREFILTER_ARIA_HIDDEN | PREFILTER_TRANSPARENT,
+  preFilter: PREFILTER_INVISIBLE | PREFILTER_TRANSPARENT,
 
   match(aAccessible) {
     var rv = FILTER_IGNORE;
     var role = aAccessible.role;
     if (hasState(aAccessible, STATE_FOCUSABLE) &&
         (role != ROLE_DOCUMENT && role != ROLE_INTERNAL_FRAME))
       rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
     else if (aAccessible.childCount == 0 &&
--- a/accessible/tests/mochitest/pivot/test_virtualcursor.html
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html
@@ -39,17 +39,17 @@
 
       gQueue = new eventQueue();
 
       gQueue.onFinish = function onFinish() {
         closeBrowserWindow();
       };
 
       queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, null,
-                             ["heading-1-1", "heading-2-1", "heading-2-2"]);
+                             ["heading-1-1", "heading-2-2"]);
 
       queueTraversalSequence(
         gQueue, docAcc, ObjectTraversalRule, null,
         ["Main Title", "Lorem ipsum ",
          "dolor", " sit amet. Integer vitae urna leo, id ",
          "semper", " nulla. ", "Second Section Title",
          "Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.",
          "An ", "embedded", " document.", "Hide me", "Link 1", "Link 2",
@@ -88,23 +88,17 @@
         gQueue, docAcc, ObjectTraversalRule,
         getAccessible(doc.getElementById("paragraph-1")),
         ["Lorem ipsum ", "dolor", " sit amet. Integer vitae urna leo, id ",
          "semper", " nulla. "]);
 
       gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
                                           NS_ERROR_INVALID_ARG));
 
-      // Put cursor in an ignored subtree
-      // set isFromUserInput to false, just to test..
-      gQueue.push(new setVCPosInvoker(docAcc, null, null,
-                                      getAccessible(doc.getElementById("hidden-link")),
-                                      false));
-      // Next item shoud be outside of that subtree
-      gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "An "));
+      gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "dolor"));
 
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function() {
       /* We open a new browser because we need to test with a top-level content
          document. */
--- a/accessible/tests/mochitest/tree/test_aria_globals.html
+++ b/accessible/tests/mochitest/tree/test_aria_globals.html
@@ -20,17 +20,16 @@
         "busy",
         "controls",
         "describedby",
         "disabled",
         "dropeffect",
         "flowto",
         "grabbed",
         "haspopup",
-        "hidden",
         "invalid",
         "label",
         "labelledby",
         "live",
         "owns",
         "relevant"
       ];
 
@@ -89,17 +88,16 @@
     <span id="busy" aria-busy="false"></span>
     <span id="controls" aria-controls="pawn"></span>
     <span id="describedby" aria-describedby="pawn"></span>
     <span id="disabled" aria-disabled="true"></span>
     <span id="dropeffect" aria-dropeffect="move"></span>
     <span id="flowto" aria-flowto="pawn"></span>
     <span id="grabbed" aria-grabbed="false"></span>
     <span id="haspopup" aria-haspopup="false"></span>
-    <span id="hidden" aria-hidden="true"></span>
     <span id="invalid" aria-invalid="false"></span>
     <span id="label" aria-label="hi"></span>
     <span id="labelledby" aria-labelledby="label"></span>
     <span id="live" aria-live="polite"></span>
     <span id="owns" aria-owns="pawn"></span>
     <span id="relevant" aria-relevant="additions"></span>
   </div>
 
@@ -110,17 +108,16 @@
       <td id="td_busy" aria-busy="false"></td>
       <td id="td_controls" aria-controls="pawn"></td>
       <td id="td_describedby" aria-describedby="pawn"></td>
       <td id="td_disabled" aria-disabled="true"></td>
       <td id="td_dropeffect" aria-dropeffect="move"></td>
       <td id="td_flowto" aria-flowto="pawn"></td>
       <td id="td_grabbed" aria-grabbed="false"></td>
       <td id="td_haspopup" aria-haspopup="false"></td>
-      <td id="td_hidden" aria-hidden="true"></td>
       <td id="td_invalid" aria-invalid="false"></td>
       <td id="td_label" aria-label="hi"></td>
       <td id="td_labelledby" aria-labelledby="label"></td>
       <td id="td_live" aria-live="polite"></td>
       <td id="td_owns" aria-owns="pawn"></td>
       <td id="td_relevant" aria-relevant="additions"></td>
     </tr>
   </table>
--- a/accessible/tests/mochitest/tree/test_media.html
+++ b/accessible/tests/mochitest/tree/test_media.html
@@ -39,27 +39,16 @@ https://bugzilla.mozilla.org/show_bug.cg
             role: ROLE_PROGRESSBAR,
             children: []
           },
           { // slider of progress bar
             role: ROLE_SLIDER,
             // name: "0:00 of 0:02 elapsed",
             children: []
           },
-          {
-            role: ROLE_TEXT_CONTAINER,
-            children: [
-              {
-                role: ROLE_TEXT_LEAF, // position text
-              },
-              {
-                role: ROLE_TEXT_LEAF, // duration text
-              }
-            ]
-          },
           { // mute button
             role: ROLE_PUSHBUTTON,
             name: "Mute",
             children: []
           },
           { // slider of volume bar
             role: ROLE_SLIDER,
             children: []
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 support-files =
   !/accessible/tests/mochitest/*.js
   !/accessible/tests/mochitest/letters.gif
   !/accessible/tests/mochitest/moz.png
 
 [test_ariadialog.html]
+[test_ariahidden.html]
 [test_ariaowns.html]
 [test_bug852150.xhtml]
 [test_bug883708.xhtml]
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
 [test_bug1100602.html]
 [test_bug1175913.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_ariahidden.html
@@ -0,0 +1,119 @@
+<html>
+
+<head>
+  <title>aria-hidden tree update tests</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="../events.js"></script>
+
+  <script type="application/javascript">
+    function t1_setARIAHidden() {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, "t1")
+      ];
+
+      this.invoke = function t1_setARIAHidden_invoke() {
+        getNode("t1_child").setAttribute("aria-hidden", "true");
+      };
+
+      this.finalCheck = function t1_setARIAHidden_finalCheck() {
+        ok(!isAccessible("t1_child"), "No accessible for aria-hidden");
+      };
+
+      this.getID = function t1_setARIAHidden_getID() {
+        return "aria-hidden set to true";
+      };
+    }
+
+    function t1_removeARIAHidden() {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, "t1")
+      ];
+
+      this.invoke = function t1_removeARIAHidden_invoke() {
+        getNode("t1_child").removeAttribute("aria-hidden");
+      };
+
+      this.finalCheck = function t1_removeARIAHidden_finalCheck() {
+        ok(isAccessible("t1_child"), "No aria-hidden, has to be accessible");
+      };
+
+      this.getID = function t1_removeARIAHidden_getID() {
+        return "remove aria-hidden";
+      };
+    }
+
+    function t2_setARIAHidden() {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, "t2")
+      ];
+
+      this.invoke = function t2_setARIAHidden_invoke() {
+        getNode("t2_child").setAttribute("aria-hidden", "true");
+      };
+
+      this.finalCheck = function t2_setARIAHidden_finalCheck() {
+        testAccessibleTree("t2", { SECTION: []});
+      };
+
+      this.getID = function t2_setARIAHidden_getID() {
+        return "t2: set aria-hidden";
+      };
+    }
+
+    function t2_insertUnderARIAHidden() {
+      this.eventSeq = [
+        new unexpectedInvokerChecker(EVENT_REORDER, "t2")
+      ];
+
+      this.invoke = function t2_insertUnderARIAHidden_invoke() {
+        getNode("t2_child").innerHTML = "<input>";
+      };
+
+      this.finalCheck = function t2_insertUnderARIAHidden_finalCheck() {
+        testAccessibleTree("t2", { SECTION: []});
+      };
+
+      this.getID = function t2_insertUnderARIAHidden_getID() {
+        return "t2: insert under aria-hidden";
+      };
+    }
+
+    // gA11yEventDumpToConsole = true;
+    function doTests() {
+      ok(!isAccessible("t1_child"), "No accessible for aria-hidden");
+
+      gQueue = new eventQueue();
+      gQueue.push(new t1_removeARIAHidden());
+      gQueue.push(new t1_setARIAHidden());
+      gQueue.push(new t2_setARIAHidden());
+      gQueue.push(new t2_insertUnderARIAHidden());
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+  <div id="t1"><div id="t1_child" aria-hidden="true">Hi</div><div>there</div></div>
+  <div id="t2">
+    <span id="t2_child">hoho</span>
+  </div>
+</body>
+</html>