Bug 756983 - make nsAccessible::GetActionName faster, r=tbsaunde
authorAlexander Surkov <surkov.alexander@gmail.com>
Mon, 04 Jun 2012 14:41:06 +0900
changeset 95738 af83ecf3ee15bf7a44d9cf159133953331a58824
parent 95737 cfee4e39759d2636affcf29289ebe96b73506170
child 95739 c1c0ce793c5ac76cec000c35f129f478268f2b19
push id22837
push usereakhgari@mozilla.com
push dateMon, 04 Jun 2012 15:31:50 +0000
treeherdermozilla-central@fc94f4339da7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstbsaunde
bugs756983
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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>