merge m-c to fx-team
authorRob Campbell <rcampbell@mozilla.com>
Wed, 28 Sep 2011 09:53:39 -0300
changeset 77748 4d27c6c86de66bf02b3431bb040bd43d360c5aec
parent 77747 713f76a29f10ca0b862f40a1a6c9d6cd843cf50d (current diff)
parent 77745 95bbaf6cb2a6c9a4d3375da8381cb8db909ec4a0 (diff)
child 77749 80b55a615ac9a8e222f8c4e95270ba914234919b
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
milestone10.0a1
merge m-c to fx-team
accessible/tests/mochitest/events/test_focus.html
accessible/tests/mochitest/events/test_focus.xul
accessible/tests/mochitest/events/test_focusdoc.html
accessible/tests/mochitest/nsIAccessible_selects.js
accessible/tests/mochitest/states/test_comboboxes.xul
accessible/tests/mochitest/test_aria_activedescendant.html
accessible/tests/mochitest/test_nsIAccessible_selects.html
browser/themes/gnomestripe/browser/browser.css
browser/themes/winstripe/browser/browser.css
build/autoconf/libIDL.m4
js/src/tests/js1_5/Function/15.3.4.4.js
media/libvorbis/bug666672_gccWarnings.diff
xpcom/typelib/xpidl/README
xpcom/typelib/xpidl/xpidl.c
xpcom/typelib/xpidl/xpidl.h
xpcom/typelib/xpidl/xpidl_doc.c
xpcom/typelib/xpidl/xpidl_header.c
xpcom/typelib/xpidl/xpidl_idl.c
xpcom/typelib/xpidl/xpidl_java.c
xpcom/typelib/xpidl/xpidl_typelib.c
xpcom/typelib/xpidl/xpidl_util.c
--- a/.hgtags
+++ b/.hgtags
@@ -63,8 +63,9 @@ a71bd564ebf5bf4f93d13e84114f759c263130b0
 a71bd564ebf5bf4f93d13e84114f759c263130b0 MOBILE_MERGE_DONE_20110406
 a95d426422816513477e5863add1b00ac7041dcb AURORA_BASE_20110412
 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
 9eae975b3d6fb7748fe5a3c0113d449b1c7cc0b2 AURORA_BASE_20110524
 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
+c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -1236,17 +1236,17 @@ nsAccessibleWrap::FirePlatformEvent(AccE
         MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_ACTIVATED\n"));
         nsRootAccessible *rootAcc =
           static_cast<nsRootAccessible *>(accessible);
         rootAcc->mActivated = PR_TRUE;
         guint id = g_signal_lookup ("activate", MAI_TYPE_ATK_OBJECT);
         g_signal_emit(atkObj, id, 0);
 
         // Always fire a current focus event after activation.
-        rootAcc->FireCurrentFocusEvent();
+        FocusMgr()->ForceFocusEvent();
       } break;
 
     case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE:
       {
         MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_DEACTIVATED\n"));
         nsRootAccessible *rootAcc =
           static_cast<nsRootAccessible *>(accessible);
         rootAcc->mActivated = PR_FALSE;
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -73,19 +73,19 @@ public:
      //    This event will always be emitted.
      eAllowDupes,
 
      // eCoalesceFromSameSubtree : For events of the same type from the same
      //    subtree or the same node, only the umbrella event on the ancestor
      //    will be emitted.
      eCoalesceFromSameSubtree,
 
-    // eCoalesceFromSameDocument : For events of the same type from the same
-    //    document, only the newest event will be emitted.
-    eCoalesceFromSameDocument,
+    // eCoalesceOfSameType : For events of the same type, only the newest event
+    // will be processed.
+    eCoalesceOfSameType,
 
      // eRemoveDupes : For repeat events, only the newest event in queue
      //    will be emitted.
      eRemoveDupes,
 
      // eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
      eDoNotEmit
   };
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/FocusManager.cpp
@@ -0,0 +1,361 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Alexander Surkov <surkov.alexander@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "FocusManager.h"
+
+#include "nsAccessibilityService.h"
+#include "nsAccUtils.h"
+#include "nsRootAccessible.h"
+
+#include "nsFocusManager.h"
+
+namespace dom = mozilla::dom;
+using namespace mozilla::a11y;
+
+FocusManager::FocusManager()
+{
+}
+
+FocusManager::~FocusManager()
+{
+}
+
+nsAccessible*
+FocusManager::FocusedAccessible() const
+{
+  if (mActiveItem)
+    return mActiveItem;
+
+  nsINode* focusedNode = FocusedDOMNode();
+  if (focusedNode)
+    return GetAccService()->GetAccessibleOrContainer(focusedNode, nsnull);
+
+  return nsnull;
+}
+
+bool
+FocusManager::IsFocused(const nsAccessible* aAccessible) const
+{
+  if (mActiveItem)
+    return mActiveItem == aAccessible;
+
+  nsINode* focusedNode = FocusedDOMNode();
+  if (focusedNode) {
+    // XXX: Before getting an accessible for node having a DOM focus make sure
+    // they belong to the same document because it can trigger unwanted document
+    // accessible creation for temporary about:blank document. Without this
+    // peculiarity we would end up with plain implementation based on
+    // FocusedAccessible() method call. Make sure this issue is fixed in
+    // bug 638465.
+    if (focusedNode->GetOwnerDoc() == aAccessible->GetNode()->GetOwnerDoc()) {
+      return aAccessible ==
+        GetAccService()->GetAccessibleOrContainer(focusedNode, nsnull);
+    }
+  }
+  return false;
+}
+
+bool
+FocusManager::IsFocusWithin(const nsAccessible* aContainer) const
+{
+  nsAccessible* child = FocusedAccessible();
+  while (child) {
+    if (child == aContainer)
+      return true;
+
+    child = child->Parent();
+  }
+  return false;
+}
+
+FocusManager::FocusDisposition
+FocusManager::IsInOrContainsFocus(const nsAccessible* aAccessible) const
+{
+  nsAccessible* focus = FocusedAccessible();
+  if (!focus)
+    return eNone;
+
+  // If focused.
+  if (focus == aAccessible)
+    return eFocused;
+
+  // If contains the focus.
+  nsAccessible* child = focus->Parent();
+  while (child) {
+    if (child == aAccessible)
+      return eContainsFocus;
+
+    child = child->Parent();
+  }
+
+  // If contained by focus.
+  child = aAccessible->Parent();
+  while (child) {
+    if (child == focus)
+      return eContainedByFocus;
+
+    child = child->Parent();
+  }
+
+  return eNone;
+}
+
+void
+FocusManager::NotifyOfDOMFocus(nsISupports* aTarget)
+{
+  A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM focus", "DOM focus target",
+                                              aTarget)
+
+  mActiveItem = nsnull;
+
+  nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
+  if (targetNode) {
+    nsDocAccessible* document =
+      GetAccService()->GetDocAccessible(targetNode->GetOwnerDoc());
+    if (document) {
+      // Set selection listener for focused element.
+      if (targetNode->IsElement()) {
+        nsRootAccessible* root = document->RootAccessible();
+        nsCaretAccessible* caretAcc = root->GetCaretAccessible();
+        caretAcc->SetControlSelectionListener(targetNode->AsElement());
+      }
+
+      document->HandleNotification<FocusManager, nsINode>
+        (this, &FocusManager::ProcessDOMFocus, targetNode);
+    }
+  }
+}
+
+void
+FocusManager::NotifyOfDOMBlur(nsISupports* aTarget)
+{
+  A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM blur", "DOM blur target",
+                                              aTarget)
+
+  mActiveItem = nsnull;
+
+  // If DOM document stays focused then fire accessible focus event to process
+  // the case when no element within this DOM document will be focused.
+  nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
+  if (targetNode && targetNode->GetOwnerDoc() == FocusedDOMDocument()) {
+    nsIDocument* DOMDoc = targetNode->GetOwnerDoc();
+    nsDocAccessible* document =
+      GetAccService()->GetDocAccessible(DOMDoc);
+    if (document) {
+      document->HandleNotification<FocusManager, nsINode>
+        (this, &FocusManager::ProcessDOMFocus, DOMDoc);
+    }
+  }
+}
+
+void
+FocusManager::ActiveItemChanged(nsAccessible* aItem, bool aCheckIfActive)
+{
+  A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("active item changed",
+                                         "Active item", aItem)
+
+  // Nothing changed, happens for XUL trees and HTML selects.
+  if (aItem && aItem == mActiveItem)
+    return;
+
+  mActiveItem = nsnull;
+
+  if (aItem && aCheckIfActive) {
+    nsAccessible* widget = aItem->ContainerWidget();
+    A11YDEBUG_FOCUS_LOG_WIDGET("Active item widget", widget)
+    if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
+      return;
+  }
+  mActiveItem = aItem;
+
+  // If active item is changed then fire accessible focus event on it, otherwise
+  // if there's no an active item then fire focus event to accessible having
+  // DOM focus.
+  nsAccessible* target = FocusedAccessible();
+  if (target)
+    DispatchFocusEvent(target->GetDocAccessible(), target);
+}
+
+void
+FocusManager::ForceFocusEvent()
+{
+  nsINode* focusedNode = FocusedDOMNode();
+  if (focusedNode) {
+    nsDocAccessible* document =
+      GetAccService()->GetDocAccessible(focusedNode->GetOwnerDoc());
+    if (document) {
+      document->HandleNotification<FocusManager, nsINode>
+        (this, &FocusManager::ProcessDOMFocus, focusedNode);
+    }
+  }
+}
+
+void
+FocusManager::DispatchFocusEvent(nsDocAccessible* aDocument,
+                                 nsAccessible* aTarget)
+{
+  NS_PRECONDITION(aDocument, "No document for focused accessible!");
+  if (aDocument) {
+    nsRefPtr<AccEvent> event =
+      new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
+                   eAutoDetect, AccEvent::eCoalesceOfSameType);
+    aDocument->FireDelayedAccessibleEvent(event);
+
+    A11YDEBUG_FOCUS_LOG_ACCTARGET("Focus notification", aTarget)
+  }
+}
+
+void
+FocusManager::ProcessDOMFocus(nsINode* aTarget)
+{
+  A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET("Process DOM focus",
+                                         "Notification target", aTarget)
+
+  nsDocAccessible* document =
+    GetAccService()->GetDocAccessible(aTarget->GetOwnerDoc());
+
+  nsAccessible* target = document->GetAccessibleOrContainer(aTarget);
+  if (target) {
+    // Check if still focused. Otherwise we can end up with storing the active
+    // item for control that isn't focused anymore.
+    nsAccessible* DOMFocus =
+      GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(), nsnull);
+    if (target != DOMFocus)
+      return;
+
+    nsAccessible* activeItem = target->CurrentItem();
+    if (activeItem) {
+      mActiveItem = activeItem;
+      target = activeItem;
+    }
+
+    DispatchFocusEvent(document, target);
+  }
+}
+
+void
+FocusManager::ProcessFocusEvent(AccEvent* aEvent)
+{
+  NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
+                  "Focus event is expected!");
+
+  EIsFromUserInput fromUserInputFlag = aEvent->IsFromUserInput() ?
+    eFromUserInput : eNoUserInput;
+
+  // Emit focus event if event target is the active item. Otherwise then check
+  // if it's still focused and then update active item and emit focus event.
+  nsAccessible* target = aEvent->GetAccessible();
+  if (target != mActiveItem) {
+    // Check if still focused. Otherwise we can end up with storing the active
+    // item for control that isn't focused anymore.
+    nsAccessible* DOMFocus =
+      GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(), nsnull);
+    if (target != DOMFocus)
+      return;
+
+    nsAccessible* activeItem = target->CurrentItem();
+    if (activeItem) {
+      mActiveItem = activeItem;
+      target = activeItem;
+    }
+  }
+
+  // Fire menu start/end events for ARIA menus.
+  if (target->ARIARole() == nsIAccessibleRole::ROLE_MENUITEM) {
+    // The focus was moved into menu.
+    nsAccessible* ARIAMenubar =
+      nsAccUtils::GetAncestorWithRole(target, nsIAccessibleRole::ROLE_MENUBAR);
+
+    if (ARIAMenubar != mActiveARIAMenubar) {
+      // Leaving ARIA menu. Fire menu_end event on current menubar.
+      if (mActiveARIAMenubar) {
+        nsRefPtr<AccEvent> menuEndEvent =
+          new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
+                       fromUserInputFlag);
+        nsEventShell::FireEvent(menuEndEvent);
+      }
+
+      mActiveARIAMenubar = ARIAMenubar;
+
+      // Entering ARIA menu. Fire menu_start event.
+      if (mActiveARIAMenubar) {
+        nsRefPtr<AccEvent> menuStartEvent =
+          new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
+                       mActiveARIAMenubar, fromUserInputFlag);
+        nsEventShell::FireEvent(menuStartEvent);
+      }
+    }
+  } else if (mActiveARIAMenubar) {
+    // Focus left a menu. Fire menu_end event.
+    nsRefPtr<AccEvent> menuEndEvent =
+      new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
+                   fromUserInputFlag);
+    nsEventShell::FireEvent(menuEndEvent);
+
+    mActiveARIAMenubar = nsnull;
+  }
+
+  A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("FIRE FOCUS EVENT", "Focus target",
+                                         target)
+
+  nsRefPtr<AccEvent> focusEvent =
+    new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag);
+  nsEventShell::FireEvent(focusEvent);
+}
+
+nsIContent*
+FocusManager::FocusedDOMElm() const
+{
+  nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
+  return DOMFocusManager->GetFocusedContent();
+}
+
+nsIDocument*
+FocusManager::FocusedDOMDocument() const
+{
+  nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
+
+  nsCOMPtr<nsIDOMWindow> focusedWnd;
+  DOMFocusManager->GetFocusedWindow(getter_AddRefs(focusedWnd));
+  if (focusedWnd) {
+    nsCOMPtr<nsIDOMDocument> DOMDoc;
+    focusedWnd->GetDocument(getter_AddRefs(DOMDoc));
+    nsCOMPtr<nsIDocument> DOMDocNode(do_QueryInterface(DOMDoc));
+    return DOMDocNode;
+  }
+  return nsnull;
+}
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/FocusManager.h
@@ -0,0 +1,297 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Alexander Surkov <surkov.alexander@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_a11y_FocusManager_h_
+#define mozilla_a11y_FocusManager_h_
+
+#include "nsAutoPtr.h"
+#include "mozilla/dom/Element.h"
+
+class AccEvent;
+class nsAccessible;
+class nsDocAccessible;
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Manage the accessible focus. Used to fire and process accessible events.
+ */
+class FocusManager
+{
+public:
+  virtual ~FocusManager();
+
+  /**
+   * Return a focused accessible.
+   */
+  nsAccessible* FocusedAccessible() const;
+
+  /**
+   * Return true if given accessible is focused.
+   */
+  bool IsFocused(const nsAccessible* aAccessible) const;
+
+  /**
+   * Return true if the given accessible is an active item, i.e. an item that
+   * is current within the active widget.
+   */
+  inline bool IsActiveItem(const nsAccessible* aAccessible)
+    { return aAccessible == mActiveItem; }
+
+  /**
+   * Return true if given DOM node has DOM focus.
+   */
+  inline bool HasDOMFocus(const nsINode* aNode) const
+    { return aNode == FocusedDOMNode(); }
+
+  /**
+   * Return true if focused accessible is within the given container.
+   */
+  bool IsFocusWithin(const nsAccessible* aContainer) const;
+
+  /**
+   * Return whether the given accessible is focused or contains the focus or
+   * contained by focused accessible.
+   */
+  enum FocusDisposition {
+    eNone,
+    eFocused,
+    eContainsFocus,
+    eContainedByFocus
+  };
+  FocusDisposition IsInOrContainsFocus(const nsAccessible* aAccessible) const;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Focus notifications and processing (don't use until you know what you do).
+
+  /**
+   * Called when DOM focus event is fired.
+   */
+  void NotifyOfDOMFocus(nsISupports* aTarget);
+
+  /**
+   * Called when DOM blur event is fired.
+   */
+  void NotifyOfDOMBlur(nsISupports* aTarget);
+
+  /**
+   * Called when active item is changed. Note: must be called when accessible
+   * tree is up to date.
+   */
+  void ActiveItemChanged(nsAccessible* aItem, bool aCheckIfActive = true);
+
+  /**
+   * Dispatch delayed focus event for the current focus accessible.
+   */
+  void ForceFocusEvent();
+
+  /**
+   * Dispatch delayed focus event for the given target.
+   */
+  void DispatchFocusEvent(nsDocAccessible* aDocument, nsAccessible* aTarget);
+
+  /**
+   * Process DOM focus notification.
+   */
+  void ProcessDOMFocus(nsINode* aTarget);
+
+  /**
+   * Process the delayed accessible event.
+   * do.
+   */
+  void ProcessFocusEvent(AccEvent* aEvent);
+
+protected:
+  FocusManager();
+
+private:
+  FocusManager(const FocusManager&);
+  FocusManager& operator =(const FocusManager&);
+
+  /**
+   * Return DOM node having DOM focus.
+   */
+  inline nsINode* FocusedDOMNode() const
+  {
+    nsINode* focusedNode = FocusedDOMElm();
+    if (focusedNode)
+      return focusedNode;
+    return FocusedDOMDocument();
+  }
+
+  /**
+   * Return DOM element having DOM focus.
+   */
+  nsIContent* FocusedDOMElm() const;
+
+  /**
+   * Return DOM document having DOM focus.
+   */
+  nsIDocument* FocusedDOMDocument() const;
+
+private:
+  nsRefPtr<nsAccessible> mActiveItem;
+  nsRefPtr<nsAccessible> mActiveARIAMenubar;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+
+//#define A11YDEBUG_FOCUS
+
+#ifdef A11YDEBUG_FOCUS
+
+// Util macros (don't use them directly)
+#define A11YDEBUG_FOCUS_STARTBLOCK                                             \
+  printf("  {\n    ");
+
+#define A11YDEBUG_FOCUS_ENDBLOCK                                               \
+  printf("\n  }\n");
+
+#define A11YDEBUG_FOCUS_BLOCKOFFSET                                            \
+  printf("    ");
+
+#define A11YDEBUG_FOCUS_LOG_TIME                                               \
+  {                                                                            \
+    PRIntervalTime time = PR_IntervalNow();                                    \
+    PRUint32 mins = (PR_IntervalToSeconds(time) / 60) % 60;                    \
+    PRUint32 secs = PR_IntervalToSeconds(time) % 60;                           \
+    PRUint32 msecs = PR_IntervalToMilliseconds(time) % 1000;                   \
+    printf("Time: %2d:%2d.%3d\n", mins, secs, msecs);                          \
+  }
+
+#define A11YDEBUG_FOCUS_LOG_DOMNODE(aNode)                                     \
+  if (aNode) {                                                                 \
+    if (aNode->IsElement()) {                                                  \
+      dom::Element* targetElm = aNode->AsElement();                            \
+      nsCAutoString tag;                                                       \
+      targetElm->Tag()->ToUTF8String(tag);                                     \
+      nsCAutoString id;                                                        \
+      nsIAtom* atomid = targetElm->GetID();                                    \
+      if (atomid)                                                              \
+        atomid->ToUTF8String(id);                                              \
+      printf("element %s@id='%s': %p", tag.get(), id.get(), (void*)aNode);     \
+    } else if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {                      \
+      nsCOMPtr<nsIDocument> document = do_QueryInterface(aNode);               \
+      nsIURI* uri = document->GetDocumentURI();                                \
+      nsCAutoString spec;                                                      \
+      uri->GetSpec(spec);                                                      \
+      printf("document: %p; uri: %s", (void*)aNode, spec.get());               \
+    }                                                                          \
+  }
+
+#define A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aAccessible)                            \
+  printf("accessible: %p; ", (void*)aAccessible);                              \
+  if (aAccessible) {                                                           \
+    nsAutoString role;                                                         \
+    GetAccService()->GetStringRole(aAccessible->Role(), role);                 \
+    nsAutoString name;                                                         \
+    aAccessible->GetName(name);                                                \
+    printf(" role: %s, name: %s; ", NS_ConvertUTF16toUTF8(role).get(),         \
+           NS_ConvertUTF16toUTF8(name).get());                                 \
+    A11YDEBUG_FOCUS_LOG_DOMNODE(aAccessible->GetNode())                        \
+  }
+
+// Public macros
+#define A11YDEBUG_FOCUS_LOG_DOMTARGET(aMsg, aTarget)                           \
+  A11YDEBUG_FOCUS_STARTBLOCK                                                   \
+  printf(aMsg "\n");                                                           \
+  if (aTarget) {                                                               \
+    A11YDEBUG_FOCUS_BLOCKOFFSET                                                \
+    A11YDEBUG_FOCUS_LOG_DOMNODE(aTarget)                                       \
+  }                                                                            \
+  A11YDEBUG_FOCUS_ENDBLOCK
+
+#define A11YDEBUG_FOCUS_LOG_ACCTARGET(aMsg, aTarget)                           \
+  A11YDEBUG_FOCUS_STARTBLOCK                                                   \
+  printf(aMsg "\n");                                                           \
+  A11YDEBUG_FOCUS_BLOCKOFFSET                                                  \
+  A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aTarget)                                      \
+  A11YDEBUG_FOCUS_ENDBLOCK
+
+#define A11YDEBUG_FOCUS_LOG_WIDGET(aMsg, aWidget)                              \
+  A11YDEBUG_FOCUS_STARTBLOCK                                                   \
+  printf(aMsg "\n");                                                           \
+  A11YDEBUG_FOCUS_BLOCKOFFSET                                                  \
+  A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aWidget)                                      \
+  printf("; widget is active: %s, has operable items: %s",                     \
+         (aWidget && aWidget->IsActiveWidget() ? "true" : "false"),            \
+         (aWidget && aWidget->AreItemsOperable() ? "true" : "false"));         \
+  A11YDEBUG_FOCUS_ENDBLOCK
+
+#define A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET(aMsg, aTargetMsg, aTarget) \
+  printf("\nA11Y FOCUS: " aMsg ". ");                                          \
+  A11YDEBUG_FOCUS_LOG_TIME                                                     \
+  if (aTarget) {                                                               \
+    A11YDEBUG_FOCUS_STARTBLOCK                                                 \
+    printf(aTargetMsg "\n");                                                   \
+    A11YDEBUG_FOCUS_BLOCKOFFSET                                                \
+    nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));                  \
+    if (targetNode) {                                                          \
+      A11YDEBUG_FOCUS_LOG_DOMNODE(targetNode)                                  \
+    } else {                                                                   \
+      printf("window: %p", (void*)aTarget);                                    \
+    }                                                                          \
+    A11YDEBUG_FOCUS_ENDBLOCK                                                   \
+  }
+
+#define A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET(aMsg, aTargetMsg, aTarget)      \
+  printf("\nA11Y FOCUS: " aMsg ". ");                                          \
+  A11YDEBUG_FOCUS_LOG_TIME                                                     \
+  A11YDEBUG_FOCUS_LOG_DOMTARGET(aTargetMsg, aTarget)
+
+#define A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET(aMsg, aTargetMsg, aTarget)      \
+  printf("\nA11Y FOCUS: " aMsg ". ");                                          \
+  A11YDEBUG_FOCUS_LOG_TIME                                                     \
+  A11YDEBUG_FOCUS_LOG_ACCTARGET(aTargetMsg, aTarget)
+
+#define A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE(aMsg, aTarget)                  \
+  A11YDEBUG_FOCUS_LOG_ACCTARGET("Caused by: " aMsg, aTarget)
+
+#else
+#define A11YDEBUG_FOCUS_LOG_DOMTARGET(aMsg, aTarget)
+#define A11YDEBUG_FOCUS_LOG_ACCTARGET(aMsg, aTarget)
+#define A11YDEBUG_FOCUS_LOG_WIDGET(aMsg, aWidget)
+#define A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET(aMsg, aTargetMsg, aTarget)
+#define A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET(aMsg, aTargetMsg, aTarget)
+#define A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET(aMsg, aTargetMsg, aTarget)
+#define A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE(aMsg, aTarget)
+#endif
+
+#endif
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -48,16 +48,17 @@ LIBXUL_LIBRARY = 1
 
 
 CPPSRCS = \
   AccCollector.cpp \
   AccEvent.cpp \
   AccGroupInfo.cpp \
   AccIterator.cpp \
   filters.cpp \
+  FocusManager.cpp \
   NotificationController.cpp \
   nsAccDocManager.cpp \
   nsAccessNode.cpp \
   nsARIAGridAccessible.cpp \
   nsARIAMap.cpp \
   nsDocAccessible.cpp \
   nsOuterDocAccessible.cpp \
   nsCoreUtils.cpp \
@@ -84,16 +85,17 @@ EXPORTS = \
   nsAccessible.h \
   nsAccessNode.h \
   nsARIAMap.h \
   $(NULL)
 
 EXPORTS_NAMESPACES = mozilla/a11y
 
 EXPORTS_mozilla/a11y = \
+  FocusManager.h \
   States.h \
   $(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -39,19 +39,22 @@
 #include "NotificationController.h"
 
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsDocAccessible.h"
 #include "nsEventShell.h"
 #include "nsTextAccessible.h"
+#include "FocusManager.h"
 #include "TextUpdater.h"
+
 #include "mozilla/dom/Element.h"
 
+using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector
 ////////////////////////////////////////////////////////////////////////////////
 
 NotificationController::NotificationController(nsDocAccessible* aDocument,
                                                nsIPresShell* aPresShell) :
   mObservingState(eNotObservingRefresh), mDocument(aDocument),
@@ -177,17 +180,18 @@ NotificationController::ScheduleProcessi
 }
 
 bool
 NotificationController::IsUpdatePending()
 {
   return mPresShell->IsLayoutFlushObserver() ||
     mObservingState == eRefreshProcessingForUpdate ||
     mContentInsertions.Length() != 0 || mNotifications.Length() != 0 ||
-    mTextHash.Count() != 0;
+    mTextHash.Count() != 0 ||
+    !mDocument->HasLoadState(nsDocAccessible::eTreeConstructed);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector: private
 
 void
 NotificationController::WillRefresh(mozilla::TimeStamp aTime)
 {
@@ -307,18 +311,25 @@ NotificationController::WillRefresh(mozi
   // Process only currently queued events.
   nsTArray<nsRefPtr<AccEvent> > events;
   events.SwapElements(mEvents);
 
   PRUint32 eventCount = events.Length();
   for (PRUint32 idx = 0; idx < eventCount; idx++) {
     AccEvent* accEvent = events[idx];
     if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
+      // Dispatch the focus event if target is still focused.
+      if (accEvent->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
+        FocusMgr()->ProcessFocusEvent(accEvent);
+        continue;
+      }
+
       mDocument->ProcessPendingEvent(accEvent);
 
+      // Fire text change event caused by tree mutation.
       AccMutationEvent* showOrHideEvent = downcast_accEvent(accEvent);
       if (showOrHideEvent) {
         if (showOrHideEvent->mTextChangeEvent)
           mDocument->ProcessPendingEvent(showOrHideEvent->mTextChangeEvent);
       }
     }
     if (!mDocument)
       return;
@@ -340,24 +351,24 @@ NotificationController::WillRefresh(mozi
 
 void
 NotificationController::CoalesceEvents()
 {
   PRUint32 numQueuedEvents = mEvents.Length();
   PRInt32 tail = numQueuedEvents - 1;
   AccEvent* tailEvent = mEvents[tail];
 
-  // No node means this is application accessible (which can be a subject
-  // of reorder events), we do not coalesce events for it currently.
-  if (!tailEvent->mNode)
-    return;
-
   switch(tailEvent->mEventRule) {
     case AccEvent::eCoalesceFromSameSubtree:
     {
+      // No node means this is application accessible (which is a subject of
+      // reorder events), we do not coalesce events for it currently.
+      if (!tailEvent->mNode)
+        return;
+
       for (PRInt32 index = tail - 1; index >= 0; index--) {
         AccEvent* thisEvent = mEvents[index];
 
         if (thisEvent->mEventType != tailEvent->mEventType)
           continue; // Different type
 
         // Skip event for application accessible since no coalescence for it
         // is supported. Ignore events from different documents since we don't
@@ -441,31 +452,28 @@ NotificationController::CoalesceEvents()
                           thisEvent->mNode, AccEvent::eDoNotEmit);
           continue;
         }
 
       } // for (index)
 
     } break; // case eCoalesceFromSameSubtree
 
-    case AccEvent::eCoalesceFromSameDocument:
+    case AccEvent::eCoalesceOfSameType:
     {
-      // Used for focus event, coalesce more older event since focus event
-      // for accessible can be duplicated by event for its document, we are
-      // interested in focus event for accessible.
+      // Coalesce old events by newer event.
       for (PRInt32 index = tail - 1; index >= 0; index--) {
-        AccEvent* thisEvent = mEvents[index];
-        if (thisEvent->mEventType == tailEvent->mEventType &&
-            thisEvent->mEventRule == tailEvent->mEventRule &&
-            thisEvent->GetDocAccessible() == tailEvent->GetDocAccessible()) {
-          thisEvent->mEventRule = AccEvent::eDoNotEmit;
+        AccEvent* accEvent = mEvents[index];
+        if (accEvent->mEventType == tailEvent->mEventType &&
+            accEvent->mEventRule == tailEvent->mEventRule) {
+          accEvent->mEventRule = AccEvent::eDoNotEmit;
           return;
         }
       }
-    } break; // case eCoalesceFromSameDocument
+    } break; // case eCoalesceOfSameType
 
     case AccEvent::eRemoveDupes:
     {
       // Check for repeat events, coalesce newly appended event by more older
       // event.
       for (PRInt32 index = tail - 1; index >= 0; index--) {
         AccEvent* accEvent = mEvents[index];
         if (accEvent->mEventType == tailEvent->mEventType &&
--- a/accessible/src/base/nsAccDocManager.cpp
+++ b/accessible/src/base/nsAccDocManager.cpp
@@ -413,16 +413,17 @@ nsAccDocManager::CreateDocOrRootAccessib
                    AccEvent::eCoalesceFromSameSubtree);
     docAcc->FireDelayedAccessibleEvent(reorderEvent);
 
   } else {
     parentDocAcc->BindChildDocument(docAcc);
   }
 
   NS_LOG_ACCDOCCREATE("document creation finished", aDocument)
+  NS_LOG_ACCDOCCREATE_STACK
 
   AddListeners(aDocument, isRootDoc);
   return docAcc;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccDocManager static
 
--- a/accessible/src/base/nsAccDocManager.h
+++ b/accessible/src/base/nsAccDocManager.h
@@ -160,16 +160,18 @@ private:
   nsDocAccessibleHashtable mDocAccessibleCache;
 };
 
 /**
  * nsAccDocManager debugging macros.
  */
 #ifdef DEBUG_ACCDOCMGR
 
+#include "nsTraceRefcntImpl.h"
+
 // Enable these to log accessible document loading, creation or destruction.
 #define DEBUG_ACCDOCMGR_DOCLOAD
 #define DEBUG_ACCDOCMGR_DOCCREATE
 #define DEBUG_ACCDOCMGR_DOCDESTROY
 
 // Common macros, do not use directly.
 #define NS_LOG_ACCDOC_ADDRESS(aDocument, aDocAcc)                              \
   printf("DOM id: %p, acc id: %p", aDocument, aDocAcc);
@@ -400,16 +402,20 @@ private:
   }
 
 #define NS_LOG_ACCDOC_MSG(aMsg)                                                \
   printf("\n" aMsg "\n");                                                      \
 
 #define NS_LOG_ACCDOC_TEXT(aMsg)                                               \
   printf("  " aMsg "\n");
 
+#define NS_LOG_ACCDOC_STACK                                                    \
+  printf("  stack: \n");                                                       \
+  nsTraceRefcntImpl::WalkTheStack(stdout);
+
 // Accessible document loading macros.
 #ifdef DEBUG_ACCDOCMGR_DOCLOAD
 
 #define NS_LOG_ACCDOCLOAD_REQUEST(aRequest)                                    \
   if (aRequest) {                                                              \
     nsCAutoString name;                                                        \
     aRequest->GetName(name);                                                   \
     printf("    request spec: %s\n", name.get());                              \
@@ -510,17 +516,20 @@ private:
       GetAccService()->GetDocAccessibleFromCache(aDocument);                   \
     NS_LOG_ACCDOCCREATE_FOR(aMsg, aDocument, docAcc)                           \
   }
 
 #define NS_LOG_ACCDOCCREATE_ACCADDRESS(aName, aAcc)                            \
   NS_LOG_ACCDOC_ACCADDRESS(aName, aAcc)
 
 #define NS_LOG_ACCDOCCREATE_TEXT(aMsg)                                         \
-    NS_LOG_ACCDOC_TEXT(aMsg)
+  NS_LOG_ACCDOC_TEXT(aMsg)
+
+#define NS_LOG_ACCDOCCREATE_STACK                                              \
+  NS_LOG_ACCDOC_STACK
 
 #endif // DEBUG_ACCDOCMGR_DOCCREATE
 
 // Accessible document destruction macros.
 #ifdef DEBUG_ACCDOCMGR_DOCDESTROY
 #define NS_LOG_ACCDOCDESTROY_FOR(aMsg, aDocument, aDocAcc)                     \
   NS_LOG_ACCDOC_MSG("A11Y DOCDESTROY: " aMsg);                                 \
   NS_LOG_ACCDOC_DOCINFO(aDocument, aDocAcc)
@@ -554,16 +563,17 @@ private:
 #define NS_LOG_ACCDOCLOAD_TEXT(aMsg)
 #endif
 
 #ifndef DEBUG_ACCDOCMGR_DOCCREATE
 #define NS_LOG_ACCDOCCREATE_FOR(aMsg, aDocument, aDocAcc)
 #define NS_LOG_ACCDOCCREATE(aMsg, aDocument)
 #define NS_LOG_ACCDOCCREATE_ACCADDRESS(aName, aAcc)
 #define NS_LOG_ACCDOCCREATE_TEXT(aMsg)
+#define NS_LOG_ACCDOCCREATE_STACK
 #endif
 
 #ifndef DEBUG_ACCDOCMGR_DOCDESTROY
 #define NS_LOG_ACCDOCDESTROY_FOR(aMsg, aDocument, aDocAcc)
 #define NS_LOG_ACCDOCDESTROY(aMsg, aDocument)
 #define NS_LOG_ACCDOCDESTROY_MSG(aMsg)
 #define NS_LOG_ACCDOCDESTROY_ACCADDRESS(aName, aAcc)
 #define NS_LOG_ACCDOCDESTROY_TEXT(aMsg)
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -70,17 +70,16 @@
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 nsIStringBundle *nsAccessNode::gStringBundle = 0;
-nsINode *nsAccessNode::gLastFocusedNode = nsnull;
 
 PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
 
 nsApplicationAccessible *nsAccessNode::gApplicationAccessible = nsnull;
 
 /*
  * Class nsAccessNode
  */
@@ -222,17 +221,16 @@ void nsAccessNode::NotifyA11yInitOrShutd
 
 void nsAccessNode::ShutdownXPAccessibility()
 {
   // Called by nsAccessibilityService::Shutdown()
   // which happens when xpcom is shutting down
   // at exit of program
 
   NS_IF_RELEASE(gStringBundle);
-  NS_IF_RELEASE(gLastFocusedNode);
 
   // Release gApplicationAccessible after everything else is shutdown
   // so we don't accidently create it again while tearing down root accessibles
   nsApplicationAccessibleWrap::Unload();
   if (gApplicationAccessible) {
     gApplicationAccessible->Shutdown();
     NS_RELEASE(gApplicationAccessible);
   }
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -103,21 +103,16 @@ public:
   nsDocAccessible *GetDocAccessible() const;
 
   /**
    * Return the root document accessible for this accessnode.
    */
   nsRootAccessible* RootAccessible() const;
 
   /**
-   * Reference to a node of focused accessible.
-   */
-  static nsINode *gLastFocusedNode;
-
-  /**
    * Return focused node within accessible window.
    *
    * XXX: it shouldn't break us if we return focused node not depending on
    * window so that we can turn this method into util method.
    */
   already_AddRefed<nsINode> GetCurrentFocus();
 
   /**
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -38,16 +38,18 @@
 
 // NOTE: alphabetically ordered
 #include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 #include "nsAccUtils.h"
 #include "nsApplicationAccessibleWrap.h"
 #include "nsARIAGridAccessibleWrap.h"
 #include "nsARIAMap.h"
+#include "FocusManager.h"
+
 #include "nsIContentViewer.h"
 #include "nsCURILoader.h"
 #include "nsDocAccessible.h"
 #include "nsHTMLImageMapAccessible.h"
 #include "nsHTMLLinkAccessible.h"
 #include "nsHTMLSelectAccessible.h"
 #include "nsHTMLTableAccessibleWrap.h"
 #include "nsHTMLTextAccessible.h"
@@ -112,17 +114,18 @@ using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService
 ////////////////////////////////////////////////////////////////////////////////
 
 nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nsnull;
 PRBool nsAccessibilityService::gIsShutdown = PR_TRUE;
 
-nsAccessibilityService::nsAccessibilityService() : nsAccDocManager()
+nsAccessibilityService::nsAccessibilityService() :
+  nsAccDocManager(), FocusManager()
 {
   NS_TIME_FUNCTION;
 }
 
 nsAccessibilityService::~nsAccessibilityService()
 {
   NS_ASSERTION(gIsShutdown, "Accessibility wasn't shutdown!");
   gAccessibilityService = nsnull;
@@ -1847,8 +1850,18 @@ nsAccessibilityService::CreateAccessible
   }
 
   // Table or tree table accessible.
   nsAccessible* accessible = new nsXULTreeGridAccessibleWrap(aContent, aWeakShell);
   NS_IF_ADDREF(accessible);
   return accessible;
 }
 #endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Services
+////////////////////////////////////////////////////////////////////////////////
+
+mozilla::a11y::FocusManager*
+mozilla::a11y::FocusMgr()
+{
+  return nsAccessibilityService::gAccessibilityService;
+}
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -39,19 +39,33 @@
 #ifndef __nsAccessibilityService_h__
 #define __nsAccessibilityService_h__
 
 #include "nsIAccessibilityService.h"
 
 #include "a11yGeneric.h"
 #include "nsAccDocManager.h"
 
+#include "mozilla/a11y/FocusManager.h"
+
 #include "nsIObserver.h"
 
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Return focus manager.
+ */
+FocusManager* FocusMgr();
+
+} // namespace a11y
+} // namespace mozilla
+
 class nsAccessibilityService : public nsAccDocManager,
+                               public mozilla::a11y::FocusManager,
                                public nsIAccessibilityService,
                                public nsIObserver
 {
 public:
   virtual ~nsAccessibilityService();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLERETRIEVAL
@@ -252,35 +266,36 @@ private:
   /**
    * Create accessible for XUL tree element.
    */
   already_AddRefed<nsAccessible>
     CreateAccessibleForXULTree(nsIContent* aContent, nsIWeakReference* aWeakShell);
 #endif
 
   /**
-   * Reference for accessibility service.
+   * Reference for accessibility service instance.
    */
-  static nsAccessibilityService *gAccessibilityService;
+  static nsAccessibilityService* gAccessibilityService;
 
   /**
    * Indicates whether accessibility service was shutdown.
    */
   static PRBool gIsShutdown;
 
   /**
    * Does this content node have a universal ARIA property set on it?
    * A universal ARIA property is one that can be defined on any element even if there is no role.
    *
    * @param aContent The content node to test
    * @return PR_TRUE if there is a universal ARIA property set on the node
    */
   PRBool HasUniversalAriaProperty(nsIContent *aContent);
 
   friend nsAccessibilityService* GetAccService();
+  friend mozilla::a11y::FocusManager* mozilla::a11y::FocusMgr();
 
   friend nsresult NS_GetAccessibilityService(nsIAccessibilityService** aResult);
 };
 
 /**
  * Return the accessibility service instance. (Handy global function)
  */
 inline nsAccessibilityService*
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -688,24 +688,22 @@ nsAccessible::NativeState()
                              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()) {
+    nsIFrame* frame = GetFrame();
+    if (frame && frame->IsFocusable())
       state |= states::FOCUSABLE;
-    }
-
-    if (gLastFocusedNode == mContent) {
+
+    if (FocusMgr()->IsFocused(this))
       state |= states::FOCUSED;
-    }
   }
 
   // Check if states::INVISIBLE and
   // states::OFFSCREEN flags should be turned on for this object.
   PRBool isOffscreen;
   if (!IsVisible(&isOffscreen)) {
     state |= states::INVISIBLE;
   }
@@ -741,26 +739,21 @@ nsAccessible::GetFocusedChild(nsIAccessi
 
   NS_IF_ADDREF(*aChild = FocusedChild());
   return NS_OK;
 }
 
 nsAccessible*
 nsAccessible::FocusedChild()
 {
-  if (!gLastFocusedNode)
-    return nsnull;
-  if (gLastFocusedNode == mContent)
-    return this;
-
-  nsAccessible* focusedChild = GetDocAccessible()->GetAccessible(gLastFocusedNode);
-  if (!focusedChild || focusedChild->Parent() != this)
-    return nsnull;
-
-  return focusedChild;
+  nsAccessible* focus = FocusMgr()->FocusedAccessible();
+  if (focus && (focus == this || focus->Parent() == this))
+    return focus;
+
+  return nsnull;
 }
 
 // nsAccessible::ChildAtPoint()
 nsAccessible*
 nsAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
                            EWhichChildAtPoint aWhichChild)
 {
   // If we can't find the point in a child, we will return the fallback answer:
@@ -1526,17 +1519,17 @@ nsAccessible::State()
     if (state & states::FOCUSED) {
       state |= states::SELECTED;
     } else {
       // If focus is in a child of the tab panel surely the tab is selected!
       Relation rel = RelationByType(nsIAccessibleRelation::RELATION_LABEL_FOR);
       nsAccessible* relTarget = nsnull;
       while ((relTarget = rel.Next())) {
         if (relTarget->Role() == nsIAccessibleRole::ROLE_PROPERTYPAGE &&
-            nsCoreUtils::IsAncestorOf(relTarget->GetNode(), gLastFocusedNode))
+            FocusMgr()->IsFocusWithin(relTarget))
           state |= states::SELECTED;
       }
     }
   }
 
   const PRUint32 kExpandCollapseStates = states::COLLAPSED | states::EXPANDED;
   if ((state & kExpandCollapseStates) == kExpandCollapseStates) {
     // Cannot be both expanded and collapsed -- this happens in ARIA expanded
@@ -2735,16 +2728,24 @@ PRUint32
 nsAccessible::EndOffset()
 {
   NS_PRECONDITION(IsLink(), "EndOffset is called on not hyper link!");
 
   nsHyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nsnull;
   return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
 }
 
+bool
+nsAccessible::IsLinkSelected()
+{
+  NS_PRECONDITION(IsLink(),
+                  "IsLinkSelected() called on something that is not a hyper link!");
+  return FocusMgr()->IsFocused(this);
+}
+
 PRUint32
 nsAccessible::AnchorCount()
 {
   NS_PRECONDITION(IsLink(), "AnchorCount is called on not hyper link!");
   return 1;
 }
 
 nsAccessible*
@@ -2905,16 +2906,77 @@ nsAccessible::UnselectAll()
   AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
   while ((selected = iter.Next())) {
     success = true;
     selected->SetSelected(PR_FALSE);
   }
   return success;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Widgets
+
+bool
+nsAccessible::IsWidget() const
+{
+  return false;
+}
+
+bool
+nsAccessible::IsActiveWidget() const
+{
+  return FocusMgr()->IsFocused(this);
+}
+
+bool
+nsAccessible::AreItemsOperable() const
+{
+  return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
+}
+
+nsAccessible*
+nsAccessible::CurrentItem()
+{
+  // Check for aria-activedescendant, which changes which element has focus.
+  // For activedescendant, the ARIA spec does not require that the user agent
+  // checks whether pointed node is actually a DOM descendant of the element
+  // with the aria-activedescendant attribute.
+  nsAutoString id;
+  if (mContent->GetAttr(kNameSpaceID_None,
+                        nsGkAtoms::aria_activedescendant, id)) {
+    nsIDocument* DOMDoc = mContent->GetOwnerDoc();
+    dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
+    if (activeDescendantElm) {
+      nsDocAccessible* document = GetDocAccessible();
+      if (document)
+        return document->GetAccessible(activeDescendantElm);
+    }
+  }
+  return nsnull;
+}
+
+nsAccessible*
+nsAccessible::ContainerWidget() const
+{
+  nsIAtom* idAttribute = mContent->GetIDAttributeName();
+  if (idAttribute) {
+    if (mContent->HasAttr(kNameSpaceID_None, idAttribute)) {
+      nsAccessible* parent = Parent();
+      do {
+        nsIContent* parentContent = parent->GetContent();
+        if (parentContent &&
+            parentContent->HasAttr(kNameSpaceID_None,
+                                   nsGkAtoms::aria_activedescendant)) {
+          return parent;
+        }
+      } while ((parent = parent->Parent()));
+    }
+  }
+  return nsnull;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible protected methods
 
 void
 nsAccessible::CacheChildren()
 {
   nsAccTreeWalker walker(mWeakShell, mContent, GetAllowsAnonChildAccessibles());
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -397,29 +397,41 @@ public:
 
   /**
    * Assert if child not in parent's cache if the cache was initialized at this
    * point.
    */
   void TestChildCache(nsAccessible* aCachedChild) const;
 
   //////////////////////////////////////////////////////////////////////////////
-  // Downcasting
+  // Downcasting and types
 
   inline bool IsApplication() const { return mFlags & eApplicationAccessible; }
 
+  bool IsAutoComplete() const { return mFlags & eAutoCompleteAccessible; }
+
+  inline bool IsAutoCompletePopup() const { return mFlags & eAutoCompletePopupAccessible; }
+
+  inline bool IsCombobox() const { return mFlags & eComboboxAccessible; }
+
   inline bool IsDoc() const { return mFlags & eDocAccessible; }
   nsDocAccessible* AsDoc();
 
   inline bool IsHyperText() const { return mFlags & eHyperTextAccessible; }
   nsHyperTextAccessible* AsHyperText();
 
   inline bool IsHTMLListItem() const { return mFlags & eHTMLListItemAccessible; }
   nsHTMLLIAccessible* AsHTMLListItem();
 
+  inline bool IsListControl() const { return mFlags & eListControlAccessible; }
+
+  inline bool IsMenuButton() const { return mFlags & eMenuButtonAccessible; }
+
+  inline bool IsMenuPopup() const { return mFlags & eMenuPopupAccessible; }
+
   inline bool IsRoot() const { return mFlags & eRootAccessible; }
   nsRootAccessible* AsRoot();
 
   inline bool IsTextLeaf() const { return mFlags & eTextLeafAccessible; }
   nsTextAccessible* AsTextLeaf();
 
   //////////////////////////////////////////////////////////////////////////////
   // ActionAccessible
@@ -470,22 +482,17 @@ public:
     // In the mean time authors can use role="link" aria-invalid="true"
     // to force it for links they internally know to be invalid
     return (0 == (State() & mozilla::a11y::states::INVALID));
   }
 
   /**
    * Return true if the link currently has the focus.
    */
-  inline bool IsLinkSelected()
-  {
-    NS_PRECONDITION(IsLink(),
-                    "IsLinkSelected() called on something that is not a hyper link!");
-    return gLastFocusedNode == GetNode();
-  }
+  bool IsLinkSelected();
 
   /**
    * Return the number of anchors within the link.
    */
   virtual PRUint32 AnchorCount();
 
   /**
    * Returns an anchor accessible at the given index.
@@ -541,16 +548,48 @@ public:
    */
   virtual bool SelectAll();
 
   /**
    * Unselect all items. Return true if success.
    */
   virtual bool UnselectAll();
 
+  //////////////////////////////////////////////////////////////////////////////
+  // Widgets
+
+  /**
+   * Return true if accessible is a widget, i.e. control or accessible that
+   * manages its items. Note, being a widget the accessible may be a part of
+   * composite widget.
+   */
+  virtual bool IsWidget() const;
+
+  /**
+   * Return true if the widget is active, i.e. has a focus within it.
+   */
+  virtual bool IsActiveWidget() const;
+
+  /**
+   * Return true if the widget has items and items are operable by user and
+   * can be activated.
+   */
+  virtual bool AreItemsOperable() const;
+
+  /**
+   * Return the current item of the widget, i.e. an item that has or will have
+   * keyboard focus when widget gets active.
+   */
+  virtual nsAccessible* CurrentItem();
+
+  /**
+   * Return container widget this accessible belongs to.
+   */
+  virtual nsAccessible* ContainerWidget() const;
+
 protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Initializing, cache and tree traverse methods
 
   /**
    * Cache accessible children.
    */
@@ -590,21 +629,27 @@ protected:
     { mFlags = (mFlags & ~kChildrenFlagsMask) | aFlag; }
 
   /**
    * Flags describing the accessible itself.
    * @note keep these flags in sync with ChildrenFlags
    */
   enum AccessibleTypes {
     eApplicationAccessible = 1 << 2,
-    eDocAccessible = 1 << 3,
-    eHyperTextAccessible = 1 << 4,
-    eHTMLListItemAccessible = 1 << 5,
-    eRootAccessible = 1 << 6,
-    eTextLeafAccessible = 1 << 7
+    eAutoCompleteAccessible = 1 << 3,
+    eAutoCompletePopupAccessible = 1 << 4,
+    eComboboxAccessible = 1 << 5,
+    eDocAccessible = 1 << 6,
+    eHyperTextAccessible = 1 << 7,
+    eHTMLListItemAccessible = 1 << 8,
+    eListControlAccessible = 1 << 9,
+    eMenuButtonAccessible = 1 << 10,
+    eMenuPopupAccessible = 1 << 11,
+    eRootAccessible = 1 << 12,
+    eTextLeafAccessible = 1 << 13
   };
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
 
   /**
    * Return ARIA role (helper method).
    */
--- a/accessible/src/base/nsApplicationAccessible.cpp
+++ b/accessible/src/base/nsApplicationAccessible.cpp
@@ -169,22 +169,20 @@ nsApplicationAccessible::ChildAtPoint(PR
                                       EWhichChildAtPoint aWhichChild)
 {
   return nsnull;
 }
 
 nsAccessible*
 nsApplicationAccessible::FocusedChild()
 {
-  if (gLastFocusedNode) {
-    nsAccessible* focusedChild =
-      GetAccService()->GetAccessible(gLastFocusedNode);
-    if (focusedChild && focusedChild->Parent() == this)
-      return focusedChild;
-  }
+  nsAccessible* focus = FocusMgr()->FocusedAccessible();
+  if (focus && focus->Parent() == this)
+    return focus;
+
   return nsnull;
 }
 
 Relation
 nsApplicationAccessible::RelationByType(PRUint32 aRelationType)
 {
   return Relation();
 }
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -290,28 +290,20 @@ nsDocAccessible::Description(nsString& a
 PRUint64
 nsDocAccessible::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;
 
-#ifdef MOZ_XUL
-  nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
-  if (!xulDoc)
-#endif
-  {
-    // XXX Need to invent better check to see if doc is focusable,
-    // which it should be if it is scrollable. A XUL document could be focusable.
-    // See bug 376803.
-    state |= states::FOCUSABLE;
-    if (gLastFocusedNode == mDocument)
-      state |= states::FOCUSED;
-  }
+  // Document is always focusable.
+  state |= states::FOCUSABLE;
+  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;
 
   // Expose state busy until the document and all its subdocuments is completely
   // loaded.
@@ -352,34 +344,26 @@ nsDocAccessible::GetAttributes(nsIPersis
     mParent->GetAttributes(aAttributes); // Add parent attributes (override inner)
   }
   return NS_OK;
 }
 
 nsAccessible*
 nsDocAccessible::FocusedChild()
 {
-  // XXXndeakin P3 accessibility shouldn't be caching the focus
-
   // Return an accessible for the current global focus, which does not have to
   // be contained within the current document.
-  return gLastFocusedNode ? GetAccService()->GetAccessible(gLastFocusedNode) :
-    nsnull;
+  return FocusMgr()->FocusedAccessible();
 }
 
 NS_IMETHODIMP nsDocAccessible::TakeFocus()
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  PRUint64 state = NativeState();
-  if (0 == (state & states::FOCUSABLE)) {
-    return NS_ERROR_FAILURE; // Not focusable
-  }
-
   // Focus the document.
   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   NS_ENSURE_STATE(fm);
 
   nsCOMPtr<nsIDOMElement> newFocus;
   return fm->MoveFocus(mDocument->GetWindow(), nsnull,
                        nsIFocusManager::MOVEFOCUS_ROOT, 0,
                        getter_AddRefs(newFocus));
@@ -1058,21 +1042,17 @@ nsDocAccessible::AttributeChangedImpl(ns
   }
 
   if (aAttribute == nsGkAtoms::selected ||
       aAttribute == nsGkAtoms::aria_selected) {
     // ARIA or XUL selection
 
     nsAccessible *multiSelect =
       nsAccUtils::GetMultiSelectableContainer(aContent);
-    // Multi selects use selection_add and selection_remove
-    // Single select widgets just mirror event_selection for
-    // whatever gets event_focus, which is done in
-    // nsRootAccessible::FireAccessibleFocusEvent()
-    // So right here we make sure only to deal with multi selects
+    // XXX: Multi selects are handled here only (bug 414302).
     if (multiSelect) {
       // Need to find the right event to use here, SELECTION_WITHIN would
       // seem right but we had started using it for something else
       FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
                                  multiSelect->GetNode(),
                                  AccEvent::eAllowDupes);
 
       static nsIContent::AttrValuesArray strings[] =
@@ -1113,27 +1093,23 @@ nsDocAccessible::ARIAAttributeChanged(ns
 
   if (aAttribute == nsGkAtoms::aria_invalid) {
     nsRefPtr<AccEvent> event =
       new AccStateChangeEvent(aContent, states::INVALID);
     FireDelayedAccessibleEvent(event);
     return;
   }
 
+  // The activedescendant universal property redirects accessible focus events
+  // to the element with the id that activedescendant points to. Make sure
+  // the tree up to date before processing.
   if (aAttribute == nsGkAtoms::aria_activedescendant) {
-    // The activedescendant universal property redirects accessible focus events
-    // to the element with the id that activedescendant points to
-    nsCOMPtr<nsINode> focusedNode = GetCurrentFocus();
-    if (nsCoreUtils::GetRoleContent(focusedNode) == aContent) {
-      nsAccessible* focusedAcc = GetAccService()->GetAccessible(focusedNode);
-      nsRootAccessible* rootAcc = RootAccessible();
-      if (rootAcc && focusedAcc) {
-        rootAcc->FireAccessibleFocusEvent(focusedAcc, nsnull, PR_TRUE);
-      }
-    }
+    mNotificationController->HandleNotification<nsDocAccessible, nsIContent>
+      (this, &nsDocAccessible::ARIAActiveDescendantChanged, aContent);
+
     return;
   }
 
   // For aria drag and drop changes we fire a generic attribute change event;
   // at least until native API comes up with a more meaningful event.
   if (aAttribute == nsGkAtoms::aria_grabbed ||
       aAttribute == nsGkAtoms::aria_dropeffect ||
       aAttribute == nsGkAtoms::aria_hidden ||
@@ -1195,16 +1171,36 @@ nsDocAccessible::ARIAAttributeChanged(ns
         aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
                               nsGkAtoms::_empty, eCaseMatters)))) {
     FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
                                aContent);
     return;
   }
 }
 
+void
+nsDocAccessible::ARIAActiveDescendantChanged(nsIContent* aElm)
+{
+  if (FocusMgr()->HasDOMFocus(aElm)) {
+    nsAutoString id;
+    if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
+      nsIDocument* DOMDoc = aElm->GetOwnerDoc();
+      dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
+      if (activeDescendantElm) {
+        nsAccessible* activeDescendant = GetAccessible(activeDescendantElm);
+        if (activeDescendant) {
+          FocusMgr()->ActiveItemChanged(activeDescendant, false);
+          A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("ARIA activedescedant changed",
+                                                 activeDescendant)
+        }
+      }
+    }
+  }
+}
+
 void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
                                       nsIContent* aContainer,
                                       nsIContent* aFirstNewContent,
                                       PRInt32 /* unused */)
 {
 }
 
 void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
@@ -1357,16 +1353,23 @@ nsDocAccessible::BindToDocument(nsAccess
 }
 
 void
 nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
 {
   NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
                "Unbinding the unbound accessible!");
 
+  // Fire focus event on accessible having DOM focus if active item was removed
+  // from the tree.
+  if (FocusMgr()->IsActiveItem(aAccessible)) {
+    FocusMgr()->ActiveItemChanged(nsnull);
+    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("tree shutdown", aAccessible)
+  }
+
   // Remove an accessible from node-to-accessible map if it exists there.
   if (aAccessible->IsPrimaryForNode() &&
       mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
     mNodeToAccessibleMap.Remove(aAccessible->GetNode());
 
   void* uniqueID = aAccessible->UniqueID();
 
   NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
@@ -1727,34 +1730,26 @@ nsDocAccessible::FireDelayedAccessibleEv
 void
 nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
 {
   nsAccessible* accessible = aEvent->GetAccessible();
   if (!accessible)
     return;
 
   PRUint32 eventType = aEvent->GetEventType();
-
   if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
     nsCOMPtr<nsIAccessibleText> accessibleText = do_QueryObject(accessible);
     PRInt32 caretOffset;
     if (accessibleText &&
         NS_SUCCEEDED(accessibleText->GetCaretOffset(&caretOffset))) {
 #ifdef DEBUG_A11Y
       PRUnichar chAtOffset;
       accessibleText->GetCharacterAtOffset(caretOffset, &chAtOffset);
       printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
 #endif
-#ifdef DEBUG_CARET
-      // Test caret line # -- fire an EVENT_ALERT on the focused node so we can watch the
-      // line-number object attribute on it
-      nsAccessible* focusedAcc =
-        GetAccService()->GetAccessible(gLastFocusedNode);
-      nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, focusedAcc);
-#endif
       nsRefPtr<AccEvent> caretMoveEvent =
           new AccCaretMoveEvent(accessible, caretOffset);
       if (!caretMoveEvent)
         return;
 
       nsEventShell::FireEvent(caretMoveEvent);
 
       PRInt32 selectionCount;
@@ -1943,20 +1938,21 @@ nsDocAccessible::UpdateTreeInternal(nsAc
       FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
                                  AccEvent::eRemoveDupes);
     }
 
     // If focused node has been shown then it means its frame was recreated
     // while it's focused. Fire focus event on new focused accessible. If
     // the queue contains focus event for this node then it's suppressed by
     // this one.
-    if (node == gLastFocusedNode) {
-      FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
-                                 node, AccEvent::eCoalesceFromSameDocument);
-    }
+    // XXX: do we really want to send focus to focused DOM node not taking into
+    // account active item?
+    if (FocusMgr()->IsFocused(aChild))
+      FocusMgr()->DispatchFocusEvent(this, aChild);
+
   } else {
     // Update the tree for content removal.
     // The accessible parent may differ from container accessible if
     // the parent doesn't have own DOM node like list accessible for HTML
     // selects.
     nsAccessible* parent = aChild->Parent();
     NS_ASSERTION(parent, "No accessible parent?!");
     if (parent)
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -452,16 +452,21 @@ protected:
      * Fires accessible events when ARIA attribute is changed.
      *
      * @param aContent - node that attribute is changed for
      * @param aAttribute - changed attribute
      */
     void ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute);
 
   /**
+   * Process ARIA active-descendant attribute change.
+   */
+  void ARIAActiveDescendantChanged(nsIContent* aElm);
+
+  /**
    * Process the event when the queue of pending events is untwisted. Fire
    * accessible events as result of the processing.
    */
   void ProcessPendingEvent(AccEvent* aEvent);
 
   /**
    * Process anchor jump notification and fire scrolling end event.
    */
--- a/accessible/src/base/nsOuterDocAccessible.cpp
+++ b/accessible/src/base/nsOuterDocAccessible.cpp
@@ -64,22 +64,16 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsOuterDocA
 // nsAccessible public (DON'T add methods here)
 
 PRUint32
 nsOuterDocAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_INTERNAL_FRAME;
 }
 
-PRUint64
-nsOuterDocAccessible::NativeState()
-{
-  return nsAccessible::NativeState() & ~states::FOCUSABLE;
-}
-
 nsAccessible*
 nsOuterDocAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                    EWhichChildAtPoint aWhichChild)
 {
   PRInt32 docX = 0, docY = 0, docWidth = 0, docHeight = 0;
   nsresult rv = GetBounds(&docX, &docY, &docWidth, &docHeight);
   NS_ENSURE_SUCCESS(rv, nsnull);
 
--- a/accessible/src/base/nsOuterDocAccessible.h
+++ b/accessible/src/base/nsOuterDocAccessible.h
@@ -62,17 +62,16 @@ public:
   NS_IMETHOD GetActionDescription(PRUint8 aIndex, nsAString& aDescription);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint32 NativeRole();
-  virtual PRUint64 NativeState();
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                      EWhichChildAtPoint aWhichChild);
 
   virtual void InvalidateChildren();
   virtual PRBool AppendChild(nsAccessible *aAccessible);
   virtual PRBool RemoveChild(nsAccessible *aAccessible);
 
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -56,24 +56,21 @@
 #include "nsIDOMEventListener.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLSelectElement.h"
 #include "nsIDOMDataContainerEvent.h"
 #include "nsIDOMNSEvent.h"
-#include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
-#include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULPopupElement.h"
 #include "nsIDocument.h"
 #include "nsEventListenerManager.h"
 #include "nsIFrame.h"
-#include "nsMenuFrame.h"
 #include "nsIHTMLDocument.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsISelectionPrivate.h"
 #include "nsIServiceManager.h"
 #include "nsPIDOMWindow.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsReadableUtils.h"
 #include "nsRootAccessible.h"
@@ -81,17 +78,17 @@
 #include "nsFocusManager.h"
 
 #ifdef MOZ_XUL
 #include "nsXULTreeAccessible.h"
 #include "nsIXULDocument.h"
 #include "nsIXULWindow.h"
 #endif
 
-using namespace mozilla;
+namespace dom = mozilla::dom;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
 // Expanded version of NS_IMPL_ISUPPORTS_INHERITED2 
 // so we can QI directly to concrete nsRootAccessible
 NS_IMPL_QUERY_HEAD(nsRootAccessible)
@@ -218,19 +215,16 @@ nsRootAccessible::NativeState()
 }
 
 const char* const docEvents[] = {
 #ifdef DEBUG_DRAGDROPSTART
   // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
   // debugging a11y objects with event viewers
   "mouseover",
 #endif
-  // capture DOM focus and DOM blur events 
-  "focus",
-  "blur",
   // capture Form change events 
   "select",
   // capture ValueChange events (fired whenever value changes, immediately after, whether focus moves or not)
   "ValueChange",
   // capture AlertActive events (fired whenever alert pops up)
   "AlertActive",
   // add ourself as a TreeViewChanged listener (custom event fired in nsTreeBodyFrame.cpp)
   "TreeViewChanged",
@@ -241,16 +235,17 @@ const char* const docEvents[] = {
   // add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
   "CheckboxStateChange",
   // add ourself as a RadioStateChange Listener ( custom event fired in in nsHTMLInputElement.cpp  & radio.xml)
   "RadioStateChange",
   "popupshown",
   "popuphiding",
   "DOMMenuInactive",
   "DOMMenuItemActive",
+  "DOMMenuItemInactive",
   "DOMMenuBarActive",
   "DOMMenuBarInactive"
 };
 
 nsresult nsRootAccessible::AddEventListeners()
 {
   // nsIDOMEventTarget interface allows to register event listeners to
   // receive untrusted events (synthetic events generated by untrusted code).
@@ -304,135 +299,16 @@ nsresult nsRootAccessible::RemoveEventLi
 
 nsCaretAccessible*
 nsRootAccessible::GetCaretAccessible()
 {
   return mCaretAccessible;
 }
 
 void
-nsRootAccessible::FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
-                                           nsIContent* aRealFocusContent,
-                                           PRBool aForceEvent,
-                                           EIsFromUserInput aIsFromUserInput)
-{
-  // Implementors: only fire delayed/async events from this method.
-
-  // Set selection listener for focused element.
-  if (mCaretAccessible && aRealFocusContent)
-    mCaretAccessible->SetControlSelectionListener(aRealFocusContent);
-
-  nsAccessible* focusAccessible = aFocusAccessible;
-
-  // Check for aria-activedescendant, which changes which element has focus.
-  // For activedescendant, the ARIA spec does not require that the user agent
-  // checks whether pointed node is actually a DOM descendant of the element
-  // with the aria-activedescendant attribute.
-  nsIContent* content = focusAccessible->GetContent();
-  if (content) {
-    nsAutoString id;
-    if (content->GetAttr(kNameSpaceID_None,
-                         nsGkAtoms::aria_activedescendant, id)) {
-      nsIDocument* DOMDoc = content->GetOwnerDoc();
-      nsIContent* activeDescendantContent = DOMDoc->GetElementById(id);
-
-      // If aria-activedescendant is set to nonexistant ID, then treat as focus
-      // on the activedescendant container (which has real DOM focus).
-      if (activeDescendantContent) {
-        nsAccessible* activeDescendant = 
-          GetAccService()->GetAccessible(activeDescendantContent);
-        if (activeDescendant) {
-          focusAccessible = activeDescendant;
-        }
-      }
-    }
-  }
-
-  // Fire focus only if it changes, but always fire focus events when
-  // aForceEvent == PR_TRUE
-  nsINode* focusNode = focusAccessible->GetNode();
-  if (gLastFocusedNode == focusNode && !aForceEvent)
-    return;
-
-  nsDocAccessible* focusDocument = focusAccessible->GetDocAccessible();
-  NS_ASSERTION(focusDocument, "No document while accessible is in document?!");
-
-  // Fire menu start/end events for ARIA menus.
-  if (focusAccessible->ARIARole() == nsIAccessibleRole::ROLE_MENUITEM) {
-    // The focus is inside a menu.
-    if (!mCurrentARIAMenubar) {
-      // Entering ARIA menu. Fire menu start event.
-      nsAccessible* menuBarAccessible =
-        nsAccUtils::GetAncestorWithRole(focusAccessible,
-                                        nsIAccessibleRole::ROLE_MENUBAR);
-      if (menuBarAccessible) {
-        mCurrentARIAMenubar = menuBarAccessible->GetNode();
-        if (mCurrentARIAMenubar) {
-          nsRefPtr<AccEvent> menuStartEvent =
-            new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
-                         menuBarAccessible, aIsFromUserInput,
-                         AccEvent::eAllowDupes);
-          if (menuStartEvent)
-            focusDocument->FireDelayedAccessibleEvent(menuStartEvent);
-        }
-      }
-    }
-  }
-  else if (mCurrentARIAMenubar) {
-    // Focus left a menu. Fire menu end event.
-    nsRefPtr<AccEvent> menuEndEvent =
-      new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mCurrentARIAMenubar,
-                   aIsFromUserInput, AccEvent::eAllowDupes);
-    if (menuEndEvent) {
-      focusDocument->FireDelayedAccessibleEvent(menuEndEvent);
-    }
-    mCurrentARIAMenubar = nsnull;
-  }
-
-  NS_IF_RELEASE(gLastFocusedNode);
-  gLastFocusedNode = focusNode;
-  NS_IF_ADDREF(gLastFocusedNode);
-
-  // Coalesce focus events from the same document, because DOM focus event might
-  // be fired for the document node and then for the focused DOM element.
-  nsRefPtr<AccEvent> focusEvent =
-    new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, focusAccessible,
-                 aIsFromUserInput, AccEvent::eCoalesceFromSameDocument);
-  focusDocument->FireDelayedAccessibleEvent(focusEvent);
-}
-
-void
-nsRootAccessible::FireCurrentFocusEvent()
-{
-  if (IsDefunct())
-    return;
-
-  // Simulate a focus event so that we can reuse code that fires focus for
-  // container children like treeitems.
-  nsCOMPtr<nsINode> focusedNode = GetCurrentFocus();
-  if (!focusedNode) {
-    return; // No current focus
-  }
-
-  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
-  if (domDoc) {
-    nsCOMPtr<nsIDOMEvent> event;
-    if (NS_SUCCEEDED(domDoc->CreateEvent(NS_LITERAL_STRING("Events"),
-                                         getter_AddRefs(event))) &&
-        NS_SUCCEEDED(event->InitEvent(NS_LITERAL_STRING("focus"), PR_TRUE, PR_TRUE))) {
-
-      nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
-      nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(focusedNode));
-      privateEvent->SetTarget(target);
-      HandleEvent(event);
-    }
-  }
-}
-
-void
 nsRootAccessible::DocumentActivated(nsDocAccessible* aDocument)
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIDOMEventListener
 
 NS_IMETHODIMP
@@ -492,24 +368,23 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
   nsAutoString eventType;
   aDOMEvent->GetType(eventType);
 
   nsCOMPtr<nsIWeakReference> weakShell =
     nsCoreUtils::GetWeakShellFor(origTargetNode);
   if (!weakShell)
     return;
 
-  nsAccessible* accessible =
-    GetAccService()->GetAccessibleOrContainer(origTargetNode, weakShell);
-
   if (eventType.EqualsLiteral("popuphiding")) {
-    HandlePopupHidingEvent(origTargetNode, accessible);
+    HandlePopupHidingEvent(origTargetNode);
     return;
   }
 
+  nsAccessible* accessible =
+    GetAccService()->GetAccessibleOrContainer(origTargetNode, weakShell);
   if (!accessible)
     return;
 
   nsDocAccessible* targetDocument = accessible->GetDocAccessible();
   NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
 
   nsINode* targetNode = accessible->GetNode();
   nsIContent* targetContent = targetNode->IsElement() ?
@@ -554,18 +429,20 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
     // nsXULListitemAccessible::GetStateInternal uses STATE_SELECTED in this case,
     // so we need to check states::SELECTED also.
     PRBool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
 
     nsRefPtr<AccEvent> accEvent =
       new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
     nsEventShell::FireEvent(accEvent);
 
-    if (isEnabled)
-      FireAccessibleFocusEvent(accessible, origTargetContent);
+    if (isEnabled) {
+      FocusMgr()->ActiveItemChanged(accessible);
+      A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("RadioStateChange", accessible)
+    }
 
     return;
   }
 
   if (eventType.EqualsLiteral("CheckboxStateChange")) {
     PRUint64 state = accessible->State();
 
     PRBool isEnabled = !!(state & states::CHECKED);
@@ -576,47 +453,36 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
     nsEventShell::FireEvent(accEvent);
     return;
   }
 
   nsAccessible *treeItemAccessible = nsnull;
 #ifdef MOZ_XUL
   // If it's a tree element, need the currently selected item
   if (isTree) {
-    nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
-      do_QueryInterface(targetNode);
-    if (multiSelect) {
-      PRInt32 treeIndex = -1;
-      multiSelect->GetCurrentIndex(&treeIndex);
-      if (treeIndex >= 0) {
-        nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(accessible);
-        if (treeAcc) {
-          treeItemAccessible = treeAcc->GetTreeItemAccessible(treeIndex);
-          if (treeItemAccessible)
-            accessible = treeItemAccessible;
-        }
-      }
-    }
+    treeItemAccessible = accessible->CurrentItem();
+    if (treeItemAccessible)
+      accessible = treeItemAccessible;
   }
 #endif
 
 #ifdef MOZ_XUL
   if (treeItemAccessible && eventType.EqualsLiteral("OpenStateChange")) {
     PRUint64 state = accessible->State();
     PRBool isEnabled = (state & states::EXPANDED) != 0;
 
     nsRefPtr<AccEvent> accEvent =
       new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
     nsEventShell::FireEvent(accEvent);
     return;
   }
 
   if (treeItemAccessible && eventType.EqualsLiteral("select")) {
     // If multiselect tree, we should fire selectionadd or selection removed
-    if (gLastFocusedNode == targetNode) {
+    if (FocusMgr()->HasDOMFocus(targetNode)) {
       nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
         do_QueryInterface(targetNode);
       nsAutoString selType;
       multiSel->GetSelType(selType);
       if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
         // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
         // for each tree item. Perhaps each tree item will need to cache its
         // selection state and fire an event after a DOM "select" event when
@@ -628,127 +494,66 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
 
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION,
                               treeItemAccessible);
       return;
     }
   }
   else
 #endif
-  if (eventType.EqualsLiteral("focus")) {
-    // Keep a reference to the target node. We might want to change
-    // it to the individual radio button or selected item, and send
-    // the focus event to that.
-    nsCOMPtr<nsINode> focusedItem = targetNode;
-    if (!treeItemAccessible) {
-      nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
-        do_QueryInterface(targetNode);
-      if (selectControl) {
-        nsCOMPtr<nsIDOMXULMenuListElement> menuList =
-          do_QueryInterface(targetNode);
-        if (!menuList) {
-          // Don't do this for menu lists, the items only get focused
-          // when the list is open, based on DOMMenuitemActive events
-          nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem;
-          selectControl->GetSelectedItem(getter_AddRefs(selectedItem));
-          if (selectedItem)
-            focusedItem = do_QueryInterface(selectedItem);
-
-          if (!focusedItem)
-            return;
-
-          accessible = GetAccService()->GetAccessibleInWeakShell(focusedItem,
-                                                                 weakShell);
-          if (!accessible)
-            return;
-        }
-      }
-    }
-    FireAccessibleFocusEvent(accessible, origTargetContent);
-  }
-  else if (eventType.EqualsLiteral("blur")) {
-    NS_IF_RELEASE(gLastFocusedNode);
-  }
-  else if (eventType.EqualsLiteral("AlertActive")) { 
+  if (eventType.EqualsLiteral("AlertActive")) {
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
   }
   else if (eventType.EqualsLiteral("popupshown")) {
     HandlePopupShownEvent(accessible);
   }
   else if (eventType.EqualsLiteral("DOMMenuInactive")) {
     if (accessible->Role() == nsIAccessibleRole::ROLE_MENUPOPUP) {
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
                               accessible);
     }
   }
   else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
-    PRBool fireFocus = PR_FALSE;
-    if (!treeItemAccessible) {
-#ifdef MOZ_XUL
-      if (isTree) {
-        return; // Tree with nothing selected
-      }
-#endif
-
-      nsMenuFrame* menuFrame = do_QueryFrame(accessible->GetFrame());
-      if (menuFrame)
-        fireFocus = PR_TRUE;
-      // QI failed for nsMenuFrame means it's not on menu bar
-      if (menuFrame && menuFrame->IsOnMenuBar() &&
-                       !menuFrame->IsOnActiveMenuBar()) {
-        // It is a top level menuitem. Only fire a focus event when the menu bar
-        // is active.
-        return;
-      } else {
-        nsAccessible* container = accessible->Parent();
-        if (!container)
-          return;
-        // It is not top level menuitem
-        // Only fire focus event if it is not inside collapsed popup
-        // and not a listitem of a combo box
-        if (container->State() & states::COLLAPSED) {
-          nsAccessible* containerParent = container->Parent();
-          if (!containerParent)
-            return;
-          if (containerParent->Role() != nsIAccessibleRole::ROLE_COMBOBOX) {
-            return;
-          }
-        }
-      }
-    }
-    if (!fireFocus) {
-      nsCOMPtr<nsINode> realFocusedNode = GetCurrentFocus();
-      nsIContent* realFocusedContent =
-        realFocusedNode->IsElement() ? realFocusedNode->AsElement() : nsnull;
-      nsIContent* containerContent = targetContent;
-      while (containerContent) {
-        nsCOMPtr<nsIDOMXULPopupElement> popup = do_QueryInterface(containerContent);
-        if (popup || containerContent == realFocusedContent) { 
-          // If we're inside the focus or a popup we can fire focus events
-          // for the changed active item
-          fireFocus = PR_TRUE;
-          break;
-        }
-        containerContent = containerContent->GetParent();
-      }
-    }
-    if (fireFocus) {
-      // Always asynch, always from user input.
-      FireAccessibleFocusEvent(accessible, origTargetContent, PR_TRUE,
-                               eFromUserInput);
+    FocusMgr()->ActiveItemChanged(accessible);
+    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemActive", accessible)
+  }
+  else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
+    // Process DOMMenuItemInactive event for autocomplete only because this is
+    // unique widget that may acquire focus from autocomplete popup while popup
+    // stays open and has no active item. In case of XUL tree autocomplete
+    // popup this event is fired for tree accessible.
+    nsAccessible* widget =
+      accessible->IsWidget() ? accessible : accessible->ContainerWidget();
+    if (widget && widget->IsAutoCompletePopup()) {
+      FocusMgr()->ActiveItemChanged(nsnull);
+      A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemInactive", accessible)
     }
   }
   else if (eventType.EqualsLiteral("DOMMenuBarActive")) {  // Always from user input
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
                             accessible, eFromUserInput);
+
+    // Notify of active item change when menubar gets active and if it has
+    // current item. This is a case of mouseover (set current menuitem) and
+    // mouse click (activate the menubar). If menubar doesn't have current item
+    // (can be a case of menubar activation from keyboard) then ignore this
+    // notification because later we'll receive DOMMenuItemActive event after
+    // current menuitem is set.
+    nsAccessible* activeItem = accessible->CurrentItem();
+    if (activeItem) {
+      FocusMgr()->ActiveItemChanged(activeItem);
+      A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarActive", accessible)
+    }
   }
   else if (eventType.EqualsLiteral("DOMMenuBarInactive")) {  // Always from user input
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
                             accessible, eFromUserInput);
-    FireCurrentFocusEvent();
+
+    FocusMgr()->ActiveItemChanged(nsnull);
+    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarInactive", accessible)
   }
   else if (eventType.EqualsLiteral("ValueChange")) {
     targetDocument->
       FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
                                  targetNode, AccEvent::eRemoveDupes);
   }
 #ifdef DEBUG_DRAGDROPSTART
   else if (eventType.EqualsLiteral("mouseover")) {
@@ -764,18 +569,16 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
 
 void
 nsRootAccessible::Shutdown()
 {
   // Called manually or by nsAccessNode::LastRelease()
   if (!mWeakShell)
     return;  // Already shutdown
 
-  mCurrentARIAMenubar = nsnull;
-
   nsDocAccessibleWrap::Shutdown();
 }
 
 // nsRootAccessible protected member
 already_AddRefed<nsIDocShellTreeItem>
 nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
 {
   if (!aStart) {
@@ -881,47 +684,116 @@ nsRootAccessible::HandlePopupShownEvent(
         new AccStateChangeEvent(combobox, states::EXPANDED, PR_TRUE);
       if (event)
         nsEventShell::FireEvent(event);
     }
   }
 }
 
 void
-nsRootAccessible::HandlePopupHidingEvent(nsINode* aNode,
-                                         nsAccessible* aAccessible)
+nsRootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
 {
-  // If accessible focus was on or inside popup that closes, then restore it
-  // to true current focus. This is the case when we've been getting
-  // DOMMenuItemActive events inside of a combo box that closes. The real focus
-  // is on the combo box. It's also the case when a popup gets focus in ATK --
-  // when it closes we need to fire an event to restore focus to where it was.
+  // Get popup accessible. There are cases when popup element isn't accessible
+  // but an underlying widget is and behaves like popup, an example is
+  // autocomplete popups.
+  nsDocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
+  if (!document)
+    return;
+
+  nsAccessible* popup = document->GetAccessible(aPopupNode);
+  if (!popup) {
+    nsAccessible* popupContainer = document->GetContainerAccessible(aPopupNode);
+    if (!popupContainer)
+      return;
 
-  if (gLastFocusedNode &&
-      nsCoreUtils::IsAncestorOf(aNode, gLastFocusedNode)) {
-    // Focus was on or inside of a popup that's being hidden
-    FireCurrentFocusEvent();
+    PRInt32 childCount = popupContainer->GetChildCount();
+    for (PRInt32 idx = 0; idx < childCount; idx++) {
+      nsAccessible* child = popupContainer->GetChildAt(idx);
+      if (child->IsAutoCompletePopup()) {
+        popup = child;
+        break;
+      }
+    }
+
+    // No popup no events. Focus is managed by DOM. This is a case for
+    // menupopups of menus on Linux since there are no accessible for popups.
+    if (!popup)
+      return;
   }
 
-  // Fire expanded state change event for comboboxes and autocompletes.
-  if (!aAccessible ||
-      aAccessible->Role() != nsIAccessibleRole::ROLE_COMBOBOX_LIST)
-    return;
+  // In case of autocompletes and comboboxes fire state change event for
+  // expanded state. Note, HTML form autocomplete isn't a subject of state
+  // change event because they aren't autocompletes strictly speaking.
+  // When popup closes (except nested popups and menus) then fire focus event to
+  // where it was. The focus event is expected even if popup didn't take a focus.
+
+  static const PRUint32 kNotifyOfFocus = 1;
+  static const PRUint32 kNotifyOfState = 2;
+  PRUint32 notifyOf = 0;
+
+  // HTML select is target of popuphidding event. Otherwise get container
+  // widget. No container widget means this is either tooltip or menupopup.
+  // No events in the former case.
+  nsAccessible* widget = nsnull;
+  if (popup->IsCombobox()) {
+    widget = popup;
+  } else {
+    widget = popup->ContainerWidget();
+    if (!widget) {
+      if (!popup->IsMenuPopup())
+        return;
+
+      widget = popup;
+    }
+  }
+
+  if (popup->IsAutoCompletePopup()) {
+    // No focus event for autocomplete because it's managed by
+    // DOMMenuItemInactive events.
+    if (widget->IsAutoComplete())
+      notifyOf = kNotifyOfState;
 
-  nsAccessible* combobox = aAccessible->Parent();
-  if (!combobox)
-    return;
+  } else if (widget->IsCombobox()) {
+    // Fire focus for active combobox, otherwise the focus is managed by DOM
+    // focus notifications. Always fire state change event.
+    if (widget->IsActiveWidget())
+      notifyOf = kNotifyOfFocus;
+    notifyOf |= kNotifyOfState;
+
+  } else if (widget->IsMenuButton()) {
+    // Can be a part of autocomplete.
+    nsAccessible* compositeWidget = widget->ContainerWidget();
+    if (compositeWidget && compositeWidget->IsAutoComplete()) {
+      widget = compositeWidget;
+      notifyOf = kNotifyOfState;
+    }
+
+    // Autocomplete (like searchbar) can be inactive when popup hiddens
+    notifyOf |= kNotifyOfFocus;
 
-  PRUint32 comboboxRole = combobox->Role();
-  if (comboboxRole == nsIAccessibleRole::ROLE_COMBOBOX ||
-      comboboxRole == nsIAccessibleRole::ROLE_AUTOCOMPLETE) {
+  } else if (widget == popup) {
+    // Top level context menus and alerts.
+    // Ignore submenus and menubar. When submenu is closed then sumbenu
+    // container menuitem takes a focus via DOMMenuItemActive notification.
+    // For menubars processing we listen DOMMenubarActive/Inactive
+    // notifications.
+    notifyOf = kNotifyOfFocus;
+  }
+
+  // Restore focus to where it was.
+  if (notifyOf & kNotifyOfFocus) {
+    FocusMgr()->ActiveItemChanged(nsnull);
+    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("popuphiding", popup)
+  }
+
+  // Fire expanded state change event.
+  if (notifyOf & kNotifyOfState) {
     nsRefPtr<AccEvent> event =
-      new AccStateChangeEvent(combobox, states::EXPANDED, PR_FALSE);
-    if (event)
-      nsEventShell::FireEvent(event);
+      new AccStateChangeEvent(widget, states::EXPANDED, PR_FALSE);
+    document->FireDelayedAccessibleEvent(event);
   }
 }
 
 #ifdef MOZ_XUL
 void
 nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
                                                  nsXULTreeAccessible* aAccessible)
 {
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -85,57 +85,24 @@ public:
   // nsAccessible
   virtual Relation RelationByType(PRUint32 aType);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
   // nsRootAccessible
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ROOTACCESSIBLE_IMPL_CID)
 
-  /**
-   * Fire an accessible focus event for the focused accessible and attach a new
-   * selection listener to real focused element, if necessary.
-   *
-   * @param  aFocusAccessible   [in] the accessible which has received focus
-   * @param  aRealFocusContent  [in] the actual DOM element which has received
-   *                              focus (see @note section)
-   * @param  aForceEvent        [in, optional] fire a focus event even if
-   *                              the last focused item was the same
-   * @param  aIsFromUserInput   [in, optional] specifies whether the event is
-   *                              from user input
-   *
-   * @note  Use the originally focused node where the selection lives as real
-   *         focus node. For example, use the anonymous HTML:input instead of
-   *         the containing XUL:textbox. In this case, sometimes it is a later
-   *         focus event which points to the actual anonymous child with focus,
-   *         so to be safe we need to reset the selection listener every time.
-   *         This happens because when some bindings handle focus, they
-   *         retarget focus to the appropriate child inside of themselves, but
-   *         DOM focus stays outside on that binding parent.
-   */
-  void FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
-                                nsIContent* aRealFocusContent,
-                                PRBool aForceEvent = PR_FALSE,
-                                EIsFromUserInput aIsFromUserInput = eAutoDetect);
-
-    /**
-      * Fire an accessible focus event for the current focused node,
-      * if there is a focus.
-      */
-    void FireCurrentFocusEvent();
-
     nsCaretAccessible *GetCaretAccessible();
 
   /**
    * Notify that the sub document presshell was activated.
    */
   virtual void DocumentActivated(nsDocAccessible* aDocument);
 
 protected:
-  NS_DECL_RUNNABLEMETHOD(nsRootAccessible, FireCurrentFocusEvent)
 
   /**
    * Add/remove DOM event listeners.
    */
   virtual nsresult AddEventListeners();
   virtual nsresult RemoveEventListeners();
 
   /**
@@ -146,30 +113,29 @@ protected:
   /**
    * Process "popupshown" event. Used by HandleEvent().
    */
   void HandlePopupShownEvent(nsAccessible* aAccessible);
 
   /*
    * Process "popuphiding" event. Used by HandleEvent().
    */
-  void HandlePopupHidingEvent(nsINode* aNode, nsAccessible* aAccessible);
+  void HandlePopupHidingEvent(nsINode* aNode);
 
 #ifdef MOZ_XUL
     void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
                                         nsXULTreeAccessible* aAccessible);
     void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
                                     nsXULTreeAccessible* aAccessible);
 
     PRUint32 GetChromeFlags();
 #endif
     already_AddRefed<nsIDocShellTreeItem>
            GetContentDocShell(nsIDocShellTreeItem *aStart);
     nsRefPtr<nsCaretAccessible> mCaretAccessible;
-  nsCOMPtr<nsINode> mCurrentARIAMenubar;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID)
 
 inline nsRootAccessible*
 nsAccessible::AsRoot()
 {
   return mFlags & eRootAccessible ?
--- a/accessible/src/html/nsHTMLFormControlAccessible.cpp
+++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp
@@ -458,50 +458,39 @@ nsHTMLTextFieldAccessible::NativeState()
 {
   PRUint64 state = nsHyperTextAccessibleWrap::NativeState();
 
   // can be focusable, focused, protected. readonly, unavailable, selected
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                             nsGkAtoms::password, eIgnoreCase)) {
     state |= states::PROTECTED;
   }
-  else {
-    nsAccessible* parent = Parent();
-    if (parent && parent->Role() == nsIAccessibleRole::ROLE_AUTOCOMPLETE)
-      state |= states::HASPOPUP;
-  }
 
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
     state |= states::READONLY;
   }
 
+  // Is it an <input> or a <textarea> ?
   nsCOMPtr<nsIDOMHTMLInputElement> htmlInput(do_QueryInterface(mContent));
-  // Is it an <input> or a <textarea> ?
-  if (htmlInput) {
-    state |= states::SINGLE_LINE;
-  }
-  else {
-    state |= states::MULTI_LINE;
-  }
+  state |= htmlInput ? states::SINGLE_LINE : states::MULTI_LINE;
 
-  if (!(state & states::EDITABLE))
+  if (!(state & states::EDITABLE) ||
+      (state & (states::PROTECTED | states::MULTI_LINE)))
     return state;
 
-  nsCOMPtr<nsIContent> bindingContent = mContent->GetBindingParent();
-  if (bindingContent &&
-      bindingContent->NodeInfo()->Equals(nsGkAtoms::textbox,
-                                         kNameSpaceID_XUL)) {
-     if (bindingContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                                     nsGkAtoms::autocomplete,
-                                     eIgnoreCase)) {
-       // If parent is XUL textbox and value of @type attribute is "autocomplete",
-       // then this accessible supports autocompletion.
-       state |= states::SUPPORTS_AUTOCOMPLETION;
-     }
-  } else if (gIsFormFillEnabled && htmlInput && !(state & states::PROTECTED)) {
+  // Expose autocomplete states if this input is part of autocomplete widget.
+  nsAccessible* widget = ContainerWidget();
+  if (widget && widget-IsAutoComplete()) {
+    state |= states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION;
+    return state;
+  }
+
+  // No parent can mean a fake widget created for XUL textbox. If accessible
+  // is unattached from tree then we don't care.
+  if (mParent && gIsFormFillEnabled) {
     // Check to see if autocompletion is allowed on this input. We don't expose
     // it for password fields even though the entire password can be remembered
     // for a page if the user asks it to be. However, the kind of autocomplete
     // we're talking here is based on what the user types, where a popup of
     // possible choices comes up.
     nsAutoString autocomplete;
     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete,
                       autocomplete);
@@ -571,16 +560,33 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible:
     stack->Pop(&cx);
     NS_ASSERTION(!cx, "context should be null");
   }
 
   return rv;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsHTMLTextFieldAccessible: Widgets
+
+bool
+nsHTMLTextFieldAccessible::IsWidget() const
+{
+  return true;
+}
+
+nsAccessible*
+nsHTMLTextFieldAccessible::ContainerWidget() const
+{
+  return mParent && mParent->Role() == nsIAccessibleRole::ROLE_AUTOCOMPLETE ?
+    mParent : nsnull;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 // nsHTMLGroupboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLGroupboxAccessible::
   nsHTMLGroupboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsHyperTextAccessibleWrap(aContent, aShell)
 {
 }
--- a/accessible/src/html/nsHTMLFormControlAccessible.h
+++ b/accessible/src/html/nsHTMLFormControlAccessible.h
@@ -161,16 +161,20 @@ public:
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
+
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual nsAccessible* ContainerWidget() const;
 };
 
 
 /**
  * Accessible for HTML fieldset element.
  */
 class nsHTMLGroupboxAccessible : public nsHyperTextAccessibleWrap
 {
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -62,38 +62,26 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectListAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLSelectListAccessible::
   nsHTMLSelectListAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
+  mFlags |= eListControlAccessible;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectListAccessible: nsAccessible public
 
 PRUint64
 nsHTMLSelectListAccessible::NativeState()
 {
   PRUint64 state = nsAccessibleWrap::NativeState();
-
-  // As a nsHTMLSelectListAccessible we can have the following states:
-  //   states::MULTISELECTABLE, states::EXTSELECTABLE
-
-  if (state & states::FOCUSED) {
-    // Treat first focusable option node as actual focus, in order
-    // to avoid confusing JAWS, which needs focus on the option
-    nsCOMPtr<nsIContent> focusedOption =
-      nsHTMLSelectOptionAccessible::GetFocusedOption(mContent);
-    if (focusedOption) { // Clear focused state since it is on option
-      state &= ~states::FOCUSED;
-    }
-  }
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple))
     state |= states::MULTISELECTABLE | states::EXTSELECTABLE;
 
   return state;
 }
 
 PRUint32
 nsHTMLSelectListAccessible::NativeRole()
@@ -123,16 +111,52 @@ nsHTMLSelectListAccessible::SelectAll()
 bool
 nsHTMLSelectListAccessible::UnselectAll()
 {
   return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
            nsAccessibleWrap::UnselectAll() : false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsHTMLSelectListAccessible: Widgets
+
+bool
+nsHTMLSelectListAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsHTMLSelectListAccessible::IsActiveWidget() const
+{
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsHTMLSelectListAccessible::AreItemsOperable() const
+{
+  return true;
+}
+
+nsAccessible*
+nsHTMLSelectListAccessible::CurrentItem()
+{
+  nsIListControlFrame* listControlFrame = do_QueryFrame(GetFrame());
+  if (listControlFrame) {
+    nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
+    if (activeOptionNode) {
+      nsDocAccessible* document = GetDocAccessible();
+      if (document)
+        return document->GetAccessible(activeOptionNode);
+    }
+  }
+  return nsnull;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectListAccessible: nsAccessible protected
 
 void
 nsHTMLSelectListAccessible::CacheChildren()
 {
   // Cache accessibles for <optgroup> and <option> DOM decendents as children,
   // as well as the accessibles for them. Avoid whitespace text nodes. We want
   // to count all the <optgroup>s and <option>s as children because we want
@@ -247,44 +271,30 @@ nsHTMLSelectOptionAccessible::NativeStat
   // As a nsHTMLSelectOptionAccessible we can have the following states:
   // SELECTABLE, SELECTED, FOCUSED, FOCUSABLE, OFFSCREEN
   // Upcall to nsAccessible, but skip nsHyperTextAccessible impl
   // because we don't want EDITABLE or SELECTABLE_TEXT
   PRUint64 state = nsAccessible::NativeState();
 
   PRUint64 selectState = 0;
   nsIContent* selectContent = GetSelectState(&selectState);
-  if (selectState & states::INVISIBLE)
+  if (!selectContent || selectState & states::INVISIBLE)
     return state;
 
-  NS_ENSURE_TRUE(selectContent, NS_ERROR_FAILURE);
-
-  // Is disabled?
-  if (0 == (state & states::UNAVAILABLE)) {
+  // Focusable and selectable
+  if (!(state & states::UNAVAILABLE))
     state |= (states::FOCUSABLE | states::SELECTABLE);
-    // When the list is focused but no option is actually focused,
-    // Firefox draws a focus ring around the first non-disabled option.
-    // We need to indicate states::FOCUSED in that case, because it
-    // prevents JAWS from ignoring the list
-    // GetFocusedOption() ensures that an option node is
-    // returned in this case, as long as some focusable option exists
-    // in the listbox
-    nsCOMPtr<nsIContent> focusedOption = GetFocusedOption(selectContent);
-    if (focusedOption == mContent)
-      state |= states::FOCUSED;
-  }
 
   // Are we selected?
   PRBool isSelected = PR_FALSE;
   nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(mContent));
   if (option) {
     option->GetSelected(&isSelected);
     if (isSelected)
       state |= states::SELECTED;
-
   }
 
   if (selectState & states::OFFSCREEN) {
     state |= states::OFFSCREEN;
   }
   else if (selectState & states::COLLAPSED) {
     // <select> is COLLAPSED: add OFFSCREEN, if not the currently
     // visible option
@@ -372,152 +382,58 @@ NS_IMETHODIMP nsHTMLSelectOptionAccessib
 }
 
 PRUint8
 nsHTMLSelectOptionAccessible::ActionCount()
 {
   return 1;
 }
 
-NS_IMETHODIMP nsHTMLSelectOptionAccessible::DoAction(PRUint8 index)
+NS_IMETHODIMP
+nsHTMLSelectOptionAccessible::DoAction(PRUint8 aIndex)
 {
-  if (index == eAction_Select) {   // default action
-    nsCOMPtr<nsIDOMHTMLOptionElement> newHTMLOption(do_QueryInterface(mContent));
-    if (!newHTMLOption) 
-      return NS_ERROR_FAILURE;
-    // Clear old selection
-    nsAccessible* parent = Parent();
-    if (!parent)
-      return NS_OK;
-
-    nsCOMPtr<nsIContent> oldHTMLOptionContent =
-      GetFocusedOption(parent->GetContent());
-    nsCOMPtr<nsIDOMHTMLOptionElement> oldHTMLOption =
-      do_QueryInterface(oldHTMLOptionContent);
-    if (oldHTMLOption)
-      oldHTMLOption->SetSelected(PR_FALSE);
-    // Set new selection
-    newHTMLOption->SetSelected(PR_TRUE);
+  if (aIndex != eAction_Select)
+    return NS_ERROR_INVALID_ARG;
 
-    // If combo box, and open, close it
-    // First, get the <select> widgets list control frame
-    nsIContent *selectContent = mContent;
-    do {
-      selectContent = selectContent->GetParent();
-      nsCOMPtr<nsIDOMHTMLSelectElement> selectControl =
-        do_QueryInterface(selectContent);
-      if (selectControl)
-        break;
-
-    } while (selectContent);
-
-    nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
-    nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(mContent));
-
-    if (!selectContent || !presShell || !option)
-      return NS_ERROR_FAILURE;
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
 
-    nsIFrame *selectFrame = selectContent->GetPrimaryFrame();
-    nsIComboboxControlFrame *comboBoxFrame = do_QueryFrame(selectFrame);
-    if (comboBoxFrame) {
-      nsIFrame *listFrame = comboBoxFrame->GetDropDown();
-      if (comboBoxFrame->IsDroppedDown() && listFrame) {
-        // use this list control frame to roll up the list
-        nsIListControlFrame *listControlFrame = do_QueryFrame(listFrame);
-        if (listControlFrame) {
-          PRInt32 newIndex = 0;
-          option->GetIndex(&newIndex);
-          listControlFrame->ComboboxFinish(newIndex);
-        }
-      }
-    }
-    return NS_OK;
-  }
-
-  return NS_ERROR_INVALID_ARG;
+  DoCommand();
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLSelectOptionAccessible::SetSelected(PRBool aSelect)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIDOMHTMLOptionElement> optionElm(do_QueryInterface(mContent));
   return optionElm->SetSelected(aSelect);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsHTMLSelectOptionAccessible: static methods
-
-/**
-  * Helper method for getting the focused DOM Node from our parent(list) node. We
-  *  need to use the frame to get the focused option because for some reason we
-  *  weren't getting the proper notification when the focus changed using the DOM
-  */
-already_AddRefed<nsIContent>
-nsHTMLSelectOptionAccessible::GetFocusedOption(nsIContent *aListNode)
-{
-  NS_ASSERTION(aListNode, "Called GetFocusedOptionNode without a valid list node");
-
-  nsIFrame *frame = aListNode->GetPrimaryFrame();
-  if (!frame)
-    return nsnull;
-
-  PRInt32 focusedOptionIndex = 0;
-
-  // Get options
-  nsCOMPtr<nsIDOMHTMLSelectElement> selectElement(do_QueryInterface(aListNode));
-  NS_ASSERTION(selectElement, "No select element where it should be");
+// nsHTMLSelectOptionAccessible: Widgets
 
-  nsCOMPtr<nsIDOMHTMLOptionsCollection> options;
-  nsresult rv = selectElement->GetOptions(getter_AddRefs(options));
-  
-  if (NS_SUCCEEDED(rv)) {
-    nsIListControlFrame *listFrame = do_QueryFrame(frame);
-    if (listFrame) {
-      // Get what's focused in listbox by asking frame for "selected item". 
-      // Can't use dom interface for this, because it will always return the first selected item
-      // when there is more than 1 item selected. We need the focused item, not
-      // the first selected item.
-      focusedOptionIndex = listFrame->GetSelectedIndex();
-      if (focusedOptionIndex == -1) {
-        nsCOMPtr<nsIDOMNode> nextOption;
-        while (PR_TRUE) {
-          ++ focusedOptionIndex;
-          options->Item(focusedOptionIndex, getter_AddRefs(nextOption));
-          nsCOMPtr<nsIDOMHTMLOptionElement> optionElement = do_QueryInterface(nextOption);
-          if (!optionElement) {
-            break;
-          }
-          PRBool disabled;
-          optionElement->GetDisabled(&disabled);
-          if (!disabled) {
-            break;
-          }
-        }
-      }
-    }
-    else  // Combo boxes can only have 1 selected option, so they can use the dom interface for this
-      rv = selectElement->GetSelectedIndex(&focusedOptionIndex);
+nsAccessible*
+nsHTMLSelectOptionAccessible::ContainerWidget() const
+{
+  if (mParent && mParent->IsListControl()) {
+    nsAccessible* grandParent = mParent->Parent();
+    if (grandParent && grandParent->IsCombobox())
+      return grandParent;
+
+    return mParent;
   }
-
-  // Either use options and focused index, or default return null
-  if (NS_SUCCEEDED(rv) && options && focusedOptionIndex >= 0) {  // Something is focused
-    nsCOMPtr<nsIDOMNode> focusedOptionNode;
-    options->Item(focusedOptionIndex, getter_AddRefs(focusedOptionNode));
-    nsIContent *focusedOption = nsnull;
-    if (focusedOptionNode)
-      CallQueryInterface(focusedOptionNode, &focusedOption);
-    return focusedOption;
-  }
-
   return nsnull;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLSelectOptionAccessible: static methods
+
 void
 nsHTMLSelectOptionAccessible::SelectionChangedIfOption(nsIContent *aPossibleOptionNode)
 {
   if (!aPossibleOptionNode ||
       aPossibleOptionNode->Tag() != nsGkAtoms::option ||
       !aPossibleOptionNode->IsHTML()) {
     return;
   }
@@ -639,16 +555,17 @@ nsHTMLSelectOptGroupAccessible::CacheChi
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLComboboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLComboboxAccessible::
   nsHTMLComboboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
+  mFlags |= eComboboxAccessible;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLComboboxAccessible: nsAccessible
 
 PRUint32
 nsHTMLComboboxAccessible::NativeRole()
 {
@@ -713,97 +630,64 @@ nsHTMLComboboxAccessible::NativeState()
 {
   // As a nsHTMLComboboxAccessible we can have the following states:
   // FOCUSED, FOCUSABLE, HASPOPUP, EXPANDED, COLLAPSED
   // Get focus status from base class
   PRUint64 state = nsAccessible::NativeState();
 
   nsIFrame *frame = GetBoundsFrame();
   nsIComboboxControlFrame *comboFrame = do_QueryFrame(frame);
-  if (comboFrame && comboFrame->IsDroppedDown()) {
+  if (comboFrame && comboFrame->IsDroppedDown())
     state |= states::EXPANDED;
-  }
-  else {
-    state &= ~states::FOCUSED; // Focus is on an option
+  else
     state |= states::COLLAPSED;
-  }
 
-  state |= states::HASPOPUP | states::FOCUSABLE;
-
+  state |= states::HASPOPUP;
   return state;
 }
 
 void
 nsHTMLComboboxAccessible::Description(nsString& aDescription)
 {
   aDescription.Truncate();
   // First check to see if combo box itself has a description, perhaps through
   // tooltip (title attribute) or via aria-describedby
   nsAccessible::Description(aDescription);
   if (!aDescription.IsEmpty())
     return;
-  // Use description of currently focused option
-  nsAccessible *option = GetFocusedOptionAccessible();
+
+  // Otherwise use description of selected option.
+  nsAccessible* option = SelectedOption();
   if (option)
     option->Description(aDescription);
 }
 
-nsAccessible *
-nsHTMLComboboxAccessible::GetFocusedOptionAccessible()
-{
-  if (IsDefunct())
-    return nsnull;
-
-  nsCOMPtr<nsIContent> focusedOption =
-    nsHTMLSelectOptionAccessible::GetFocusedOption(mContent);
-  if (!focusedOption) {
-    return nsnull;
-  }
-
-  return GetAccService()->GetAccessibleInWeakShell(focusedOption,
-                                                   mWeakShell);
-}
-
-/**
-  * MSAA/ATK accessible value != HTML value, especially not in combo boxes.
-  * Our accessible value is the text label for of our ( first ) selected child.
-  * The easiest way to get this is from the first child which is the readonly textfield.
-  */
 NS_IMETHODIMP nsHTMLComboboxAccessible::GetValue(nsAString& aValue)
 {
-  // Use accessible name of currently focused option.
-  nsAccessible *option = GetFocusedOptionAccessible();
+  // Use accessible name of selected option.
+  nsAccessible* option = SelectedOption();
   return option ? option->GetName(aValue) : NS_OK;
 }
 
 PRUint8
 nsHTMLComboboxAccessible::ActionCount()
 {
   return 1;
 }
 
-/**
-  * Programmaticaly toggle the combo box
-  */
-NS_IMETHODIMP nsHTMLComboboxAccessible::DoAction(PRUint8 aIndex)
+NS_IMETHODIMP
+nsHTMLComboboxAccessible::DoAction(PRUint8 aIndex)
 {
-  if (aIndex != nsHTMLComboboxAccessible::eAction_Click) {
+  if (aIndex != eAction_Click)
     return NS_ERROR_INVALID_ARG;
-  }
-  nsIFrame *frame = GetFrame();
-  if (!frame) {
+
+  if (IsDefunct())
     return NS_ERROR_FAILURE;
-  }
-  nsIComboboxControlFrame *comboFrame = do_QueryFrame(frame);
-  if (!comboFrame) {
-    return NS_ERROR_FAILURE;
-  }
-  // Reverse whether it's dropped down or not
-  comboFrame->ShowDropDown(!comboFrame->IsDroppedDown());
 
+  DoCommand();
   return NS_OK;
 }
 
 /**
   * Our action name is the reverse of our state: 
   *     if we are closed -> open is our name.
   *     if we are open -> closed is our name.
   * Uses the frame to get the state, updated on every click
@@ -824,16 +708,73 @@ NS_IMETHODIMP nsHTMLComboboxAccessible::
   if (comboFrame->IsDroppedDown())
     aName.AssignLiteral("close"); 
   else
     aName.AssignLiteral("open"); 
 
   return NS_OK;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLComboboxAccessible: Widgets
+
+bool
+nsHTMLComboboxAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsHTMLComboboxAccessible::IsActiveWidget() const
+{
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsHTMLComboboxAccessible::AreItemsOperable() const
+{
+  nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(GetFrame());
+  return comboboxFrame && comboboxFrame->IsDroppedDown();
+}
+
+nsAccessible*
+nsHTMLComboboxAccessible::CurrentItem()
+{
+  // No current item for collapsed combobox.
+  return SelectedOption(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLComboboxAccessible: protected
+
+nsAccessible*
+nsHTMLComboboxAccessible::SelectedOption(bool aIgnoreIfCollapsed) const
+{
+  nsIFrame* frame = GetFrame();
+  nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
+  if (comboboxFrame) {
+    if (aIgnoreIfCollapsed && !comboboxFrame->IsDroppedDown())
+      return nsnull;
+
+    frame = comboboxFrame->GetDropDown();
+  }
+
+  nsIListControlFrame* listControlFrame = do_QueryFrame(frame);
+  if (listControlFrame) {
+    nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
+    if (activeOptionNode) {
+      nsDocAccessible* document = GetDocAccessible();
+      if (document)
+        return document->GetAccessible(activeOptionNode);
+    }
+  }
+
+  return nsnull;
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLComboboxListAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLComboboxListAccessible::
   nsHTMLComboboxListAccessible(nsIAccessible *aParent, nsIContent *aContent,
                                nsIWeakReference *aShell) :
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -75,16 +75,22 @@ public:
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
   // SelectAccessible
   virtual bool IsSelect();
   virtual bool SelectAll();
   virtual bool UnselectAll();
 
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+  virtual nsAccessible* CurrentItem();
+
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
 
   // nsHTMLSelectListAccessible
 
   /**
@@ -116,20 +122,18 @@ public:
 
   virtual PRInt32 GetLevelInternal();
   virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                           PRInt32 *aSetSize);
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
-  /**
-   * Return focused option if any.
-   */
-  static already_AddRefed<nsIContent> GetFocusedOption(nsIContent *aListNode);
+  // Widgets
+  virtual nsAccessible* ContainerWidget() const;
 
   static void SelectionChangedIfOption(nsIContent *aPossibleOption);
 
 protected:
   // nsAccessible
   virtual nsIFrame* GetBoundsFrame();
 
 private:
@@ -197,26 +201,30 @@ public:
   virtual void Description(nsString& aDescription);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual void InvalidateChildren();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+  virtual nsAccessible* CurrentItem();
+
 protected:
   // nsAccessible
   virtual void CacheChildren();
 
-  // nsHTMLComboboxAccessible
-
   /**
-   * Return focused option accessible.
+   * Return selected option.
    */
-  nsAccessible *GetFocusedOptionAccessible();
+  nsAccessible* SelectedOption(bool aIgnoreIfCollapsed = false) const;
 
 private:
   nsRefPtr<nsHTMLComboboxListAccessible> mListAccessible;
 };
 
 /*
  * A class that represents the window that lives to the right
  * of the drop down button inside the Select. This is the window
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -1237,17 +1237,17 @@ nsHyperTextAccessible::GetAttributesInte
   // use 'display' attribute instead.
   nsIFrame *frame = GetFrame();
   if (frame && frame->GetType() == nsGkAtoms::blockFrame) {
     nsAutoString oldValueUnused;
     aAttributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"), NS_LITERAL_STRING("block"),
                                    oldValueUnused);
   }
 
-  if (gLastFocusedNode == GetNode()) {
+  if (FocusMgr()->IsFocused(this)) {
     PRInt32 lineNumber = GetCaretLineNumber();
     if (lineNumber >= 1) {
       nsAutoString strLineNumber;
       strLineNumber.AppendInt(lineNumber);
       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::lineNumber,
                              strLineNumber);
     }
   }
@@ -1623,23 +1623,19 @@ nsHyperTextAccessible::SetCaretOffset(PR
  */
 NS_IMETHODIMP
 nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
 {
   *aCaretOffset = -1;
 
   // No caret if the focused node is not inside this DOM node and this DOM node
   // is not inside of focused node.
-
-  nsINode* thisNode = GetNode();
-  PRBool isInsideOfFocusedNode =
-    nsCoreUtils::IsAncestorOf(gLastFocusedNode, thisNode);
-
-  if (!isInsideOfFocusedNode && thisNode != gLastFocusedNode &&
-      !nsCoreUtils::IsAncestorOf(thisNode, gLastFocusedNode))
+  FocusManager::FocusDisposition focusDisp =
+    FocusMgr()->IsInOrContainsFocus(this);
+  if (focusDisp == FocusManager::eNone)
     return NS_OK;
 
   // Turn the focus node and offset of the selection into caret hypretext
   // offset.
   nsCOMPtr<nsISelection> domSel;
   nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
                               nsnull, getter_AddRefs(domSel));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1650,20 +1646,21 @@ nsHyperTextAccessible::GetCaretOffset(PR
 
   PRInt32 focusOffset;
   rv = domSel->GetFocusOffset(&focusOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // No caret if this DOM node is inside of focused node but the selection's
   // focus point is not inside of this DOM node.
   nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
-  if (isInsideOfFocusedNode) {
+  if (focusDisp == FocusManager::eContainedByFocus) {
     nsINode *resultNode =
       nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
 
+    nsINode* thisNode = GetNode();
     if (resultNode != thisNode &&
         !nsCoreUtils::IsAncestorOf(thisNode, resultNode))
       return NS_OK;
   }
 
   DOMPointToHypertextOffset(focusNode, focusOffset, aCaretOffset);
   return NS_OK;
 }
--- a/accessible/src/mac/mozTextAccessible.mm
+++ b/accessible/src/mac/mozTextAccessible.mm
@@ -77,17 +77,21 @@ extern const NSString *kTopLevelUIElemen
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
     return [NSNumber numberWithInt:[self textLength]];
   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
     return [self selectedTextRange];
   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
     return [self selectedText];
-  
+  // Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText
+  // object's AXSelectedText attribute.  See bug 674612.
+  if ([attribute isEqualToString:NSAccessibilityValueAttribute])
+    return [self selectedText];
+
   // let mozAccessible handle all other attributes
   return [super accessibilityAttributeValue:attribute];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
 {
--- a/accessible/src/xul/Makefile.in
+++ b/accessible/src/xul/Makefile.in
@@ -67,9 +67,11 @@ CPPSRCS = \
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES = \
   -I$(srcdir) \
   -I$(srcdir)/../base \
   -I$(srcdir)/../html \
+  -I$(srcdir)/../../../layout/generic \
+  -I$(srcdir)/../../../layout/xul/base/src \
   $(NULL)
--- a/accessible/src/xul/nsXULAlertAccessible.cpp
+++ b/accessible/src/xul/nsXULAlertAccessible.cpp
@@ -68,8 +68,26 @@ nsXULAlertAccessible::NativeState()
 NS_IMETHODIMP
 nsXULAlertAccessible::GetName(nsAString& aName)
 {
   // Screen readers need to read contents of alert, not the accessible name.
   // If we have both some screen readers will read the alert twice.
   aName.Truncate();
   return NS_OK;
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Widgets
+
+bool
+nsXULAlertAccessible::IsWidget() const
+{
+  return true;
+}
+
+nsAccessible*
+nsXULAlertAccessible::ContainerWidget() const
+{
+  // If a part of colorpicker widget.
+  if (mParent && mParent->IsMenuButton())
+    return mParent;
+  return nsnull;
+}
--- a/accessible/src/xul/nsXULAlertAccessible.h
+++ b/accessible/src/xul/nsXULAlertAccessible.h
@@ -52,11 +52,15 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetName(nsAString& aName);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual nsAccessible* ContainerWidget() const;
 };
 
-#endif  
+#endif
--- a/accessible/src/xul/nsXULColorPickerAccessible.cpp
+++ b/accessible/src/xul/nsXULColorPickerAccessible.cpp
@@ -40,16 +40,17 @@
 
 #include "States.h"
 #include "nsAccUtils.h"
 #include "nsAccTreeWalker.h"
 #include "nsCoreUtils.h"
 #include "nsDocAccessible.h"
 
 #include "nsIDOMElement.h"
+#include "nsMenuPopupFrame.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerTileAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULColorPickerTileAccessible::
@@ -80,46 +81,50 @@ PRUint32
 nsXULColorPickerTileAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_PUSHBUTTON;
 }
 
 PRUint64
 nsXULColorPickerTileAccessible::NativeState()
 {
-  // Possible states: focused, focusable, selected.
-
-  // get focus and disable status from base class
-  PRUint64 states = nsAccessibleWrap::NativeState();
-
-  states |= states::FOCUSABLE;
+  PRUint64 state = nsAccessibleWrap::NativeState();
+  if (!(state & states::UNAVAILABLE))
+    state |= states::FOCUSABLE | states::SELECTABLE;
 
-  // Focused?
-  PRBool isFocused = mContent->HasAttr(kNameSpaceID_None,
-                                       nsGkAtoms::hover);
-  if (isFocused)
-    states |= states::FOCUSED;
+  if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::selected))
+    state |= states::SELECTED;
 
-  PRBool isSelected = mContent->HasAttr(kNameSpaceID_None,
-                                        nsGkAtoms::selected);
-  if (isSelected)
-    states |= states::SELECTED;
-
-  return states;
+  return state;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULColorPickerTileAccessible: Widgets
+
+nsAccessible*
+nsXULColorPickerTileAccessible::ContainerWidget() const
+{
+  nsAccessible* parent = Parent();
+  if (parent) {
+    nsAccessible* grandParent = parent->Parent();
+    if (grandParent && grandParent->IsMenuButton())
+      return grandParent;
+  }
+  return nsnull;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULColorPickerAccessible::
   nsXULColorPickerAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsXULColorPickerTileAccessible(aContent, aShell)
 {
+  mFlags |= eMenuButtonAccessible;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerAccessible: nsAccessible
 
 PRUint64
 nsXULColorPickerAccessible::NativeState()
 {
@@ -135,16 +140,42 @@ nsXULColorPickerAccessible::NativeState(
 
 PRUint32
 nsXULColorPickerAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_BUTTONDROPDOWNGRID;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsXULColorPickerAccessible: Widgets
+
+bool
+nsXULColorPickerAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsXULColorPickerAccessible::IsActiveWidget() const
+{
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsXULColorPickerAccessible::AreItemsOperable() const
+{
+  nsAccessible* menuPopup = mChildren.SafeElementAt(0, nsnull);
+  if (menuPopup) {
+    nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
+    return menuPopupFrame && menuPopupFrame->IsOpen();
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerAccessible: protected nsAccessible
 
 void
 nsXULColorPickerAccessible::CacheChildren()
 {
   nsAccTreeWalker walker(mWeakShell, mContent, PR_TRUE);
 
   nsAccessible* child = nsnull;
--- a/accessible/src/xul/nsXULColorPickerAccessible.h
+++ b/accessible/src/xul/nsXULColorPickerAccessible.h
@@ -34,17 +34,16 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsXULColorPickerAccessible_H_
 #define _nsXULColorPickerAccessible_H_
 
-// NOTE: alphabetically ordered
 #include "nsAccessibleWrap.h"
 
 /**
  * Used for color button in colorpicker palette.
  */
 class nsXULColorPickerTileAccessible : public nsAccessibleWrap
 {
 public:
@@ -52,30 +51,38 @@ public:
                                  nsIWeakReference *aShell);
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& _retval);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // Widgets
+  virtual nsAccessible* ContainerWidget() const;
 };
 
 
 /**
  * Used for colorpicker button (xul:colorpicker@type="button").
  */
 class nsXULColorPickerAccessible : public nsXULColorPickerTileAccessible
 {
 public:
   nsXULColorPickerAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
 };
 
 #endif  
--- a/accessible/src/xul/nsXULComboboxAccessible.cpp
+++ b/accessible/src/xul/nsXULComboboxAccessible.cpp
@@ -39,36 +39,41 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXULComboboxAccessible.h"
 
 #include "States.h"
 #include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 
+#include "nsIAutoCompleteInput.h"
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULComboboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULComboboxAccessible::
   nsXULComboboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
+  if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                            nsGkAtoms::autocomplete, eIgnoreCase))
+    mFlags |= eAutoCompleteAccessible;
+  else
+    mFlags |= eComboboxAccessible;
 }
 
 PRUint32
 nsXULComboboxAccessible::NativeRole()
 {
-  if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                            nsGkAtoms::autocomplete, eIgnoreCase))
+  if (IsAutoComplete())
     return nsIAccessibleRole::ROLE_AUTOCOMPLETE;
   return nsIAccessibleRole::ROLE_COMBOBOX;
 }
 
 PRUint64
 nsXULComboboxAccessible::NativeState()
 {
   // As a nsComboboxAccessible we can have the following states:
@@ -201,8 +206,53 @@ nsXULComboboxAccessible::GetActionName(P
   menuList->GetOpen(&isDroppedDown);
   if (isDroppedDown)
     aName.AssignLiteral("close"); 
   else
     aName.AssignLiteral("open"); 
 
   return NS_OK;
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Widgets
+
+bool
+nsXULComboboxAccessible::IsActiveWidget() const
+{
+  if (IsAutoComplete() ||
+     mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
+                           nsGkAtoms::_true, eIgnoreCase)) {
+    PRInt32 childCount = mChildren.Length();
+    for (PRInt32 idx = 0; idx < childCount; idx++) {
+      nsAccessible* child = mChildren[idx];
+      if (child->Role() == nsIAccessibleRole::ROLE_ENTRY)
+        return FocusMgr()->HasDOMFocus(child->GetContent());
+    }
+    return false;
+  }
+
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsXULComboboxAccessible::AreItemsOperable() const
+{
+  if (IsAutoComplete()) {
+    nsCOMPtr<nsIAutoCompleteInput> autoCompleteInputElm =
+      do_QueryInterface(mContent);
+    if (autoCompleteInputElm) {
+      PRBool isOpen = PR_FALSE;
+      autoCompleteInputElm->GetPopupOpen(&isOpen);
+      return isOpen;
+    }
+    return false;
+  }
+
+  nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = do_QueryInterface(mContent);
+  if (menuListElm) {
+    PRBool isOpen = PR_FALSE;
+    menuListElm->GetOpen(&isOpen);
+    return isOpen;
+  }
+
+  return false;
+}
--- a/accessible/src/xul/nsXULComboboxAccessible.h
+++ b/accessible/src/xul/nsXULComboboxAccessible.h
@@ -35,17 +35,16 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __nsXULComboboxAccessible_h__
 #define __nsXULComboboxAccessible_h__
 
-#include "nsCOMPtr.h"
 #include "nsXULMenuAccessible.h"
 
 /**
  * Used for XUL comboboxes like xul:menulist and autocomplete textbox.
  */
 class nsXULComboboxAccessible : public nsAccessibleWrap
 {
 public:
@@ -61,11 +60,15 @@ public:
   // nsAccessible
   virtual void Description(nsString& aDescription);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual PRBool GetAllowsAnonChildAccessibles();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
+
+  // Widgets
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
 };
 
 #endif
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -56,27 +56,30 @@
 #include "nsIDOMXULCheckboxElement.h"
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULTextboxElement.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
 #include "nsINameSpaceManager.h"
 #include "nsITextControlFrame.h"
+#include "nsMenuPopupFrame.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULButtonAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULButtonAccessible::
   nsXULButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
+  if (ContainsMenu())
+    mFlags |= eMenuButtonAccessible;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULButtonAccessible: nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED0(nsXULButtonAccessible, nsAccessible)
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -160,16 +163,52 @@ nsXULButtonAccessible::NativeState()
 
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_default))
     state |= states::DEFAULT;
 
   return state;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsXULButtonAccessible: Widgets
+
+bool
+nsXULButtonAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsXULButtonAccessible::IsActiveWidget() const
+{
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsXULButtonAccessible::AreItemsOperable() const
+{
+  if (IsMenuButton()) {
+    nsAccessible* menuPopup = mChildren.SafeElementAt(0, nsnull);
+    if (menuPopup) {
+      nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
+      return menuPopupFrame->IsOpen();
+    }
+  }
+  return false; // no items
+}
+
+nsAccessible*
+nsXULButtonAccessible::ContainerWidget() const
+{
+  if (IsMenuButton() && mParent && mParent->IsAutoComplete())
+    return mParent;
+  return nsnull;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsXULButtonAccessible: nsAccessible protected
 
 void
 nsXULButtonAccessible::CacheChildren()
 {
   // In general XUL button has not accessible children. Nevertheless menu
   // buttons can have button (@type="menu-button") and popup accessibles
   // (@type="menu-button" or @type="menu").
@@ -459,29 +498,29 @@ nsXULGroupboxAccessible::RelationByType(
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULRadioButtonAccessible::
   nsXULRadioButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsRadioButtonAccessible(aContent, aShell)
 {
 }
 
-/** We are Focusable and can be Checked and focused */
 PRUint64
 nsXULRadioButtonAccessible::NativeState()
 {
   PRUint64 state = nsFormControlAccessible::NativeState();
+  state |= states::CHECKABLE;
 
-  state |= states::CHECKABLE;
-  
-  PRBool selected = PR_FALSE;   // Radio buttons can be selected
+  if (!(state & states::UNAVAILABLE))
+    state |= states::FOCUSABLE;
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
     do_QueryInterface(mContent);
   if (radioButton) {
+    PRBool selected = PR_FALSE;   // Radio buttons can be selected
     radioButton->GetSelected(&selected);
     if (selected) {
       state |= states::CHECKED;
     }
   }
 
   return state;
 }
@@ -489,16 +528,25 @@ nsXULRadioButtonAccessible::NativeState(
 void
 nsXULRadioButtonAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                                        PRInt32 *aSetSize)
 {
   nsAccUtils::GetPositionAndSizeForXULSelectControlItem(mContent, aPosInSet,
                                                         aSetSize);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULRadioButtonAccessible: Widgets
+
+nsAccessible*
+nsXULRadioButtonAccessible::ContainerWidget() const
+{
+  return mParent;
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULRadioGroupAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
   * XUL Radio Group
   *   The Radio Group proxies for the Radio Buttons themselves. The Group gets
@@ -524,17 +572,38 @@ PRUint64
 nsXULRadioGroupAccessible::NativeState()
 {
   // 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 nsAccessible::NativeState() & ~(states::FOCUSABLE | states::FOCUSED);
 }
 
-                      
+////////////////////////////////////////////////////////////////////////////////
+// nsXULRadioGroupAccessible: Widgets
+
+bool
+nsXULRadioGroupAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsXULRadioGroupAccessible::IsActiveWidget() const
+{
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsXULRadioGroupAccessible::AreItemsOperable() const
+{
+  return true;
+}
+
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULStatusBarAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULStatusBarAccessible::
   nsXULStatusBarAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
@@ -714,19 +783,16 @@ nsXULTextFieldAccessible::NativeState()
   // the accessible state from. Doesn't add to cache into document cache.
   nsRefPtr<nsHTMLTextFieldAccessible> tempAccessible =
     new nsHTMLTextFieldAccessible(inputField, mWeakShell);
   if (!tempAccessible)
     return state;
 
   state |= tempAccessible->NativeState();
 
-  if (gLastFocusedNode == mContent)
-    state |= states::FOCUSED;
-
   nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
   if (menuList) {
     // <xul:menulist droppable="false">
     if (!mContent->AttrValueIs(kNameSpaceID_None,
                                nsGkAtoms::editable,
                                nsGkAtoms::_true, eIgnoreCase)) {
       state |= states::READONLY;
     }
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -72,16 +72,22 @@ public:
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+  virtual nsAccessible* ContainerWidget() const;
+
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
 
   // nsXULButtonAccessible
   PRBool ContainsMenu();
 };
@@ -154,29 +160,37 @@ class nsXULRadioButtonAccessible : publi
 
 public:
   nsXULRadioButtonAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsAccessible
   virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                           PRInt32 *aSetSize);
   virtual PRUint64 NativeState();
+
+  // Widgets
+  virtual nsAccessible* ContainerWidget() const;
 };
 
 /**
  * Used for XUL radiogroup element.
  */
 class nsXULRadioGroupAccessible : public nsXULSelectableAccessible
 {
 public:
   nsXULRadioGroupAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
 };
 
 /**
  * Used for XUL statusbar element.
  */
 class nsXULStatusBarAccessible : public nsAccessibleWrap
 {
 public:
--- a/accessible/src/xul/nsXULListboxAccessible.cpp
+++ b/accessible/src/xul/nsXULListboxAccessible.cpp
@@ -39,21 +39,24 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXULListboxAccessible.h"
 
 #include "States.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 
+#include "nsComponentManagerUtils.h"
+#include "nsIAutoCompleteInput.h"
+#include "nsIAutoCompletePopup.h"
+#include "nsIDOMXULMenuListElement.h"
+#include "nsIDOMXULMultSelectCntrlEl.h"
+#include "nsIDOMNodeList.h"
 #include "nsIDOMXULPopupElement.h"
-#include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
-#include "nsIDOMNodeList.h"
-#include "nsComponentManagerUtils.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColumnsAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULColumnsAccessible::
@@ -126,16 +129,23 @@ nsXULColumnItemAccessible::DoAction(PRUi
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULListboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULListboxAccessible::
   nsXULListboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsXULSelectableAccessible(aContent, aShell)
 {
+  nsIContent* parentContent = mContent->GetParent();
+  if (parentContent) {
+    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+      do_QueryInterface(parentContent);
+    if (autoCompletePopupElm)
+      mFlags |= eAutoCompletePopupAccessible;
+  }
 }
 
 NS_IMPL_ADDREF_INHERITED(nsXULListboxAccessible, nsXULSelectableAccessible)
 NS_IMPL_RELEASE_INHERITED(nsXULListboxAccessible, nsXULSelectableAccessible)
 
 nsresult
 nsXULListboxAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
@@ -821,16 +831,82 @@ nsXULListboxAccessible::IsProbablyForLay
 {
   NS_ENSURE_ARG_POINTER(aIsProbablyForLayout);
   *aIsProbablyForLayout = PR_FALSE;
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsXULListboxAccessible: Widgets
+
+bool
+nsXULListboxAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsXULListboxAccessible::IsActiveWidget() const
+{
+  if (IsAutoCompletePopup()) {
+    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+      do_QueryInterface(mContent->GetParent());
+
+    if (autoCompletePopupElm) {
+      PRBool isOpen = PR_FALSE;
+      autoCompletePopupElm->GetPopupOpen(&isOpen);
+      return isOpen;
+    }
+  }
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsXULListboxAccessible::AreItemsOperable() const
+{
+  if (IsAutoCompletePopup()) {
+    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+      do_QueryInterface(mContent->GetParent());
+
+    if (autoCompletePopupElm) {
+      PRBool isOpen = PR_FALSE;
+      autoCompletePopupElm->GetPopupOpen(&isOpen);
+      return isOpen;
+    }
+  }
+  return true;
+}
+
+nsAccessible*
+nsXULListboxAccessible::ContainerWidget() const
+{
+  if (IsAutoCompletePopup()) {
+    // This works for XUL autocompletes. It doesn't work for HTML forms
+    // autocomplete because of potential crossprocess calls (when autocomplete
+    // lives in content process while popup lives in chrome process). If that's
+    // a problem then rethink Widgets interface.
+    nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
+      do_QueryInterface(mContent->GetParent());
+    if (menuListElm) {
+      nsCOMPtr<nsIDOMNode> inputElm;
+      menuListElm->GetInputField(getter_AddRefs(inputElm));
+      if (inputElm) {
+        nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
+        if (inputNode) {
+          nsAccessible* input = GetAccService()->GetAccessible(inputNode);
+          return input ? input->ContainerWidget() : nsnull;
+        }
+      }
+    }
+  }
+  return nsnull;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsXULListitemAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULListitemAccessible::
   nsXULListitemAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsXULMenuitemAccessible(aContent, aShell)
 {
   mIsCheckbox = mContent->AttrValueIs(kNameSpaceID_None,
@@ -925,19 +1001,18 @@ nsXULListitemAccessible::NativeState()
     do_QueryInterface(mContent);
 
   if (listItem) {
     PRBool isSelected;
     listItem->GetSelected(&isSelected);
     if (isSelected)
       states |= states::SELECTED;
 
-    if (gLastFocusedNode == mContent)
+    if (FocusMgr()->IsFocused(this))
       states |= states::FOCUSED;
-
   }
 
   return states;
 }
 
 NS_IMETHODIMP nsXULListitemAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
 {
   if (aIndex == eAction_Click && mIsCheckbox) {
@@ -964,16 +1039,25 @@ nsXULListitemAccessible::GetAllowsAnonCh
 void
 nsXULListitemAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                                     PRInt32 *aSetSize)
 {
   nsAccUtils::GetPositionAndSizeForXULSelectControlItem(mContent, aPosInSet,
                                                         aSetSize);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULListitemAccessible: Widgets
+
+nsAccessible*
+nsXULListitemAccessible::ContainerWidget() const
+{
+  return Parent();
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULListCellAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULListCellAccessible::
   nsXULListCellAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsHyperTextAccessibleWrap(aContent, aShell)
--- a/accessible/src/xul/nsXULListboxAccessible.h
+++ b/accessible/src/xul/nsXULListboxAccessible.h
@@ -100,16 +100,23 @@ public:
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
 
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+
+  virtual nsAccessible* ContainerWidget() const;
+
 protected:
   PRBool IsMulticolumn();
 };
 
 /**
   * Listitems -- used in listboxes 
   */
 class nsXULListitemAccessible : public nsXULMenuitemAccessible
@@ -130,16 +137,19 @@ public:
   virtual void Description(nsString& aDesc);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual void GetPositionAndSizeInternal(PRInt32 *aPosInSet,
                                           PRInt32 *aSetSize);
   virtual PRBool GetAllowsAnonChildAccessibles();
 
+  // Widgets
+  virtual nsAccessible* ContainerWidget() const;
+
 protected:
   /**
    * Return listbox accessible for the listitem.
    */
   nsAccessible *GetListAccessible();
 
 private:
   PRBool mIsCheckbox;
--- a/accessible/src/xul/nsXULMenuAccessible.cpp
+++ b/accessible/src/xul/nsXULMenuAccessible.cpp
@@ -35,30 +35,33 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXULMenuAccessible.h"
 
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
+#include "nsDocAccessible.h"
 #include "nsXULFormControlAccessible.h"
 #include "States.h"
 
 #include "nsIDOMElement.h"
 #include "nsIDOMXULElement.h"
 #include "nsIMutableArray.h"
 #include "nsIDOMXULContainerElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIServiceManager.h"
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsGUIEvent.h"
+#include "nsMenuBarFrame.h"
+#include "nsMenuPopupFrame.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
@@ -264,36 +267,61 @@ nsXULSelectableAccessible::SelectAll()
     multiSelectControl->SelectAll();
     return true;
   }
 
   // otherwise, don't support this method
   return false;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULSelectableAccessible: Widgets
+
+nsAccessible*
+nsXULSelectableAccessible::CurrentItem()
+{
+  if (!mSelectControl)
+    return nsnull;
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> currentItemElm;
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+  if (multiSelectControl)
+    multiSelectControl->GetCurrentItem(getter_AddRefs(currentItemElm));
+  else
+    mSelectControl->GetSelectedItem(getter_AddRefs(currentItemElm));
+
+  nsCOMPtr<nsINode> DOMNode;
+  if (currentItemElm)
+    DOMNode = do_QueryInterface(currentItemElm);
+
+  if (DOMNode) {
+    nsDocAccessible* document = GetDocAccessible();
+    if (document)
+      return document->GetAccessible(DOMNode);
+  }
+
+  return nsnull;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULMenuitemAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenuitemAccessible::
   nsXULMenuitemAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
 }
 
 PRUint64
 nsXULMenuitemAccessible::NativeState()
 {
   PRUint64 state = nsAccessible::NativeState();
 
-  // Focused?
-  if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::menuactive))
-    state |= states::FOCUSED;
-
   // Has Popup?
   if (mContent->NodeInfo()->Equals(nsGkAtoms::menu, kNameSpaceID_XUL)) {
     state |= states::HASPOPUP;
     if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::open))
       state |= states::EXPANDED;
     else
       state |= states::COLLAPSED;
   }
@@ -357,17 +385,20 @@ nsXULMenuitemAccessible::NativeState()
     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;
     }
   }
+
   state |= (states::FOCUSABLE | states::SELECTABLE);
+  if (FocusMgr()->IsFocused(this))
+    state |= states::FOCUSED;
 
   return state;
 }
 
 nsresult
 nsXULMenuitemAccessible::GetNameInternal(nsAString& aName)
 {
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
@@ -558,16 +589,66 @@ NS_IMETHODIMP nsXULMenuitemAccessible::G
 }
 
 PRUint8
 nsXULMenuitemAccessible::ActionCount()
 {
   return 1;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULMenuitemAccessible: Widgets
+
+bool
+nsXULMenuitemAccessible::IsActiveWidget() const
+{
+  // Parent menu item is a widget, it's active when its popup is open.
+  nsIContent* menuPopupContent = mContent->GetFirstChild();
+  if (menuPopupContent) {
+    nsMenuPopupFrame* menuPopupFrame =
+      do_QueryFrame(menuPopupContent->GetPrimaryFrame());
+    return menuPopupFrame && menuPopupFrame->IsOpen();
+  }
+  return false;
+}
+
+bool
+nsXULMenuitemAccessible::AreItemsOperable() const
+{
+  // Parent menu item is a widget, its items are operable when its popup is open.
+  nsIContent* menuPopupContent = mContent->GetFirstChild();
+  if (menuPopupContent) {
+    nsMenuPopupFrame* menuPopupFrame =
+      do_QueryFrame(menuPopupContent->GetPrimaryFrame());
+    return menuPopupFrame && menuPopupFrame->IsOpen();
+  }
+  return false;
+}
+
+nsAccessible*
+nsXULMenuitemAccessible::ContainerWidget() const
+{
+  nsMenuFrame* menuFrame = do_QueryFrame(GetFrame());
+  if (menuFrame) {
+    nsMenuParent* menuParent = menuFrame->GetMenuParent();
+    if (menuParent) {
+      if (menuParent->IsMenuBar()) // menubar menu
+        return mParent;
+
+      // a menupoup or parent menu item
+      if (menuParent->IsMenu())
+        return mParent;
+
+      // otherwise it's different kind of popups (like panel or tooltip), it
+      // shouldn't be a real case.
+    }
+  }
+  return nsnull;
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULMenuSeparatorAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenuSeparatorAccessible::
   nsXULMenuSeparatorAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsXULMenuitemAccessible(aContent, aShell)
@@ -612,17 +693,21 @@ nsXULMenuSeparatorAccessible::ActionCoun
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULMenupopupAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenupopupAccessible::
   nsXULMenupopupAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsXULSelectableAccessible(aContent, aShell)
-{ 
+{
+  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
+  if (menuPopupFrame && menuPopupFrame->IsMenu())
+    mFlags |= eMenuPopupAccessible;
+
   // May be the anonymous <menupopup> inside <menulist> (a combobox)
   mSelectControl = do_QueryInterface(mContent->GetParent());
 }
 
 PRUint64
 nsXULMenupopupAccessible::NativeState()
 {
   PRUint64 state = nsAccessible::NativeState();
@@ -684,16 +769,75 @@ nsXULMenupopupAccessible::NativeRole()
           grandParent->Role() == nsIAccessibleRole::ROLE_AUTOCOMPLETE)
         return nsIAccessibleRole::ROLE_COMBOBOX_LIST;
     }
   }
 
   return nsIAccessibleRole::ROLE_MENUPOPUP;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULMenupopupAccessible: Widgets
+
+bool
+nsXULMenupopupAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsXULMenupopupAccessible::IsActiveWidget() const
+{
+  // If menupopup is a widget (the case of context menus) then active when open.
+  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
+  return menuPopupFrame && menuPopupFrame->IsOpen();
+}
+
+bool
+nsXULMenupopupAccessible::AreItemsOperable() const
+{
+  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
+  return menuPopupFrame && menuPopupFrame->IsOpen();
+}
+
+nsAccessible*
+nsXULMenupopupAccessible::ContainerWidget() const
+{
+  nsDocAccessible* document = GetDocAccessible();
+
+  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
+  while (menuPopupFrame) {
+    nsAccessible* menuPopup =
+      document->GetAccessible(menuPopupFrame->GetContent());
+    if (!menuPopup) // shouldn't be a real case
+      return nsnull;
+
+    nsMenuFrame* menuFrame = menuPopupFrame->GetParentMenu();
+    if (!menuFrame) // context menu or popups
+      return nsnull;
+
+    nsMenuParent* menuParent = menuFrame->GetMenuParent();
+    if (!menuParent) // menulist or menubutton
+      return menuPopup->Parent();
+
+    if (menuParent->IsMenuBar()) { // menubar menu
+      nsMenuBarFrame* menuBarFrame = static_cast<nsMenuBarFrame*>(menuParent);
+      return document->GetAccessible(menuBarFrame->GetContent());
+    }
+
+    // different kind of popups like panel or tooltip
+    if (!menuParent->IsMenu())
+      return nsnull;
+
+    menuPopupFrame = static_cast<nsMenuPopupFrame*>(menuParent);
+  }
+
+  NS_NOTREACHED("Shouldn't be a real case.");
+  return nsnull;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULMenubarAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenubarAccessible::
   nsXULMenubarAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
@@ -719,8 +863,37 @@ nsXULMenubarAccessible::GetNameInternal(
 }
 
 PRUint32
 nsXULMenubarAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_MENUBAR;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULMenubarAccessible: Widgets
+
+bool
+nsXULMenubarAccessible::IsActiveWidget() const
+{
+  nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
+  return menuBarFrame && menuBarFrame->IsActive();
+}
+
+bool
+nsXULMenubarAccessible::AreItemsOperable() const
+{
+  return true;
+}
+
+nsAccessible*
+nsXULMenubarAccessible::CurrentItem()
+{
+  nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
+  if (menuBarFrame) {
+    nsMenuFrame* menuFrame = menuBarFrame->GetCurrentMenuItem();
+    if (menuFrame) {
+      nsIContent* menuItemNode = menuFrame->GetContent();
+      return GetAccService()->GetAccessible(menuItemNode);
+    }
+  }
+  return nsnull;
+}
--- a/accessible/src/xul/nsXULMenuAccessible.h
+++ b/accessible/src/xul/nsXULMenuAccessible.h
@@ -60,16 +60,19 @@ public:
   virtual PRUint32 SelectedItemCount();
   virtual nsAccessible* GetSelectedItem(PRUint32 aIndex);
   virtual bool IsItemSelected(PRUint32 aIndex);
   virtual bool AddItemToSelection(PRUint32 aIndex);
   virtual bool RemoveItemFromSelection(PRUint32 aIndex);
   virtual bool SelectAll();
   virtual bool UnselectAll();
 
+  // Widgets
+  virtual nsAccessible* CurrentItem();
+
 protected:
   // nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have
   // one of these if the widget is valid and not defunct
   nsCOMPtr<nsIDOMXULSelectControlElement> mSelectControl;
 };
 
 /**
  * Used for XUL menu, menuitem elements.
@@ -95,16 +98,21 @@ public:
                                           PRInt32 *aSetSize);
 
   virtual PRBool GetAllowsAnonChildAccessibles();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
   virtual KeyBinding KeyboardShortcut() const;
+
+  // Widgets
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+  virtual nsAccessible* ContainerWidget() const;
 };
 
 /**
  * Used for XUL menuseparator element.
  */
 class nsXULMenuSeparatorAccessible : public nsXULMenuitemAccessible
 {
 public:
@@ -131,25 +139,37 @@ class nsXULMenupopupAccessible : public 
 {
 public:
   nsXULMenupopupAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+
+  virtual nsAccessible* ContainerWidget() const;
 };
 
 /**
  * Used for XUL menubar element.
  */
 class nsXULMenubarAccessible : public nsAccessibleWrap
 {
 public:
   nsXULMenubarAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
+
+  // Widget
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+  virtual nsAccessible* CurrentItem();
 };
 
 #endif  
--- a/accessible/src/xul/nsXULSliderAccessible.cpp
+++ b/accessible/src/xul/nsXULSliderAccessible.cpp
@@ -33,16 +33,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXULSliderAccessible.h"
 
+#include "nsAccessibilityService.h"
 #include "States.h"
 
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentXBL.h"
 #include "nsIFrame.h"
 
 using namespace mozilla::a11y;
 
@@ -77,17 +78,17 @@ nsXULSliderAccessible::NativeState()
 
   nsCOMPtr<nsIContent> sliderContent(GetSliderNode());
   NS_ENSURE_STATE(sliderContent);
 
   nsIFrame *frame = sliderContent->GetPrimaryFrame();
   if (frame && frame->IsFocusable())
     states |= states::FOCUSABLE;
 
-  if (gLastFocusedNode == mContent)
+  if (FocusMgr()->IsFocused(this))
     states |= states::FOCUSED;
 
   return states;
 }
 
 // nsIAccessible
 
 NS_IMETHODIMP
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -41,23 +41,26 @@
 
 #include "nsAccCache.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsDocAccessible.h"
 #include "Relation.h"
 #include "States.h"
 
+#include "nsComponentManagerUtils.h"
 #include "nsIAccessibleRelation.h"
+#include "nsIAutoCompleteInput.h"
+#include "nsIAutoCompletePopup.h"
 #include "nsIDOMXULElement.h"
+#include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULTreeElement.h"
 #include "nsITreeSelection.h"
 #include "nsIMutableArray.h"
-#include "nsComponentManagerUtils.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULTreeAccessible::
@@ -65,16 +68,24 @@ nsXULTreeAccessible::
   nsAccessibleWrap(aContent, aShell)
 {
   mTree = nsCoreUtils::GetTreeBoxObject(aContent);
   if (mTree)
     mTree->GetView(getter_AddRefs(mTreeView));
 
   NS_ASSERTION(mTree && mTreeView, "Can't get mTree or mTreeView!\n");
 
+  nsIContent* parentContent = mContent->GetParent();
+  if (parentContent) {
+    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+      do_QueryInterface(parentContent);
+    if (autoCompletePopupElm)
+      mFlags |= eAutoCompletePopupAccessible;
+  }
+
   mAccessibleCache.Init(kDefaultTreeCacheSize);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeAccessible: nsISupports and cycle collection implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTreeAccessible)
 
@@ -102,19 +113,16 @@ PRUint64
 nsXULTreeAccessible::NativeState()
 {
   // Get focus status from base class.
   PRUint64 state = nsAccessible::NativeState();
 
   // readonly state
   state |= states::READONLY;
 
-  // remove focusable and focused states since tree items are focusable for AT
-  state &= ~(states::FOCUSABLE | states::FOCUSED);
-
   // multiselectable state.
   nsCOMPtr<nsITreeSelection> selection;
   mTreeView->GetSelection(getter_AddRefs(selection));
   NS_ENSURE_TRUE(selection, state);
 
   PRBool isSingle = PR_FALSE;
   nsresult rv = selection->GetSingle(&isSingle);
   NS_ENSURE_SUCCESS(rv, state);
@@ -197,37 +205,16 @@ nsXULTreeAccessible::NativeRole()
     cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
 
   return primaryCol ?
     static_cast<PRUint32>(nsIAccessibleRole::ROLE_OUTLINE) :
     static_cast<PRUint32>(nsIAccessibleRole::ROLE_LIST);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsXULTreeAccessible: nsIAccessible implementation
-
-nsAccessible*
-nsXULTreeAccessible::FocusedChild()
-{
-  if (gLastFocusedNode != mContent)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
-    do_QueryInterface(mContent);
-  if (multiSelect) {
-    PRInt32 row = -1;
-    multiSelect->GetCurrentIndex(&row);
-    if (row >= 0)
-      return GetTreeItemAccessible(row);
-  }
-
-  return nsnull;
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeAccessible: nsAccessible implementation (DON'T put methods here)
 
 nsAccessible*
 nsXULTreeAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                   EWhichChildAtPoint aWhichChild)
 {
   nsIFrame *frame = GetFrame();
   if (!frame)
@@ -272,16 +259,31 @@ nsXULTreeAccessible::ChildAtPoint(PRInt3
 // nsXULTreeAccessible: SelectAccessible
 
 bool
 nsXULTreeAccessible::IsSelect()
 {
   return true;
 }
 
+nsAccessible*
+nsXULTreeAccessible::CurrentItem()
+{
+  nsCOMPtr<nsITreeSelection> selection;
+  mTreeView->GetSelection(getter_AddRefs(selection));
+  if (selection) {
+    PRInt32 currentIndex = -1;
+    selection->GetCurrentIndex(&currentIndex);
+    if (currentIndex >= 0)
+      return GetTreeItemAccessible(currentIndex);
+  }
+
+  return nsnull;
+}
+
 already_AddRefed<nsIArray>
 nsXULTreeAccessible::SelectedItems()
 {
   nsCOMPtr<nsITreeSelection> selection;
   mTreeView->GetSelection(getter_AddRefs(selection));
   if (!selection)
     return nsnull;
 
@@ -448,16 +450,82 @@ nsXULTreeAccessible::GetChildCount()
   PRInt32 rowCount = 0;
   mTreeView->GetRowCount(&rowCount);
   childCount += rowCount;
 
   return childCount;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsXULTreeAccessible: Widgets
+
+bool
+nsXULTreeAccessible::IsWidget() const
+{
+  return true;
+}
+
+bool
+nsXULTreeAccessible::IsActiveWidget() const
+{
+  if (IsAutoCompletePopup()) {
+    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+      do_QueryInterface(mContent->GetParent());
+
+    if (autoCompletePopupElm) {
+      PRBool isOpen = PR_FALSE;
+      autoCompletePopupElm->GetPopupOpen(&isOpen);
+      return isOpen;
+    }
+  }
+  return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+nsXULTreeAccessible::AreItemsOperable() const
+{
+  if (IsAutoCompletePopup()) {
+    nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+      do_QueryInterface(mContent->GetParent());
+
+    if (autoCompletePopupElm) {
+      PRBool isOpen = PR_FALSE;
+      autoCompletePopupElm->GetPopupOpen(&isOpen);
+      return isOpen;
+    }
+  }
+  return true;
+}
+
+nsAccessible*
+nsXULTreeAccessible::ContainerWidget() const
+{
+  if (IsAutoCompletePopup()) {
+    // This works for XUL autocompletes. It doesn't work for HTML forms
+    // autocomplete because of potential crossprocess calls (when autocomplete
+    // lives in content process while popup lives in chrome process). If that's
+    // a problem then rethink Widgets interface.
+    nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
+      do_QueryInterface(mContent->GetParent());
+    if (menuListElm) {
+      nsCOMPtr<nsIDOMNode> inputElm;
+      menuListElm->GetInputField(getter_AddRefs(inputElm));
+      if (inputElm) {
+        nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
+        if (inputNode) {
+          nsAccessible* input = GetAccService()->GetAccessible(inputNode);
+          return input ? input->ContainerWidget() : nsnull;
+        }
+      }
+    }
+  }
+  return nsnull;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeAccessible: public implementation
 
 nsAccessible*
 nsXULTreeAccessible::GetTreeItemAccessible(PRInt32 aRow)
 {
   if (aRow < 0 || IsDefunct())
     return nsnull;
 
@@ -657,30 +725,17 @@ NS_IMPL_ADDREF_INHERITED(nsXULTreeItemAc
 NS_IMPL_RELEASE_INHERITED(nsXULTreeItemAccessibleBase, nsAccessible)
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeItemAccessibleBase: nsIAccessible implementation
 
 nsAccessible*
 nsXULTreeItemAccessibleBase::FocusedChild()
 {
-  if (gLastFocusedNode != mContent)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
-    do_QueryInterface(mContent);
-
-  if (multiSelect) {
-    PRInt32 row = -1;
-    multiSelect->GetCurrentIndex(&row);
-    if (row == mRow)
-      return this;
-  }
-
-  return nsnull;
+  return FocusMgr()->FocusedAccessible() == this ? this : nsnull;
 }
 
 NS_IMETHODIMP
 nsXULTreeItemAccessibleBase::GetBounds(PRInt32 *aX, PRInt32 *aY,
                                        PRInt32 *aWidth, PRInt32 *aHeight)
 {
   NS_ENSURE_ARG_POINTER(aX);
   *aX = 0;
@@ -939,25 +994,18 @@ nsXULTreeItemAccessibleBase::NativeState
   if (selection) {
     PRBool isSelected;
     selection->IsSelected(mRow, &isSelected);
     if (isSelected)
       state |= states::SELECTED;
   }
 
   // focused state
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
-    do_QueryInterface(mContent);
-  if (multiSelect) {
-    PRInt32 currentIndex;
-    multiSelect->GetCurrentIndex(&currentIndex);
-    if (currentIndex == mRow) {
-      state |= states::FOCUSED;
-    }
-  }
+  if (FocusMgr()->IsFocused(this))
+    state |= states::FOCUSED;
 
   // invisible state
   PRInt32 firstVisibleRow, lastVisibleRow;
   mTree->GetFirstVisibleRow(&firstVisibleRow);
   mTree->GetLastVisibleRow(&lastVisibleRow);
   if (mRow < firstVisibleRow || mRow > lastVisibleRow)
     state |= states::INVISIBLE;
 
@@ -966,16 +1014,25 @@ nsXULTreeItemAccessibleBase::NativeState
 
 PRInt32
 nsXULTreeItemAccessibleBase::IndexInParent() const
 {
   return mParent ? mParent->ContentChildCount() + mRow : -1;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsXULTreeItemAccessibleBase: Widgets
+
+nsAccessible*
+nsXULTreeItemAccessibleBase::ContainerWidget() const
+{
+  return mParent;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeItemAccessibleBase: nsAccessible protected methods
 
 void
 nsXULTreeItemAccessibleBase::DispatchClickEvent(nsIContent *aContent,
                                                 PRUint32 aActionIndex)
 {
   if (IsDefunct())
     return;
--- a/accessible/src/xul/nsXULTreeAccessible.h
+++ b/accessible/src/xul/nsXULTreeAccessible.h
@@ -81,32 +81,39 @@ public:
   virtual bool IsDefunct() const;
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual PRUint64 NativeState();
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                      EWhichChildAtPoint aWhichChild);
-  virtual nsAccessible* FocusedChild();
 
   virtual nsAccessible* GetChildAt(PRUint32 aIndex);
   virtual PRInt32 GetChildCount();
 
   // SelectAccessible
   virtual bool IsSelect();
   virtual already_AddRefed<nsIArray> SelectedItems();
   virtual PRUint32 SelectedItemCount();
   virtual nsAccessible* GetSelectedItem(PRUint32 aIndex);
   virtual bool IsItemSelected(PRUint32 aIndex);
   virtual bool AddItemToSelection(PRUint32 aIndex);
   virtual bool RemoveItemFromSelection(PRUint32 aIndex);
   virtual bool SelectAll();
   virtual bool UnselectAll();
 
+  // Widgets
+  virtual bool IsWidget() const;
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
+  virtual nsAccessible* CurrentItem();
+
+  virtual nsAccessible* ContainerWidget() const;
+
   // nsXULTreeAccessible
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEACCESSIBLE_IMPL_CID)
 
   /**
    * Return tree item accessible at the givem row. If accessible doesn't exist
    * in the cache then create and cache it.
    *
@@ -204,16 +211,19 @@ public:
   virtual PRUint64 NativeState();
   virtual PRInt32 IndexInParent() const;
   virtual Relation RelationByType(PRUint32 aType);
   virtual nsAccessible* FocusedChild();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
+  // Widgets
+  virtual nsAccessible* ContainerWidget() const;
+
   // nsXULTreeItemAccessibleBase
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULTREEITEMBASEACCESSIBLE_IMPL_CID)
 
   /**
    * Return row index associated with the accessible.
    */
   PRInt32 GetRowIndex() const { return mRow; }
 
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -67,43 +67,41 @@ include $(topsrcdir)/config/rules.mk
 _TEST_FILES =\
 		formimage.png \
 		letters.gif \
 		moz.png \
 		$(topsrcdir)/content/media/test/bug461281.ogg \
 		longdesc_src.html \
 		actions.js \
 		attributes.js \
+		autocomplete.js \
 		common.js \
 		events.js \
 		grid.js \
 		layout.js \
 		name.js \
-		nsIAccessible_selects.js \
 		relations.js \
 		role.js \
 		selectable.js \
 		states.js \
 		table.js \
 		value.js \
-		test_aria_activedescendant.html \
 		test_aria_role_article.html \
 		test_aria_role_equation.html \
 		test_aria_roles.html \
 		test_aria_roles.xul \
 		test_aria_token_attrs.html \
 		test_bug420863.html \
 		test_childAtPoint.html \
 		test_childAtPoint.xul \
 		test_descr.html \
 		test_elm_landmarks.html \
 		test_elm_listbox.xul \
 		test_elm_nsApplicationAcc.html \
 		test_elm_plugin.html \
- 		test_nsIAccessible_selects.html \
 		test_nsIAccessibleDocument.html \
 		test_nsIAccessibleImage.html \
 		test_nsIAccessNode_utils.html \
 		test_nsOuterDocAccessible.html \
 		test_role_nsHyperTextAcc.html \
 		test_text_caret.html \
 		test_textboxes.html \
 		test_textboxes.xul \
--- a/accessible/tests/mochitest/actions/Makefile.in
+++ b/accessible/tests/mochitest/actions/Makefile.in
@@ -50,14 +50,15 @@ include $(topsrcdir)/config/rules.mk
 		test_aria.html \
 		test_general.html \
 		test_general.xul \
 		test_inputs.html \
 		test_keys_menu.xul \
 		test_keys.html \
 		test_link.html \
 		test_media.html \
+		test_select.html \
 		test_tree.xul \
 		test_treegrid.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_select.html
@@ -0,0 +1,103 @@
+<html>
+
+<head>
+  <title>nsIAccessible actions testing for HTML select</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../actions.js"></script>
+
+  <script type="application/javascript">
+    //gA11yEventDumpToConsole = true; // debugging
+    function doTest()
+    {
+      var actionsArray = [
+        {
+          ID: "lb_apple",
+          actionIndex: 0,
+          actionName: "select",
+          events: CLICK_EVENTS,
+          eventSeq: [
+            new focusChecker("lb_apple")
+          ]
+        },
+        {
+          ID: "combobox",
+          actionIndex: 0,
+          actionName: "open",
+          events: CLICK_EVENTS,
+          eventSeq: [
+            new focusChecker("cb_orange")
+          ]
+        },
+        {
+          ID: "cb_apple",
+          actionIndex: 0,
+          actionName: "select",
+          events: CLICK_EVENTS,
+          eventSeq: [
+            new focusChecker("combobox")
+          ]
+        },
+        {
+          ID: "combobox",
+          actionIndex: 0,
+          actionName: "open",
+          events: CLICK_EVENTS,
+          eventSeq: [
+            new focusChecker("cb_apple")
+          ]
+        },
+        {
+          ID: "combobox",
+          actionIndex: 0,
+          actionName: "close",
+          events: CLICK_EVENTS,
+          eventSeq: [
+            new focusChecker("combobox")
+          ]
+        }
+      ];
+
+      testActions(actionsArray);
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+     title="Rework accessible focus handling">
+    Mozilla Bug 673958
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <select id="listbox" size="2">
+    <option id="lb_orange">orange</option>
+    <option id="lb_apple">apple</option>
+  </select>
+
+  <select id="combobox">
+    <option id="cb_orange">orange</option>
+    <option id="cb_apple">apple</option>
+  </select>
+</body>
+</html>
--- a/accessible/tests/mochitest/actions/test_tree.xul
+++ b/accessible/tests/mochitest/actions/test_tree.xul
@@ -21,35 +21,32 @@
   <script type="application/javascript"
           src="../actions.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Accessible tree testers
 
-    function focusChecker(aAcc, aStates)
+    function stateFocusChecker(aAcc, aStates)
     {
-      this.type = EVENT_FOCUS;
-      this.target = aAcc;
-      this.getID = function focusChecker_getID()
-      {
-        return "focus handling";
-      }
+      this.__proto__ = new focusChecker(aAcc);
+
       this.check = function focusChecker_check(aEvent)
       {
         var states = aStates ? aStates : 0;
         testStates(this.target, STATE_FOCUSED | STATE_SELECTED | states);
       }
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     // gA11yEventDumpID = "debug";
+    //gA11yEventDumpToConsole = true; // debug
 
     function doTestActions()
     {
       var treeNode = getNode("tree");
 
       var treeBodyNode = treeNode.boxObject.treeBody;
 
       var tree = getAccessible(treeNode);
@@ -59,17 +56,17 @@
       var actions = [
         {
           ID: expandedTreeItem,
           actionName: "activate",
           actionIndex: 0,
           events: CLICK_EVENTS,
           targetID: treeBodyNode,
           eventSeq: [
-            new focusChecker(expandedTreeItem, STATE_EXPANDED)
+            new stateFocusChecker(expandedTreeItem, STATE_EXPANDED)
           ]
         },
         {
           ID: collapsedTreeItem,
           actionName: "expand",
           actionIndex: 1,
           events: CLICK_EVENTS,
           targetID: treeBodyNode,
--- a/accessible/tests/mochitest/attributes/test_obj_group.xul
+++ b/accessible/tests/mochitest/attributes/test_obj_group.xul
@@ -65,16 +65,18 @@
       }
 
       this.getID = function openSubMenu_getID()
       {
         return "open submenu " + prettyName(aID);
       }
     }
 
+    //gA11yEventDumpToConsole = true; // debug stuff
+
     var gQueue = null;
     function doTest()
     {
       //////////////////////////////////////////////////////////////////////////
       // xul:listbox (bug 417317)
       testGroupAttrs("item1", 1, 2);
       testGroupAttrs("item2", 2, 2);
 
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/autocomplete.js
@@ -0,0 +1,216 @@
+
+const nsISupports = Components.interfaces.nsISupports;
+const nsIAutoCompleteResult = Components.interfaces.nsIAutoCompleteResult;
+const nsIAutoCompleteSearch = Components.interfaces.nsIAutoCompleteSearch;
+const nsIFactory = Components.interfaces.nsIFactory;
+const nsIUUIDGenerator = Components.interfaces.nsIUUIDGenerator;
+const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
+
+var gDefaultAutoCompleteSearch = null;
+
+/**
+ * Register 'test-a11y-search' AutoCompleteSearch.
+ *
+ * @param aValues [in] set of possible results values
+ * @param aComments [in] set of possible results descriptions
+ */
+function initAutoComplete(aValues, aComments)
+{
+  var allResults = new ResultsHeap(aValues, aComments);
+  gDefaultAutoCompleteSearch =
+    new AutoCompleteSearch("test-a11y-search", allResults);
+  registerAutoCompleteSearch(gDefaultAutoCompleteSearch,
+                             "Accessibility Test AutoCompleteSearch");
+}
+
+/**
+ * Unregister 'test-a11y-search' AutoCompleteSearch.
+ */
+function shutdownAutoComplete()
+{
+  unregisterAutoCompleteSearch(gDefaultAutoCompleteSearch);
+  gDefaultAutoCompleteSearch.cid = null;
+  gDefaultAutoCompleteSearch = null;
+}
+
+
+/**
+ * Register the given AutoCompleteSearch.
+ *
+ * @param aSearch       [in] AutoCompleteSearch object
+ * @param aDescription  [in] description of the search object
+ */
+function registerAutoCompleteSearch(aSearch, aDescription)
+{
+  var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+
+  var uuidGenerator = Components.classes["@mozilla.org/uuid-generator;1"].
+    getService(nsIUUIDGenerator);
+  var cid = uuidGenerator.generateUUID();
+
+  var componentManager = Components.manager.QueryInterface(nsIComponentRegistrar);
+  componentManager.registerFactory(cid, aDescription, name, aSearch);
+
+  // Keep the id on the object so we can unregister later.
+  aSearch.cid = cid;
+}
+
+/**
+ * Unregister the given AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch)
+{
+  var componentManager = Components.manager.QueryInterface(nsIComponentRegistrar);
+  componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
+
+
+/**
+ * A container to keep all possible results of autocomplete search.
+ */
+function ResultsHeap(aValues, aComments)
+{
+  this.values = aValues;
+  this.comments = aComments;
+}
+
+ResultsHeap.prototype =
+{
+  constructor: ResultsHeap,
+
+  /**
+   * Return AutoCompleteResult for the given search string.
+   */
+  getAutoCompleteResultFor: function(aSearchString)
+  {
+    var values = [], comments = [];
+    for (var idx = 0; idx < this.values.length; idx++) {
+      if (this.values[idx].indexOf(aSearchString) != -1) {
+        values.push(this.values[idx]);
+        comments.push(this.comments[idx]);
+      }
+    }
+    return new AutoCompleteResult(values, comments);
+  }
+}
+
+
+/**
+ * nsIAutoCompleteSearch implementation.
+ *
+ * @param aName       [in] the name of autocomplete search
+ * @param aAllResults [in] ResultsHeap object
+ */
+function AutoCompleteSearch(aName, aAllResults)
+{
+  this.name = aName;
+  this.allResults = aAllResults;
+}
+
+AutoCompleteSearch.prototype =
+{
+  constructor: AutoCompleteSearch,
+
+  // nsIAutoCompleteSearch implementation
+  startSearch: function(aSearchString, aSearchParam, aPreviousResult,
+                        aListener)
+  {
+    var result = this.allResults.getAutoCompleteResultFor(aSearchString);
+    aListener.onSearchResult(this, result);
+  },
+
+  stopSearch: function() {},
+
+  // nsISupports implementation
+  QueryInterface: function(iid)
+  {
+    if (iid.equals(nsISupports) ||
+        iid.equals(nsIFactory) ||
+        iid.equals(nsIAutoCompleteSearch))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  // nsIFactory implementation
+  createInstance: function(outer, iid)
+  {
+    return this.QueryInterface(iid);
+  },
+
+  // Search name. Used by AutoCompleteController.
+  name: null,
+
+  // Results heap.
+  allResults: null
+}
+
+
+/**
+ * nsIAutoCompleteResult implementation.
+ */
+function AutoCompleteResult(aValues, aComments)
+{
+  this.values = aValues;
+  this.comments = aComments;
+
+  if (this.values.length > 0)
+    this.searchResult = nsIAutoCompleteResult.RESULT_SUCCESS;
+  else
+    this.searchResult = nsIAutoCompleteResult.NOMATCH;
+}
+
+AutoCompleteResult.prototype =
+{
+  constructor: AutoCompleteResult,
+
+  searchString: "",
+  searchResult: null,
+
+  defaultIndex: 0,
+
+  get matchCount()
+  {
+    return this.values.length;
+  },
+
+  getValueAt: function(aIndex)
+  {
+    return this.values[aIndex];
+  },
+
+  getLabelAt: function(aIndex)
+  {
+    return this.getValueAt(aIndex);
+  },
+
+  getCommentAt: function(aIndex)
+  {
+    return this.comments[aIndex];
+  },
+
+  getStyleAt: function(aIndex)
+  {
+    return null;
+  },
+
+  getImageAt: function(aIndex)
+  {
+    return "";
+  },
+
+  removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+  // nsISupports implementation
+  QueryInterface: function(iid) {
+    if (iid.equals(nsISupports) ||
+        iid.equals(nsIAutoCompleteResult))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  // Data
+  values: null,
+  comments: null
+}
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -8,26 +8,24 @@ const nsIAccessibleStateChangeEvent =
   Components.interfaces.nsIAccessibleStateChangeEvent;
 const nsIAccessibleCaretMoveEvent =
   Components.interfaces.nsIAccessibleCaretMoveEvent;
 const nsIAccessibleTextChangeEvent =
   Components.interfaces.nsIAccessibleTextChangeEvent;
 
 const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates;
 const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
-const nsIAccessibleTypes = Components.interfaces.nsIAccessibleTypes;
+const nsIAccessibleScrollType = Components.interfaces.nsIAccessibleScrollType;
+const nsIAccessibleCoordinateType = Components.interfaces.nsIAccessibleCoordinateType;
 
 const nsIAccessibleRelation = Components.interfaces.nsIAccessibleRelation;
 
 const nsIAccessNode = Components.interfaces.nsIAccessNode;
 const nsIAccessible = Components.interfaces.nsIAccessible;
 
-const nsIAccessibleCoordinateType =
-      Components.interfaces.nsIAccessibleCoordinateType;
-
 const nsIAccessibleDocument = Components.interfaces.nsIAccessibleDocument;
 const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication;
 
 const nsIAccessibleText = Components.interfaces.nsIAccessibleText;
 const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableText;
 
 const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink;
 const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText;
@@ -57,54 +55,58 @@ const LINUX = (navigator.platform.indexO
 const SOLARIS = (navigator.platform.indexOf("SunOS") != -1)? true : false;
 const WIN = (navigator.platform.indexOf("Win") != -1)? true : false;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible general
 
 const STATE_BUSY = nsIAccessibleStates.STATE_BUSY;
 
+const SCROLL_TYPE_ANYWHERE = nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE;
+
 const kEmbedChar = String.fromCharCode(0xfffc);
 
 const kDiscBulletText = String.fromCharCode(0x2022) + " ";
 const kCircleBulletText = String.fromCharCode(0x25e6) + " ";
 const kSquareBulletText = String.fromCharCode(0x25aa) + " ";
 
 /**
- * nsIAccessibleRetrieval, initialized when test is loaded.
+ * nsIAccessibleRetrieval service.
  */
-var gAccRetrieval = null;
+var gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
+  getService(nsIAccessibleRetrieval);
 
 /**
  * Invokes the given function when document is loaded and focused. Preferable
  * to mochitests 'addLoadEvent' function -- additionally ensures state of the
  * document accessible is not busy.
  *
  * @param aFunc  the function to invoke
  */
-function addA11yLoadEvent(aFunc)
+function addA11yLoadEvent(aFunc, aWindow)
 {
   function waitForDocLoad()
   {
     window.setTimeout(
       function()
       {
-        var accDoc = getAccessible(document);
+        var targetDocument = aWindow ? aWindow.document : document;
+        var accDoc = getAccessible(targetDocument);
         var state = {};
         accDoc.getState(state, {});
         if (state.value & STATE_BUSY)
           return waitForDocLoad();
 
         window.setTimeout(aFunc, 0);
       },
       0
     );
   }
 
-  SimpleTest.waitForFocus(waitForDocLoad);
+  SimpleTest.waitForFocus(waitForDocLoad, aWindow);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Helpers for getting DOM node/accessible
 
 /**
  * Return the DOM node by identifier (may be accessible, DOM node or ID).
  */
@@ -584,58 +586,55 @@ function prettyName(aIdentifier)
     try {
       msg += ", role: " + roleToString(acc.role);
       if (acc.name)
         msg += ", name: '" + acc.name + "'";
     } catch (e) {
       msg += "defunct";
     }
 
-    if (acc) {
-      var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
-      var match = exp.exec(acc.valueOf());
-      if (match)
-        msg += ", address: " + match[1];
-      else
-        msg += ", address: " + acc.valueOf();
-    }
+    if (acc)
+      msg += ", address: " + getObjAddress(acc);
     msg += "]";
 
     return msg;
   }
 
   if (aIdentifier instanceof nsIDOMNode)
-    return getNodePrettyName(aIdentifier);
+    return "[ " + getNodePrettyName(aIdentifier) + " ]";
 
   return " '" + aIdentifier + "' ";
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Private
 ////////////////////////////////////////////////////////////////////////////////
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible general
 
-function initialize()
-{
-  gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
-    getService(nsIAccessibleRetrieval);
-}
-
-addLoadEvent(initialize);
-
 function getNodePrettyName(aNode)
 {
   try {
-    if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE)
-      return " 'document node' ";
+    var tag = "";
+    if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE) {
+      tag = "document";
+    } else {
+      tag = aNode.localName;
+      if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
+        tag += "@id=\"" + aNode.getAttribute("id") + "\"";
+    }
 
-    var name = " '" + aNode.localName;
-    if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
-      name += "@id='" + aNode.getAttribute("id") + "'";
-
-    name += " node' "
-    return name;
+    return "'" + tag + " node', address: " + getObjAddress(aNode);
   } catch (e) {
     return "' no node info '";
   }
 }
+
+function getObjAddress(aObj)
+{
+  var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
+  var match = exp.exec(aObj.valueOf());
+  if (match)
+    return match[1];
+
+  return aObj.valueOf();
+}
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -163,24 +163,33 @@ const DO_NOT_FINISH_TEST = 1;
  *     //   type getter: function() {},
  *     //
  *     //   * DOM node or accessible. *
  *     //   target getter: function() {},
  *     //
  *     //   * DOM event phase (false - bubbling). *
  *     //   phase getter: function() {},
  *     //
+ *     //   * Callback, called to match handled event. *
+ *     //   match : function() {},
+ *     //
  *     //   * Callback, called when event is handled
  *     //   check: function(aEvent) {},
  *     //
  *     //   * Checker ID *
  *     //   getID: function() {},
  *     //
  *     //   * Event that don't have predefined order relative other events. *
- *     //   async getter: function() {}
+ *     //   async getter: function() {},
+ *     //
+ *     //   * Event that is not expected. *
+ *     //   unexpected getter: function() {},
+ *     //
+ *     //   * No other event of the same type is not allowed. *
+ *     //   unique getter: function() {}
  *     // };
  *     eventSeq getter() {},
  *
  *     // Array of checker objects defining unexpected events on invoker's
  *     // action.
  *     unexpectedEventSeq getter() {},
  *
  *     // The ID of invoker.
@@ -311,17 +320,17 @@ function eventQueue(aEventType)
     if (!aUncondProcess && this.areAllEventsExpected()) {
       // We need delay to avoid events coalesce from different invokers.
       var queue = this;
       SimpleTest.executeSoon(function() { queue.processNextInvoker(); });
       return;
     }
 
     // Check in timeout invoker didn't fire registered events.
-    window.setTimeout(function(aQueue) { aQueue.processNextInvoker(); }, 500,
+    window.setTimeout(function(aQueue) { aQueue.processNextInvoker(); }, 100,
                       this);
   }
 
   /**
    * Handle events for the current invoker.
    */
   this.handleEvent = function eventQueue_handleEvent(aEvent)
   {
@@ -339,21 +348,21 @@ function eventQueue(aEventType)
     if ("debugCheck" in invoker)
       invoker.debugCheck(aEvent);
 
     // Search through handled expected events to report error if one of them is
     // handled for a second time.
     var idx = 0;
     for (; idx < this.mEventSeq.length; idx++) {
       if (this.isEventExpected(idx) && (invoker.wasCaught[idx] == true) &&
-          this.isAlreadyCaught(idx, aEvent)) {
+          this.isSameEvent(idx, aEvent)) {
 
         var msg = "Doubled event { event type: " +
           this.getEventTypeAsString(idx) + ", target: " +
-          prettyName(this.getEventTarget(idx)) + "} in test with ID = '" +
+          this.getEventTargetDescr(idx) + "} in test with ID = '" +
           this.getEventID(idx) + "'.";
         ok(false, msg);
       }
     }
 
     // Search through unexpected events, any matches result in error report
     // after this invoker processing.
     for (idx = 0; idx < this.mEventSeq.length; idx++) {
@@ -440,26 +449,59 @@ function eventQueue(aEventType)
 
   this.getNextInvoker = function eventQueue_getNextInvoker()
   {
     return this.mInvokers[++this.mIndex];
   }
 
   this.setEventHandler = function eventQueue_setEventHandler(aInvoker)
   {
-    // Create unique event sequence concatenating expected and unexpected
+    // Create unified event sequence concatenating expected and unexpected
     // events.
     this.mEventSeq = ("eventSeq" in aInvoker) ?
       aInvoker.eventSeq :
       [ new invokerChecker(this.mDefEventType, aInvoker.DOMNode) ];
 
-    for (var idx = 0; idx < this.mEventSeq.length; idx++) {
-      this.mEventSeq[idx].unexpected = false;
+    var len = this.mEventSeq.length;
+    for (var idx = 0; idx < len; idx++) {
+      var seqItem = this.mEventSeq[idx];
+      // Allow unexpected events in primary event sequence.
+      if (!("unexpected" in this.mEventSeq[idx]))
+        seqItem.unexpected = false;
+
       if (!("async" in this.mEventSeq[idx]))
-        this.mEventSeq[idx].async = false;
+        seqItem.async = false;
+
+      // If the event is of unique type (regardless whether it's expected or
+      // not) then register additional unexpected event that matches to any
+      // event of the same type with any target different from registered
+      // expected events.
+      if (("unique" in seqItem) && seqItem.unique) {
+        var uniquenessChecker = {
+          type: seqItem.type,
+          unexpected: true,
+          match: function uniquenessChecker_match(aEvent)
+          {
+            // The handled event is matched if its target doesn't match to any
+            // registered expected event.
+            var matched = true;
+            for (var idx = 0; idx < this.queue.mEventSeq.length; idx++) {
+              if (this.queue.isEventExpected(idx) &&
+                  this.queue.compareEvents(idx, aEvent)) {
+                matched = false;
+                break;
+              }
+            }
+            return matched;
+          },
+          targetDescr: "any target different from expected events",
+          queue: this
+        };
+        this.mEventSeq.push(uniquenessChecker);
+      }
     }
 
     var unexpectedSeq = aInvoker.unexpectedEventSeq;
     if (unexpectedSeq) {
       for (var idx = 0; idx < unexpectedSeq.length; idx++) {
         unexpectedSeq[idx].unexpected = true;
         unexpectedSeq[idx].async = false;
       }
@@ -478,25 +520,29 @@ function eventQueue(aEventType)
         var eventType = this.getEventType(idx);
 
         if (gLogger.isEnabled()) {
           var msg = "registered";
           if (this.isEventUnexpected(idx))
             msg += " unexpected";
 
           msg += ": event type: " + this.getEventTypeAsString(idx) +
-            ", target: " + this.getEventTargetDescr(idx);
+            ", target: " + this.getEventTargetDescr(idx, true);
 
           gLogger.logToConsole(msg);
           gLogger.logToDOM(msg, true);
         }
 
         if (typeof eventType == "string") {
           // DOM event
           var target = this.getEventTarget(idx);
+          if (!target) {
+            ok(false, "no target for DOM event!");
+            return;
+          }
           var phase = this.getEventPhase(idx);
           target.ownerDocument.addEventListener(eventType, this, phase);
 
         } else {
           // A11y event
           addA11yEventListener(eventType, this);
         }
       }
@@ -535,20 +581,29 @@ function eventQueue(aEventType)
     return (typeof type == "string") ? type : eventTypeToString(type);
   }
 
   this.getEventTarget = function eventQueue_getEventTarget(aIdx)
   {
     return this.mEventSeq[aIdx].target;
   }
 
-  this.getEventTargetDescr = function eventQueue_getEventTargetDescr(aIdx)
+  this.getEventTargetDescr =
+    function eventQueue_getEventTargetDescr(aIdx, aDontForceTarget)
   {
     var descr = this.mEventSeq[aIdx].targetDescr;
-    return descr ? descr : "no target description";
+    if (descr)
+      return descr;
+
+    if (aDontForceTarget)
+      return "no target description";
+
+    var target = ("target" in this.mEventSeq[aIdx]) ?
+      this.mEventSeq[aIdx].target : null;
+    return prettyName(target);
   }
 
   this.getEventPhase = function eventQueue_getEventPhase(aIdx)
   {
      var eventItem = this.mEventSeq[aIdx];
     if ("phase" in eventItem)
       return eventItem.phase;
 
@@ -569,42 +624,51 @@ function eventQueue(aEventType)
   {
     return this.mEventSeq[aIdx].unexpected;
   }
   this.isEventExpected = function eventQueue_isEventExpected(aIdx)
   {
     return !this.mEventSeq[aIdx].unexpected;
   }
 
-  this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
+  this.compareEventTypes = function eventQueue_compareEventTypes(aIdx, aEvent)
   {
     var eventType1 = this.getEventType(aIdx);
-
     var eventType2 = (aEvent instanceof nsIDOMEvent) ?
       aEvent.type : aEvent.eventType;
 
-    if (eventType1 != eventType2)
+    return eventType1 == eventType2;
+  }
+
+  this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
+  {
+    if (!this.compareEventTypes(aIdx, aEvent))
       return false;
 
+    // If checker provides "match" function then allow the checker to decide
+    // whether event is matched.
+    if ("match" in this.mEventSeq[aIdx])
+      return this.mEventSeq[aIdx].match(aEvent);
+
     var target1 = this.getEventTarget(aIdx);
     if (target1 instanceof nsIAccessible) {
       var target2 = (aEvent instanceof nsIDOMEvent) ?
         getAccessible(aEvent.target) : aEvent.accessible;
 
       return target1 == target2;
     }
 
     // If original target isn't suitable then extend interface to support target
     // (original target is used in test_elm_media.html).
     var target2 = (aEvent instanceof nsIDOMEvent) ?
       aEvent.originalTarget : aEvent.DOMNode;
     return target1 == target2;
   }
 
-  this.isAlreadyCaught = function eventQueue_isAlreadyCaught(aIdx, aEvent)
+  this.isSameEvent = function eventQueue_isSameEvent(aIdx, aEvent)
   {
     // We don't have stored info about handled event other than its type and
     // target, thus we should filter text change and state change events since
     // they may occur on the same element because of complex changes.
     return this.compareEvents(aIdx, aEvent) &&
       !(aEvent instanceof nsIAccessibleTextChangeEvent) &&
       !(aEvent instanceof nsIAccessibleStateChangeEvent);
   }
@@ -657,21 +721,21 @@ function eventQueue(aEventType)
 
     if (!aMatch)
       return;
 
     var msg = "EQ: ";
     var emphText = "matched ";
 
     var currType = this.getEventTypeAsString(aExpectedEventIdx);
-    var currTarget = this.getEventTarget(aExpectedEventIdx);
+    var currTargetDescr = this.getEventTargetDescr(aExpectedEventIdx);
     var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
     gLogger.logToConsole(consoleMsg);
 
-    msg += " event, type: " + currType + ", target: " + prettyName(currTarget);
+    msg += " event, type: " + currType + ", target: " + currTargetDescr;
 
     gLogger.logToDOM(msg, true, emphText);
   }
 
   this.mDefEventType = aEventType;
 
   this.mInvokers = new Array();
   this.mIndex = -1;
@@ -731,212 +795,413 @@ function sequence()
   this.idx = -1;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Event queue invokers
 
 /**
- * Invokers defined below take a checker object (or array of checker objects)
- * implementing 'check' method which will be called when proper event is
- * handled. Invokers listen default event type registered in event queue object
- * until it is passed explicetly.
+ * Invokers defined below take a checker object (or array of checker objects).
+ * An invoker listens for default event type registered in event queue object
+ * until its checker is provided.
  *
  * Note, checker object or array of checker objects is optional.
- * Note, you don't need to initialize 'target' and 'type' members of checker
- * object. The 'target' member will be initialized by invoker object and you are
- * free to use it in 'check' method.
  */
 
 /**
  * Click invoker.
  */
-function synthClick(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthClick(aNodeOrID, aCheckerOrEventSeq, aArgs)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq);
 
   this.invoke = function synthClick_invoke()
   {
+    var targetNode = this.DOMNode;
+    if (targetNode instanceof nsIDOMDocument) {
+      targetNode =
+        this.DOMNode.body ? this.DOMNode.body : this.DOMNode.documentElement;
+    }
+
     // Scroll the node into view, otherwise synth click may fail.
-    if (this.DOMNode instanceof nsIDOMNSHTMLElement)
-      this.DOMNode.scrollIntoView(true);
+    if (targetNode instanceof nsIDOMNSHTMLElement) {
+      targetNode.scrollIntoView(true);
+    } else if (targetNode instanceof nsIDOMXULElement) {
+      var targetAcc = getAccessible(targetNode);
+      targetAcc.scrollTo(SCROLL_TYPE_ANYWHERE);
+    }
 
-    synthesizeMouse(this.DOMNode, 1, 1, {});
+    var x = 1, y = 1;
+    if (aArgs && ("where" in aArgs) && aArgs.where == "right") {
+      if (targetNode instanceof nsIDOMNSHTMLElement)
+        x = targetNode.offsetWidth - 1;
+      else if (targetNode instanceof nsIDOMXULElement)
+        x = targetNode.boxObject.width - 1;
+    }
+    synthesizeMouse(targetNode, x, y, aArgs ? aArgs : {});
   }
 
   this.finalCheck = function synthClick_finalCheck()
   {
     // Scroll top window back.
     window.top.scrollTo(0, 0);
   }
 
   this.getID = function synthClick_getID()
   {
-    return prettyName(aNodeOrID) + " click"; 
+    return prettyName(aNodeOrID) + " click";
   }
 }
 
 /**
  * Mouse move invoker.
  */
-function synthMouseMove(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthMouseMove(aID, aCheckerOrEventSeq)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
+  this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
 
   this.invoke = function synthMouseMove_invoke()
   {
     synthesizeMouse(this.DOMNode, 1, 1, { type: "mousemove" });
     synthesizeMouse(this.DOMNode, 2, 2, { type: "mousemove" });
   }
 
   this.getID = function synthMouseMove_getID()
   {
-    return prettyName(aNodeOrID) + " mouse move"; 
+    return prettyName(aID) + " mouse move";
   }
 }
 
 /**
  * General key press invoker.
  */
-function synthKey(aNodeOrID, aKey, aArgs, aCheckerOrEventSeq, aEventType)
+function synthKey(aNodeOrID, aKey, aArgs, aCheckerOrEventSeq)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq);
 
   this.invoke = function synthKey_invoke()
   {
-    synthesizeKey(this.mKey, this.mArgs);
+    synthesizeKey(this.mKey, this.mArgs, this.mWindow);
   }
 
   this.getID = function synthKey_getID()
   {
-    return prettyName(aNodeOrID) + " '" + this.mKey + "' key"; 
+    var key = this.mKey;
+    switch (this.mKey) {
+      case "VK_TAB":
+        key = "tab";
+        break;
+      case "VK_DOWN":
+        key = "down";
+        break;
+      case "VK_UP":
+        key = "up";
+        break;
+      case "VK_LEFT":
+        key = "left";
+        break;
+      case "VK_RIGHT":
+        key = "right";
+        break;
+      case "VK_HOME":
+        key = "home";
+        break;
+      case "VK_ESCAPE":
+        key = "escape";
+        break;
+      case "VK_RETURN":
+        key = "enter";
+        break;
+    }
+    if (aArgs) {
+      if (aArgs.shiftKey)
+        key += " shift";
+      if (aArgs.ctrlKey)
+        key += " ctrl";
+      if (aArgs.altKey)
+        key += " alt";
+    }
+    return prettyName(aNodeOrID) + " '" + key + " ' key";
   }
 
   this.mKey = aKey;
   this.mArgs = aArgs ? aArgs : {};
+  this.mWindow = aArgs ? aArgs.window : null;
 }
 
 /**
  * Tab key invoker.
  */
-function synthTab(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthTab(aNodeOrID, aCheckerOrEventSeq, aWindow)
 {
-  this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: false },
-                                aCheckerOrEventSeq, aEventType);
-
-  this.getID = function synthTab_getID() 
-  { 
-    return prettyName(aNodeOrID) + " tab";
-  }
+  this.__proto__ = new synthKey(aNodeOrID, "VK_TAB",
+                                { shiftKey: false, window: aWindow },
+                                aCheckerOrEventSeq);
 }
 
 /**
  * Shift tab key invoker.
  */
-function synthShiftTab(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthShiftTab(aNodeOrID, aCheckerOrEventSeq)
 {
   this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: true },
-                                aCheckerOrEventSeq, aEventType);
+                                aCheckerOrEventSeq);
+}
 
-  this.getID = function synthTabTest_getID() 
-  { 
-    return prettyName(aNodeOrID) + " shift tab";
-  }
+/**
+ * Escape key invoker.
+ */
+function synthEscapeKey(aNodeOrID, aCheckerOrEventSeq)
+{
+  this.__proto__ = new synthKey(aNodeOrID, "VK_ESCAPE", null,
+                                aCheckerOrEventSeq);
 }
 
 /**
  * Down arrow key invoker.
  */
-function synthDownKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthDownKey(aNodeOrID, aCheckerOrEventSeq, aArgs)
 {
-  this.__proto__ = new synthKey(aNodeOrID, "VK_DOWN", null, aCheckerOrEventSeq,
-                                aEventType);
+  this.__proto__ = new synthKey(aNodeOrID, "VK_DOWN", aArgs,
+                                aCheckerOrEventSeq);
+}
 
-  this.getID = function synthDownKey_getID()
-  {
-    return prettyName(aNodeOrID) + " key down";
-  }
+/**
+ * Up arrow key invoker.
+ */
+function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs)
+{
+  this.__proto__ = new synthKey(aNodeOrID, "VK_UP", aArgs,
+                                aCheckerOrEventSeq);
 }
 
 /**
  * Right arrow key invoker.
  */
-function synthRightKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthRightKey(aNodeOrID, aCheckerOrEventSeq)
 {
-  this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq,
-                                aEventType);
-
-  this.getID = function synthRightKey_getID()
-  {
-    return prettyName(aNodeOrID) + " key right";
-  }
+  this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq);
 }
 
 /**
  * Home key invoker.
  */
-function synthHomeKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthHomeKey(aNodeOrID, aCheckerOrEventSeq)
+{
+  this.__proto__ = new synthKey(aNodeOrID, "VK_HOME", null, aCheckerOrEventSeq);
+}
+
+/**
+ * Enter key invoker
+ */
+function synthEnterKey(aID, aCheckerOrEventSeq)
 {
-  this.__proto__ = new synthKey(aNodeOrID, "VK_HOME", null, aCheckerOrEventSeq,
-                                aEventType);
-  
-  this.getID = function synthHomeKey_getID()
+  this.__proto__ = new synthKey(aID, "VK_RETURN", null, aCheckerOrEventSeq);
+}
+
+/**
+ * Synth alt + down arrow to open combobox.
+ */
+function synthOpenComboboxKey(aID, aCheckerOrEventSeq)
+{
+  this.__proto__ = new synthDownKey(aID, aCheckerOrEventSeq, { altKey: true });
+
+  this.getID = function synthOpenComboboxKey_getID()
   {
-    return prettyName(aNodeOrID) + " key home";
+    return "open combobox (atl + down arrow) " + prettyName(aID);
   }
 }
 
 /**
  * Focus invoker.
  */
-function synthFocus(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthFocus(aNodeOrID, aCheckerOrEventSeq)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
+  var checkerOfEventSeq =
+    aCheckerOrEventSeq ? aCheckerOrEventSeq : new focusChecker(aNodeOrID);
+  this.__proto__ = new synthAction(aNodeOrID, checkerOfEventSeq);
 
   this.invoke = function synthFocus_invoke()
   {
-    if (this.DOMNode instanceof Components.interfaces.nsIDOMNSEditableElement ||
+    if (this.DOMNode instanceof Components.interfaces.nsIDOMNSEditableElement &&
+        this.DOMNode.editor ||
         this.DOMNode instanceof Components.interfaces.nsIDOMXULTextBoxElement) {
       this.DOMNode.selectionStart = this.DOMNode.selectionEnd = this.DOMNode.value.length;
     }
     this.DOMNode.focus();
   }
 
   this.getID = function synthFocus_getID() 
   { 
     return prettyName(aNodeOrID) + " focus";
   }
 }
 
 /**
  * Focus invoker. Focus the HTML body of content document of iframe.
  */
-function synthFocusOnFrame(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthFocusOnFrame(aNodeOrID, aCheckerOrEventSeq)
 {
-  this.__proto__ = new synthAction(getNode(aNodeOrID).contentDocument,
-                                   aCheckerOrEventSeq, aEventType);
-  
+  var frameDoc = getNode(aNodeOrID).contentDocument;
+  var checkerOrEventSeq =
+    aCheckerOrEventSeq ? aCheckerOrEventSeq : new focusChecker(frameDoc);
+  this.__proto__ = new synthAction(frameDoc, checkerOrEventSeq);
+
   this.invoke = function synthFocus_invoke()
   {
     this.DOMNode.body.focus();
   }
-  
+
   this.getID = function synthFocus_getID() 
   { 
     return prettyName(aNodeOrID) + " frame document focus";
   }
 }
 
 /**
+ * Change the current item when the widget doesn't have a focus.
+ */
+function changeCurrentItem(aID, aItemID)
+{
+  this.eventSeq = [ new nofocusChecker() ];
+
+  this.invoke = function changeCurrentItem_invoke()
+  {
+    var controlNode = getNode(aID);
+    var itemNode = getNode(aItemID);
+
+    // HTML
+    if (controlNode.localName == "input") {
+      if (controlNode.checked)
+        this.reportError();
+
+      controlNode.checked = true;
+      return;
+    }
+
+    if (controlNode.localName == "select") {
+      if (controlNode.selectedIndex == itemNode.index)
+        this.reportError();
+
+      controlNode.selectedIndex = itemNode.index;
+      return;
+    }
+
+    // XUL
+    if (controlNode.localName == "tree") {
+      if (controlNode.currentIndex == aItemID)
+        this.reportError();
+
+      controlNode.currentIndex = aItemID;
+      return;
+    }
+
+    if (controlNode.localName == "menulist") {
+      if (controlNode.selectedItem == itemNode)
+        this.reportError();
+
+      controlNode.selectedItem = itemNode;
+      return;
+    }
+
+    if (controlNode.currentItem == itemNode)
+      ok(false, "Error in test: proposed current item is already current" + prettyName(aID));
+
+    controlNode.currentItem = itemNode;
+  }
+
+  this.getID = function changeCurrentItem_getID()
+  {
+    return "current item change for " + prettyName(aID);
+  }
+
+  this.reportError = function changeCurrentItem_reportError()
+  {
+    ok(false,
+       "Error in test: proposed current item '" + aItemID + "' is already current");
+  }
+}
+
+/**
+ * Toggle top menu invoker.
+ */
+function toggleTopMenu(aID, aCheckerOrEventSeq)
+{
+  this.__proto__ = new synthKey(aID, "VK_ALT", null,
+                                aCheckerOrEventSeq);
+
+  this.getID = function toggleTopMenu_getID()
+  {
+    return "toggle top menu on " + prettyName(aID);
+  }
+}
+
+/**
+ * Context menu invoker.
+ */
+function synthContextMenu(aID, aCheckerOrEventSeq)
+{
+  this.__proto__ = new synthClick(aID, aCheckerOrEventSeq,
+                                  { button: 0, type: "contextmenu" });
+
+  this.getID = function synthContextMenu_getID()
+  {
+    return "context menu on " + prettyName(aID);
+  }
+}
+
+/**
+ * Open combobox, autocomplete and etc popup, check expandable states.
+ */
+function openCombobox(aComboboxID)
+{
+  this.eventSeq = [
+    new stateChangeChecker(STATE_EXPANDED, false, true, aComboboxID)
+  ];
+
+  this.invoke = function openCombobox_invoke()
+  {
+    getNode(aComboboxID).focus();
+    synthesizeKey("VK_DOWN", { altKey: true });
+  }
+
+  this.getID = function openCombobox_getID()
+  {
+    return "open combobox " + prettyName(aComboboxID);
+  }
+}
+
+/**
+ * Close combobox, autocomplete and etc popup, check expandable states.
+ */
+function closeCombobox(aComboboxID)
+{
+  this.eventSeq = [
+    new stateChangeChecker(STATE_EXPANDED, false, false, aComboboxID)
+  ];
+
+  this.invoke = function closeCombobox_invoke()
+  {
+    synthesizeKey("VK_ESCAPE", { });
+  }
+
+  this.getID = function closeCombobox_getID()
+  {
+    return "close combobox " + prettyName(aComboboxID);
+  }
+}
+
+
+/**
  * Select all invoker.
  */
-function synthSelectAll(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthSelectAll(aNodeOrID, aCheckerOrEventSeq)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq);
 
   this.invoke = function synthSelectAll_invoke()
   {
     if (this.DOMNode instanceof Components.interfaces.nsIDOMHTMLInputElement ||
         this.DOMNode instanceof Components.interfaces.nsIDOMXULTextBoxElement) {
       this.DOMNode.select();
 
     } else {
@@ -965,16 +1230,18 @@ function invokerChecker(aEventType, aTar
   this.__defineGetter__("target", invokerChecker_targetGetter);
   this.__defineSetter__("target", invokerChecker_targetSetter);
 
   // implementation details
   function invokerChecker_targetGetter()
   {
     if (typeof this.mTarget == "function")
       return this.mTarget.call(null, this.mTargetFuncArg);
+    if (typeof this.mTarget == "string")
+      return getNode(this.mTarget);
 
     return this.mTarget;
   }
 
   function invokerChecker_targetSetter(aValue)
   {
     this.mTarget = aValue;
     return this.mTarget;
@@ -998,16 +1265,35 @@ function invokerChecker(aEventType, aTar
  * Common invoker checker for async events.
  */
 function asyncInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
 {
   this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
                                       aTargetFuncArg, true);
 }
 
+function focusChecker(aTargetOrFunc, aTargetFuncArg)
+{
+  this.__proto__ = new invokerChecker(EVENT_FOCUS, aTargetOrFunc,
+                                      aTargetFuncArg, false);
+
+  this.unique = true; // focus event must be unique for invoker action
+
+  this.check = function focusChecker_check(aEvent)
+  {
+    testStates(aEvent.accessible, STATE_FOCUSED);
+  }
+}
+
+function nofocusChecker(aID)
+{
+  this.__proto__ = new focusChecker(aID);
+  this.unexpected = true;
+}
+
 /**
  * Text inserted/removed events checker.
  */
 function textChangeChecker(aID, aStart, aEnd, aTextOrFunc, aIsInserted)
 {
   this.target = getNode(aID);
   this.type = aIsInserted ? EVENT_TEXT_INSERTED : EVENT_TEXT_REMOVED;
 
@@ -1027,18 +1313,21 @@ function textChangeChecker(aID, aStart, 
     is(aEvent.modifiedText, modifiedText,
        "Wrong " + changeInfo + " text for " + prettyName(aID));
   }
 }
 
 /**
  * Caret move events checker.
  */
-function caretMoveChecker(aCaretOffset)
+function caretMoveChecker(aCaretOffset, aTargetOrFunc, aTargetFuncArg)
 {
+  this.__proto__ = new invokerChecker(EVENT_TEXT_CARET_MOVED,
+                                      aTargetOrFunc, aTargetFuncArg);
+
   this.check = function caretMoveChecker_check(aEvent)
   {
     is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
        aCaretOffset,
        "Wrong caret offset for " + prettyName(aEvent.accessible));
   }
 }
 
@@ -1363,33 +1652,23 @@ function sequenceItem(aProcessor, aEvent
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Event queue invokers
 
 /**
  * Invoker base class for prepare an action.
  */
-function synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType)
+function synthAction(aNodeOrID, aCheckerOrEventSeq)
 {
   this.DOMNode = getNode(aNodeOrID);
 
-  this.checker = null;
   if (aCheckerOrEventSeq) {
     if (aCheckerOrEventSeq instanceof Array) {
       this.eventSeq = aCheckerOrEventSeq;
     } else {
-      this.checker = aCheckerOrEventSeq;
-      this.checker.target = this.DOMNode;
+      this.eventSeq = [ aCheckerOrEventSeq ];
     }
   }
 
-  if (aEventType)
-    this.eventSeq = [ new invokerChecker(aEventType, this.DOMNode) ];
-
-  this.check = function synthAction_check(aEvent)
-  {
-    if (this.checker)
-      this.checker.check(aEvent);
-  }
-
-  this.getID = function synthAction_getID() { return aNodeOrID + " action"; }
+  this.getID = function synthAction_getID()
+    { return prettyName(aNodeOrID) + " action"; }
 }
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -58,21 +58,30 @@ include $(topsrcdir)/config/rules.mk
 		test_caretmove.html \
 		test_caretmove.xul \
 		test_coalescence.html \
 		test_contextmenu.html \
 		test_docload.html \
 		test_docload.xul \
 		test_dragndrop.html \
 		test_flush.html \
-		test_focus.html \
-		test_focus.xul \
+		test_focus_aria_activedescendant.html \
+		test_focus_autocomplete.xul \
+		test_focus_browserui.xul \
+		test_focus_contextmenu.xul \
+		test_focus_controls.html \
+		test_focus_dialog.html \
+		test_focus_doc.html \
+		test_focus_general.html \
+		test_focus_general.xul \
+		test_focus_listcontrols.xul \
+		test_focus_menu.xul \
 		test_focus_name.html \
+		test_focus_selects.html \
 		test_focus_tree.xul \
-		test_focusdoc.html \
 		test_menu.xul \
 		test_mutation.html \
 		test_mutation.xhtml \
 		test_scroll.xul \
 		test_selection.html \
 		test_statechange.html \
 		test_text_alg.html \
 		test_text.html \
--- a/accessible/tests/mochitest/events/test_aria_menu.html
+++ b/accessible/tests/mochitest/events/test_aria_menu.html
@@ -17,22 +17,27 @@
           src="../states.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
     const kViaDisplayStyle = 0;
     const kViaVisibilityStyle = 1;
 
-    function focusMenu(aMenuBarID, aMenuID)
+    function focusMenu(aMenuBarID, aMenuID, aActiveMenuBarID)
     {
-      this.eventSeq = [
-        new invokerChecker(EVENT_MENU_START, getNode(aMenuBarID)),
-        new invokerChecker(EVENT_FOCUS, getNode(aMenuID)),
-      ];
+      this.eventSeq = [];
+
+      if (aActiveMenuBarID) {
+        this.eventSeq.push(new invokerChecker(EVENT_MENU_END,
+                                              getNode(aActiveMenuBarID)));
+      }
+
+      this.eventSeq.push(new invokerChecker(EVENT_MENU_START, getNode(aMenuBarID)));
+      this.eventSeq.push(new invokerChecker(EVENT_FOCUS, getNode(aMenuID)));
 
       this.invoke = function focusMenu_invoke()
       {
         getNode(aMenuID).focus();
       }
 
       this.getID = function focusMenu_getID()
       {
@@ -138,24 +143,25 @@
       }
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
 
     var gQueue = null;
 
-    //gA11yEventDumpID = "eventdump";
-    //gA11yEventDumpToConsole = true;
+    //gA11yEventDumpID = "eventdump"; // debuging
+    //gA11yEventDumpToConsole = true; // debuging
 
     function doTests()
     {
       gQueue = new eventQueue();
 
-      gQueue.push(new focusMenu("menubar", "menu-file"));
+      gQueue.push(new focusMenu("menubar2", "menu-help"));
+      gQueue.push(new focusMenu("menubar", "menu-file", "menubar2"));
       gQueue.push(new showMenu("menupopup-file", "menu-file", kViaDisplayStyle));
       gQueue.push(new closeMenu("menupopup-file", "menu-file", kViaDisplayStyle));
       gQueue.push(new showMenu("menupopup-edit", "menu-edit", kViaVisibilityStyle));
       gQueue.push(new closeMenu("menupopup-edit", "menu-edit", kViaVisibilityStyle));
       gQueue.push(new focusInsideMenu("menu-edit", "menubar"));
       gQueue.push(new blurMenu("menubar"));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
@@ -178,16 +184,21 @@
      title="Menupopup end event isn't fired for ARIA menus">
     Mozilla Bug 614829
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=615189"
      title="Clean up FireAccessibleFocusEvent">
     Mozilla Bug 615189
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+     title="Rework accessible focus handling">
+    Mozilla Bug 673958
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="menubar" role="menubar">
     <div id="menu-file" role="menuitem" tabindex="0">
@@ -200,14 +211,22 @@
     <div id="menu-edit" role="menuitem" tabindex="0">
       Edit
       <div id="menupopup-edit" role="menu" style="visibility: hidden;">
         <div id="menuitem-undo" role="menuitem" tabindex="0">Undo</div>
         <div id="menuitem-redo" role="menuitem" tabindex="0">Redo</div>
       </div>
     </div>
   </div>
+  <div id="menubar2" role="menubar">
+    <div id="menu-help" role="menuitem" tabindex="0">
+      Help
+      <div id="menupopup-help" role="menu" style="display: none;">
+        <div id="menuitem-about" role="menuitem" tabindex="0">About</div>
+      </div>
+    </div>
+  </div>
   <div tabindex="0" id="outsidemenu">outsidemenu</div>
 
   <div id="eventdump"></div>
 
 </body>
 </html>
--- a/accessible/tests/mochitest/events/test_attrs.html
+++ b/accessible/tests/mochitest/events/test_attrs.html
@@ -18,18 +18,21 @@
   <script type="application/javascript"
           src="../attributes.js"></script>
 
   <script type="application/javascript">
 
     /**
      * Test "event-from-input" object attribute.
      */
-    function checker(aValue, aNoTargetID)
+    function eventFromInputChecker(aEventType, aID, aValue, aNoTargetID)
     {
+      this.type = aEventType;
+      this.target = getAccessible(aID);
+
       this.noTarget = getNode(aNoTargetID);
 
       this.check = function checker_check(aEvent)
       {
         testAttrs(aEvent.accessible, { "event-from-input": aValue }, true);
 
         var accessible = getAccessible(this.noTarget);
         testAbsentAttrs(accessible, { "event-from-input": "" });
@@ -37,26 +40,31 @@
     }
 
     /**
      * Do tests.
      */
     var gQueue = null;
 
     // gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true; // debug stuff
 
     function doTests()
     {
       gQueue = new eventQueue();
 
       var id = "textbox", noTargetId = "textarea";
-      gQueue.push(new synthFocus(id, new checker("false", noTargetId), EVENT_FOCUS));
-      
+      var checker =
+        new eventFromInputChecker(EVENT_FOCUS, id, "false", noTargetId);
+      gQueue.push(new synthFocus(id, checker));
+
       if (!MAC) { // Mac failure is bug 541093
-        gQueue.push(new synthHomeKey(id, new checker("false", noTargetId), EVENT_TEXT_CARET_MOVED));
+        var checker =
+          new eventFromInputChecker(EVENT_TEXT_CARET_MOVED, id, "false", noTargetId);
+        gQueue.push(new synthHomeKey(id, checker));
       }
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
--- a/accessible/tests/mochitest/events/test_caretmove.html
+++ b/accessible/tests/mochitest/events/test_caretmove.html
@@ -15,19 +15,19 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
     /**
      * Click checker.
      */
-    function clickChecker(aCaretOffset, aExtraNodeOrID, aExtraCaretOffset)
+    function clickChecker(aCaretOffset, aID, aExtraNodeOrID, aExtraCaretOffset)
     {
-      this.__proto__ = new caretMoveChecker(aCaretOffset);
+      this.__proto__ = new caretMoveChecker(aCaretOffset, aID);
 
       this.extraNode = getNode(aExtraNodeOrID);
 
       this.check = function clickChecker_check(aEvent)
       {
         this.__proto__.check(aEvent);
 
         if (this.extraNode) {
@@ -57,40 +57,43 @@
     {
       todo(false, "enable commented tests Bug 510128 is fixed");
       // test no focused accessibles
       //testCaretOffset("textbox", -1);
       //testCaretOffset("textarea", -1);
       testCaretOffset("p", -1);
 
       // test caret move events and caret offsets
-      gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
+      gQueue = new eventQueue();
 
       var id = "textbox";
-      gQueue.push(new synthFocus(id, new caretMoveChecker(5)));
-      gQueue.push(new synthSelectAll(id, new caretMoveChecker(5)));
-      gQueue.push(new synthClick(id, new caretMoveChecker(0)));
-      gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
+      gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
+      gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
+      gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+      gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
 
       id = "textarea";
-      gQueue.push(new synthClick(id, new caretMoveChecker(0)));
-      gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
-      gQueue.push(new synthDownKey(id, new caretMoveChecker(12)));
+      gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+      gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+      gQueue.push(new synthDownKey(id, new caretMoveChecker(12, id)));
 
       id = "p";
-      gQueue.push(new synthClick(id, new caretMoveChecker(0)));
-      gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
-      gQueue.push(new synthDownKey(id, new caretMoveChecker(6)));
+      gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+      gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+      gQueue.push(new synthDownKey(id, new caretMoveChecker(6, id)));
+
+      id = "p1_in_div";
+      gQueue.push(new synthClick(id, new clickChecker(0, id, "p2_in_div", -1)));
 
-      gQueue.push(new synthClick("p1_in_div",
-                                 new clickChecker(0, "p2_in_div", -1)));
-
-      gQueue.push(new synthShiftTab("p", new caretMoveChecker(0)));
-      gQueue.push(new synthShiftTab("textarea", new caretMoveChecker(12)));
-      gQueue.push(new synthTab("p", new caretMoveChecker(0)));
+      id = "p";
+      gQueue.push(new synthShiftTab(id, new caretMoveChecker(0, id)));
+      id = "textarea";
+      gQueue.push(new synthShiftTab(id, new caretMoveChecker(12, id)));
+      id = "p";
+      gQueue.push(new synthTab(id, new caretMoveChecker(0, id)));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
--- a/accessible/tests/mochitest/events/test_caretmove.xul
+++ b/accessible/tests/mochitest/events/test_caretmove.xul
@@ -17,29 +17,29 @@
           src="../events.js" />
 
   <script type="application/javascript">
     /**
      * Do tests.
      */
 
     //gA11yEventDumpID = "eventdump"; // debug stuff
-    gA11yEventDumpToConsole = true;
+    //gA11yEventDumpToConsole = true;
 
     var gQueue = null;
 
     function doTests()
     {
       gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
 
       var id = "textbox";
-      gQueue.push(new synthFocus(id, new caretMoveChecker(5)));
-      gQueue.push(new synthSelectAll(id, new caretMoveChecker(5)));
-      gQueue.push(new synthHomeKey(id, new caretMoveChecker(0)));
-      gQueue.push(new synthRightKey(id, new caretMoveChecker(1)));
+      gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
+      gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
+      gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, id)));
+      gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 
rename from accessible/tests/mochitest/test_aria_activedescendant.html
rename to accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
--- a/accessible/tests/mochitest/test_aria_activedescendant.html
+++ b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
@@ -1,53 +1,82 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=429547
 -->
 <head>
-  <title>aria-activedescendant property chrome tests</title>
+  <title>aria-activedescendant focus tests</title>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
-          src="common.js"></script>
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
 
   <script type="application/javascript">
-    const ELEMENT_NODE = nsIDOMNode.ELEMENT_NODE;
+    //gA11yEventDumpToConsole = true; // debugging
+
+    function changeARIAActiveDescendant(aID, aItemID)
+    {
+      this.eventSeq = [
+        new focusChecker(aItemID)
+      ];
+
+      this.invoke = function changeARIAActiveDescendant_invoke()
+      {
+        getNode(aID).setAttribute("aria-activedescendant", aItemID);
+      }
+
+      this.getID = function changeARIAActiveDescendant_getID()
+      {
+        return "change aria-activedescendant on " + aItemID;
+      }
+    }
 
-    var gContainerFocused = false;
+    function insertItemNFocus(aID, aNewItemID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, aNewItemID),
+        new focusChecker(aNewItemID)
+      ];
 
+      this.invoke = function insertItemNFocus_invoke()
+      {
+        var container  = getNode(aID);
+        var itemNode = document.createElement("div");
+        itemNode.setAttribute("id", aNewItemID);
+        itemNode.textContent = "item3";
+        container.appendChild(itemNode);
+
+        container.setAttribute("aria-activedescendant", aNewItemID);
+      }
+
+      this.getID = function insertItemNFocus_getID()
+      {
+        return "insert new node and focus it with ID: " + aNewItemID;
+      }
+    }
+
+    var gQueue = null;
     function doTest()
     {
-      var focusHandler = {
-        handleEvent: function handleEvent(aEvent) {
-          var target = aEvent.target;
-          if (target.nodeType ==  ELEMENT_NODE &&
-              target.getAttribute("id") == "container")
-            gContainerFocused = true;
-        }
-      };
-
-      var container = document.getElementById("container");
-      container.addEventListener("focus", focusHandler, false);
+      gQueue = new eventQueue();
 
-      var item2Acc = getAccessible("item2");
-      if (item2Acc) {
-        item2Acc.takeFocus();
-  
-        ok(gContainerFocused,
-           "Container with active descendant didn't receive the focus.");
-      }
+      gQueue.push(new synthFocus("container", new focusChecker("item1")));
+      gQueue.push(new changeARIAActiveDescendant("container", "item2"));
+      todo(false, "No focus for inserted element, bug 687011");
+      //gQueue.push(new insertItemNFocus("container", "item3"));
 
-      container.removeEventListener("focus", focusHandler, false);
-
-      SimpleTest.finish();
+      gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
@@ -56,15 +85,14 @@ https://bugzilla.mozilla.org/show_bug.cg
      title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
     Mozilla Bug 429547
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <div aria-activedescendant="item1" id="container" tabindex="1">
-    <div id="item1">item1</div>
-    <div id="item2">item2</div>
-    <div id="item3">item3</div>
+  <div role="listbox" aria-activedescendant="item1" id="container" tabindex="1">
+    <div role="listitem" id="item1">item1</div>
+    <div role="listitem" id="item2">item2</div>
   </div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -0,0 +1,463 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Accessible focus event testing">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript"
+          src="../autocomplete.js" />
+
+  <script type="application/javascript">
+  <![CDATA[
+    ////////////////////////////////////////////////////////////////////////////
+    // Hacky stuffs
+
+    // This is the hack needed for searchbar work outside of browser.
+    function getBrowser()
+    {
+      return {
+        mCurrentBrowser: { engines: new Array() }
+      };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function loadFormAutoComplete(aIFrameID)
+    {
+      this.iframeNode = getNode(aIFrameID);
+      this.iframe = getAccessible(aIFrameID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.iframe)
+      ];
+
+      this.invoke = function loadFormAutoComplete_invoke()
+      {
+        var url = "data:text/html,<html><body><form id='form'>" +
+          "<input id='input' name='a11ytest-formautocomplete'>" +
+          "</form></body></html>";
+        this.iframeNode.setAttribute("src", url);
+      }
+
+      this.getID = function loadFormAutoComplete_getID()
+      {
+        return "load form autocomplete page";
+      }
+    }
+
+    function initFormAutoCompleteBy(aIFrameID, aAutoCompleteValue)
+    {
+      this.iframe = getAccessible(aIFrameID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.iframe)
+      ];
+
+      this.invoke = function initFormAutoCompleteBy_invoke()
+      {
+        var iframeDOMDoc = getFormAutoCompleteDOMDoc(aIFrameID);
+
+        var inputNode = iframeDOMDoc.getElementById("input");
+        inputNode.value = aAutoCompleteValue;
+        var formNode = iframeDOMDoc.getElementById("form");
+        formNode.submit();
+      }
+
+      this.getID = function initFormAutoCompleteBy_getID()
+      {
+        return "init form autocomplete by '" + aAutoCompleteValue + "'";
+      }
+    }
+
+    function removeChar(aID, aCheckerOrEventSeq)
+    {
+      this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
+
+      this.invoke = function removeChar_invoke()
+      {
+        synthesizeKey("VK_LEFT", { shiftKey: true });
+        synthesizeKey("VK_DELETE", {});
+      }
+
+      this.getID = function removeChar_getID()
+      {
+        return "remove char on " + prettyName(aID);
+      }
+    }
+
+    function replaceOnChar(aID, aChar, aCheckerOrEventSeq)
+    {
+      this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
+
+      this.invoke = function replaceOnChar_invoke()
+      {
+        this.DOMNode.select();
+        synthesizeKey(aChar, {});
+      }
+
+      this.getID = function replaceOnChar_getID()
+      {
+        return "replace on char '" + aChar + "' for" + prettyName(aID);
+      }
+    }
+
+    function focusOnMouseOver(aIDFunc, aIDFuncArg)
+    {
+      this.eventSeq = [ new focusChecker(aIDFunc, aIDFuncArg) ];
+
+      this.invoke = function focusOnMouseOver_invoke()
+      {
+        this.id = aIDFunc.call(null, aIDFuncArg);
+        this.node = getNode(this.id);
+        this.window = this.node.ownerDocument.defaultView;
+
+        if (this.node.localName == "tree") {
+          var tree = getAccessible(this.node);
+          var accessible = getAccessible(this.id);
+          if (tree != accessible) {
+            var itemX = {}, itemY = {}, treeX = {}, treeY = {};
+            accessible.getBounds(itemX, itemY, {}, {});
+            tree.getBounds(treeX, treeY, {}, {});
+            this.x = itemX.value - treeX.value;
+            this.y = itemY.value - treeY.value;
+          }
+        }
+
+        // Generate mouse move events in timeouts until autocomplete popup list
+        // doesn't have it, the reason is do that because autocomplete popup
+        // ignores mousemove events firing in too short range.
+        synthesizeMouse(this.node, this.x, this.y, { type: "mousemove" });
+        this.doMouseMoveFlood(this);
+      }
+
+      this.finalCheck = function focusOnMouseOver_getID()
+      {
+        this.isFlooding = false;
+      }
+
+      this.getID = function focusOnMouseOver_getID()
+      {
+        return "mouse over on " + prettyName(aIDFunc.call(null, aIDFuncArg));
+      }
+
+      this.doMouseMoveFlood = function focusOnMouseOver_doMouseMoveFlood(aThis)
+      {
+        synthesizeMouse(aThis.node, aThis.x + 1, aThis.y + 1,
+                        { type: "mousemove" }, aThis.window);
+
+        if (aThis.isFlooding)
+          aThis.window.setTimeout(aThis.doMouseMoveFlood, 0, aThis);
+      }
+
+      this.id = null;
+      this.node = null;
+      this.window = null;
+
+      this.isFlooding = true;
+      this.x = 1;
+      this.y = 1;
+    }
+
+    function selectByClick(aIDFunc, aIDFuncArg,
+                           aFocusTargetFunc, aFocusTargetFuncArg)
+    {
+      this.eventSeq = [ new focusChecker(aFocusTargetFunc, aFocusTargetFuncArg) ];
+
+      this.invoke = function selectByClick_invoke()
+      {
+        var id = aIDFunc.call(null, aIDFuncArg);
+        var node = getNode(id);
+        var targetWindow = node.ownerDocument.defaultView;
+
+        var x = 0, y = 0;
+        if (node.localName == "tree") {
+          var tree = getAccessible(node);
+          var accessible = getAccessible(id);
+          if (tree != accessible) {
+            var itemX = {}, itemY = {}, treeX = {}, treeY = {};
+            accessible.getBounds(itemX, itemY, {}, {});
+            tree.getBounds(treeX, treeY, {}, {});
+            x = itemX.value - treeX.value;
+            y = itemY.value - treeY.value;
+          }
+        }
+
+        synthesizeMouse(node, x + 1, y + 1, {}, targetWindow);
+      }
+
+      this.getID = function selectByClick_getID()
+      {
+        return "select by click " + prettyName(aIDFunc.call(null, aIDFuncArg));
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Target getters
+
+    function getItem(aItemObj)
+    {
+      var autocomplete = aItemObj.autocomplete;
+      var autocompleteNode = aItemObj.autocompleteNode;
+
+      // XUL searchbar
+      if (autocompleteNode.localName == "searchbar") {
+        var popupNode = autocompleteNode._popup;
+        if (popupNode) {
+          var list = getAccessible(popupNode);
+          return list.getChildAt(aItemObj.index);
+        }
+      }
+
+      // XUL autocomplete
+      var popupNode = autocompleteNode.popup;
+      if (!popupNode) {
+        // HTML form autocomplete
+        var controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
+          getService(Components.interfaces.nsIAutoCompleteController);
+        popupNode = controller.input.popup.QueryInterface(nsIDOMNode);
+      }
+
+      if (popupNode) {
+        if ("richlistbox" in popupNode) {
+          var list = getAccessible(popupNode.richlistbox);
+          return list.getChildAt(aItemObj.index);
+        }
+
+        var list = getAccessible(popupNode.tree);
+        return list.getChildAt(aItemObj.index + 1);
+      }
+    }
+
+    function getTextEntry(aID)
+    {
+      // For form autocompletes the autocomplete widget and text entry widget
+      // is the same widget, for XUL autocompletes the text entry is a first
+      // child.
+      var localName = getNode(aID).localName;
+
+      // XUL autocomplete
+      if (localName == "textbox")
+        return getAccessible(aID).firstChild;
+
+      // HTML form autocomplete
+      if (localName == "input")
+        return getAccessible(aID);
+
+      // XUL searchbar
+      if (localName == "searchbar")
+        return getAccessible(getNode(aID).textbox.inputField);
+
+      return null;
+    }
+
+    function itemObj(aID, aIdx)
+    {
+      this.autocompleteNode = getNode(aID);
+
+      this.autocomplete = this.autocompleteNode.localName == "searchbar" ?
+        getAccessible(this.autocompleteNode.textbox) :
+        getAccessible(this.autocompleteNode);
+
+      this.index = aIdx;
+    }
+
+    function getFormAutoCompleteDOMDoc(aIFrameID)
+    {
+      return getNode(aIFrameID).contentDocument;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Test helpers
+
+    function queueAutoCompleteTests(aID)
+    {
+      // focus autocomplete text entry
+      gQueue.push(new synthFocus(aID, new focusChecker(getTextEntry, aID)));
+
+      // open autocomplete popup
+      gQueue.push(new synthDownKey(aID, new nofocusChecker()));
+
+      // select second option ('hi' option), focus on it
+      gQueue.push(new synthUpKey(aID,
+                                 new focusChecker(getItem, new itemObj(aID, 1))));
+
+      // choose selected option, focus on text entry
+      gQueue.push(new synthEnterKey(aID, new focusChecker(getTextEntry, aID)));
+
+      // remove char, autocomplete popup appears
+      gQueue.push(new removeChar(aID, new nofocusChecker()));
+
+      // select first option ('hello' option), focus on it
+      gQueue.push(new synthDownKey(aID,
+                                   new focusChecker(getItem, new itemObj(aID, 0))));
+
+      // mouse move on second option ('hi' option), focus on it
+      gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 1)));
+
+      // autocomplete popup updated (no selected item), focus on textentry
+      gQueue.push(new synthKey(aID, "e", null, new focusChecker(getTextEntry, aID)));
+
+      // select first option ('hello' option), focus on it
+      gQueue.push(new synthDownKey(aID,
+                                   new focusChecker(getItem, new itemObj(aID, 0))));
+
+      // popup gets hidden, focus on textentry
+      gQueue.push(new synthRightKey(aID, new focusChecker(getTextEntry, aID)));
+
+      // popup gets open, no focus
+      gQueue.push(new synthOpenComboboxKey(aID, new nofocusChecker()));
+
+      // select first option again ('hello' option), focus on it
+      gQueue.push(new synthDownKey(aID,
+                                   new focusChecker(getItem, new itemObj(aID, 0))));
+
+      // no option is selected, focus on text entry
+      gQueue.push(new synthUpKey(aID, new focusChecker(getTextEntry, aID)));
+
+      // close popup, no focus
+      gQueue.push(new synthEscapeKey(aID, new nofocusChecker()));
+
+      // autocomplete popup appears (no selected item), focus stays on textentry
+      gQueue.push(new replaceOnChar(aID, "h", new nofocusChecker()));
+
+      // mouse move on first option ('hello' option), focus on it
+      gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 0)));
+
+      // click first option ('hello' option), popup closes, focus on text entry
+      gQueue.push(new selectByClick(getItem, new itemObj(aID, 0), getTextEntry, aID));
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Tests
+
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true; // debug stuff
+
+    var gInitQueue = null;
+    function initTests()
+    {
+      // register 'test-a11y-search' autocomplete search
+      initAutoComplete([ "hello", "hi" ],
+                       [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
+      gInitQueue = new eventQueue();
+      gInitQueue.push(new loadFormAutoComplete("iframe"));
+      gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello"));
+      gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
+      gInitQueue.onFinish = function initQueue_onFinish()
+      {
+        SimpleTest.executeSoon(doTests);
+        return DO_NOT_FINISH_TEST;
+      }
+      gInitQueue.invoke();
+    }
+
+    var gQueue = null;
+    function doTests()
+    {
+      // Test focus events.
+      gQueue = new eventQueue();
+
+      ////////////////////////////////////////////////////////////////////////////
+      // tree popup autocomplete tests
+      queueAutoCompleteTests("autocomplete");
+
+      ////////////////////////////////////////////////////////////////////////////
+      // richlistbox popup autocomplete tests
+      queueAutoCompleteTests("richautocomplete");
+
+      ////////////////////////////////////////////////////////////////////////////
+      // HTML form autocomplete tests
+      queueAutoCompleteTests(getFormAutoCompleteDOMDoc("iframe").getElementById("input"));
+
+      ////////////////////////////////////////////////////////////////////////////
+      // searchbar tests
+
+      // focus searchbar, focus on text entry
+      gQueue.push(new synthFocus("searchbar",
+                                 new focusChecker(getTextEntry, "searchbar")));
+      // open search engine popup, no focus
+      gQueue.push(new synthOpenComboboxKey("searchbar", new nofocusChecker()));
+      // select first item, focus on it
+      gQueue.push(new synthDownKey("searchbar",
+                                   new focusChecker(getItem, new itemObj("searchbar", 0))));
+      // mouse over on second item, focus on it
+      gQueue.push(new focusOnMouseOver(getItem, new itemObj("searchbar", 1)));
+      // press enter key, focus on text entry
+      gQueue.push(new synthEnterKey("searchbar",
+                                    new focusChecker(getTextEntry, "searchbar")));
+      // click on search button, open popup, focus goes to document
+      var searchBtn = getAccessible(getNode("searchbar").searchButton);
+      gQueue.push(new synthClick(searchBtn, new focusChecker(document)));
+      // select first item, focus on it
+      gQueue.push(new synthDownKey("searchbar",
+                                   new focusChecker(getItem, new itemObj("searchbar", 0))));
+      // close popup, focus goes on document
+      gQueue.push(new synthEscapeKey("searchbar", new focusChecker(document)));
+
+      gQueue.onFinish = function()
+      {
+        // unregister 'test-a11y-search' autocomplete search
+        shutdownAutoComplete();
+      }
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(initTests);
+  ]]>
+  </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=383759"
+         title="Focus event inconsistent for search box autocomplete">
+        Mozilla Bug 383759
+      </a>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+         title="Rework accessible focus handling">
+        Mozilla Bug 673958
+      </a>
+      <p id="display"></p>
+      <div id="content" style="display: none"></div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+      <textbox id="autocomplete" type="autocomplete"
+               autocompletesearch="test-a11y-search"/>
+
+      <textbox id="richautocomplete" type="autocomplete"
+               autocompletesearch="test-a11y-search"
+               autocompletepopup="richpopup"/>
+      <panel id="richpopup" type="autocomplete-richlistbox" noautofocus="true"/>
+
+      <iframe id="iframe"/>
+
+      <searchbar id="searchbar"/>
+
+      <vbox id="eventdump"/>
+    </vbox>
+  </hbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_browserui.xul
@@ -0,0 +1,178 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Accessibility Loading Document Events Test.">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+  <![CDATA[
+    ////////////////////////////////////////////////////////////////////////////
+    // Helpers
+
+    function tabBrowser()
+    {
+      return gBrowserWnd.gBrowser;
+    }
+
+    function currentBrowser()
+    {
+      return tabBrowser().selectedBrowser;
+    }
+
+    function currentTabDocument()
+    {
+      return currentBrowser().contentDocument;
+    }
+
+    function inputInDocument()
+    {
+      var tabdoc = currentTabDocument();
+      return tabdoc.getElementById("input");
+    }
+
+    function urlbarInput()
+    {
+      return gBrowserWnd.document.getElementById("urlbar").inputField;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function loadURI(aURI)
+    {
+      this.invoke = function loadURI_invoke()
+      {
+        tabBrowser().loadURI(aURI);
+      }
+
+      this.eventSeq = [
+        new focusChecker(currentTabDocument)
+      ];
+
+      this.getID = function loadURI_getID()
+      {
+        return "load uri " + aURI;
+      }
+    }
+
+    function goBack()
+    {
+      this.invoke = function goBack_invoke()
+      {
+        tabBrowser().goBack();
+      }
+
+      this.eventSeq = [
+        new focusChecker(inputInDocument)
+      ];
+
+      this.getID = function goBack_getID()
+      {
+        return "go back one page in history ";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Testing
+
+    var gInputDocURI = "data:text/html,<html><input id='input'></html>";
+    var gButtonDocURI = "data:text/html,<html><input id='input' type='button' value='button'></html>";
+
+    var gBrowserWnd = null;
+    function loadBrowser()
+    {
+      gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank",
+                                      "chrome,all,dialog=no", gInputDocURI);
+
+      addA11yLoadEvent(startTests, gBrowserWnd);
+    }
+
+    function startTests()
+    {
+      // Wait for tab load.
+      var browser = gBrowserWnd.gBrowser.selectedBrowser;
+      addA11yLoadEvent(doTests, browser.contentWindow);
+    }
+
+    //gA11yEventDumpToConsole = true; // debug
+
+    var gQueue = null;
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      var tabDocument = currentTabDocument();
+      var input = inputInDocument();
+
+      // move focus to input inside tab document
+      gQueue.push(new synthTab(tabDocument, new focusChecker(input), gBrowserWnd));
+
+      // open new url, focus moves to new document
+      gQueue.push(new loadURI(gButtonDocURI));
+
+      // back one page in history, moves moves on input of tab document
+      gQueue.push(new goBack());
+
+      // open new tab, focus moves to urlbar
+      gQueue.push(new synthKey(tabDocument, "t", { ctrlKey: true, window: gBrowserWnd },
+                               new focusChecker(urlbarInput)));
+
+      // close open tab, focus goes on input of tab document
+      gQueue.push(new synthKey(tabDocument, "w", { ctrlKey: true, window: gBrowserWnd },
+                               new focusChecker(inputInDocument)));
+
+      gQueue.onFinish = function()
+      {
+        gBrowserWnd.close();
+      }
+      gQueue.invoke();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addLoadEvent(loadBrowser);
+  ]]>
+  </script>
+
+  <vbox 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=644452"
+       title="Focus not set when switching to cached document with back or forward if anything other than the document was last focused">
+      Mozilla Bug 644452
+    </a>
+    <a target="_blank"
+       href="https://bugzilla.mozilla.org/show_bug.cgi?id=665412"
+       title="Broken focus when returning to editable text field after closing a tab while focused in the Navigation toolbar">
+      Mozilla Bug 665412
+    </a>
+    <a target="_blank"
+       href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+       title="Rework accessible focus handling">
+      Mozilla Bug 673958
+    </a>
+    <p id="display"></p>
+    <div id="content" style="display: none">
+    </div>
+    <pre id="test">
+    </pre>
+  </body>
+
+  <vbox id="eventdump"></vbox>
+  </vbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_contextmenu.xul
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Context nenu focus testing">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript">
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true; // debug stuff
+
+    var gQueue = null;
+    function doTests()
+    {
+      // Test focus events.
+      gQueue = new eventQueue();
+
+      gQueue.push(new synthFocus("button"));
+      gQueue.push(new synthContextMenu("button",
+                                       new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
+      gQueue.push(new synthEscapeKey("contextmenu", new focusChecker("button")));
+
+      gQueue.push(new synthContextMenu("button",
+                                       new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
+      gQueue.push(new synthDownKey("contextmenu", new focusChecker("item1")));
+      gQueue.push(new synthDownKey("item1", new focusChecker("item2")));
+      gQueue.push(new synthRightKey("item2", new focusChecker("item2.1")));
+      gQueue.push(new synthEscapeKey("item2.1", new focusChecker("item2")));
+      gQueue.push(new synthEscapeKey("item2", new focusChecker("button")));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </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=673958"
+         title="Rework accessible focus handling">
+        Mozilla Bug 673958
+      </a>
+      <p id="display"></p>
+      <div id="content" style="display: none"></div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+      <button id="button" context="contextmenu" label="button"/>
+      <menupopup id="contextmenu">
+        <menuitem id="item1" label="item1"/>
+        <menu id="item2" label="item2">
+          <menupopup>
+            <menuitem id="item2.1" label="item2.1"/>
+          </menupopup>
+        </menu>
+      </menupopup>
+
+      <vbox id="eventdump"/>
+    </vbox>
+  </hbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_controls.html
@@ -0,0 +1,73 @@
+<html>
+
+<head>
+  <title>Accessible focus testing on HTML controls</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+    function doTests()
+    {
+      gQueue = new eventQueue(EVENT_FOCUS);
+
+      gQueue.push(new synthFocus("textbox"));
+      gQueue.push(new synthFocus("textarea"));
+      gQueue.push(new synthFocus("button1"));
+      gQueue.push(new synthFocus("button2"));
+      gQueue.push(new synthFocus("checkbox"));
+      gQueue.push(new synthFocus("radio1"));
+      gQueue.push(new synthDownKey("radio1", new focusChecker("radio2")));
+
+      // no focus events for checkbox or radio inputs when they are checked
+      // programmatically
+      gQueue.push(new changeCurrentItem("checkbox"));
+      gQueue.push(new changeCurrentItem("radio1"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+     title="Rework accessible focus handling">
+    Mozilla Bug 673958
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <input id="textbox">
+  <textarea id="textarea"></textarea>
+
+  <input id="button1" type="button" value="button">
+  <button id="button2">button</button>
+  <input id="checkbox" type="checkbox">
+  <input id="radio1" type="radio" name="radiogroup">
+  <input id="radio2" type="radio" name="radiogroup">
+
+  <div id="eventdump"></div>
+</body>
+</html>
rename from accessible/tests/mochitest/events/test_focus.html
rename to accessible/tests/mochitest/events/test_focus_dialog.html
--- a/accessible/tests/mochitest/events/test_focus.html
+++ b/accessible/tests/mochitest/events/test_focus_dialog.html
@@ -10,103 +10,121 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
 
   <script type="application/javascript">
     function openCloseDialog(aID)
     {
-      this.DOMNode = getNode(aID);
+      this.eventSeq = [
+        new focusChecker(getNode(aID))
+      ];
 
       this.invoke = function openCloseDialog_invoke()
       {
         var wnd = window.open("focus.html");
         wnd.close();
       }
 
       this.getID = function openCloseDialog_getID()
       {
-        return "Open close dialog on " + prettyName(aID);
+        return "Open close dialog while focus on " + prettyName(aID);
       }
     }
 
-    function focusElmWhileSubdocIsFocused(aID)
+    var gDialogWnd = null;
+    function getDialogDocument()
     {
-      this.DOMNode = getNode(aID);
+      return gDialogWnd.document;
+    }
 
-      this.invoke = function focusElmWhileSubdocIsFocused_invoke()
+    function openDialog(aID)
+    {
+      this.eventSeq = [
+        new focusChecker(getDialogDocument)
+      ];
+
+      this.invoke = function openDialog_invoke()
       {
-        this.DOMNode.focus();
+        gDialogWnd = window.open("focus.html");
       }
 
+      this.getID = function openDialog_getID()
+      {
+        return "Open dialog while focus on " + prettyName(aID);
+      }
+    }
+
+    function closeDialog(aID)
+    {
       this.eventSeq = [
-        new invokerChecker(EVENT_FOCUS, this.DOMNode)
+        new focusChecker(aID)
       ];
 
-      this.unexpectedEventSeq = [
-        new invokerChecker(EVENT_FOCUS, this.DOMNode.ownerDocument)
-      ];
+      this.invoke = function closeDialog_invoke()
+      {
+        gDialogWnd.close();
+      }
 
-      this.getID = function focusElmWhileSubdocIsFocused_getID()
+      this.getID = function closeDialog_getID()
       {
-        return "Focus element while subdocument is focused " + prettyName(aID);
+        return "Close dialog while focus on " + prettyName(aID);
       }
     }
 
     function showNFocusAlertDialog()
     {
       this.ID = "alertdialog";
       this.DOMNode = getNode(this.ID);
 
       this.invoke = function showNFocusAlertDialog_invoke()
       {
         document.getElementById(this.ID).style.display = 'block';
         document.getElementById(this.ID).focus();
       }
 
       this.eventSeq = [
         new invokerChecker(EVENT_SHOW, this.DOMNode),
-        new invokerChecker(EVENT_FOCUS, this.DOMNode)
+        new focusChecker(this.DOMNode)
       ];
 
       this.getID = function showNFocusAlertDialog_getID()
       {
         return "Show and focus alert dialog " + prettyName(this.ID);
       }
     }
 
     /**
      * Do tests.
      */
 
     //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
 
     var gQueue = null;
 
     function doTests()
     {
       gQueue = new eventQueue(EVENT_FOCUS);
 
-      gQueue.push(new synthFocus("editablearea"));
-      gQueue.push(new synthFocus("textbox"));
-
       gQueue.push(new synthFocus("button"));
-      gQueue.push(new openCloseDialog("button"));
+      gQueue.push(new openDialog("button"));
+      gQueue.push(new closeDialog("button"));
 
       var frameNode = getNode("editabledoc");
       gQueue.push(new synthFocusOnFrame(frameNode));
       gQueue.push(new openCloseDialog(frameNode.contentDocument));
 
-      gQueue.push(new focusElmWhileSubdocIsFocused("button"));
-
       gQueue.push(new showNFocusAlertDialog());
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
@@ -115,32 +133,25 @@
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=551679"
      title="focus is not fired for focused document when switching between windows">
     Mozilla Bug 551679
   </a>
   <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=352220"
-     title=" Inconsistent focus events when returning to a document frame">
-    Mozilla Bug 352220
-  </a>
-  <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=580464"
      title="Accessible focus incorrect after JS focus() but correct after switching apps or using menu bar">
     Mozilla Bug 580464
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <div id="editablearea" contentEditable="true">editable area</div>
-  <input id="textbox">
   <button id="button">button</button>
   <iframe id="editabledoc" src="focus.html"></iframe>
 
   <div id="alertdialog" style="display: none" tabindex="-1" role="alertdialog" aria-labelledby="title2" aria-describedby="desc2">
     <div id="title2">Blah blah</div>
     <div id="desc2">Woof woof woof.</div>
     <button>Close</button>
   </div>
rename from accessible/tests/mochitest/events/test_focusdoc.html
rename to accessible/tests/mochitest/events/test_focus_doc.html
--- a/accessible/tests/mochitest/events/test_focusdoc.html
+++ b/accessible/tests/mochitest/events/test_focus_doc.html
@@ -2,71 +2,58 @@
 
 <head>
   <title>Accessible document focus event testing</title>
 
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
-      src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
   <script type="application/javascript"
       src="../common.js"></script>
   <script type="application/javascript"
     src="../events.js"></script>
     <script type="application/javascript"
       src="../states.js"></script>
 
   <script type="application/javascript">
-
-    /**
-     * Focus invoker.
-     */
-    function takeFocus(aAcc)
-    {
-      this.DOMNode = aAcc; // xxx rename this expected property in events.js
-
-      this.invoke = function takeFocus_invoke()
-      {
-        this.DOMNode.takeFocus();
-      };
-
-      this.check = function takeFocus_check()
-      {
-        testStates(this.DOMNode, STATE_FOCUSABLE | STATE_FOCUSED);
-      };
-
-      this.getID = function takeFocus_getID() { return aAcc.name + " focus"; };
-    }
-
-
-    /**
-     * Do tests.
-     */
     var gQueue = null;
 
     //var gA11yEventDumpID = "eventdump";
+    //gA11yEventDumpToConsole = true;
 
     function doTests()
     {
       // setup
       var frameDoc = document.getElementById("iframe").contentDocument;
       frameDoc.designMode = "on";
       var frameDocAcc = getAccessible(frameDoc, [nsIAccessibleDocument]);
       var buttonAcc = getAccessible("b1");
 
+      var frame2Doc = document.getElementById("iframe2").contentDocument;
+      var frame2Input = frame2Doc.getElementById("input");
+      var frame2DocAcc = getAccessible(frame2Doc);
+      var frame2InputAcc = getAccessible(frame2Input);
+
       // Test focus events.
-      gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
+      gQueue = new eventQueue();
 
       // try to give focus to contentEditable frame twice to cover bug 512059
-      gQueue.push(new takeFocus(buttonAcc));
-      gQueue.push(new takeFocus(frameDocAcc));
-      gQueue.push(new takeFocus(buttonAcc));
-      gQueue.push(new takeFocus(frameDocAcc));
+      gQueue.push(new synthFocus(buttonAcc));
+      gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
+      gQueue.push(new synthFocus(buttonAcc));
+      gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
+
+      // focus on not editable document
+      gQueue.push(new synthFocus(frame2InputAcc));
+      gQueue.push(new synthShiftTab(frame2DocAcc, new focusChecker(frame2DocAcc)));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -78,23 +65,29 @@
     title="Can't set focus to designMode document via accessibility APIs">
    Mozilla Bug 512058
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=512059"
      title="Accessibility focus event never fired for designMode document after the first focus">
     Mozilla Bug 512059
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=618046"
+     title="No focus change event when Shift+Tab at top of screen">
+    Mozilla Bug 618046
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="eventdump"></div>
 
   <div id="testContainer">
   <button id="b1">a button</button>
   <iframe id="iframe" src="about:blank"></iframe>
   <button id="b2">a button</button>
+  <iframe id="iframe2" src="data:text/html,<html><input id='input'></html>"></iframe>
   </div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_general.html
@@ -0,0 +1,150 @@
+<html>
+
+<head>
+  <title>Accessible focus testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+    function focusElmWhileSubdocIsFocused(aID)
+    {
+      this.DOMNode = getNode(aID);
+
+      this.invoke = function focusElmWhileSubdocIsFocused_invoke()
+      {
+        this.DOMNode.focus();
+      }
+
+      this.eventSeq = [
+        new focusChecker(this.DOMNode)
+      ];
+
+      this.unexpectedEventSeq = [
+        new invokerChecker(EVENT_FOCUS, this.DOMNode.ownerDocument)
+      ];
+
+      this.getID = function focusElmWhileSubdocIsFocused_getID()
+      {
+        return "Focus element while subdocument is focused " + prettyName(aID);
+      }
+    }
+
+    function topMenuChecker()
+    {
+      this.type = EVENT_FOCUS;
+      this.match = function topMenuChecker_match(aEvent)
+      {
+        return aEvent.accessible.role == ROLE_PARENT_MENUITEM;
+      }
+    }
+
+    function contextMenuChecker()
+    {
+      this.type = EVENT_MENUPOPUP_START;
+      this.match = function contextMenuChecker_match(aEvent)
+      {
+        return aEvent.accessible.role == ROLE_MENUPOPUP;
+      }
+    }
+
+    function focusContextMenuItemChecker()
+    {
+      this.__proto__ = new focusChecker();
+
+      this.match = function focusContextMenuItemChecker_match(aEvent)
+      {
+        return aEvent.accessible.role == ROLE_MENUITEM;
+      }
+    }
+
+    /**
+     * Do tests.
+     */
+
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+
+    function doTests()
+    {
+      var frameDoc = document.getElementById("iframe").contentDocument;
+
+      var editableDoc = document.getElementById('editabledoc').contentDocument;
+      editableDoc.designMode = 'on';
+
+      gQueue = new eventQueue();
+
+      gQueue.push(new synthFocus("editablearea"));
+      gQueue.push(new synthFocus("navarea"));
+      gQueue.push(new synthTab("navarea", new focusChecker(frameDoc)));
+      gQueue.push(new focusElmWhileSubdocIsFocused("link"));
+
+      gQueue.push(new synthTab(editableDoc, new focusChecker(editableDoc)));
+      if (WIN) {
+        // Alt key is used to active menubar and focus menu item on Windows,
+        // other platforms requires setting a ui.key.menuAccessKeyFocuses
+        // preference.
+        gQueue.push(new toggleTopMenu(editableDoc, new topMenuChecker()));
+        gQueue.push(new toggleTopMenu(editableDoc, new focusChecker(editableDoc)));
+      }
+      gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
+      gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
+      gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
+
+      todo(false, "shift+tab doesn't issue the focus, see bug 684818");
+      //gQuee.push(new synthShiftTab("link", new focusChecker("link")));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=352220"
+     title="Inconsistent focus events when returning to a document frame">
+    Mozilla Bug 352220
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=550338"
+     title="Broken focus when returning to editable documents from menus">
+    Mozilla Bug 550338
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+     title="Rework accessible focus handling">
+    Mozilla Bug 673958
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="editablearea" contentEditable="true">editable area</div>
+  <div id="navarea" tabindex="0">navigable area</div>
+  <iframe id="iframe" src="data:text/html,<html></html>"></iframe>
+  <a id="link" href="">link</a>
+  <iframe id="editabledoc" src="about:blank"></iframe>
+
+  <div id="eventdump"></div>
+</body>
+</html>
rename from accessible/tests/mochitest/events/test_focus.xul
rename to accessible/tests/mochitest/events/test_focus_general.xul
--- a/accessible/tests/mochitest/events/test_focus.xul
+++ b/accessible/tests/mochitest/events/test_focus_general.xul
@@ -9,57 +9,102 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
           src="../events.js" />
 
   <script type="application/javascript">
-    /**
-     * Click menu item invoker.
-     */
-    function clickMenuItem(aNodeOrID, aFocusNodeOrID)
+    function getColorBtn(aBtnObj)
     {
-      this.DOMNode = getNode(aFocusNodeOrID);
-
-      this.invoke = function clickMenuItem_invoke()
-      {
-        synthesizeMouse(getNode(aNodeOrID), 1, 1, {});
-      }
-
-      this.getID = function clickMenuItem_getID()
-      {
-        return prettyName(aNodeOrID) + " click menu item";
-      }
+      var colorpicker = aBtnObj.colorpicker;
+      var container = colorpicker.firstChild;
+      var btn = container.getChildAt(aBtnObj.btnIndex);
+      return btn;
     }
-    
-    /**
-     * Do tests.
-     */
 
     //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true; // debug stuff
 
     var gQueue = null;
-
     function doTests()
     {
       // Test focus events.
-      gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
+      gQueue = new eventQueue();
 
       gQueue.push(new synthFocus("textbox"));
+      gQueue.push(new synthFocus("textbox_multiline"));
       gQueue.push(new synthFocus("scale"));
       gQueue.push(new synthFocusOnFrame("editabledoc"));
-      gQueue.push(new synthClick("menu"));
-      gQueue.push(new synthMouseMove("menuitem"));
-      gQueue.push(new clickMenuItem("menuitem",
-                                    getNode("editabledoc").contentDocument));
+      gQueue.push(new synthFocus("radioclothes",
+                                 new focusChecker("radiosweater")));
+      gQueue.push(new synthDownKey("radiosweater",
+                                   new focusChecker("radiojacket")));
+      gQueue.push(new synthFocus("checkbox"));
+      gQueue.push(new synthFocus("button"));
+      gQueue.push(new synthFocus("checkbutton"));
+      gQueue.push(new synthFocus("radiobutton"));
+
+      // focus menubutton
+      gQueue.push(new synthFocus("menubutton"));
+      // click menubutton, open popup, focus stays on menu button
+      gQueue.push(new synthClick("menubutton", new nofocusChecker()));
+      // select first menu item ("item 1"), focus on menu item
+      gQueue.push(new synthDownKey("menubutton", new focusChecker("mb_item1")));
+      // choose select menu item, focus gets back to menubutton
+      gQueue.push(new synthEnterKey("mb_item1", new focusChecker("menubutton")));
+      // press enter to open popup, focus stays on menubutton
+      gQueue.push(new synthEnterKey("menubutton", new nofocusChecker()));
+      // select second menu item ("item 2"), focus on menu item
+      gQueue.push(new synthUpKey("menubutton", new focusChecker("mb_item2")));
+
+      // clicking on button having associated popup doesn't change a focus
+      gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
+      // select first menu item ("item 1"), focus on menu item
+      gQueue.push(new synthDownKey("popupbutton", new focusChecker("bp_item1")));
+      // choose select menu item, focus gets back to menubutton
+      gQueue.push(new synthEnterKey("bp_item1", new focusChecker("menubutton")));
+      // show popup again for the next test
+      gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
+
+      // click menubutton of the 'menubutton' button while popup of button open.
+      gQueue.push(new synthClick("mbb", new focusChecker("mbb"), { where: "right" }));
+      // close popup, focus stays on menubutton, fire focus event
+      gQueue.push(new synthEscapeKey("mbb", new focusChecker("mbb")));
+      // click menubutton, open popup, focus stays on menubutton
+      gQueue.push(new synthClick("mbb", new nofocusChecker(), { where: "right" }));
+      // select first menu item ("item 1"), focus on menu item
+      gQueue.push(new synthDownKey("mbb", new focusChecker("mbb_item1")));
+      // choose select menu item, focus gets back to menubutton
+      gQueue.push(new synthEnterKey("mbb_item1", new focusChecker("mbb")));
+      // open popup, focus stays on menubutton
+      gQueue.push(new synthOpenComboboxKey("mbb", new nofocusChecker()));
+      // select second menu item ("item 2"), focus on menu item
+      gQueue.push(new synthUpKey("menubutton", new focusChecker("mbb_item2")));
+      // click on menu item of menubutton menu, focus menubutton
+      gQueue.push(new synthClick("mbb_item2", new focusChecker("mbb")));
+
+      // focus colorpicker button
+      gQueue.push(new synthFocus("colorpicker"));
+      // click on button, open popup, focus goes to current color button
+      var btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 0 };
+      var checker = new focusChecker(getColorBtn, btnObj);
+      gQueue.push(new synthClick("colorpicker", checker));
+      // select sibling color button, focus on it
+      btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 1 };
+      var checker = new focusChecker(getColorBtn, btnObj);
+      gQueue.push(new synthRightKey("colorpicker", checker));
+      // choose selected color button, close popup, focus on colorpicker button
+      gQueue.push(new synthEnterKey("colorpicker", new focusChecker("colorpicker")));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 
@@ -78,22 +123,49 @@
       <p id="display"></p>
       <div id="content" style="display: none"></div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox flex="1">
       <textbox id="textbox" value="hello"/>
+      <textbox id="textbox_multiline" multiline="true" value="hello"/>
       <scale id="scale" min="0" max="9" value="5"/>
-      <menubar>
-        <menu id="menu" label="menu">
-          <menupopup>
-            <menuitem id="menuitem" label="menuitem"/>
-          </menupopup>
-        </menu>
-      </menubar>
       <iframe id="editabledoc" src="focus.html"/>
+      <radiogroup id="radioclothes">
+        <radio id="radiosweater" label="radiosweater"/>
+        <radio id="radiocap" label="radiocap" disabled="true"/>
+        <radio id="radiojacket" label="radiojacket"/>
+      </radiogroup>
+      <checkbox id="checkbox" label="checkbox"/>
+      <button id="button" label="button"/>
+      <button id="checkbutton" type="checkbox" label="checkbutton"/>
+      <button id="radiobutton" type="radio" group="rbgroup" label="radio1"/>
+
+      <button id="menubutton" type="menu" label="menubutton">
+        <menupopup>
+          <menuitem id="mb_item1" label="item1"/>
+          <menuitem id="mb_item2" label="item2"/>
+        </menupopup>
+      </button>
+      <button id="mbb" type="menu-button" label="menubutton button">
+        <menupopup>
+          <menuitem id="mbb_item1" label="item1"/>
+          <menuitem id="mbb_item2" label="item2"/>
+        </menupopup>
+      </button>
+
+      <colorpicker id="colorpicker" type="button" label="color picker"
+                   color="#FFFFFF"/>
+
+      <popupset>
+        <menupopup id="backpopup" position="after_start">
+          <menuitem id="bp_item1" label="Page 1"/>
+          <menuitem id="bp_item2" label="Page 2"/>
+        </menupopup>
+      </popupset>
+      <button id="popupbutton" label="Pop Me Up" popup="backpopup"/>
 
       <vbox id="eventdump"/>
     </vbox>
   </hbox>
 </window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_listcontrols.xul
@@ -0,0 +1,179 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Accessible focus event testing">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript">
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true; // debug stuff
+
+    var gQueue = null;
+    function doTests()
+    {
+      // Test focus events.
+      gQueue = new eventQueue();
+
+      gQueue.push(new synthFocus("listbox", new focusChecker("lb_item1")));
+      gQueue.push(new synthDownKey("lb_item1", new focusChecker("lb_item2")));
+      gQueue.push(new synthTab("lb_item2", new focusChecker("mslb_item1")));
+      gQueue.push(new synthDownKey("mslb_item1", new focusChecker("mslb_item2"), { shiftKey: true }));
+      gQueue.push(new synthTab("mslb_item2", new focusChecker("emptylistbox")));
+      gQueue.push(new synthFocus("mcolumnlistbox", new focusChecker("mclb_item1")));
+      gQueue.push(new synthDownKey("mclb_item1", new focusChecker("mclb_item2")));
+      gQueue.push(new synthFocus("headerlistbox", new focusChecker("hlb_item1")));
+      gQueue.push(new synthDownKey("hlb_item1", new focusChecker("hlb_item2")));
+
+      gQueue.push(new synthFocus("richlistbox", new focusChecker("rlb_item1")));
+      gQueue.push(new synthDownKey("rlb_item1", new focusChecker("rlb_item2")));
+      gQueue.push(new synthFocus("multiselrichlistbox", new focusChecker("msrlb_item1")));
+      gQueue.push(new synthDownKey("msrlb_item1", new focusChecker("msrlb_item2"), { shiftKey: true }));
+      gQueue.push(new synthFocus("emptyrichlistbox", new focusChecker("emptyrichlistbox")));
+
+      gQueue.push(new synthFocus("menulist"));
+      gQueue.push(new synthClick("menulist", new focusChecker("ml_tangerine")));
+      gQueue.push(new synthDownKey("ml_tangerine", new focusChecker("ml_marmalade")));
+      gQueue.push(new synthEscapeKey("ml_marmalade", new focusChecker("menulist")));
+      gQueue.push(new synthDownKey("menulist", new nofocusChecker("ml_marmalade")));
+      gQueue.push(new synthOpenComboboxKey("menulist", new focusChecker("ml_marmalade")));
+      gQueue.push(new synthEnterKey("ml_marmalade", new focusChecker("menulist")));
+
+      var textentry = getAccessible("emenulist").firstChild;
+      gQueue.push(new synthFocus("emenulist", new focusChecker(textentry)));
+      gQueue.push(new synthDownKey(textentry, new nofocusChecker("eml_tangerine")));
+      gQueue.push(new synthUpKey(textentry, new focusChecker("eml_marmalade")));
+      gQueue.push(new synthEnterKey("eml_marmalade", new focusChecker(textentry)));
+      gQueue.push(new synthOpenComboboxKey("emenulist", new focusChecker("eml_marmalade")));
+      gQueue.push(new synthEscapeKey("eml_marmalade", new focusChecker(textentry)));
+
+      // no focus events for unfocused list controls when current item is
+      // changed.
+      gQueue.push(new synthFocus("emptylistbox"));
+
+      gQueue.push(new changeCurrentItem("listbox", "lb_item1"));
+      gQueue.push(new changeCurrentItem("richlistbox", "rlb_item1"));
+      gQueue.push(new changeCurrentItem("menulist", "ml_tangerine"));
+      gQueue.push(new changeCurrentItem("emenulist", "eml_tangerine"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </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=433418"
+         title="Accessibles for focused HTML Select elements are not getting focused state">
+        Mozilla Bug 433418
+      </a>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
+         title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
+        Mozilla Bug 474893
+      </a>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
+         title=" fire focus event on document accessible whenever the root or body element is focused">
+        Mozilla Bug 552368
+      </a>
+      <p id="display"></p>
+      <div id="content" style="display: none"></div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+      <listbox id="listbox" rows="3">
+        <listitem id="lb_item1" label="item1"/>
+        <listitem id="lb_item2" label="item1"/>
+      </listbox>
+      <listbox id="multisellistbox" rows="3" seltype="multiple">
+        <listitem id="mslb_item1" label="item1"/>
+        <listitem id="mslb_item2" label="item1"/>
+      </listbox>
+      <listbox id="emptylistbox" rows="3"/>
+      <listbox id="mcolumnlistbox" rows="3">
+        <listcols>
+          <listcol/>
+          <listcol/>
+        </listcols>
+        <listitem id="mclb_item1">
+          <listcell label="George"/>
+          <listcell label="House Painter"/>
+        </listitem>
+        <listitem id="mclb_item2">
+          <listcell label="Mary Ellen"/>
+          <listcell label="Candle Maker"/>
+        </listitem>
+      </listbox>
+      <listbox id="headerlistbox" rows="3">
+        <listhead>
+          <listheader label="Name"/>
+          <listheader label="Occupation"/>
+        </listhead>
+        <listcols>
+          <listcol/>
+          <listcol flex="1"/>
+        </listcols>
+        <listitem id="hlb_item1">
+          <listcell label="George"/>
+          <listcell label="House Painter"/>
+        </listitem>
+        <listitem id="hlb_item2">
+          <listcell label="Mary Ellen"/>
+          <listcell label="Candle Maker"/>
+        </listitem>
+      </listbox>
+
+      <richlistbox id="richlistbox">
+        <richlistitem id="rlb_item1">
+          <description>A XUL Description!</description>
+        </richlistitem>
+        <richlistitem id="rlb_item2">
+          <button label="A XUL Button"/>
+        </richlistitem>
+      </richlistbox>
+      <richlistbox id="multiselrichlistbox" seltype="multiple">
+        <richlistitem id="msrlb_item1">
+          <description>A XUL Description!</description>
+        </richlistitem>
+        <richlistitem id="msrlb_item2">
+          <button label="A XUL Button"/>
+        </richlistitem>
+      </richlistbox>
+      <richlistbox id="emptyrichlistbox" seltype="multiple"/>
+
+      <menulist id="menulist">
+        <menupopup>
+          <menuitem id="ml_tangerine" label="tangerine trees"/>
+          <menuitem id="ml_marmalade" label="marmalade skies"/>
+        </menupopup>
+      </menulist>
+      <menulist id="emenulist" editable="true">
+        <menupopup>
+          <menuitem id="eml_tangerine" label="tangerine trees"/>
+          <menuitem id="eml_marmalade" label="marmalade skies"/>
+        </menupopup>
+      </menulist>
+
+      <vbox id="eventdump"/>
+    </vbox>
+  </hbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_menu.xul
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Menu focus testing">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript">
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true; // debug stuff
+
+    var gQueue = null;
+    function doTests()
+    {
+      // Test focus events.
+      gQueue = new eventQueue();
+
+      if (WIN) {
+        gQueue.push(new toggleTopMenu("fruit", new focusChecker("fruit")));
+        gQueue.push(new synthRightKey("fruit", new focusChecker("vehicle")));
+        gQueue.push(new synthEscapeKey("vehicle", new focusChecker(document)));
+      }
+
+      // mouse move activate items but no focus event until menubar is active
+      gQueue.push(new synthMouseMove("fruit", new nofocusChecker("apple")));
+
+      // mouseover and click on menuitem makes it active before menubar is
+      // active
+      gQueue.push(new synthClick("fruit", new focusChecker("fruit")));
+
+      // mouseover on menuitem when menubar is active
+      gQueue.push(new synthMouseMove("apple", new focusChecker("apple")));
+
+      // keydown on disabled menuitem (disabled items are skipped on linux)
+      if (WIN)
+        gQueue.push(new synthDownKey("apple", new focusChecker("orange")));
+
+      // menu and menuitem are both active
+      // XXX: intermitent failure because two focus events may be coalesced,
+      // think to workaround or fix this issue, when done enable queue invoker
+      // below and remove next two.
+      //gQueue.push(new synthRightKey("apple",
+      //                              [ new focusChecker("vehicle"),
+      //                                new focusChecker("cycle")]));
+      gQueue.push(new synthClick("vehicle", new focusChecker("vehicle")));
+      gQueue.push(new synthMouseMove("cycle", new focusChecker("cycle")));
+
+      // open submenu
+      gQueue.push(new synthRightKey("cycle", new focusChecker("tricycle")));
+
+      // move to first menu in cycle, DOMMenuItemActive is fired for fruit,
+      // cycle and apple menuitems (bug 685191)
+      todo(false, "focus is fired for 'cycle' menuitem");
+      //gQueue.push(new synthRightKey("vehicle", new focusChecker("apple")));
+
+      // click menuitem to close menu, focus gets back to document
+      gQueue.push(new synthClick("tricycle", new focusChecker(document)));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </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=673958"
+         title="Rework accessible focus handling">
+        Mozilla Bug 673958
+      </a>
+      <p id="display"></p>
+      <div id="content" style="display: none"></div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+      <menubar>
+        <menu id="fruit" label="Fruit">
+          <menupopup>
+            <menuitem id="apple" label="Apple"/>
+            <menuitem id="orange" label="Orange" disabled="true"/>
+          </menupopup>
+        </menu>
+        <menu id="vehicle" label="Vehicle">
+          <menupopup>
+            <menu id="cycle" label="cycle">
+              <menupopup>
+                <menuitem id="tricycle" label="tricycle"/>
+              </menupopup>
+            </menu>
+            <menuitem id="car" label="Car" disabled="true"/>
+          </menupopup>
+        </menu>
+      </menubar>
+
+      <vbox id="eventdump"/>
+    </vbox>
+  </hbox>
+</window>
--- a/accessible/tests/mochitest/events/test_focus_name.html
+++ b/accessible/tests/mochitest/events/test_focus_name.html
@@ -15,18 +15,20 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
     /**
      * Checker for invokers.
      */
-    function actionChecker(aDescription)
+    function actionChecker(aID, aDescription)
     {
+      this.__proto__ = new invokerChecker(EVENT_FOCUS, aID);
+
       this.check = function actionChecker_check(aEvent)
       {
         var target = aEvent.accessible;
         is(target.description, aDescription,
            "Wrong description for " + prettyName(target));
       }
     }
 
@@ -52,16 +54,17 @@
       }
     };
 
     /**
      * Do tests.
      */
 
     // gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
 
     var gQueue = null;
 
     var gButtonElm = null;
     var gTextboxElm = null;
     var gTooltipElm = null;
 
     function doTests()
@@ -81,19 +84,19 @@
       gQueue.onFinish = function()
       {
         gButtonElm.removeEventListener("focus", gFocusHandler, false);
         gButtonElm.removeEventListener("blur", gBlurHandler, false);
         gTextboxElm.removeEventListener("focus", gFocusHandler, false);
         gTextboxElm.removeEventListener("blur", gBlurHandler, false);
       }
 
-      var checker = new actionChecker("It's a tooltip");
-      gQueue.push(new synthFocus("button", checker));
-      gQueue.push(new synthTab("textbox", checker));
+      var descr = "It's a tooltip";
+      gQueue.push(new synthFocus("button", new actionChecker("button", descr)));
+      gQueue.push(new synthTab("textbox", new actionChecker("textbox", descr)));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_selects.html
@@ -0,0 +1,109 @@
+<html>
+
+<head>
+  <title>Accessible focus testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      // first item is focused until there's selection
+      gQueue.push(new synthFocus("list", new focusChecker("orange")));
+
+      // item is selected and stays focused
+      gQueue.push(new synthDownKey("list", new nofocusChecker()));
+
+      // last selected item is focused
+      gQueue.push(new synthDownKey("list", new focusChecker("apple"), { shiftKey: true }));
+
+      // no focus event if nothing is changed
+      gQueue.push(new synthDownKey("list", new nofocusChecker("apple")));
+
+      // current item is focused
+      gQueue.push(new synthUpKey("list", new focusChecker("orange"), { ctrlKey: true }));
+
+      // focus on empty list (no items to be focused)
+      gQueue.push(new synthTab("list", new focusChecker("emptylist")));
+
+      // current item is focused
+      gQueue.push(new synthShiftTab("emptylist", new focusChecker("orange")));
+
+      // collapsed combobox keeps a focus
+      gQueue.push(new synthFocus("combobox", new focusChecker("combobox")));
+      gQueue.push(new synthDownKey("combobox", new nofocusChecker("combobox")));
+
+      // selected item is focused for expanded combobox
+      gQueue.push(new synthOpenComboboxKey("combobox", new focusChecker("cb_apple")));
+      gQueue.push(new synthUpKey("combobox", new focusChecker("cb_orange")));
+
+      // collapsed combobx keeps a focus
+      gQueue.push(new synthEscapeKey("combobox", new focusChecker("combobox")));
+
+      // no focus events for unfocused list controls when current item is
+      // changed
+      gQueue.push(new synthFocus("emptylist"));
+
+      gQueue.push(new changeCurrentItem("list", "orange"));
+      gQueue.push(new changeCurrentItem("combobox", "cb_apple"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=433418"
+     title="Accessibles for focused HTML Select elements are not getting focused state">
+    Mozilla Bug 433418
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
+     title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
+    Mozilla Bug 474893
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <select id="list" size="5" multiple="">
+    <option id="orange">Orange</option>
+    <option id="apple">Apple</option>
+  </select>
+
+  <select id="emptylist" size="5"></select>
+
+  <select id="combobox">
+    <option id="cb_orange">Orange</option>
+    <option id="cb_apple">Apple</option>
+  </select>
+
+  <div id="eventdump"></div>
+</body>
+</html>
--- a/accessible/tests/mochitest/events/test_focus_tree.xul
+++ b/accessible/tests/mochitest/events/test_focus_tree.xul
@@ -14,16 +14,18 @@
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
           src="../events.js" />
 
   <script type="application/javascript">
   <![CDATA[
 
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
@@ -41,23 +43,23 @@
       }
 
       this.getID = function setTreeView_getID()
         { return "set tree view for " + prettyName(aTreeID); }
     };
 
     function focusTree(aTreeID)
     {
-      var checker = new invokerChecker(EVENT_FOCUS, getFirstTreeItem, aTreeID);
+      var checker = new focusChecker(getFirstTreeItem, aTreeID);
       this.__proto__ = new synthFocus(aTreeID, [ checker ]);
     }
 
     function moveToNextItem(aTreeID)
     {
-      var checker = new invokerChecker(EVENT_FOCUS, getSecondTreeItem, aTreeID);
+      var checker = new focusChecker(getSecondTreeItem, aTreeID);
       this.__proto__ = new synthDownKey(aTreeID, [ checker ]);
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Helpers
 
     function getTreeItemAt(aTreeID, aIdx)
       { return getAccessible(aTreeID).getChildAt(aIdx + 1); }
@@ -78,43 +80,59 @@
 
     function doTest()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new setTreeView("tree", new nsTableTreeView(5)));
       gQueue.push(new focusTree("tree"));
       gQueue.push(new moveToNextItem("tree"));
+      gQueue.push(new synthFocus("emptytree"));
+
+      // no focus event for changed current item for unfocused tree
+      gQueue.push(new changeCurrentItem("tree", 0));
 
       gQueue.invoke();
     }
 
     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=386821"
          title="Need better solution for firing delayed event against xul tree">
         Mozilla Bug 386821
-      </a><br/>
+      </a>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=406308"
+         title="Don't fire accessible focus events if widget is not actually in focus, confuses screen readers">
+        Mozilla Bug 406308
+      </a>
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox id="debug"/>
     <tree id="tree" flex="1">
       <treecols>
         <treecol id="col1" flex="1" primary="true" label="column"/>
         <treecol id="col2" flex="1" label="column 2"/>
       </treecols>
       <treechildren id="treechildren"/>
     </tree>
+    <tree id="emptytree" flex="1">
+      <treecols>
+        <treecol id="emptytree_col1" flex="1" primary="true" label="column"/>
+        <treecol id="emptytree_col2" flex="1" label="column 2"/>
+      </treecols>
+      <treechildren id="emptytree_treechildren"/>
+    </tree>
   </hbox>
 
 </window>
 
--- a/accessible/tests/mochitest/events/test_menu.xul
+++ b/accessible/tests/mochitest/events/test_menu.xul
@@ -72,18 +72,18 @@
       {
         return "close edit menu, leave menubar";
       }
     }
 
     function focusFileMenu()
     {
       this.eventSeq = [
-        new invokerChecker(EVENT_MENU_START, getNode("menubar")),
-        new invokerChecker(EVENT_FOCUS, getNode("menuitem-file"))
+        new invokerChecker(EVENT_MENU_START, getNode("menubar"))
+        // new invokerChecker(EVENT_FOCUS, getNode("menuitem-file")) //intermitent failure
       ];
 
       this.invoke = function focusFileMenu_invoke()
       {
         synthesizeKey("VK_ALT", { });
       }
 
       this.getID = function focusFileMenu_getID()
@@ -127,17 +127,17 @@
       }
     }
 
     /**
      * Do tests.
      */
 
     //gA11yEventDumpID = "eventdump";
-    gA11yEventDumpToConsole = true;
+    //gA11yEventDumpToConsole = true;
 
     var gQueue = null;
 
     function doTests()
     {
       if (!WIN) {
         todo(false, "Enable this test on other platforms.");
         SimpleTest.finish();
--- a/accessible/tests/mochitest/events/test_text_alg.html
+++ b/accessible/tests/mochitest/events/test_text_alg.html
@@ -38,17 +38,17 @@
 
         var isInserted = event[0];
         var str = event[1];
         var offset = event[2];
         var checker = new textChangeChecker(this.containerNode, offset,
                                             offset + str.length, str,
                                             isInserted);
 
-        if (eventItem[3] == kUnexpected)
+        if (event[3] == kUnexpected)
           this.unexpectedEventSeq.push(checker);
         else
           this.eventSeq.push(checker);
       }
 
       this.invoke = function changeText_invoke()
       {
         this.textNode.data = aValue;
--- a/accessible/tests/mochitest/events/test_textattrchange.html
+++ b/accessible/tests/mochitest/events/test_textattrchange.html
@@ -9,16 +9,18 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../attributes.js"></script>
 
   <script type="application/javascript">
 
     const nsIDOMNSEditableElement =
       Components.interfaces.nsIDOMNSEditableElement;
@@ -75,17 +77,17 @@
 
     var gQueue = null;
     function doTests()
     {
       // Synth focus before spellchecking turning on to make sure editor
       // gets a time for initialization.
 
       gQueue = new eventQueue();
-      gQueue.push(new synthFocus("input", null, EVENT_FOCUS));
+      gQueue.push(new synthFocus("input"));
       gQueue.push(new spelledTextInvoker("input"));
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
--- a/accessible/tests/mochitest/focus/test_takeFocus.html
+++ b/accessible/tests/mochitest/focus/test_takeFocus.html
@@ -21,61 +21,76 @@
     // Test
 
     var gQueue = null;
 
     function takeFocusInvoker(aID)
     {
       this.accessible = getAccessible(aID);
 
-      this.eventSeq = [ new invokerChecker(EVENT_FOCUS, this.accessible) ];
+      this.eventSeq = [ new focusChecker(this.accessible) ];
 
       this.invoke = function takeFocusInvoker_invoke()
       {
         this.accessible.takeFocus();
       }
 
-      this.finalCheck = function takeFocusInvoker()
-      {
-        testStates(this.accessible, STATE_FOCUSED);
-      }
-
       this.getID = function takeFocusInvoker_getID()
       {
         return "takeFocus for " + prettyName(aID);
       }
     }
 
     function doTest()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new takeFocusInvoker("aria-link"));
       gQueue.push(new takeFocusInvoker("aria-link2"));
       gQueue.push(new takeFocusInvoker("link"));
+      gQueue.push(new takeFocusInvoker("item2"));
+      gQueue.push(new takeFocusInvoker("plugin"));
+      gQueue.push(new takeFocusInvoker(document));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=429547"
+     title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
+    Mozilla Bug 429547
+  </a>
+  <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=452710"
      title="nsIAccessible::takeFocus testing">
     Mozilla Bug 452710
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=646361"
+     title="No focus event fired on document when focus is set to the document while focused in a plugin">
+    Mozilla Bug 646361
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <span id="aria-link" role="link" tabindex="0">link</span>
   <span id="aria-link2" role="link" tabindex="0">link</span>
 
   <a id="link" href="">link</span>
 
+  <div role="listbox" aria-activedescendant="item1" id="container" tabindex="1">
+    <div role="listitem" id="item1">item1</div>
+    <div role="listitem" id="item2">item2</div>
+    <div role="listitem" id="item3">item3</div>
+  </div>
+
+  <embed id="plugin" type="application/x-test" width="200" height="200" wmode="window"></embed>
 </body>
 </html>
deleted file mode 100644
--- a/accessible/tests/mochitest/nsIAccessible_selects.js
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- * Tests an accessible and its children.
- * The accessible is one of the following:
- * - HTML:select (ROLE_COMBOBOX)
- * - HTML:select @size (ROLE_LIST)
- * - HTML:label containing either of the above (ROLE_LABEL)
- *
- * @param aID               The ID of the base element to test.
- * @param aNames            The array of expected names for the accessible and
- *                          its children.
- * @param aRoles            The array of expected roles for the accessible and
- *                          its children.
- * @param aStates           The array of states to test for on each accessible.
- * @param aUndesiredStates  Array of states we don't want to have for each
- *                          accessible.
- *
- * @note Each of the three arrays must have an equal number of elements. The
- * order of elements corresponds to the order in which the tree is walked from
- * the base accessible downwards.
- */
-function testSelect(aID, aNames, aRoles, aStates, aUndesiredStates)
-{
-  // used to walk the tree starting at the aID's accessible.
-  var acc = getAccessible(aID);
-  if (!acc) {
-    return;
-  }
-
-  testThis(aID, acc, aNames, aRoles, aStates, aUndesiredStates, 0);
-}
-
-/**
- * Tests a single accessible.
- * Recursively calls itself until it reaches the last option item.
- * Called first time from the testSelect function.
- *
- * @param aID               @see testSelect
- * @param aAcc              The accessible to test.
- * @param aNames            @see testSelect
- * @param aRoles            @see testSelect
- * @param aStates           @see testSelect
- * @param aUndesiredStates  @see testSelect
- * @param aIndex            The index in the three arrays to use. Used for both
- *                          the error message and for walking the tree downwards
- *                          from the base accessible.
- */
-function testThis(aID, aAcc, aNames, aRoles, aStates, aUndesiredStates, aIndex)
-{
-  if (aIndex >= aNames.length)
-    return;  // End of test
-  else if (!aAcc) {
-    ok(false, "No accessible for " + aID + " at index " + aIndex + "!");
-    return;
-  }
-
-  is(aAcc.name, aNames[aIndex],
-     "wrong name for " + aID + " at index " + aIndex + "!");
-  var role = getRole(aAcc);
-  is(role, aRoles[aIndex],
-     "Wrong role for " + aID + " at index " + aIndex + "!");
-  testStates(aID, aAcc, aStates, aUndesiredStates, aIndex);
-  switch(role) {
-    case ROLE_COMBOBOX:
-    case ROLE_COMBOBOX_LIST:
-    case ROLE_LABEL:
-    case ROLE_LIST:
-      // All of these expect the next item to be the first child of the current
-      // accessible.
-      var acc = null;
-      try {
-        acc = aAcc.firstChild;
-      } catch(e) {}
-      testThis(aID, acc, aNames, aRoles, aStates, aUndesiredStates, ++aIndex);
-      break;
-    case ROLE_COMBOBOX_OPTION:
-    case ROLE_OPTION:
-    case ROLE_TEXT_LEAF:
-      // All of these expect the next item's accessible to be the next sibling.
-      var acc = null;
-      try {
-        acc = aAcc.nextSibling;
-      } catch(e) {}
-      testThis(aID, acc, aNames, aRoles, aStates, aUndesiredStates, ++aIndex);
-      break;
-    default:
-      break;
-  }
-}
-
-/**
- * Tests the states for the given accessible.
- * Does not test for extraStates since we don't need these.
- *
- * @param aID  the ID of the base element.
- * @param aAcc  the current accessible from the recursive algorithm.
- * @param aStates  the states array passed down from the testThis function.
- * @param aUndesiredStates  the array of states we don't want to see, if any.
- * @param aIndex  the index of the item to test, determined and passed in from
- *                the testThis function.
- */
-function testStates(aID, aAcc, aStates, aUndesiredStates, aIndex)
-{
-  var state = {}, extraState = {};
-  aAcc.getState(state, extraState);
-  if (aStates[aIndex] != 0)
-    is(state.value & aStates[aIndex], aStates[aIndex],
-       "Wrong state bits for " + aID + " at index " + aIndex + "!");
-  if (aUndesiredStates[aIndex] != 0)
-    is(state.value & aUndesiredStates[aIndex], 0,
-       "Wrong undesired state bits for " + aID + " at index " + aIndex + "!");
-}
--- a/accessible/tests/mochitest/states.js
+++ b/accessible/tests/mochitest/states.js
@@ -84,16 +84,26 @@ function testStates(aAccOrElmOrID, aStat
             "state bits should not be present in ID " + id + "!");
 
   if (aAbsentExtraState)
     isState(extraState & aAbsentExtraState, 0, true,
             "extraState bits should not be present in ID " + id + "!");
 
   // Additional test.
 
+  // focused/focusable
+  if (state & STATE_FOCUSED)
+    isState(state & STATE_FOCUSABLE, STATE_FOCUSABLE, false,
+            "Focussed " + id + " must be focusable!");
+
+  if (aAbsentState && (aAbsentState & STATE_FOCUSABLE)) {
+    isState(state & STATE_FOCUSED, 0, false,
+              "Not focusable " + id + " must be not focused!");
+  }
+
   // readonly/editable
   if (state & STATE_READONLY)
     isState(extraState & EXT_STATE_EDITABLE, 0, true,
             "Read-only " + id + " cannot be editable!");
 
   if (extraState & EXT_STATE_EDITABLE)
     isState(state & STATE_READONLY, 0, true,
             "Editable " + id + " cannot be readonly!");
@@ -105,43 +115,53 @@ function testStates(aAccOrElmOrID, aStat
 
   if (extraState & EXT_STATE_SINGLE_LINE)
     isState(extraState & EXT_STATE_MULTI_LINE, 0, true,
             "Singleline " + id + " cannot be multiline!");
 
   // expanded/collapsed/expandable
   if (state & STATE_COLLAPSED || state & STATE_EXPANDED)
     isState(extraState & EXT_STATE_EXPANDABLE, EXT_STATE_EXPANDABLE, true,
-            "Collapsed or expanded " + id + " should be expandable!");
+            "Collapsed or expanded " + id + " must be expandable!");
 
   if (state & STATE_COLLAPSED)
     isState(state & STATE_EXPANDED, 0, false,
             "Collapsed " + id + " cannot be expanded!");
 
   if (state & STATE_EXPANDED)
     isState(state & STATE_COLLAPSED, 0, false,
             "Expanded " + id + " cannot be collapsed!");
 
+  if (aAbsentState && (extraState & EXT_STATE_EXPANDABLE)) {
+    if (aAbsentState & STATE_EXPANDED) {
+      isState(state & STATE_COLLAPSED, STATE_COLLAPSED, false,
+              "Not expanded " + id + " must be collapsed!");
+    } else if (aAbsentState & STATE_COLLAPSED) {
+      isState(state & STATE_EXPANDED, STATE_EXPANDED, false,
+              "Not collapsed " + id + " must be expanded!");
+    }
+  }
+
   // checked/mixed/checkable
   if (state & STATE_CHECKED || state & STATE_MIXED)
     isState(state & STATE_CHECKABLE, STATE_CHECKABLE, false,
             "Checked or mixed element must be checkable!");
 
   if (state & STATE_CHECKED)
     isState(state & STATE_MIXED, 0, false,
             "Checked element cannot be state mixed!");
 
   if (state & STATE_MIXED)
     isState(state & STATE_CHECKED, 0, false,
             "Mixed element cannot be state checked!");
 
   // selected/selectable
   if (state & STATE_SELECTED) {
     isState(state & STATE_SELECTABLE, STATE_SELECTABLE, false,
-            "Selected element should be selectable!");
+            "Selected element must be selectable!");
   }
 }
 
 /**
  * Tests an acessible and its sub tree for the passed in state bits.
  * Used to make sure that states are propagated to descendants, for example the
  * STATE_UNAVAILABLE from a container to its children.
  *
--- a/accessible/tests/mochitest/states/Makefile.in
+++ b/accessible/tests/mochitest/states/Makefile.in
@@ -44,25 +44,26 @@ relativesrcdir  = accessible/states
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		test_aria.html \
 		test_aria_imgmap.html \
 		test_aria_tabs.html \
-		test_comboboxes.xul \
 		test_doc.html \
 		test_docarticle.html \
 		test_editablebody.html \
+		test_expandable.xul \
 		test_frames.html \
 		test_inputs.html \
 		test_inputs.xul \
 		test_link.html \
 		test_popup.xul \
+		test_selects.html \
 		test_stale.html \
 		test_textbox.xul \
 		test_tree.xul \
 		z_frames.html \
 		z_frames_article.html \
 		z_frames_checkbox.html \
 		z_frames_textbox.html \
 		z_frames_update.html \
rename from accessible/tests/mochitest/states/test_comboboxes.xul
rename to accessible/tests/mochitest/states/test_expandable.xul
--- a/accessible/tests/mochitest/states/test_comboboxes.xul
+++ b/accessible/tests/mochitest/states/test_expandable.xul
@@ -1,74 +1,66 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/browser.css"
                  type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="nsIAccessible interface for xul:menulist test.">
+        title="Expanded state change events tests for comboboxes and autocompletes.">
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
 
   <script type="application/javascript"
+          src="../autocomplete.js" />
+
+  <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
           src="../events.js" />
 
   <script type="application/javascript">
   <![CDATA[
-    function openHideCombobox(aComboboxNodeOrID, aIsOpen)
-    {
-      this.invoke = function invoke()
-      {
-        synthesizeMouse(this.DOMNode, 5, 5, {});
-      }
-      this.check = function check(aEvent)
-      {
-        aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
-
-        var id = this.getID();
-        is(aEvent.state, nsIAccessibleStates.STATE_EXPANDED,
-            "Wrong state change event is handled in test '" + id + "'.");
-        is(aEvent.isEnabled(), this.mIsOpen,
-            "Wrong value of state expanded in test '" + id + "'.");
-      }
-      this.getID = function getID()
-      {
-        if (this.mIsOpen)
-          return this.DOMNodeOrID + " open combobox";
-        return this.DOMNodeOrID + " close combobox";
-      }
-
-      this.DOMNodeOrID = aComboboxNodeOrID;
-      this.DOMNode = getNode(aComboboxNodeOrID);
-      this.mIsOpen = aIsOpen;
-    }
+    //gA11yEventDumpToConsole = true; // debuggin
 
     var gQueue = null;
     function doTest()
     {
-      gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
+      // register 'test-a11y-search' autocomplete search
+      initAutoComplete([ "hello", "hi" ],
+                       [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
+      gQueue = new eventQueue();
 
-      var ID = "menulist";
-      gQueue.push(new openHideCombobox(ID, true));
-      gQueue.push(new openHideCombobox(ID, false));
+      gQueue.push(new openCombobox("menulist"));
+      gQueue.push(new closeCombobox("menulist"));
+
+      todo(false, "Autocompletes don't fire expanded state change events when popup open. See bug 688480!");
+      //gQueue.push(new openCombobox("autocomplete"));
+      //gQueue.push(new closeCombobox("autocomplete"));
 
       // XXX: searchbar doesn't fire state change events because accessible
       // parent of combobox_list accessible is pushbutton accessible.
       //var searchbar = document.getElementById("searchbar");
       //gQueue.push(new openHideCombobox(searchbar, true));
       //gQueue.push(new openHideCombobox(searchbar, false));
       todo(false, "Enable states test for XUL searchbar widget!");
 
+      gQueue.onFinish = function()
+      {
+        // unregister 'test-a11y-search' autocomplete search
+        shutdownAutoComplete();
+      }
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     // This is the hack needed for searchbar work outside of browser.
     function getBrowser()
     {
       return {
         mCurrentBrowser: { engines: new Array() }
@@ -98,14 +90,17 @@
       <menulist id="menulist">
         <menupopup>
           <menuitem label="item1"/>
           <menuitem label="item2"/>
           <menuitem label="item3"/>
         </menupopup>
       </menulist>
 
+      <textbox id="autocomplete" type="autocomplete"
+               autocompletesearch="test-a11y-search"/>
+
       <searchbar id="searchbar"/>
     </vbox>
   </hbox>
 
 </window>
 
rename from accessible/tests/mochitest/test_nsIAccessible_selects.html
rename to accessible/tests/mochitest/states/test_selects.html
--- a/accessible/tests/mochitest/test_nsIAccessible_selects.html
+++ b/accessible/tests/mochitest/states/test_selects.html
@@ -1,206 +1,81 @@
 <html>
 
 <head>
-  <title>nsIAccessible selects tests</title>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <title>HTML selects accessible states tests</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
-          src="common.js"></script>
-  <script type="application/javascript"
-          src="role.js"></script>
+          src="../common.js"></script>
   <script type="application/javascript"
-          src="states.js"></script>
-  <script type="application/javascript"
-          src="nsIAccessible_selects.js"></script>
+          src="../states.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
-      // Label and combo, separate tags
-      var names = [
-        "Foo:", // combobox
-        "Foo:", // combobox list
-        "item 1", // first item
-        "item 2" // second item
-      ];
-      var roles = [
-        ROLE_COMBOBOX, // root
-        ROLE_COMBOBOX_LIST, // list accessible
-        ROLE_COMBOBOX_OPTION, // first option
-        ROLE_COMBOBOX_OPTION // second option
-      ];
-      var states = [
-        (STATE_FOCUSABLE | STATE_HASPOPUP | STATE_COLLAPSED), // combobox
-        (0), // combobox_list
-        (STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE | STATE_FOCUSED),
-        (STATE_SELECTABLE | STATE_FOCUSABLE) // second item, not focused
-      ];
-      var undesiredStates = [
-        (STATE_FOCUSED), // combobox
-        (STATE_FOCUSED), // combobox_list
-        (0), // first item
-        (STATE_SELECTED | STATE_FOCUSED) // second, not currently focused, item
-      ];
-      testSelect("combo1", names, roles, states, undesiredStates);
+      // combobox
+      var combobox = getAccessible("combobox");
+      testStates(combobox,
+                 STATE_HASPOPUP | STATE_COLLAPSED | STATE_FOCUSABLE, 0,
+                 STATE_FOCUSED, 0);
 
-      // Select nested within label element.
-      names = [
-        "Search in: Newsticker", // label
-        "Search in:", // text leaf
-        "Search in:", // combobox
-        "Search in: Newsticker", // combobox_list
-        "Newsticker", // option 1
-        "Entire site" // Option 2
-      ];
-      roles = [
-        ROLE_LABEL, // root
-        ROLE_TEXT_LEAF, // inner text
-        ROLE_COMBOBOX, // combobox accessible
-        ROLE_COMBOBOX_LIST, // list accessible
-        ROLE_COMBOBOX_OPTION, // first option
-        ROLE_COMBOBOX_OPTION // second option
-      ];
-      states = [
-        (0), // label
-        (0), // text leaf
-        (STATE_FOCUSABLE | STATE_HASPOPUP | STATE_COLLAPSED), // combobox
-        (0), // combobox_list
-        (STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE | STATE_FOCUSED),
-        (STATE_SELECTABLE | STATE_FOCUSABLE) // second item, not focused
-      ];
-      undesiredStates = [
-        (0), // label
-        (0), // text leaf
-        (STATE_FOCUSED), // combobox
-        (STATE_FOCUSED), // combobox_list
-        (0), // first item
-        (STATE_SELECTED | STATE_FOCUSED) // second, not currently focused, item
-      ];
-      testSelect("combo2", names, roles, states, undesiredStates);
+      var comboboxList = combobox.firstChild;
+      testStates(comboboxList, 0, 0, STATE_FOCUSABLE, 0);
 
-      // select @size with label as separate tags.
-      names = [
-        "Component:", // list
-        "Build", // item 1
-        "Disability Access APIs", // item 2
-        "General", // item 3
-        "UI" // item 4
-      ];
-      roles = [
-        ROLE_LISTBOX, // root
-        ROLE_OPTION, // item 1
-        ROLE_OPTION, // item 2
-        ROLE_OPTION, // item 4
-        ROLE_OPTION // item 4
-      ];
-      states = [
-        (STATE_FOCUSABLE), // list
-        (STATE_SELECTABLE | STATE_FOCUSABLE | STATE_FOCUSED),
-        (STATE_SELECTABLE | STATE_FOCUSABLE), // second item, not focused
-        (STATE_SELECTABLE | STATE_FOCUSABLE), // third item, not focused
-        (STATE_SELECTABLE | STATE_FOCUSABLE) // fourth item, not focused
-      ];
-      undesiredStates = [
-        (STATE_FOCUSED), // listbox
-        (STATE_SELECTED), // first item
-        (STATE_SELECTED | STATE_FOCUSED), // second, not currently focused, item
-        (STATE_SELECTED | STATE_FOCUSED), // third, not currently focused, item
-        (STATE_SELECTED | STATE_FOCUSED) // fourth, not currently focused, item
-      ];
-      testSelect("list1", names, roles, states, undesiredStates);
+      var opt1 = comboboxList.firstChild;
+      testStates(opt1, STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE, 0,
+                 STATE_FOCUSED, 0);
+
+      var opt2 = comboboxList.lastChild;
+      testStates(opt2, STATE_SELECTABLE | STATE_FOCUSABLE, 0, STATE_SELECTED, 0,
+                 STATE_FOCUSED, 0);
 
-      // Select @size nested within label element.
-      names = [
-        "Version:", // label
-        "Version:", // text leaf
-        "Version:", // list
-        "2.0", // option 1
-        "3.0", // Option 2
-        "3.1", // Option 3
-        "trunk" // Option 4
-      ];
-      roles = [
-        ROLE_LABEL, // root
-        ROLE_TEXT_LEAF, // inner text
-        ROLE_LISTBOX, // listbox accessible
-        ROLE_OPTION, // first option
-        ROLE_OPTION, // second option
-        ROLE_OPTION, // third option
-        ROLE_OPTION // fourth option
-      ];
-      states = [
-        (0), // label
-        (0), // text leaf
-        (STATE_FOCUSABLE), // listbox
-        (STATE_SELECTABLE | STATE_FOCUSABLE | STATE_FOCUSED), // Option 1
-        (STATE_SELECTABLE | STATE_FOCUSABLE), // second item, not focused
-        (STATE_SELECTABLE | STATE_FOCUSABLE), // third item, not focused
-        (STATE_SELECTABLE | STATE_FOCUSABLE) // fourth item, not focused
-      ];
-      undesiredStates = [
-        (0), // label
-        (0), // text leaf
-        (STATE_FOCUSED | STATE_HASPOPUP | STATE_COLLAPSED), // listbox
-        (STATE_SELECTED), // first item
-        (STATE_SELECTED | STATE_FOCUSED), // second, not currently focused, item
-        (STATE_SELECTED | STATE_FOCUSED), // third, not currently focused, item
-        (STATE_SELECTED | STATE_FOCUSED) // fourth, not currently focused, item
-      ];
-      testSelect("list2", names, roles, states, undesiredStates);
+      // listbox
+      var listbox = getAccessible("listbox");
+      testStates(listbox, STATE_FOCUSABLE, 0,
+                 STATE_HASPOPUP | STATE_COLLAPSED | STATE_FOCUSED, 0);
+
+      testStates(listbox.firstChild, STATE_SELECTABLE, 0,
+                 STATE_SELECTED | STATE_FOCUSED | STATE_FOCUSED, 0);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
 </head>
 
 <body>
-
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=443889"
      title="mochitest for selects and lists">
     Mozilla Bug 443889
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=640716"
+     title="mochitest for selects and lists">
+    Mozilla Bug 640716
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <form action="post.php" method="post">
-  <!-- Label and select separate tags -->
-  <label for="combo1">Foo:</label>
-  <select id="combo1" name="combo1">
+  <select id="combobox">
     <option>item 1</option>
     <option>item 2</option>
-  </select><br />
+  </select>
 
-  <!-- Select embedded in label -->
-  <label id="combo2">Search in:<select name="search">
-    <option>Newsticker</option>
-    <option>Entire site</option>
-  </select></label><br />
-
-  <!-- Label and select @size -->
-  <label for="list1">Component:</label>
-  <select id="list1" name="component" size="3">
+  <select id="listbox" name="component" size="3">
     <option>Build</option>
     <option>Disability Access APIs</option>
     <option>General</option>
     <option>UI</option>
-  </select><br />
+  </select>
 
-  <!-- Select @size nested within label -->
-  <label id="list2">Version:<select name="version" size="3">
-    <option>2.0</option>
-    <option>3.0</option>
-    <option>3.1</option>
-    <option>trunk</option>
-  </select></label><br />
-  </form>
 </body>
 </html>
--- a/accessible/tests/mochitest/test_nsOuterDocAccessible.html
+++ b/accessible/tests/mochitest/test_nsOuterDocAccessible.html
@@ -26,18 +26,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       var docAcc = getAccessible(document);
 
       if (docAcc) {
         var outerDocAcc = getAccessible(docAcc.parent);
 
         if (outerDocAcc) {
           testRole(outerDocAcc, ROLE_INTERNAL_FRAME);
 
-          // check if it is focusable, not desired.
-          testStates(outerDocAcc, 0, 0, STATE_FOCUSABLE);
+          // check if it is focusable.
+          testStates(outerDocAcc, STATE_FOCUSABLE, 0);
 
           // see bug 428954: No name wanted for internal frame
           is(outerDocAcc.name, null, "Wrong name for internal frame!");
 
           // see bug 440770, no actions wanted on outer doc
           is(outerDocAcc.numActions, 0,
              "Wrong number of actions for internal frame!");
           var actionTempStr; // not really used, just needs to receive a value
--- a/accessible/tests/mochitest/value/test_general.html
+++ b/accessible/tests/mochitest/value/test_general.html
@@ -45,16 +45,20 @@
       testValue("aria_checkbox_link", "");
       testValue("aria_application_link", "");
 
       // roles that can live as nsHTMLLinkAccessibles
       testValue("aria_link_link", href);
       testValue("aria_main_link", href);
       testValue("aria_navigation_link", href);
 
+      // HTML controls
+      testValue("combobox1", "item1");
+      testValue("combobox2", "item2");
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
 </head>
@@ -79,10 +83,18 @@
   <!-- landmark links -->
   <a id="aria_application_link" role="application" href="foo">app</a>
   <a id="aria_main_link" role="main" href="foo">main</a>
   <a id="aria_navigation_link" role="navigation" href="foo">nav</a>
 
   <!-- strange edge case: please don't do this in the wild -->
   <a id="aria_link_link" role="link" href="foo">link</a>
 
+  <select id="combobox1">
+    <option id="cb1_item1">item1</option>
+    <option id="cb1_item2">item2</option>
+  </select>
+  <select id="combobox2">
+    <option id="cb2_item1">item1</option>
+    <option id="cb2_item2" selected="true">item2</option>
+  </select>
 </body>
 </html>
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,15 +1,14 @@
 dnl
 dnl Local autoconf macros used with mozilla
 dnl The contents of this file are under the Public Domain.
 dnl 
 
 builtin(include, build/autoconf/glib.m4)dnl
-builtin(include, build/autoconf/libIDL.m4)dnl
 builtin(include, build/autoconf/nspr.m4)dnl
 builtin(include, build/autoconf/nss.m4)dnl
 builtin(include, build/autoconf/pkg.m4)dnl
 builtin(include, build/autoconf/freetype2.m4)dnl
 builtin(include, build/autoconf/codeset.m4)dnl
 builtin(include, build/autoconf/altoptions.m4)dnl
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -249,17 +249,17 @@ pref("browser.chrome.favicons", true);
 // browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
 pref("browser.warnOnQuit", true);
 pref("browser.warnOnRestart", false);
 // browser.showQuitWarning specifically controls the quit warning dialog. We
 // might still show the window closing dialog with showQuitWarning == false.
 pref("browser.showQuitWarning", false);
 pref("browser.fullscreen.autohide", true);
 pref("browser.fullscreen.animateUp", 1);
-pref("browser.overlink-delay", 70);
+pref("browser.overlink-delay", 80);
 
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.urlbar.clickSelectsAll", false);
 #else
 pref("browser.urlbar.clickSelectsAll", true);
 #endif
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.urlbar.doubleClickSelectsAll", true);
@@ -861,20 +861,16 @@ pref("browser.privatebrowsing.autostart"
 
 // Whether we should skip prompting before starting the private browsing mode
 pref("browser.privatebrowsing.dont_prompt_on_enter", false);
 
 // Don't try to alter this pref, it'll be reset the next time you use the
 // bookmarking dialog
 pref("browser.bookmarks.editDialog.firstEditField", "namePicker");
 
-// base url for the wifi geolocation network provider
-pref("geo.wifi.uri", "https://maps.googleapis.com/maps/api/browserlocation/json");
-pref("geo.wifi.protocol", 0);
-
 // Whether to use a panel that looks like an OS X sheet for customization
 #ifdef XP_MACOSX
 pref("toolbar.customization.usesheet", true);
 #else
 pref("toolbar.customization.usesheet", false);
 #endif
 
 // The default for this pref reflects whether the build is capable of IPC.
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -506,26 +506,30 @@ statuspanel[type=status] {
 
 @media all and (max-width: 800px) {
   statuspanel[type=status] {
     min-width: 33%;
   }
 }
 
 statuspanel[type=overLink] {
-  -moz-transition: opacity 100ms ease-out;
+  -moz-transition: opacity 120ms ease-out;
   direction: ltr;
 }
 
-statuspanel[label=""] {
+statuspanel[inactive] {
   -moz-transition: none;
   opacity: 0;
   pointer-events: none;
 }
 
+statuspanel[inactive][previoustype=overLink] {
+  -moz-transition: opacity 200ms ease-out;
+}
+
 .statuspanel-inner {
   height: 3em;
   width: 100%;
   -moz-box-align: end;
 }
 
 .styleInspector {
   min-width: 350px;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4678,17 +4678,17 @@ var XULBrowserWindow = {
 };
 
 var LinkTargetDisplay = {
   get DELAY_SHOW() {
      delete this.DELAY_SHOW;
      return this.DELAY_SHOW = Services.prefs.getIntPref("browser.overlink-delay");
   },
 
-  DELAY_HIDE: 150,
+  DELAY_HIDE: 250,
   _timer: 0,
 
   get _isVisible () XULBrowserWindow.statusTextField.label != "",
 
   update: function () {
     clearTimeout(this._timer);
     window.removeEventListener("mousemove", this, true);
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -951,17 +951,17 @@
     <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
     <vbox id="appcontent" flex="1">
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="return contentAreaClick(event, false);"/>
-      <statuspanel id="statusbar-display" label=""/>
+      <statuspanel id="statusbar-display" inactive="true"/>
     </vbox>
     <vbox id="browser-border-end" hidden="true" layer="true"/>
   </hbox>
 
   <vbox id="browser-bottombox" layer="true">
     <toolbar id="inspector-toolbar"
              nowindowdrag="true"
              hidden="true">
--- a/browser/base/content/sanitizeDialog.js
+++ b/browser/base/content/sanitizeDialog.js
@@ -74,21 +74,26 @@ var gSanitizePromptDialog = {
 
   init: function ()
   {
     // This is used by selectByTimespan() to determine if the window has loaded.
     this._inited = true;
 
     var s = new Sanitizer();
     s.prefDomain = "privacy.cpd.";
-    for (let i = 0; i < this.sanitizePreferences.childNodes.length; ++i) {
-      var preference = this.sanitizePreferences.childNodes[i];
-      var name = s.getNameFromPreference(preference.name);
-      if (!s.canClearItem(name)) 
-        preference.disabled = true;
+
+    let sanitizeItemList = document.querySelectorAll("#itemList > [preference]");
+    for (let i = 0; i < sanitizeItemList.length; i++) {
+      let prefItem = sanitizeItemList[i];
+      let name = s.getNameFromPreference(prefItem.getAttribute("preference"));
+      if (!s.canClearItem(name)) {
+        prefItem.preference = null;
+        prefItem.checked = false;
+        prefItem.disabled = true;
+      }
     }
 
     document.documentElement.getButton("accept").label =
       this.bundleBrowser.getString("sanitizeButtonOK");
 
     if (this.selectedTimespan === Sanitizer.TIMESPAN_EVERYTHING) {
       this.prepareWarning();
       this.warningBox.hidden = false;
@@ -303,21 +308,26 @@ var gSanitizePromptDialog = {
 
   init: function ()
   {
     // This is used by selectByTimespan() to determine if the window has loaded.
     this._inited = true;
 
     var s = new Sanitizer();
     s.prefDomain = "privacy.cpd.";
-    for (let i = 0; i < this.sanitizePreferences.childNodes.length; ++i) {
-      var preference = this.sanitizePreferences.childNodes[i];
-      var name = s.getNameFromPreference(preference.name);
-      if (!s.canClearItem(name)) 
-        preference.disabled = true;
+
+    let sanitizeItemList = document.querySelectorAll("#itemList > [preference]");
+    for (let i = 0; i < sanitizeItemList.length; i++) {
+      let prefItem = sanitizeItemList[i];
+      let name = s.getNameFromPreference(prefItem.getAttribute("preference"));
+      if (!s.canClearItem(name)) {
+        prefItem.preference = null;
+        prefItem.checked = false;
+        prefItem.disabled = true;
+      }
     }
 
     document.documentElement.getButton("accept").label =
       this.bundleBrowser.getString("sanitizeButtonOK");
 
     this.selectByTimespan();
   },
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4172,16 +4172,21 @@
           if (aTab.hasAttribute("busy")) {
             aMenuitem.setAttribute("busy", aTab.getAttribute("busy"));
             aMenuitem.removeAttribute("image");
           } else {
             aMenuitem.setAttribute("image", aTab.getAttribute("image"));
             aMenuitem.removeAttribute("busy");
           }
 
+          if (aTab.hasAttribute("pending"))
+            aMenuitem.setAttribute("pending", aTab.getAttribute("pending"));
+          else
+            aMenuitem.removeAttribute("pending");
+
           if (aTab.selected)
             aMenuitem.setAttribute("selected", "true");
           else
             aMenuitem.removeAttribute("selected");
         ]]></body>
       </method>
     </implementation>
 
@@ -4269,21 +4274,27 @@
             else
               this.removeAttribute("mirror");
           }
 
           this.style.minWidth = this.getAttribute("type") == "status" &&
                                 this.getAttribute("previoustype") == "status"
                                   ? getComputedStyle(this).width : "";
 
-          this.setAttribute("label", val);
+          if (val) {
+            this.setAttribute("label", val);
+            this.removeAttribute("inactive");
+          } else {
+            this.setAttribute("inactive", "true");
+          }
+
           return val;
         ]]></setter>
         <getter>
-          return this.getAttribute("label");
+          return this.hasAttribute("inactive") ? "" : this.getAttribute("label");
         </getter>
       </property>
 
       <method name="handleEvent">
         <parameter name="event"/>
         <body><![CDATA[
           if (event.type == "findbaropen" &&
               this.label)
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -284,16 +284,98 @@ var gAllTests = [
                 "timeSpan pref should be everything after accepting dialog " +
                 "with everything selected");
       ensureHistoryClearedState(uris, true);
     };
     wh.open();
   },
 
   /**
+   * The next three tests checks that when a certain history item cannot be
+   * cleared then the checkbox should be both disabled and unchecked.
+   * In addition, we ensure that this behavior does not modify the preferences.
+   */
+  function () {
+    // Add history.
+    let uris = [ addHistoryWithMinutesAgo(10) ];
+    let formEntries = [ addFormEntryWithMinutesAgo(10) ];
+
+    let wh = new WindowHelper();
+    wh.onload = function() {
+      // Check that the relevant checkboxes are enabled
+      var cb = this.win.document.querySelectorAll(
+                 "#itemList > [preference='privacy.cpd.formdata']");
+      ok(cb.length == 1 && !cb[0].disabled, "There is formdata, checkbox to " +
+         "clear formdata should be enabled.");
+
+      var cb = this.win.document.querySelectorAll(
+                 "#itemList > [preference='privacy.cpd.history']");
+      ok(cb.length == 1 && !cb[0].disabled, "There is history, checkbox to " +
+         "clear history should be enabled.");
+
+      this.checkAllCheckboxes();
+      this.acceptDialog();
+
+      ensureHistoryClearedState(uris, true);
+      ensureFormEntriesClearedState(formEntries, true);
+    };
+    wh.open();
+  },
+  function () {
+    let wh = new WindowHelper();
+    wh.onload = function() {
+      boolPrefIs("cpd.history", true,
+                 "history pref should be true after accepting dialog with " +
+                 "history checkbox checked");
+      boolPrefIs("cpd.formdata", true,
+                 "formdata pref should be true after accepting dialog with " +
+                 "formdata checkbox checked");
+
+
+      // Even though the formdata pref is true, because there is no history
+      // left to clear, the checkbox will be disabled.
+      var cb = this.win.document.querySelectorAll(
+                 "#itemList > [preference='privacy.cpd.formdata']");
+      ok(cb.length == 1 && cb[0].disabled && !cb[0].checked,
+         "There is no formdata history, checkbox should be disabled and be " +
+         "cleared to reduce user confusion (bug 497664).");
+
+      var cb = this.win.document.querySelectorAll(
+                 "#itemList > [preference='privacy.cpd.history']");
+      ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
+         "There is no history, but history checkbox should always be enabled " +
+         "and will be checked from previous preference.");
+
+      this.acceptDialog();
+    }
+    wh.open();
+  },
+  function () {
+    let formEntries = [ addFormEntryWithMinutesAgo(10) ];
+
+    let wh = new WindowHelper();
+    wh.onload = function() {
+      boolPrefIs("cpd.formdata", true,
+                 "formdata pref should persist previous value after accepting " +
+                 "dialog where you could not clear formdata.");
+
+      var cb = this.win.document.querySelectorAll(
+                 "#itemList > [preference='privacy.cpd.formdata']");
+      ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
+         "There exists formEntries so the checkbox should be in sync with " +
+         "the pref.");
+
+      this.acceptDialog();
+      ensureFormEntriesClearedState(formEntries, true);
+    };
+    wh.open();
+  },
+
+
+  /**
    * These next six tests together ensure that toggling details persists
    * across dialog openings.
    */
   function () {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Check all items and select "Everything"
       this.checkAllCheckboxes();
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -777,17 +777,17 @@ BrowserGlue.prototype = {
     var browser = win.gBrowser; // for closure in notification bar callback
     var notifyBox = browser.getNotificationBox();
 
     var browserBundle   = Services.strings.createBundle("chrome://browser/locale/browser.properties");
     var brandBundle     = Services.strings.createBundle("chrome://branding/locale/brand.properties");
 
     var productName        = brandBundle.GetStringFromName("brandFullName");
     var serverOwner        = Services.prefs.getCharPref(PREF_TELEMETRY_SERVER_OWNER);
-    var telemetryText      = browserBundle.formatStringFromName("telemetryText", [productName, serverOwner], 2);
+    var telemetryPrompt    = browserBundle.formatStringFromName("telemetryPrompt", [productName, serverOwner], 2);
 
     var buttons = [
                     {
                       label:     browserBundle.GetStringFromName("telemetryYesButtonLabel"),
                       accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"),
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {
                         Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
@@ -799,31 +799,31 @@ BrowserGlue.prototype = {
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {}
                     }
                   ];
 
     // Set pref to indicate we've shown the notification.
     Services.prefs.setBoolPref(PREF_TELEMETRY_PROMPTED, true);
 
-    var notification = notifyBox.appendNotification(telemetryText, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
-    notification.persistence = 3; // arbitrary number, just so bar sticks around for a bit
+    var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+    notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit
 
     let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let link = notification.ownerDocument.createElementNS(XULNS, "label");
     link.className = "text-link telemetry-text-link";
     link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel"));
     link.addEventListener('click', function() {
       // Open the learn more url in a new tab
       browser.selectedTab = browser.addTab(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL));
       // Remove the notification on which the user clicked
       notification.parentNode.removeNotification(notification, true);
       // Add a new notification to that tab, with no "Learn more" link
       var notifyBox = browser.getNotificationBox();
-      notifyBox.appendNotification(telemetryText, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+      notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
     }, false);
     let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
     description.appendChild(link);
   },
 #endif
 
   _showPluginUpdatePage: function BG__showPluginUpdatePage() {
     Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -239,20 +239,18 @@ Site.prototype = {
   },
 
   /**
    * Gets logins stored for the site.
    *
    * @return An array of the logins stored for the site.
    */
   get logins() {
-    // There could be more logins for different schemes/ports, but this covers
-    // the vast majority of cases.
-    let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", null);
-    let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", null);
+    let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", "");
+    let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", "");
     return httpLogins.concat(httpsLogins);
   },
 
   get loginSavingEnabled() {
     // Only say that login saving is blocked if it is blocked for both http and https.
     return Services.logins.getLoginSavingEnabled(this.httpURI.prePath) &&
            Services.logins.getLoginSavingEnabled(this.httpsURI.prePath);
   },
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -2820,16 +2820,17 @@ SessionStoreService.prototype = {
         this.xulAttributes[name] = true;
 
       tabData._tabStillLoading = true;
 
       // keep the data around to prevent dataloss in case
       // a tab gets closed before it's been properly restored
       browser.__SS_data = tabData;
       browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
+      tab.setAttribute("pending", "true");
 
       // Make sure that set/getTabValue will set/read the correct data by
       // wiping out any current value in tab.__SS_extdata.
       delete tab.__SS_extdata;
 
       if (!tabData.entries || tabData.entries.length == 0) {
         // make sure to blank out this tab's content
         // (just purging the tab's history won't be enough)
@@ -3002,16 +3003,17 @@ SessionStoreService.prototype = {
     // Make sure that this tab is removed from _tabsToRestore
     this._removeTabFromTabsToRestore(aTab);
 
     // Increase our internal count.
     this._tabsRestoringCount++;
 
     // Set this tab's state to restoring
     browser.__SS_restoreState = TAB_STATE_RESTORING;
+    aTab.removeAttribute("pending");
 
     // Remove the history listener, since we no longer need it once we start restoring
     this._removeSHistoryListener(aTab);
 
     let activeIndex = (tabData.index || tabData.entries.length) - 1;
     if (activeIndex >= tabData.entries.length)
       activeIndex = tabData.entries.length - 1;
     // Reset currentURI.  This creates a new session history entry with a new
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -147,16 +147,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_624727.js \
 	browser_625257.js \
 	browser_628270.js \
 	browser_635418.js \
 	browser_636279.js \
 	browser_645428.js \
 	browser_659591.js \
 	browser_662812.js \
+	browser_682507.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	browser_625016.js \
 	$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_682507.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
+  gBrowser.addTab("about:mozilla");
+
+  ss.setTabState(gBrowser.tabs[1], ss.getTabState(gBrowser.tabs[1]));
+  ok(gBrowser.tabs[1].hasAttribute("pending"), "second tab should have 'pending' attribute");
+
+  gBrowser.selectedTab = gBrowser.tabs[1];
+  ok(!gBrowser.tabs[1].hasAttribute("pending"), "second tab should have not 'pending' attribute");
+
+  gBrowser.removeTab(gBrowser.tabs[1]);
+  Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
+}
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-9.0a1
+10.0a1
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -322,16 +322,16 @@ browser.menu.showCharacterEncoding=false
 syncPromoNotification.bookmarks.description=You can access your bookmarks on all your devices with %S.\u0020
 # LOCALIZATION NOTE (syncPromoNotification.passwords.label): This appears in
 # the remember password panel.  %S will be replaced by syncBrandShortName.
 # The final space separates this text from the Learn More link.
 syncPromoNotification.passwords.description=You can access your passwords on all your devices with %S.\u0020
 syncPromoNotification.learnMoreLinkText=Learn More
 
 # Telemetry prompt
-# LOCALIZATION NOTE (telemetryText): %1$S will be replaced by brandFullName,
+# LOCALIZATION NOTE (telemetryPrompt): %1$S will be replaced by brandFullName,
 # and %2$S by the value of the toolkit.telemetry.server_owner preference.
-telemetryText = Would you like to help improve %1$S by automatically reporting memory usage, performance, and responsiveness to %2$S?
+telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S?
 telemetryLinkLabel = Learn More
 telemetryYesButtonLabel = Yes
 telemetryYesButtonAccessKey = Y
 telemetryNoButtonLabel = No
 telemetryNoButtonAccessKey = N
--- a/browser/locales/shipped-locales
+++ b/browser/locales/shipped-locales
@@ -73,11 +73,12 @@ sq
 sr
 sv-SE
 ta
 ta-LK
 te
 th
 tr
 uk
+vi
 zh-CN
 zh-TW
 zu
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -599,47 +599,28 @@ toolbar[mode="full"] .toolbarbutton-1 > 
   list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar");
 }
 #back-button[disabled="true"]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled");
 }
 
 #forward-button {
   list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar");
-  -moz-transition: 250ms ease-out;
 }
+#forward-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
+}
+
 #forward-button:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar");
 }
-
-toolbar:not([mode=icons]) #forward-button[disabled="true"] {
-  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
-}
-toolbar:not([mode=icons]) #forward-button[disabled="true"]:-moz-locale-dir(rtl) {
+#forward-button[disabled="true"]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
 }
 
-toolbar[mode=icons] #forward-button[disabled="true"] {