Bug 756983 - make nsAccessible::GetActionName faster, r=tbsaunde
authorAlexander Surkov <surkov.alexander@gmail.com>
Mon, 04 Jun 2012 14:41:06 +0900
changeset 98005 af83ecf3ee15bf7a44d9cf159133953331a58824
parent 98004 cfee4e39759d2636affcf29289ebe96b73506170
child 98006 c1c0ce793c5ac76cec000c35f129f478268f2b19
push idunknown
push userunknown
push dateunknown
reviewerstbsaunde
bugs756983
milestone15.0a1
Bug 756983 - make nsAccessible::GetActionName faster, r=tbsaunde
accessible/src/base/filters.cpp
accessible/src/generic/Accessible.cpp
accessible/src/generic/Accessible.h
accessible/src/generic/DocAccessible.cpp
accessible/src/generic/DocAccessible.h
accessible/src/generic/HyperTextAccessible.cpp
accessible/src/html/HTMLListAccessible.cpp
accessible/src/html/nsHTMLLinkAccessible.cpp
accessible/src/html/nsHTMLLinkAccessible.h
accessible/src/html/nsHTMLSelectAccessible.cpp
accessible/src/html/nsHTMLSelectAccessible.h
accessible/src/html/nsHTMLTableAccessible.cpp
accessible/src/html/nsHTMLTableAccessible.h
accessible/src/mac/mozAccessible.mm
accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
accessible/src/xforms/nsXFormsAccessible.cpp
accessible/src/xforms/nsXFormsAccessible.h
accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
accessible/src/xforms/nsXFormsFormControlsAccessible.h
accessible/src/xforms/nsXFormsWidgetsAccessible.cpp
accessible/src/xforms/nsXFormsWidgetsAccessible.h
accessible/src/xul/XULFormControlAccessible.cpp
accessible/src/xul/XULFormControlAccessible.h
accessible/src/xul/nsXULColorPickerAccessible.cpp
accessible/src/xul/nsXULColorPickerAccessible.h
accessible/src/xul/nsXULComboboxAccessible.cpp
accessible/src/xul/nsXULListboxAccessible.cpp
accessible/src/xul/nsXULListboxAccessible.h
accessible/src/xul/nsXULMenuAccessible.cpp
accessible/src/xul/nsXULMenuAccessible.h
accessible/src/xul/nsXULSliderAccessible.cpp
accessible/src/xul/nsXULSliderAccessible.h
accessible/src/xul/nsXULTabAccessible.cpp
accessible/src/xul/nsXULTabAccessible.h
accessible/src/xul/nsXULTextAccessible.cpp
accessible/src/xul/nsXULTreeAccessible.cpp
accessible/src/xul/nsXULTreeAccessible.h
accessible/src/xul/nsXULTreeGridAccessible.cpp
accessible/src/xul/nsXULTreeGridAccessible.h
accessible/tests/mochitest/states/test_controls.xul
accessible/tests/mochitest/states/test_selects.html
--- a/accessible/src/base/filters.cpp
+++ b/accessible/src/base/filters.cpp
@@ -15,17 +15,17 @@ bool
 filters::GetSelected(Accessible* aAccessible)
 {
   return aAccessible->State() & states::SELECTED;
 }
 
 bool
 filters::GetSelectable(Accessible* aAccessible)
 {
-  return aAccessible->State() & states::SELECTABLE;
+  return aAccessible->InteractiveState() & states::SELECTABLE;
 }
 
 bool
 filters::GetRow(Accessible* aAccessible)
 {
   return aAccessible->Role() == roles::ROW;
 }
 
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -648,43 +648,26 @@ PRUint64
 Accessible::NativeState()
 {
   PRUint64 state = 0;
 
   DocAccessible* document = Document();
   if (!document || !document->IsInDocument(this))
     state |= states::STALE;
 
-  bool disabled = false;
   if (mContent->IsElement()) {
     nsEventStates elementState = mContent->AsElement()->State();
 
     if (elementState.HasState(NS_EVENT_STATE_INVALID))
       state |= states::INVALID;
 
     if (elementState.HasState(NS_EVENT_STATE_REQUIRED))
       state |= states::REQUIRED;
 
-    disabled = mContent->IsHTML() ? 
-      (elementState.HasState(NS_EVENT_STATE_DISABLED)) :
-      (mContent->AttrValueIs(kNameSpaceID_None,
-                             nsGkAtoms::disabled,
-                             nsGkAtoms::_true,
-                             eCaseMatters));
-  }
-
-  // Set unavailable state based on disabled state, otherwise set focus states
-  if (disabled) {
-    state |= states::UNAVAILABLE;
-  }
-  else if (mContent->IsElement()) {
-    nsIFrame* frame = GetFrame();
-    if (frame && frame->IsFocusable())
-      state |= states::FOCUSABLE;
-
+    state |= NativeInteractiveState();
     if (FocusMgr()->IsFocused(this))
       state |= states::FOCUSED;
   }
 
   // Gather states::INVISIBLE and states::OFFSCREEN flags for this object.
   state |= VisibilityState();
 
   nsIFrame *frame = GetFrame();
@@ -700,22 +683,48 @@ Accessible::NativeState()
   if (!mRoleMapEntry || mRoleMapEntry->roleRule == kUseNativeRole ||
       mRoleMapEntry->role == roles::LINK)
     state |= NativeLinkState();
 
   return state;
 }
 
 PRUint64
+Accessible::NativeInteractiveState() const
+{
+  if (!mContent->IsElement())
+    return 0;
+
+  if (NativelyUnavailable())
+    return states::UNAVAILABLE;
+
+  nsIFrame* frame = GetFrame();
+  if (frame && frame->IsFocusable())
+    return states::FOCUSABLE;
+
+  return 0;
+}
+
+PRUint64
 Accessible::NativeLinkState() const
 {
   // Expose linked state for simple xlink.
   return nsCoreUtils::IsXLink(mContent) ? states::LINKED : 0;
 }
 
+bool
+Accessible::NativelyUnavailable() const
+{
+  if (mContent->IsHTML())
+    return mContent->AsElement()->State().HasState(NS_EVENT_STATE_DISABLED);
+
+  return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
+                               nsGkAtoms::_true, eCaseMatters);
+}
+
   /* readonly attribute boolean focusedChild; */
 NS_IMETHODIMP
 Accessible::GetFocusedChild(nsIAccessible** aChild)
 {
   NS_ENSURE_ARG_POINTER(aChild);
   *aChild = nsnull;
 
   if (IsDefunct())
@@ -1833,81 +1842,83 @@ Accessible::GetActionCount(PRUint8* aAct
 
   *aActionCount = ActionCount();
   return NS_OK;
 }
 
 PRUint8
 Accessible::ActionCount()
 {
-  return GetActionRule(State()) == eNoAction ? 0 : 1;
+  return GetActionRule() == eNoAction ? 0 : 1;
 }
 
 /* DOMString getAccActionName (in PRUint8 index); */
 NS_IMETHODIMP
 Accessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   aName.Truncate();
 
   if (aIndex != 0)
     return NS_ERROR_INVALID_ARG;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  PRUint64 states = State();
-  PRUint32 actionRule = GetActionRule(states);
+  PRUint32 actionRule = GetActionRule();
 
  switch (actionRule) {
    case eActivateAction:
      aName.AssignLiteral("activate");
      return NS_OK;
 
    case eClickAction:
      aName.AssignLiteral("click");
      return NS_OK;
 
    case ePressAction:
      aName.AssignLiteral("press");
      return NS_OK;
 
    case eCheckUncheckAction:
-     if (states & states::CHECKED)
+   {
+     PRUint64 state = State();
+     if (state & states::CHECKED)
        aName.AssignLiteral("uncheck");
-     else if (states & states::MIXED)
+     else if (state & states::MIXED)
        aName.AssignLiteral("cycle");
      else
        aName.AssignLiteral("check");
      return NS_OK;
+   }
 
    case eJumpAction:
      aName.AssignLiteral("jump");
      return NS_OK;
 
    case eOpenCloseAction:
-     if (states & states::COLLAPSED)
+     if (State() & states::COLLAPSED)
        aName.AssignLiteral("open");
      else
        aName.AssignLiteral("close");
      return NS_OK;
 
    case eSelectAction:
      aName.AssignLiteral("select");
      return NS_OK;
 
    case eSwitchAction:
      aName.AssignLiteral("switch");
      return NS_OK;
-     
+
    case eSortAction:
      aName.AssignLiteral("sort");
      return NS_OK;
-   
+
    case eExpandAction:
-     if (states & states::COLLAPSED)
+     if (State() & states::COLLAPSED)
        aName.AssignLiteral("expand");
      else
        aName.AssignLiteral("collapse");
      return NS_OK;
   }
 
   return NS_ERROR_INVALID_ARG;
 }
@@ -1930,17 +1941,17 @@ NS_IMETHODIMP
 Accessible::DoAction(PRUint8 aIndex)
 {
   if (aIndex != 0)
     return NS_ERROR_INVALID_ARG;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  if (GetActionRule(State()) != eNoAction) {
+  if (GetActionRule() != eNoAction) {
     DoCommand();
     return NS_OK;
   }
 
   return NS_ERROR_INVALID_ARG;
 }
 
 /* DOMString getHelp (); */
@@ -3137,21 +3148,21 @@ Accessible::GetAttrValue(nsIAtom *aPrope
   double value = attrValue.ToDouble(&error);
   if (NS_SUCCEEDED(error))
     *aValue = value;
 
   return NS_OK;
 }
 
 PRUint32
-Accessible::GetActionRule(PRUint64 aStates)
+Accessible::GetActionRule()
 {
-  if (aStates & states::UNAVAILABLE)
+  if (InteractiveState() & states::UNAVAILABLE)
     return eNoAction;
-  
+
   // Check if it's simple xlink.
   if (nsCoreUtils::IsXLink(mContent))
     return eJumpAction;
 
   // Return "click" action on elements that have an attached popup menu.
   if (mContent->IsXUL())
     if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
       return eClickAction;
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -203,42 +203,63 @@ public:
   virtual mozilla::a11y::role NativeRole();
 
   /**
    * Return all states of accessible (including ARIA states).
    */
   virtual PRUint64 State();
 
   /**
+   * Return interactive states present on the accessible
+   * (@see NativeInteractiveState).
+   */
+  PRUint64 InteractiveState() const
+  {
+    PRUint64 state = NativeInteractiveState();
+    ApplyARIAState(&state);
+    return state;
+  }
+
+  /**
    * Return link states present on the accessible.
    */
   PRUint64 LinkState() const
   {
     PRUint64 state = NativeLinkState();
     ApplyARIAState(&state);
     return state;
   }
 
   /**
    * Return the states of accessible, not taking into account ARIA states.
    * Use State() to get complete set of states.
    */
   virtual PRUint64 NativeState();
 
   /**
+   * Return native interactice state (unavailable, focusable or selectable).
+   */
+  virtual PRUint64 NativeInteractiveState() const;
+
+  /**
    * Return native link states present on the accessible.
    */
   virtual PRUint64 NativeLinkState() const;
 
   /**
    * Return bit set of invisible and offscreen states.
    */
   PRUint64 VisibilityState();
 
   /**
+   * Return true if native unavailable state present.
+   */
+  virtual bool NativelyUnavailable() const;
+
+  /**
    * Returns attributes for accessible without explicitly setted ARIA
    * attributes.
    */
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 
   /**
    * Return group position (level, position in set and set size).
    */
@@ -819,20 +840,18 @@ protected:
    *
    * @return - NS_OK_NO_ARIA_VALUE if there is no setted ARIA attribute
    */
   nsresult GetAttrValue(nsIAtom *aAriaProperty, double *aValue);
 
   /**
    * Return the action rule based on ARIA enum constants EActionRule
    * (see nsARIAMap.h). Used by ActionCount() and GetActionName().
-   *
-   * @param aStates  [in] states of the accessible
    */
-  PRUint32 GetActionRule(PRUint64 aStates);
+  PRUint32 GetActionRule();
 
   /**
    * Return group info.
    */
   AccGroupInfo* GetGroupInfo();
 
   /**
    * Fires platform accessible event. It's notification method only. It does
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -280,17 +280,17 @@ PRUint64
 DocAccessible::NativeState()
 {
   // The root content of the document might be removed so that mContent is
   // out of date.
   PRUint64 state = (mContent->GetCurrentDoc() == mDocument) ?
     0 : states::STALE;
 
   // Document is always focusable.
-  state |= states::FOCUSABLE;
+  state |= states::FOCUSABLE; // keep in sync with NativeIteractiveState() impl
   if (FocusMgr()->IsFocused(this))
     state |= states::FOCUSED;
 
   // Expose stale state until the document is ready (DOM is loaded and tree is
   // constructed).
   if (!HasLoadState(eReady))
     state |= states::STALE;
 
@@ -306,16 +306,29 @@ DocAccessible::NativeState()
   }
 
   nsCOMPtr<nsIEditor> editor = GetEditor();
   state |= editor ? states::EDITABLE : states::READONLY;
 
   return state;
 }
 
+PRUint64
+DocAccessible::NativeInteractiveState() const
+{
+  // Document is always focusable.
+  return states::FOCUSABLE;
+}
+
+bool
+DocAccessible::NativelyUnavailable() const
+{
+  return false;
+}
+
 // Accessible public method
 void
 DocAccessible::ApplyARIAState(PRUint64* aState) const
 {
   // Combine with states from outer doc
   // 
   Accessible::ApplyARIAState(aState);
 
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -79,16 +79,18 @@ public:
   virtual nsIDocument* GetDocumentNode() const { return mDocument; }
 
   // Accessible
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
   virtual void Description(nsString& aDescription);
   virtual Accessible* FocusedChild();
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
+  virtual bool NativelyUnavailable() const;
   virtual void ApplyARIAState(PRUint64* aState) const;
 
   virtual void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry);
 
 #ifdef DEBUG
   virtual nsresult HandleAccEvent(AccEvent* aEvent);
 #endif
 
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -1536,17 +1536,17 @@ HyperTextAccessible::GetEditor() const
 
 /**
   * =================== Caret & Selection ======================
   */
 
 nsresult
 HyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  bool isFocusable = State() & states::FOCUSABLE;
+  bool isFocusable = InteractiveState() & states::FOCUSABLE;
 
   // If accessible is focusable then focus it before setting the selection to
   // neglect control's selection changes on focus if any (for example, inputs
   // that do select all on focus).
   // some input controls
   if (isFocusable)
     TakeFocus();
 
@@ -1607,17 +1607,17 @@ HyperTextAccessible::GetCaretOffset(PRIn
   *aCaretOffset = -1;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   // Not focused focusable accessible except document accessible doesn't have
   // a caret.
   if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
-      (State() & states::FOCUSABLE)) {
+      (InteractiveState() & states::FOCUSABLE)) {
     return NS_OK;
   }
 
   // No caret if the focused node is not inside this DOM node and this DOM node
   // is not inside of focused node.
   FocusManager::FocusDisposition focusDisp =
     FocusMgr()->IsInOrContainsFocus(this);
   if (focusDisp == FocusManager::eNone)
--- a/accessible/src/html/HTMLListAccessible.cpp
+++ b/accessible/src/html/HTMLListAccessible.cpp
@@ -183,21 +183,17 @@ role
 HTMLListBulletAccessible::NativeRole()
 {
   return roles::STATICTEXT;
 }
 
 PRUint64
 HTMLListBulletAccessible::NativeState()
 {
-  PRUint64 state = nsLeafAccessible::NativeState();
-
-  state &= ~states::FOCUSABLE;
-  state |= states::READONLY;
-  return state;
+  return nsLeafAccessible::NativeState() | states::READONLY;
 }
 
 void
 HTMLListBulletAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
                                        PRUint32 aLength)
 {
   nsAutoString bulletText;
   nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
--- a/accessible/src/html/nsHTMLLinkAccessible.cpp
+++ b/accessible/src/html/nsHTMLLinkAccessible.cpp
@@ -37,28 +37,17 @@ role
 nsHTMLLinkAccessible::NativeRole()
 {
   return roles::LINK;
 }
 
 PRUint64
 nsHTMLLinkAccessible::NativeState()
 {
-  PRUint64 states = HyperTextAccessibleWrap::NativeState();
-
-  states  &= ~states::READONLY;
-
-  if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name)) {
-    // This is how we indicate it is a named anchor
-    // In other words, this anchor can be selected as a location :)
-    // There is no other better state to use to indicate this.
-    states |= states::SELECTABLE;
-  }
-
-  return states;
+  return HyperTextAccessibleWrap::NativeState() & ~states::READONLY;
 }
 
 PRUint64
 nsHTMLLinkAccessible::NativeLinkState() const
 {
   nsEventStates eventState = mContent->AsElement()->State();
   if (eventState.HasState(NS_EVENT_STATE_UNVISITED))
     return states::LINKED;
@@ -67,16 +56,30 @@ nsHTMLLinkAccessible::NativeLinkState() 
     return states::LINKED | states::TRAVERSED;
 
   // This is a either named anchor (a link with also a name attribute) or
   // it doesn't have any attributes. Check if 'click' event handler is
   // registered, otherwise bail out.
   return nsCoreUtils::HasClickListener(mContent) ? states::LINKED : 0;
 }
 
+PRUint64
+nsHTMLLinkAccessible::NativeInteractiveState() const
+{
+  PRUint64 state = HyperTextAccessibleWrap::NativeInteractiveState();
+
+  // This is how we indicate it is a named anchor. In other words, this anchor
+  // can be selected as a location :) There is no other better state to use to
+  // indicate this.
+  if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name))
+    state |= states::SELECTABLE;
+
+  return state;
+}
+
 void
 nsHTMLLinkAccessible::Value(nsString& aValue)
 {
   aValue.Truncate();
 
   HyperTextAccessible::Value(aValue);
   if (aValue.IsEmpty())
     nsContentUtils::GetLinkLocation(mContent->AsElement(), aValue);
--- a/accessible/src/html/nsHTMLLinkAccessible.h
+++ b/accessible/src/html/nsHTMLLinkAccessible.h
@@ -19,16 +19,17 @@ public:
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // Accessible
   virtual void Value(nsString& aValue);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
   virtual PRUint64 NativeLinkState() const;
+  virtual PRUint64 NativeInteractiveState() const;
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // HyperLinkAccessible
   virtual bool IsLink();
   virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
 
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -232,20 +232,16 @@ nsHTMLSelectOptionAccessible::NativeStat
   Accessible* select = GetSelect();
   if (!select)
     return state;
 
   PRUint64 selectState = select->State();
   if (selectState & states::INVISIBLE)
     return state;
 
-  // Focusable and selectable
-  if (!(state & states::UNAVAILABLE))
-    state |= (states::FOCUSABLE | states::SELECTABLE);
-
   // Are we selected?
   bool isSelected = false;
   nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(mContent));
   if (option) {
     option->GetSelected(&isSelected);
     if (isSelected)
       state |= states::SELECTED;
   }
@@ -280,16 +276,23 @@ nsHTMLSelectOptionAccessible::NativeStat
         state |= states::OFFSCREEN;
       }
     }
   }
  
   return state;
 }
 
+PRUint64
+nsHTMLSelectOptionAccessible::NativeInteractiveState() const
+{
+  return NativelyUnavailable() ?
+    states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE;
+}
+
 PRInt32
 nsHTMLSelectOptionAccessible::GetLevelInternal()
 {
   nsIContent *parentContent = mContent->GetParent();
 
   PRInt32 level =
     parentContent->NodeInfo()->Equals(nsGkAtoms::optgroup) ? 2 : 1;
 
@@ -371,23 +374,19 @@ nsHTMLSelectOptGroupAccessible::
 
 role
 nsHTMLSelectOptGroupAccessible::NativeRole()
 {
   return roles::HEADING;
 }
 
 PRUint64
-nsHTMLSelectOptGroupAccessible::NativeState()
+nsHTMLSelectOptGroupAccessible::NativeInteractiveState() const
 {
-  PRUint64 state = nsHTMLSelectOptionAccessible::NativeState();
-
-  state &= ~(states::FOCUSABLE | states::SELECTABLE);
-
-  return state;
+  return NativelyUnavailable() ? states::UNAVAILABLE : 0;
 }
 
 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::DoAction(PRUint8 index)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -81,16 +81,17 @@ public:
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD SetSelected(bool aSelect);
 
   // Accessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 
   virtual PRInt32 GetLevelInternal();
   virtual void GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame);
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // Widgets
@@ -136,17 +137,17 @@ public:
   virtual ~nsHTMLSelectOptGroupAccessible() {}
 
   // nsIAccessible
   NS_IMETHOD DoAction(PRUint8 index);  
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
-  virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
 protected:
   // Accessible
   virtual void CacheChildren();
 };
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -67,25 +67,28 @@ nsHTMLTableCellAccessible::NativeRole()
 PRUint64
 nsHTMLTableCellAccessible::NativeState()
 {
   PRUint64 state = HyperTextAccessibleWrap::NativeState();
 
   nsIFrame *frame = mContent->GetPrimaryFrame();
   NS_ASSERTION(frame, "No frame for valid cell accessible!");
 
-  if (frame) {
-    state |= states::SELECTABLE;
-    if (frame->IsSelected())
-      state |= states::SELECTED;
-  }
+  if (frame && frame->IsSelected())
+    state |= states::SELECTED;
 
   return state;
 }
 
+PRUint64
+nsHTMLTableCellAccessible::NativeInteractiveState() const
+{
+  return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE;
+}
+
 nsresult
 nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   nsresult rv = HyperTextAccessibleWrap::GetAttributesInternal(aAttributes);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/accessible/src/html/nsHTMLTableAccessible.h
+++ b/accessible/src/html/nsHTMLTableAccessible.h
@@ -27,16 +27,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleTableCell
   NS_DECL_NSIACCESSIBLETABLECELL
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 
 protected:
   /**
    * Return host table accessible.
    */
   already_AddRefed<nsIAccessibleTable> GetTableAccessible();
 
--- a/accessible/src/mac/mozAccessible.mm
+++ b/accessible/src/mac/mozAccessible.mm
@@ -551,33 +551,33 @@ GetNativeFromGeckoAccessible(nsIAccessib
 
   return [NSString stringWithFormat:@"(%p) %@", self, [self role]];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (BOOL)isFocused
 {
-  return (mGeckoAccessible->State() & states::FOCUSED) != 0;
+  return FocusMgr()->IsFocused(mGeckoAccessible);
 }
 
 - (BOOL)canBeFocused
 {
-  return mGeckoAccessible->State() & states::FOCUSABLE;
+  return mGeckoAccessible->InteractiveState() & states::FOCUSABLE;
 }
 
 - (BOOL)focus
 {
   nsresult rv = mGeckoAccessible->TakeFocus();
   return NS_SUCCEEDED(rv);
 }
 
 - (BOOL)isEnabled
 {
-  return (mGeckoAccessible->State() & states::UNAVAILABLE) == 0;
+  return (mGeckoAccessible->InteractiveState() & states::UNAVAILABLE) == 0;
 }
 
 // The root accessible calls this when the focused node was
 // changed to us.
 - (void)didReceiveFocus
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
--- a/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
+++ b/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
@@ -37,22 +37,22 @@ nsHTMLWin32ObjectOwnerAccessible::Shutdo
 // nsHTMLWin32ObjectOwnerAccessible: Accessible implementation
 
 role
 nsHTMLWin32ObjectOwnerAccessible::NativeRole()
 {
   return roles::EMBEDDED_OBJECT;
 }
 
-PRUint64
-nsHTMLWin32ObjectOwnerAccessible::NativeState()
+bool
+nsHTMLWin32ObjectOwnerAccessible::NativelyUnavailable() const
 {
   // XXX: No HWND means this is windowless plugin which is not accessible in
   // the meantime.
-  return mHwnd ? AccessibleWrap::NativeState() : states::UNAVAILABLE;
+  return !mHwnd;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLWin32ObjectOwnerAccessible: Accessible protected implementation
 
 void
 nsHTMLWin32ObjectOwnerAccessible::CacheChildren()
 {
--- a/accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
+++ b/accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
@@ -23,17 +23,17 @@ public:
                                    DocAccessible* aDoc, void* aHwnd);
   virtual ~nsHTMLWin32ObjectOwnerAccessible() {}
 
   // nsAccessNode
   virtual void Shutdown();
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
-  virtual PRUint64 NativeState();
+  virtual bool NativelyUnavailable() const;
 
 protected:
 
   // Accessible
   virtual void CacheChildren();
 
   void* mHwnd;
   nsRefPtr<Accessible> mNativeAccessible;
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -114,49 +114,55 @@ nsXFormsAccessible::CacheSelectChildren(
 
 PRUint64
 nsXFormsAccessible::NativeState()
 {
   NS_ENSURE_TRUE(sXFormsService, 0);
 
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
 
-  bool isRelevant = false;
-  nsresult rv = sXFormsService->IsRelevant(DOMNode, &isRelevant);
-  NS_ENSURE_SUCCESS(rv, 0);
-
   bool isReadonly = false;
-  rv = sXFormsService->IsReadonly(DOMNode, &isReadonly);
+  nsresult rv = sXFormsService->IsReadonly(DOMNode, &isReadonly);
   NS_ENSURE_SUCCESS(rv, 0);
 
   bool isRequired = false;
   rv = sXFormsService->IsRequired(DOMNode, &isRequired);
   NS_ENSURE_SUCCESS(rv, 0);
 
   bool isValid = false;
   rv = sXFormsService->IsValid(DOMNode, &isValid);
   NS_ENSURE_SUCCESS(rv, 0);
 
   PRUint64 states = HyperTextAccessibleWrap::NativeState();
 
-  if (!isRelevant)
+  if (NativelyUnavailable())
     states |= states::UNAVAILABLE;
 
   if (isReadonly)
     states |= states::READONLY;
 
   if (isRequired)
     states |= states::REQUIRED;
 
   if (!isValid)
     states |= states::INVALID;
 
   return states;
 }
 
+bool
+nsXFormsAccessible::NativelyUnavailable() const
+{
+  nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
+
+  bool isRelevant = false;
+  sXFormsService->IsRelevant(DOMNode, &isRelevant);
+  return !isRelevant;
+}
+
 nsresult
 nsXFormsAccessible::GetNameInternal(nsAString& aName)
 {
   // search the xforms:label element
   return GetBoundChildElementValue(NS_LITERAL_STRING("label"), aName);
 }
 
 void
--- a/accessible/src/xforms/nsXFormsAccessible.h
+++ b/accessible/src/xforms/nsXFormsAccessible.h
@@ -47,16 +47,17 @@ public:
   virtual void Value(nsString& aValue);
 
   // Returns value of child xforms 'label' element.
   virtual nsresult GetNameInternal(nsAString& aName);
 
   // Returns state of xforms element taking into account state of instance node
   // that it is bound to.
   virtual PRUint64 NativeState();
+  virtual bool NativelyUnavailable() const;
 
   // Denies accessible nodes in anonymous content of xforms element by
   // always returning false value.
   virtual bool CanHaveAnonChildren();
 
 protected:
   // Returns value of first child xforms element by tagname that is bound to
   // instance node.
--- a/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
@@ -550,17 +550,23 @@ nsXFormsSelectComboboxAccessible::Native
   nsresult rv = sXFormsService->IsDropmarkerOpen(DOMNode, &isOpen);
   NS_ENSURE_SUCCESS(rv, state);
 
   if (isOpen)
     state |= states::EXPANDED;
   else
     state |= states::COLLAPSED;
 
-  return state | states::HASPOPUP | states::FOCUSABLE;
+  return state | states::HASPOPUP;
+}
+
+PRUint64
+nsXFormsSelectComboboxAccessible::NativeInteractiveState() const
+{
+  return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
 }
 
 bool
 nsXFormsSelectComboboxAccessible::CanHaveAnonChildren()
 {
   return true;
 }
 
@@ -580,27 +586,29 @@ nsXFormsItemComboboxAccessible::NativeRo
 {
   return roles::LISTITEM;
 }
 
 PRUint64
 nsXFormsItemComboboxAccessible::NativeState()
 {
   PRUint64 state = nsXFormsSelectableItemAccessible::NativeState();
-
-  if (state & states::UNAVAILABLE)
-    return state;
-
-  state |= states::SELECTABLE;
   if (IsSelected())
     state |= states::SELECTED;
 
   return state;
 }
 
+PRUint64
+nsXFormsItemComboboxAccessible::NativeInteractiveState() const
+{
+  return NativelyUnavailable() ?
+    states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE;
+}
+
 NS_IMETHODIMP
 nsXFormsItemComboboxAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex != eAction_Click)
     return NS_ERROR_INVALID_ARG;
 
   aName.AssignLiteral("select");
   return NS_OK;
--- a/accessible/src/xforms/nsXFormsFormControlsAccessible.h
+++ b/accessible/src/xforms/nsXFormsFormControlsAccessible.h
@@ -256,16 +256,17 @@ class nsXFormsSelectComboboxAccessible :
 {
 public:
   nsXFormsSelectComboboxAccessible(nsIContent* aContent,
                                    DocAccessible* aDoc);
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
   virtual bool CanHaveAnonChildren();
 };
 
 
 /**
  * Accessible object for xforms:item element when it is represented by a
  * listitem. This occurs when the item is contained in a xforms:select with
  * minimal appearance. Such a xforms:select is represented by a combobox.
@@ -278,12 +279,13 @@ public:
                                  DocAccessible* aDoc);
 
   // nsIAccessible
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 };
 
 #endif
 
--- a/accessible/src/xforms/nsXFormsWidgetsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsWidgetsAccessible.cpp
@@ -114,26 +114,30 @@ nsXFormsComboboxPopupWidgetAccessible::N
 {
   PRUint64 state = nsXFormsAccessible::NativeState();
 
   bool isOpen = false;
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
   nsresult rv = sXFormsService->IsDropmarkerOpen(DOMNode, &isOpen);
   NS_ENSURE_SUCCESS(rv, state);
 
-  state |= states::FOCUSABLE;
-
   if (isOpen)
     state = states::FLOATING;
   else
     state = states::INVISIBLE;
 
   return state;
 }
 
+PRUint64
+nsXFormsComboboxPopupWidgetAccessible::NativeInteractiveState() const
+{
+  return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
+}
+
 nsresult
 nsXFormsComboboxPopupWidgetAccessible::GetNameInternal(nsAString& aName)
 {
   // Override nsXFormsAccessible::GetName() to prevent name calculation by
   // XForms rules.
   return NS_OK;
 }
 
--- a/accessible/src/xforms/nsXFormsWidgetsAccessible.h
+++ b/accessible/src/xforms/nsXFormsWidgetsAccessible.h
@@ -59,15 +59,16 @@ public:
                                         DocAccessible* aDoc);
 
   // Accessible
   virtual void Description(nsString& aDescription);
   virtual void Value(nsString& aValue);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 
 protected:
   // Accessible
   virtual void CacheChildren();
 };
 
 #endif
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -90,26 +90,16 @@ XULButtonAccessible::NativeRole()
 PRUint64
 XULButtonAccessible::NativeState()
 {
   // Possible states: focused, focusable, unavailable(disabled).
 
   // get focus and disable status from base class
   PRUint64 state = Accessible::NativeState();
 
-  bool disabled = false;
-  nsCOMPtr<nsIDOMXULControlElement> xulFormElement(do_QueryInterface(mContent));
-  if (xulFormElement) {
-    xulFormElement->GetDisabled(&disabled);
-    if (disabled)
-      state |= states::UNAVAILABLE;
-    else 
-      state |= states::FOCUSABLE;
-  }
-
   // Buttons can be checked -- they simply appear pressed in rather than checked
   nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement(do_QueryInterface(mContent));
   if (xulButtonElement) {
     nsAutoString type;
     xulButtonElement->GetType(type);
     if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
       state |= states::CHECKABLE;
       bool checked = false;
@@ -476,32 +466,35 @@ XULRadioButtonAccessible::
 }
 
 PRUint64
 XULRadioButtonAccessible::NativeState()
 {
   PRUint64 state = nsLeafAccessible::NativeState();
   state |= states::CHECKABLE;
 
-  if (!(state & states::UNAVAILABLE))
-    state |= states::FOCUSABLE;
-
   nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
     do_QueryInterface(mContent);
   if (radioButton) {
     bool selected = false;   // Radio buttons can be selected
     radioButton->GetSelected(&selected);
     if (selected) {
       state |= states::CHECKED;
     }
   }
 
   return state;
 }
 
+PRUint64
+XULRadioButtonAccessible::NativeInteractiveState() const
+{
+  return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // XULRadioButtonAccessible: Widgets
 
 Accessible*
 XULRadioButtonAccessible::ContainerWidget() const
 {
   return mParent;
 }
@@ -528,22 +521,22 @@ XULRadioGroupAccessible::
 
 role
 XULRadioGroupAccessible::NativeRole()
 {
   return roles::GROUPING;
 }
 
 PRUint64
-XULRadioGroupAccessible::NativeState()
+XULRadioGroupAccessible::NativeInteractiveState() const
 {
   // The radio group is not focusable. Sometimes the focus controller will
   // report that it is focused. That means that the actual selected radio button
   // should be considered focused.
-  return Accessible::NativeState() & ~(states::FOCUSABLE | states::FOCUSED);
+  return NativelyUnavailable() ? states::UNAVAILABLE : 0;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULRadioGroupAccessible: Widgets
 
 bool
 XULRadioGroupAccessible::IsWidget() const
 {
--- a/accessible/src/xul/XULFormControlAccessible.h
+++ b/accessible/src/xul/XULFormControlAccessible.h
@@ -127,32 +127,33 @@ public:
 class XULRadioButtonAccessible : public RadioButtonAccessible
 {
 
 public:
   XULRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // Accessible
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 
   // Widgets
   virtual Accessible* ContainerWidget() const;
 };
 
 /**
  * Used for XUL radiogroup element.
  */
 class XULRadioGroupAccessible : public XULSelectControlAccessible
 {
 public:
   XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
-  virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 
   // Widgets
   virtual bool IsWidget() const;
   virtual bool IsActiveWidget() const;
   virtual bool AreItemsOperable() const;
 };
 
 /**
--- a/accessible/src/xul/nsXULColorPickerAccessible.cpp
+++ b/accessible/src/xul/nsXULColorPickerAccessible.cpp
@@ -47,25 +47,29 @@ nsXULColorPickerTileAccessible::NativeRo
 {
   return roles::PUSHBUTTON;
 }
 
 PRUint64
 nsXULColorPickerTileAccessible::NativeState()
 {
   PRUint64 state = AccessibleWrap::NativeState();
-  if (!(state & states::UNAVAILABLE))
-    state |= states::FOCUSABLE | states::SELECTABLE;
-
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::selected))
     state |= states::SELECTED;
 
   return state;
 }
 
+PRUint64
+nsXULColorPickerTileAccessible::NativeInteractiveState() const
+{
+  return NativelyUnavailable() ?
+    states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerTileAccessible: Widgets
 
 Accessible*
 nsXULColorPickerTileAccessible::ContainerWidget() const
 {
   Accessible* parent = Parent();
   if (parent) {
@@ -88,24 +92,18 @@ nsXULColorPickerAccessible::
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerAccessible: Accessible
 
 PRUint64
 nsXULColorPickerAccessible::NativeState()
 {
-  // Possible states: focused, focusable, unavailable(disabled).
-
-  // get focus and disable status from base class
-  PRUint64 states = AccessibleWrap::NativeState();
-
-  states |= states::FOCUSABLE | states::HASPOPUP;
-
-  return states;
+  PRUint64 state = AccessibleWrap::NativeState();
+  return state | states::HASPOPUP;
 }
 
 role
 nsXULColorPickerAccessible::NativeRole()
 {
   return roles::BUTTONDROPDOWNGRID;
 }
 
--- a/accessible/src/xul/nsXULColorPickerAccessible.h
+++ b/accessible/src/xul/nsXULColorPickerAccessible.h
@@ -16,16 +16,17 @@ class nsXULColorPickerTileAccessible : p
 public:
   nsXULColorPickerTileAccessible(nsIContent* aContent,
                                  DocAccessible* aDoc);
 
   // Accessible
   virtual void Value(nsString& aValue);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 
   // Widgets
   virtual Accessible* ContainerWidget() const;
 };
 
 
 /**
  * Used for colorpicker button (xul:colorpicker@type="button").
--- a/accessible/src/xul/nsXULComboboxAccessible.cpp
+++ b/accessible/src/xul/nsXULComboboxAccessible.cpp
@@ -45,33 +45,29 @@ nsXULComboboxAccessible::NativeState()
   // As a nsComboboxAccessible we can have the following states:
   //     STATE_FOCUSED
   //     STATE_FOCUSABLE
   //     STATE_HASPOPUP
   //     STATE_EXPANDED
   //     STATE_COLLAPSED
 
   // Get focus status from base class
-  PRUint64 states = Accessible::NativeState();
+  PRUint64 state = Accessible::NativeState();
 
   nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
   if (menuList) {
-    bool isOpen;
+    bool isOpen = false;
     menuList->GetOpen(&isOpen);
-    if (isOpen) {
-      states |= states::EXPANDED;
-    }
-    else {
-      states |= states::COLLAPSED;
-    }
+    if (isOpen)
+      state |= states::EXPANDED;
+    else
+      state |= states::COLLAPSED;
   }
 
-  states |= states::HASPOPUP | states::FOCUSABLE;
-
-  return states;
+  return state | states::HASPOPUP;
 }
 
 void
 nsXULComboboxAccessible::Description(nsString& aDescription)
 {
   aDescription.Truncate();
   // Use description of currently focused option
   nsCOMPtr<nsIDOMXULMenuListElement> menuListElm(do_QueryInterface(mContent));
--- a/accessible/src/xul/nsXULListboxAccessible.cpp
+++ b/accessible/src/xul/nsXULListboxAccessible.cpp
@@ -864,17 +864,17 @@ nsXULListitemAccessible::NativeRole()
 }
 
 PRUint64
 nsXULListitemAccessible::NativeState()
 {
   if (mIsCheckbox)
     return nsXULMenuitemAccessible::NativeState();
 
-  PRUint64 states = states::FOCUSABLE | states::SELECTABLE;
+  PRUint64 states = NativeInteractiveState();
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
     do_QueryInterface(mContent);
 
   if (listItem) {
     bool isSelected;
     listItem->GetSelected(&isSelected);
     if (isSelected)
@@ -882,16 +882,23 @@ nsXULListitemAccessible::NativeState()
 
     if (FocusMgr()->IsFocused(this))
       states |= states::FOCUSED;
   }
 
   return states;
 }
 
+PRUint64
+nsXULListitemAccessible::NativeInteractiveState() const
+{
+  return NativelyUnavailable() || mParent->NativelyUnavailable() ?
+    states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE;
+}
+
 NS_IMETHODIMP nsXULListitemAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click && mIsCheckbox) {
     // check or uncheck
     PRUint64 states = NativeState();
 
     if (states & states::CHECKED)
       aName.AssignLiteral("uncheck");
--- a/accessible/src/xul/nsXULListboxAccessible.h
+++ b/accessible/src/xul/nsXULListboxAccessible.h
@@ -112,16 +112,17 @@ public:
   NS_IMETHOD GetActionName(PRUint8 index, nsAString& aName);
   // Don't use XUL menuitems's description attribute
 
   // Accessible
   virtual void Description(nsString& aDesc);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
   virtual bool CanHaveAnonChildren();
 
   // Widgets
   virtual Accessible* ContainerWidget() const;
 
 protected:
   /**
    * Return listbox accessible for the listitem.
--- a/accessible/src/xul/nsXULMenuAccessible.cpp
+++ b/accessible/src/xul/nsXULMenuAccessible.cpp
@@ -105,34 +105,38 @@ nsXULMenuitemAccessible::NativeState()
         state &= ~(states::OFFSCREEN | states::INVISIBLE);
         state |= (grandParentState & states::OFFSCREEN) |
                  (grandParentState & states::INVISIBLE) |
                  (grandParentState & states::OPAQUE1);
       } // isCollapsed
     } // isSelected
   } // ROLE_COMBOBOX_OPTION
 
-  // Set focusable and selectable for items that are available
-  // and whose metric setting does allow disabled items to be focused.
-  if (state & states::UNAVAILABLE) {
-    // Honour the LookAndFeel metric.
-    PRInt32 skipDisabledMenuItems =
-      LookAndFeel::GetInt(LookAndFeel::eIntID_SkipNavigatingDisabledMenuItem);
-    // We don't want the focusable and selectable states for combobox items,
-    // so exclude them here as well.
-    if (skipDisabledMenuItems || isComboboxOption) {
-      return state;
+  return state;
+}
+
+PRUint64
+nsXULMenuitemAccessible::NativeInteractiveState() const
+{
+  if (NativelyUnavailable()) {
+    // Note: keep in sinc with nsXULPopupManager::IsValidMenuItem() logic.
+    bool skipNavigatingDisabledMenuItem = true;
+    nsMenuFrame* menuFrame = do_QueryFrame(GetFrame());
+    if (!menuFrame->IsOnMenuBar()) {
+      skipNavigatingDisabledMenuItem = LookAndFeel::
+        GetInt(LookAndFeel::eIntID_SkipNavigatingDisabledMenuItem, 0) != 0;
     }
+
+    if (skipNavigatingDisabledMenuItem)
+      return states::UNAVAILABLE;
+
+    return states::UNAVAILABLE | states::FOCUSABLE | states::SELECTABLE;
   }
 
-  state |= (states::FOCUSABLE | states::SELECTABLE);
-  if (FocusMgr()->IsFocused(this))
-    state |= states::FOCUSED;
-
-  return state;
+  return states::FOCUSABLE | states::SELECTABLE;
 }
 
 nsresult
 nsXULMenuitemAccessible::GetNameInternal(nsAString& aName)
 {
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
   return NS_OK;
 }
@@ -550,27 +554,16 @@ nsXULMenupopupAccessible::ContainerWidge
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenubarAccessible::
   nsXULMenubarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
 }
 
-PRUint64
-nsXULMenubarAccessible::NativeState()
-{
-  PRUint64 state = Accessible::NativeState();
-
-  // Menu bar itself is not actually focusable
-  state &= ~states::FOCUSABLE;
-  return state;
-}
-
-
 nsresult
 nsXULMenubarAccessible::GetNameInternal(nsAString& aName)
 {
   aName.AssignLiteral("Application");
   return NS_OK;
 }
 
 role
--- a/accessible/src/xul/nsXULMenuAccessible.h
+++ b/accessible/src/xul/nsXULMenuAccessible.h
@@ -24,16 +24,17 @@ public:
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // Accessible
   virtual void Description(nsString& aDescription);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
   virtual PRInt32 GetLevelInternal();
 
   virtual bool CanHaveAnonChildren();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
   virtual KeyBinding KeyboardShortcut() const;
@@ -93,17 +94,16 @@ public:
 class nsXULMenubarAccessible : public AccessibleWrap
 {
 public:
   nsXULMenubarAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // Accessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
-  virtual PRUint64 NativeState();
 
   // Widget
   virtual bool IsActiveWidget() const;
   virtual bool AreItemsOperable() const;
   virtual Accessible* CurrentItem();
   virtual void SetCurrentItem(Accessible* aItem);
 };
 
--- a/accessible/src/xul/nsXULSliderAccessible.cpp
+++ b/accessible/src/xul/nsXULSliderAccessible.cpp
@@ -33,32 +33,36 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsXULSlider
 
 role
 nsXULSliderAccessible::NativeRole()
 {
   return roles::SLIDER;
 }
 
 PRUint64
-nsXULSliderAccessible::NativeState()
-{
-  PRUint64 state = AccessibleWrap::NativeState();
+nsXULSliderAccessible::NativeInteractiveState() const
+ {
+  if (NativelyUnavailable())
+    return states::UNAVAILABLE;
 
   nsIContent* sliderElm = GetSliderElement();
-  if (!sliderElm)
-    return state;
+  if (sliderElm) {
+    nsIFrame* frame = sliderElm->GetPrimaryFrame();
+    if (frame && frame->IsFocusable())
+      return states::FOCUSABLE;
+  }
 
-  nsIFrame *frame = sliderElm->GetPrimaryFrame();
-  if (frame && frame->IsFocusable())
-    state |= states::FOCUSABLE;
+  return 0;
+}
 
-  if (FocusMgr()->IsFocused(this))
-    state |= states::FOCUSED;
-
-  return state;
+bool
+nsXULSliderAccessible::NativelyUnavailable() const
+{
+  return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
+                               nsGkAtoms::_true, eCaseMatters);
 }
 
 // nsIAccessible
 
 void
 nsXULSliderAccessible::Value(nsString& aValue)
 {
   GetSliderAttr(nsGkAtoms::curpos, aValue);
@@ -160,17 +164,17 @@ nsXULSliderAccessible::CanHaveAnonChildr
 {
   // Do not allow anonymous xul:slider be accessible.
   return false;
 }
 
 // Utils
 
 nsIContent*
-nsXULSliderAccessible::GetSliderElement()
+nsXULSliderAccessible::GetSliderElement() const
 {
   if (!mSliderNode) {
     // XXX: we depend on anonymous content.
     mSliderNode = mContent->OwnerDoc()->
       GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid,
                                      NS_LITERAL_STRING("slider"));
   }
 
--- a/accessible/src/xul/nsXULSliderAccessible.h
+++ b/accessible/src/xul/nsXULSliderAccessible.h
@@ -26,36 +26,37 @@ public:
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsIAccessibleValue
   NS_DECL_NSIACCESSIBLEVALUE
 
   // Accessible
   virtual void Value(nsString& aValue);
   virtual mozilla::a11y::role NativeRole();
-  virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
+  virtual bool NativelyUnavailable() const;
   virtual bool CanHaveAnonChildren();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
 protected:
   /**
    * Return anonymous slider element.
    */
-  nsIContent* GetSliderElement();
+  nsIContent* GetSliderElement() const;
 
   nsresult GetSliderAttr(nsIAtom *aName, nsAString& aValue);
   nsresult SetSliderAttr(nsIAtom *aName, const nsAString& aValue);
 
   nsresult GetSliderAttr(nsIAtom *aName, double *aValue);
   nsresult SetSliderAttr(nsIAtom *aName, double aValue);
 
 private:
-  nsCOMPtr<nsIContent> mSliderNode;
+  mutable nsCOMPtr<nsIContent> mSliderNode;
 };
 
 
 /**
  * Used for slider's thumb element.
  */
 class nsXULThumbAccessible : public AccessibleWrap
 {
--- a/accessible/src/xul/nsXULTabAccessible.cpp
+++ b/accessible/src/xul/nsXULTabAccessible.cpp
@@ -77,40 +77,33 @@ nsXULTabAccessible::NativeRole()
 PRUint64
 nsXULTabAccessible::NativeState()
 {
   // Possible states: focused, focusable, unavailable(disabled), offscreen.
 
   // get focus and disable status from base class
   PRUint64 state = AccessibleWrap::NativeState();
 
-  // In the past, tabs have been focusable in classic theme
-  // They may be again in the future
-  // Check style for -moz-user-focus: normal to see if it's focusable
-  state &= ~states::FOCUSABLE;
-
-  nsIFrame *frame = mContent->GetPrimaryFrame();
-  if (frame) {
-    const nsStyleUserInterface* ui = frame->GetStyleUserInterface();
-    if (ui->mUserFocus == NS_STYLE_USER_FOCUS_NORMAL)
-      state |= states::FOCUSABLE;
-  }
-
   // Check whether the tab is selected
-  state |= states::SELECTABLE;
-  state &= ~states::SELECTED;
   nsCOMPtr<nsIDOMXULSelectControlItemElement> tab(do_QueryInterface(mContent));
   if (tab) {
     bool selected = false;
     if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected)
       state |= states::SELECTED;
   }
   return state;
 }
 
+PRUint64
+nsXULTabAccessible::NativeInteractiveState() const
+{
+  PRUint64 state = Accessible::NativeInteractiveState();
+  return (state & states::UNAVAILABLE) ? state : state | states::SELECTABLE;
+}
+
 // nsIAccessible
 Relation
 nsXULTabAccessible::RelationByType(PRUint32 aType)
 {
   Relation rel = AccessibleWrap::RelationByType(aType);
   if (aType != nsIAccessibleRelation::RELATION_LABEL_FOR)
     return rel;
 
--- a/accessible/src/xul/nsXULTabAccessible.h
+++ b/accessible/src/xul/nsXULTabAccessible.h
@@ -23,16 +23,17 @@ public:
 
   // nsIAccessible
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
   virtual Relation RelationByType(PRUint32 aType);
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 };
 
 
 /**
--- a/accessible/src/xul/nsXULTextAccessible.cpp
+++ b/accessible/src/xul/nsXULTextAccessible.cpp
@@ -82,21 +82,17 @@ nsXULTooltipAccessible::
   nsXULTooltipAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   nsLeafAccessible(aContent, aDoc)
 {
 }
 
 PRUint64
 nsXULTooltipAccessible::NativeState()
 {
-  PRUint64 states = nsLeafAccessible::NativeState();
-
-  states &= ~states::FOCUSABLE;
-  states |= states::READONLY;
-  return states;
+  return nsLeafAccessible::NativeState() | states::READONLY;
 }
 
 role
 nsXULTooltipAccessible::NativeRole()
 {
   return roles::TOOLTIP;
 }
 
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -943,17 +943,17 @@ nsXULTreeItemAccessibleBase::GroupPositi
 
 PRUint64
 nsXULTreeItemAccessibleBase::NativeState()
 {
   if (!mTreeView)
     return states::DEFUNCT;
 
   // focusable and selectable states
-  PRUint64 state = states::FOCUSABLE | states::SELECTABLE;
+  PRUint64 state = NativeInteractiveState();
 
   // expanded/collapsed state
   if (IsExpandable()) {
     bool isContainerOpen;
     mTreeView->IsContainerOpen(mRow, &isContainerOpen);
     state |= isContainerOpen ? states::EXPANDED : states::COLLAPSED;
   }
 
@@ -976,16 +976,22 @@ nsXULTreeItemAccessibleBase::NativeState
   mTree->GetFirstVisibleRow(&firstVisibleRow);
   mTree->GetLastVisibleRow(&lastVisibleRow);
   if (mRow < firstVisibleRow || mRow > lastVisibleRow)
     state |= states::INVISIBLE;
 
   return state;
 }
 
+PRUint64
+nsXULTreeItemAccessibleBase::NativeInteractiveState() const
+{
+  return states::FOCUSABLE | states::SELECTABLE;
+}
+
 PRInt32
 nsXULTreeItemAccessibleBase::IndexInParent() const
 {
   return mParent ? mParent->ContentChildCount() + mRow : -1;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeItemAccessibleBase: Widgets
--- a/accessible/src/xul/nsXULTreeAccessible.h
+++ b/accessible/src/xul/nsXULTreeAccessible.h
@@ -151,16 +151,17 @@ public:
 
   // nsAccessNode
   virtual void Shutdown();
   virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual mozilla::a11y::GroupPos GroupPosition();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
   virtual PRInt32 IndexInParent() const;
   virtual Relation RelationByType(PRUint32 aType);
   virtual Accessible* FocusedChild();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // Widgets
--- a/accessible/src/xul/nsXULTreeGridAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeGridAccessible.cpp
@@ -1098,17 +1098,17 @@ nsXULTreeGridCellAccessible::NativeRole(
 
 PRUint64
 nsXULTreeGridCellAccessible::NativeState()
 {
   if (!mTreeView)
     return states::DEFUNCT;
 
   // selectable/selected state
-  PRUint64 states = states::SELECTABLE;
+  PRUint64 states = states::SELECTABLE; // keep in sync with NativeInteractiveState
 
   nsCOMPtr<nsITreeSelection> selection;
   mTreeView->GetSelection(getter_AddRefs(selection));
   if (selection) {
     bool isSelected = false;
     selection->IsSelected(mRow, &isSelected);
     if (isSelected)
       states |= states::SELECTED;
@@ -1123,16 +1123,22 @@ nsXULTreeGridCellAccessible::NativeState
     mTreeView->GetCellValue(mRow, mColumn, checked);
     if (checked.EqualsIgnoreCase("true"))
       states |= states::CHECKED;
   }
 
   return states;
 }
 
+PRUint64
+nsXULTreeGridCellAccessible::NativeInteractiveState() const
+{
+  return states::SELECTABLE;
+}
+
 PRInt32
 nsXULTreeGridCellAccessible::IndexInParent() const
 {
   return GetColumnIndex();
 }
 
 Relation
 nsXULTreeGridCellAccessible::RelationByType(PRUint32 aType)
--- a/accessible/src/xul/nsXULTreeGridAccessible.h
+++ b/accessible/src/xul/nsXULTreeGridAccessible.h
@@ -137,16 +137,17 @@ public:
   // Accessible
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
   virtual Accessible* FocusedChild();
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
   virtual PRInt32 IndexInParent() const;
   virtual Relation RelationByType(PRUint32 aType);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeInteractiveState() const;
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // nsXULTreeGridCellAccessible
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEGRIDCELLACCESSIBLE_IMPL_CID)
 
   /**
--- a/accessible/tests/mochitest/states/test_controls.xul
+++ b/accessible/tests/mochitest/states/test_controls.xul
@@ -10,66 +10,173 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
 
   <script type="application/javascript">
   <![CDATA[
+    function openColorpicker(aID)
+    {
+      this.popupNode = getNode(aID).mPicker.parentNode;
+      this.popup = getAccessible(this.popupNode);
 
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.popupNode)
+      ];
+
+      this.invoke = function openColorpicker_invoke()
+      {
+        getNode(aID).showPopup();
+      }
+
+      this.finalCheck = function openColorpicker_finalCheck()
+      {
+        testStates(this.popup.firstChild,
+                   STATE_FOCUSABLE | STATE_SELECTABLE, 0,
+                   STATE_UNAVAILABLE);
+      }
+
+      this.getID = function openColorpicker_getID()
+      {
+        return "open colorpicker";
+      }
+    }
+
+    var gQueue = null;
     function doTest()
     {
       testStates("checkbox", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
       testStates("checkbox2", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
-      testStates("radio-group", 0, 0, STATE_UNAVAILABLE);
-      testStates("orange", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
-      testStates("violet", 0, 0, STATE_UNAVAILABLE);
-      testStates("radio-group2", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
-      testStates("orange2", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+      testStates("radiogroup", 0, 0, STATE_FOCUSABLE | STATE_UNAVAILABLE);
+      testStates("radio", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+      testStates("radio-disabled", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+      testStates("radiogroup-disabled", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+      testStates("radio-disabledradiogroup", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+      testStates("button", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+      testStates("button-disabled", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+      testStates("colorpicker", STATE_FOCUSABLE | STATE_HASPOPUP, 0, STATE_UNAVAILABLE);
+      testStates("colorpicker-disabled", STATE_HASPOPUP, 0, STATE_FOCUSABLE);
+      testStates("combobox", STATE_FOCUSABLE | STATE_HASPOPUP, 0, STATE_UNAVAILABLE);
+      testStates("combobox-disabled", STATE_UNAVAILABLE | STATE_HASPOPUP, 0, STATE_FOCUSABLE);
+      testStates("listbox", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+      testStates("listitem", STATE_FOCUSABLE | STATE_SELECTABLE, 0, STATE_UNAVAILABLE);
+      testStates("listbox-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE);
+      testStates("listitem-disabledlistbox", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE);
+      testStates("menubar", 0, 0, STATE_FOCUSABLE);
+      testStates("menu", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+      testStates("menu-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE);
+      testStates("scale", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+      testStates("scale-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE);
+      testStates("tab", STATE_FOCUSABLE | STATE_SELECTABLE | STATE_SELECTED, 0, STATE_UNAVAILABLE);
+      testStates("tab-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE | STATE_SELECTED);
 
-      SimpleTest.finish()
+      gQueue = new eventQueue();
+      gQueue.push(new openColorpicker("colorpicker"));
+      gQueue.invoke(); // Will call SimpleTest.finish()
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
   <hbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
      <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=599163"
          title="check disabled state instead of attribute">
         Mozilla Bug 599163
-     </a><br/>
+     </a>
+     <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=756983"
+         title="Isolate focusable and unavailable states from State()">
+        Mozilla Bug 756983
+     </a>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox flex="1">
 
     <checkbox id="checkbox" checked="true" label="Steak"/>
     <checkbox id="checkbox2" checked="true" label="Salad" disabled="true"/>
 
-    <radiogroup id="radio-group">
-      <radio id="orange" label="Orange" disabled="true"/>
-      <radio id="violet" selected="true" label="Violet"/>
-      <radio id="yellow" label="Yellow"/>
+    <radiogroup id="radiogroup">
+      <radio id="radio" label="Orange"/>
+      <radio id="radio-disabled" selected="true" label="Violet" disabled="true"/>
+    </radiogroup>
+
+    <radiogroup id="radiogroup-disabled" disabled="true">
+      <radio id="radio-disabledradiogroup" label="Orange"/>
+      <radio id="violet2" selected="true" label="Violet"/>
     </radiogroup>
 
-    <radiogroup id="radio-group2" disabled="true">
-      <radio id="orange2" label="Orange"/>
-      <radio id="violet2" selected="true" label="Violet"/>
-      <radio id="yellow2" label="Yellow"/>
-    </radiogroup>
+    <button id="button" value="button"/>
+    <button id="button-disabled" disabled="true" value="button"/>
+
+    <colorpicker id="colorpicker" type="button"/>
+    <colorpicker id="colorpicker-disabled" type="button" disabled="true"/>
+
+    <menulist id="combobox">
+      <menupopup>
+        <menuitem label="item1"/>
+      </menupopup>
+    </menulist>
+
+    <menulist id="combobox-disabled" disabled="true">
+      <menupopup>
+        <menuitem label="item1"/>
+      </menupopup>
+    </menulist>
+
+    <listbox id="listbox">
+      <listitem id="listitem" label="list item"/>
+    </listbox>
+
+    <listbox id="listbox-disabled" disabled="true">
+      <listitem id="listitem-disabledlistbox" label="list item"/>
+    </listbox>
 
+    <toolbox>
+      <menubar id="menubar">
+        <menu id="menu" label="menu1">
+          <menupopup>
+            <menuitem id="menu1-item1" label="menuitem1.1"/>
+          </menupopup>
+        </menu>
+        <menu id="menu-disabled" label="menu2" disabled="true">
+          <menupopup>
+            <menuitem id="menu-disabled-item1" label="menuitem2.1"/>
+          </menupopup>
+        </menu>
+      </menubar>
+    </toolbox>
+
+    <scale id="scale" min="1" max="10"/>
+    <scale id="scale-disabled" min="1" max="10" disabled="true"/>
+
+    <tabbox>
+      <tabs>
+        <tab id="tab" label="tab1" tooltip="tooltip"/>
+        <tab id="tab-disabled" label="tab1" disabled="true"/>
+      </tabs>
+      <tabpanels>
+        <tabpanel/>
+        <tabpanel/>
+      </tabpanels>
+    </tabbox>
+
+    <tooltip id="tooltip"><description>tooltip</description></tooltip>
     </vbox>
   </hbox>
 
 </window>
 
--- a/accessible/tests/mochitest/states/test_selects.html
+++ b/accessible/tests/mochitest/states/test_selects.html
@@ -69,31 +69,63 @@
       testStates(opt1, STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
                  EXT_STATE_ACTIVE, STATE_FOCUSED, 0);
 
       var opt2 = comboboxList.lastChild;
       testStates(opt2, STATE_SELECTABLE | STATE_FOCUSABLE, 0, STATE_SELECTED, 0,
                  STATE_FOCUSED, EXT_STATE_ACTIVE);
 
       // listbox
-      var listbox = getAccessible("listbox");
-      testStates(listbox, STATE_FOCUSABLE, 0,
+      testStates("listbox",
+                 STATE_FOCUSABLE, 0,
                  STATE_HASPOPUP | STATE_COLLAPSED | STATE_FOCUSED);
 
-      testStates(listbox.firstChild, STATE_SELECTABLE, EXT_STATE_ACTIVE,
-                 STATE_SELECTED | STATE_FOCUSED | STATE_FOCUSED);
+      testStates("listitem-active",
+                 STATE_FOCUSABLE | STATE_SELECTABLE, EXT_STATE_ACTIVE,
+                 STATE_SELECTED | STATE_FOCUSED);
+
+      testStates("listitem",
+                 STATE_FOCUSABLE | STATE_SELECTABLE, 0,
+                 STATE_SELECTED | STATE_FOCUSED, EXT_STATE_ACTIVE);
+
+      testStates("listitem-disabled",
+                 STATE_UNAVAILABLE, 0,
+                 STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+                 EXT_STATE_ACTIVE);
+
+      testStates("listgroup",
+                 0, 0,
+                 STATE_UNAVAILABLE | STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+                 EXT_STATE_ACTIVE);
 
-      testStates(listbox.lastChild, STATE_SELECTABLE, 0,
-                 STATE_SELECTED | STATE_FOCUSED | STATE_FOCUSED,
-                 0, 0, EXT_STATE_ACTIVE);
+      testStates("listgroup-disabled",
+                 STATE_UNAVAILABLE, 0,
+                 STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+                 EXT_STATE_ACTIVE);
+
+      todo(false, "no unavailable state on option in disabled group (bug 759666)");
+//      testStates("listitem-disabledgroup",
+//                 STATE_UNAVAILABLE, 0,
+//                 STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+//                 EXT_STATE_ACTIVE);
+
+      testStates("listbox-disabled",
+                 STATE_UNAVAILABLE, 0,
+                 STATE_FOCUSABLE);
+
+      todo(false, "no unavailable state on option in disabled select (bug 759666)");
+//      testStates("listitem-disabledlistbox",
+//                 STATE_UNAVAILABLE, 0,
+//                 STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+//                 EXT_STATE_ACTIVE);
 
       // open combobox
       gQueue = new eventQueue();
       gQueue.push(new openComboboxNCheckStates("combobox"));
-      gQueue.invoke(); // Will call SimpleTest.finish();
+      gQueue.invoke(); // Will call */SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
 </head>
 
@@ -108,27 +140,41 @@
      title="mochitest for selects and lists">
     Mozilla Bug 640716
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=689847"
      title="Expose active state on current item of selectable widgets">
     Mozilla Bug 689847
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=756983"
+     title="Isolate focusable and unavailable states from State()">
+    Mozilla Bug 756983
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <select id="combobox">
     <option>item 1</option>
     <option>item 2</option>
   </select>
 
   <select id="listbox" name="component" size="3">
-    <option>Build</option>
-    <option>Disability Access APIs</option>
-    <option>General</option>
-    <option>UI</option>
+    <option id="listitem-active">Build</option>
+    <option id="listitem">Disability Access APIs</option>
+    <option id="listitem-disabled" disabled>General</option>
+    <optgroup id="listgroup" label="group">
+      <option>option</option>
+    </optgroup>
+    <optgroup id="listgroup-disabled" disabled label="group2">
+      <option id="listitem-disabledgroup">UI</option>
+    </optgroup>
+  </select>
+
+  <select id="listbox-disabled" size="3" disabled>
+    <option id="listitem-disabledlistbox">option</option>
   </select>
 
 </body>
 </html>