fixed virtualcursor test
authorAlexander Surkov <surkov.alexander@gmail.com>
Fri, 01 Jun 2018 11:21:44 -0400
changeset 1528988 d6129f5a72ebffbef2c97183392399b2b207a4af
parent 1528987 040aa41f523e4abecdd1d7546e90ff179407693d
child 1528989 b0e8b387f142e54acc23543549fc983a8bf541c3
push id276055
push usersurkov.alexander@gmail.com
push dateFri, 01 Jun 2018 15:22:50 +0000
treeherdertry@b0e8b387f142 [default view] [failures only]
milestone62.0a1
fixed virtualcursor test MozReview-Commit-ID: 75XukdcbnAL
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/test_aria_objattr.html
accessible/tests/mochitest/pivot.js
accessible/tests/mochitest/pivot/test_virtualcursor.html
accessible/tests/mochitest/tree/test_aria_globals.html
accessible/tests/mochitest/treeupdate/a11y.ini
accessible/tests/mochitest/treeupdate/test_ariahidden.html
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -1399,22 +1399,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,17 +745,18 @@ 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);
+      Accessible* container = mDocument->AccessibleOrTrueContainer(
+        containerNode, DocAccessible::eNoContainerIfARIAHidden);
       MOZ_ASSERT(container,
                  "Text node having rendered text hasn't accessible document!");
       if (container) {
         nsTArray<nsCOMPtr<nsIContent>>* list =
           mContentInsertions.LookupOrAdd(container);
         list->AppendElement(textNode);
       }
     }
--- 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
@@ -888,23 +888,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
@@ -964,21 +964,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"));
     }
@@ -2095,19 +2090,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()
@@ -2624,30 +2616,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
@@ -760,16 +760,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)
@@ -981,32 +994,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);
 
@@ -1222,23 +1219,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;
 }
 
@@ -1786,17 +1791,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
@@ -222,18 +222,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);
@@ -306,32 +291,44 @@ this.EventManager.prototype = {
         }
       }
     }
   },
 
   _handleShow: function _handleShow(aEvent) {
     let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
       ["additions", "all"]);
+    if (this.inTest) {
+      this.sendMsgFunc("AccessFu:Shown", {
+        name: aEvent.accessible.name,
+        role: Utils.AccService.getStringRole(aEvent.accessible.role)
+      });
+    }
     // Only handle show if it is a relevant live region.
     if (!liveRegion) {
       return;
     }
     // Show for text is handled by the EVENT_TEXT_INSERTED handler.
     if (aEvent.accessible.role === Roles.TEXT_LEAF) {
       return;
     }
     this._dequeueLiveEvent(Events.HIDE, liveRegion);
     this.present(Presentation.liveRegion(liveRegion, isPolite, false));
   },
 
   _handleHide: function _handleHide(aEvent) {
     let {liveRegion, isPolite} = this._handleLiveRegion(
       aEvent, ["removals", "all"]);
     let acc = aEvent.accessible;
+    if (this.inTest) {
+      this.sendMsgFunc("AccessFu:Hidden", {
+        name: aEvent.accessible.name,
+        role: Utils.AccService.getStringRole(aEvent.accessible.role)
+      });
+    }
     if (liveRegion) {
       // Hide for text is handled by the EVENT_TEXT_REMOVED handler.
       if (acc.role === Roles.TEXT_LEAF) {
         return;
       }
       this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
     } else {
       let vc = Utils.getVirtualCursor(this.contentScope.content.document);
@@ -624,17 +621,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
@@ -273,51 +273,38 @@ var Utils = { // jshint ignore:line
         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";
+    return this.getState(aAccessible).contains(States.INVISIBLE);
   },
 
   visibleChildCount: function visibleChildCount(aAccessible) {
     let count = 0;
     for (let child = aAccessible.firstChild; child; child = child.nextSibling) {
       if (!this.isHidden(child)) {
         ++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;
   },
@@ -532,22 +519,26 @@ var Logger = { // jshint ignore:line
       let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
       let stateStrings = event.isExtraState ?
         Utils.AccService.getStringStates(0, event.state) :
         Utils.AccService.getStringStates(event.state, 0);
       str += " (" + stateStrings.item(0) + ")";
     }
 
     if (aEvent.eventType == Events.VIRTUALCURSOR_CHANGED) {
-      let event = aEvent.QueryInterface(
-        Ci.nsIAccessibleVirtualCursorChangeEvent);
-      let pivot = aEvent.accessible.QueryInterface(
-        Ci.nsIAccessibleDocument).virtualCursor;
-      str += " (" + this.accessibleToString(event.oldAccessible) + " -> " +
-        this.accessibleToString(pivot.position) + ")";
+      try {
+        let event = aEvent.QueryInterface(
+          Ci.nsIAccessibleVirtualCursorChangeEvent);
+        let pivot = aEvent.accessible.QueryInterface(
+          Ci.nsIAccessibleDocument).virtualCursor;
+        str += ` (${this.accessibleToString(event.oldAccessible)} -> ${this.accessibleToString(pivot.position)})`;
+      } catch (x) {
+        // Likely can't query nsIAccessibleVirtualCursorChangeEvent in parent
+        // process when event is received from the content process.
+      }
     }
 
     return str;
   },
 
   statesToString: function statesToString(aAccessible) {
     return Utils.getState(aAccessible).toString();
   },
--- 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/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/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/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>