Bug 593743 - about:startup page showing historical startup timings; final merge with trunk. ui-r=beltzner feedback=mak77 r=mossop sr=vlad a=bsmedberg
authorDaniel Brooks <db48x@db48x.net>
Tue, 07 Dec 2010 22:10:14 -0600
changeset 58876 a89f24bf179869eab5697ac82c0c42b626d87e4c
parent 58875 bc85e9f6df4e1256dec3c5222ded7d24baa16d75 (current diff)
parent 58814 aa593835cb8de54a6250539a96f58239b8e18ceb (diff)
child 58880 95452499f3d6a92feae380e5432def073742f3fd
push idunknown
push userunknown
push dateunknown
reviewersbeltzner, mossop, vlad, bsmedberg
bugs593743
milestone2.0b8pre
Bug 593743 - about:startup page showing historical startup timings; final merge with trunk. ui-r=beltzner feedback=mak77 r=mossop sr=vlad a=bsmedberg
browser/base/content/browser.js
browser/base/content/pageReportFirstTime.xul
browser/base/jar.mn
browser/locales/en-US/chrome/browser/pageReportFirstTime.dtd
browser/locales/jar.mn
browser/themes/pinstripe/browser/hud-panel.png
browser/themes/pinstripe/browser/hud-style-button-middle-background.png
browser/themes/pinstripe/browser/hud-style-new-folder-bar-background-active.png
browser/themes/pinstripe/browser/hud-style-new-folder-bar-background.gif
browser/themes/pinstripe/browser/hud-style-new-folder-bar-background.png
browser/themes/pinstripe/browser/tabbrowser/tab-bkgnd.png
browser/themes/pinstripe/browser/tabbrowser/tabbrowser-tabs-bkgnd.png
content/media/test/test_timeupdate_seek.html
content/svg/content/src/nsSVGAnimatedNumberList.cpp
content/svg/content/src/nsSVGAnimatedNumberList.h
content/svg/content/src/nsSVGNumber.cpp
content/svg/content/src/nsSVGNumber.h
content/svg/content/src/nsSVGNumberList.cpp
content/svg/content/src/nsSVGNumberList.h
extensions/access-builtin/Makefile.in
extensions/access-builtin/README
extensions/access-builtin/accessproxy/Makefile.in
extensions/access-builtin/accessproxy/nsAccessProxy.cpp
extensions/access-builtin/accessproxy/nsAccessProxy.h
extensions/access-builtin/accessproxy/nsAccessProxyRegistration.cpp
extensions/access-builtin/accessproxy/nsIAccessProxy.idl
extensions/access-builtin/makefiles.sh
extensions/metrics/Makefile.in
extensions/metrics/build/Makefile.in
extensions/metrics/build/nsMetricsModule.cpp
extensions/metrics/content/prefs.xul
extensions/metrics/install.rdf
extensions/metrics/jar.mn
extensions/metrics/locale/en-US/prefs.dtd
extensions/metrics/makefiles.sh
extensions/metrics/metrics.js
extensions/metrics/public/Makefile.in
extensions/metrics/public/nsIMetricsCollector.idl
extensions/metrics/public/nsIMetricsService.idl
extensions/metrics/public/nsMetricsModule.h
extensions/metrics/skin/prefs.css
extensions/metrics/src/Makefile.in
extensions/metrics/src/nsAutoCompleteCollector.cpp
extensions/metrics/src/nsAutoCompleteCollector.h
extensions/metrics/src/nsLoadCollector.cpp
extensions/metrics/src/nsLoadCollector.h
extensions/metrics/src/nsMetricsConfig.cpp
extensions/metrics/src/nsMetricsConfig.h
extensions/metrics/src/nsMetricsEventItem.cpp
extensions/metrics/src/nsMetricsEventItem.h
extensions/metrics/src/nsMetricsService.cpp
extensions/metrics/src/nsMetricsService.h
extensions/metrics/src/nsProfileCollector.cpp
extensions/metrics/src/nsProfileCollector.h
extensions/metrics/src/nsPtrHashKey.h
extensions/metrics/src/nsStringUtils.cpp
extensions/metrics/src/nsStringUtils.h
extensions/metrics/src/nsUICommandCollector.cpp
extensions/metrics/src/nsUICommandCollector.h
extensions/metrics/src/nsWindowCollector.cpp
extensions/metrics/src/nsWindowCollector.h
extensions/metrics/src/nssstubs.c
extensions/metrics/test/Makefile.in
extensions/metrics/test/TestCommon.h
extensions/metrics/test/TestMetricsConfig.cpp
extensions/metrics/test/TestUICommandCollector.cpp
extensions/metrics/test/data/test_config.xml
extensions/metrics/test/unit/head_content.js
extensions/metrics/test/unit/test_event_item.js
gfx/angle/angle-nspr.patch
gfx/angle/fix-compile.patch
gfx/angle/generated/glslang.cpp
gfx/angle/generated/glslang_tab.cpp
gfx/angle/generated/glslang_tab.h
gfx/angle/src/compiler/tools/COPYING.bison
gfx/angle/src/compiler/tools/COPYING.flex
gfx/angle/src/compiler/tools/README
gfx/angle/src/compiler/tools/bison.exe
gfx/angle/src/compiler/tools/bison.hairy
gfx/angle/src/compiler/tools/bison.simple
gfx/angle/src/compiler/tools/flex.exe
gfx/cairo/cairo/src/cairo-ddraw-private.h
gfx/cairo/cairo/src/cairo-ddraw-surface.c
gfx/thebes/gfxDDrawSurface.cpp
gfx/thebes/gfxDDrawSurface.h
gfx/thebes/gfxThebesUtils.cpp
gfx/thebes/gfxThebesUtils.h
layout/reftests/editor/spellcheck-1.html
layout/reftests/editor/spellcheck-ref.html
services/crypto/tests/unit/test_crypto_keypair.js
services/crypto/tests/unit/test_crypto_rewrap.js
services/crypto/tests/unit/test_crypto_verify.js
services/sync/modules/base_records/keys.js
services/sync/tests/unit/test_bookmark_predecessor.js
services/sync/tests/unit/test_records_cryptometa.js
services/sync/tests/unit/test_records_keys.js
services/sync/tests/unit/test_service_passphraseUTF8.js
testing/mozmill/tests/firefox/restartTests/testDefaultBookmarks/test1.js
toolkit/content/license.html
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/themes/gnomestripe/global/jar.mn
toolkit/themes/gnomestripe/mozapps/extensions/go-back.png
toolkit/themes/gnomestripe/mozapps/extensions/rating-unrated.png
toolkit/themes/gnomestripe/mozapps/extensions/utilities.png
toolkit/themes/gnomestripe/mozapps/extensions/warning-stripes.png
toolkit/themes/pinstripe/global/icons/console-close.png
toolkit/themes/pinstripe/global/jar.mn
toolkit/themes/pinstripe/mozapps/extensions/go-back.png
toolkit/themes/pinstripe/mozapps/extensions/rating-unrated.png
toolkit/themes/pinstripe/mozapps/extensions/warning-stripes.png
toolkit/themes/winstripe/global/jar.mn
toolkit/themes/winstripe/mozapps/extensions/go-back.png
toolkit/themes/winstripe/mozapps/extensions/rating-unrated.png
toolkit/themes/winstripe/mozapps/extensions/warning-stripes.png
--- a/Makefile.in
+++ b/Makefile.in
@@ -187,17 +187,20 @@ ifdef MOZ_CRASHREPORTER
 	  $(DIST)/crashreporter-symbols                                   \
 	  $(MAKE_SYM_STORE_PATH) >                                        \
 	  $(DIST)/crashreporter-symbols/$(SYMBOL_INDEX_NAME)
 	echo packing symbols
 	$(NSINSTALL) -D $(DIST)/$(PKG_PATH)
 	cd $(DIST)/crashreporter-symbols && \
           zip -r9D "../$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip" .
 	cd $(DIST)/crashreporter-symbols && \
-          zip -r9D "../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip" . -i "*.sym"
+	grep "sym" $(SYMBOL_INDEX_NAME) > $(SYMBOL_INDEX_NAME).tmp && \
+	  mv $(SYMBOL_INDEX_NAME).tmp $(SYMBOL_INDEX_NAME)
+	cd $(DIST)/crashreporter-symbols && \
+          zip -r9D "../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip" . -i "*.sym" -i "*.txt"
 else
 ifdef WINCE
 ifdef SYMBOLSTORE_PATH
 	echo building symbol store with symstore.exe
 	$(RM) -rf $(DIST)/symbols
 	$(RM) -f "$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip"
 	$(NSINSTALL) -D $(DIST)/symbols
 	$(SYMBOLSTORE_PATH) add -r -f "$(subst /,\,$(shell pwd -W))\*.PDB" \
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -77,16 +77,17 @@ CPPSRCS = \
   nsTextEquivUtils.cpp \
   nsTextAttrs.cpp \
   $(NULL)
 
 EXPORTS = \
   a11yGeneric.h \
   nsAccessible.h \
   nsAccessNode.h \
+  nsARIAMap.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
 
 LOCAL_INCLUDES += \
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -33,19 +33,23 @@
  * 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 "nsARIAMap.h"
+
 #include "nsIAccessibleRole.h"
 #include "nsIAccessibleStates.h"
 
+#include "nsAccessibilityAtoms.h"
+#include "nsIContent.h"
+
 /**
  *  This list of WAI-defined roles are currently hardcoded.
  *  Eventually we will most likely be loading an RDF resource that contains this information
  *  Using RDF will also allow for role extensibility. See bug 280138.
  *
  *  Definition of nsRoleMapEntry and nsStateMapEntry contains comments explaining this table.
  *
  *  When no nsIAccessibleRole enum mapping exists for an ARIA role, the
--- a/accessible/src/base/nsARIAMap.h
+++ b/accessible/src/base/nsARIAMap.h
@@ -36,19 +36,19 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsARIAMap_H_
 #define _nsARIAMap_H_
 
 #include "prtypes.h"
-#include "nsAccessibilityAtoms.h"
 
-#include "nsIContent.h"
+class nsIAtom;
+class nsIContent;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Value constants
 
 /**
  * Used to define if role requires to expose nsIAccessibleValue.
  */
 enum EValueRule
--- a/accessible/src/base/nsAccDocManager.cpp
+++ b/accessible/src/base/nsAccDocManager.cpp
@@ -486,27 +486,35 @@ nsAccDocManager::CreateDocOrRootAccessib
   AddListeners(aDocument, isRootDoc);
   return docAcc;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccDocManager static
 
 PLDHashOperator
-nsAccDocManager::ClearDocCacheEntry(const nsIDocument* aKey,
-                                    nsRefPtr<nsDocAccessible>& aDocAccessible,
-                                    void* aUserArg)
+nsAccDocManager::GetFirstEntryInDocCache(const nsIDocument* aKey,
+                                         nsDocAccessible* aDocAccessible,
+                                         void* aUserArg)
 {
   NS_ASSERTION(aDocAccessible,
-               "Calling ClearDocCacheEntry with a NULL pointer!");
+               "No doc accessible for the object in doc accessible cache!");
+  *reinterpret_cast<nsDocAccessible**>(aUserArg) = aDocAccessible;
+
+  return PL_DHASH_STOP;
+}
 
-  if (aDocAccessible)
-    aDocAccessible->Shutdown();
-
-  return PL_DHASH_REMOVE;
+void
+nsAccDocManager::ClearDocCache()
+{
+  nsDocAccessible* docAcc = nsnull;
+  while (mDocAccessibleCache.EnumerateRead(GetFirstEntryInDocCache, static_cast<void*>(&docAcc))) {
+    if (docAcc)
+      docAcc->Shutdown();
+  }
 }
 
 PLDHashOperator
 nsAccDocManager::SearchAccessibleInDocCache(const nsIDocument* aKey,
                                             nsDocAccessible* aDocAccessible,
                                             void* aUserArg)
 {
   NS_ASSERTION(aDocAccessible,
--- a/accessible/src/base/nsAccDocManager.h
+++ b/accessible/src/base/nsAccDocManager.h
@@ -147,30 +147,27 @@ private:
    * Create document or root accessible.
    */
   nsDocAccessible *CreateDocOrRootAccessible(nsIDocument *aDocument);
 
   typedef nsRefPtrHashtable<nsPtrHashKey<const nsIDocument>, nsDocAccessible>
     nsDocAccessibleHashtable;
 
   /**
-   * Shutdown and remove the document accessible from cache.
+   * Get first entry of the document accessible from cache.
    */
   static PLDHashOperator
-    ClearDocCacheEntry(const nsIDocument* aKey,
-                       nsRefPtr<nsDocAccessible>& aDocAccessible,
-                       void* aUserArg);
+    GetFirstEntryInDocCache(const nsIDocument* aKey,
+                            nsDocAccessible* aDocAccessible,
+                            void* aUserArg);
 
   /**
    * Clear the cache and shutdown the document accessibles.
    */
-  void ClearDocCache()
-  {
-    mDocAccessibleCache.Enumerate(ClearDocCacheEntry, static_cast<void*>(this));
-  }
+  void ClearDocCache();
 
   struct nsSearchAccessibleInCacheArg
   {
     nsAccessible *mAccessible;
     nsINode* mNode;
   };
 
   static PLDHashOperator
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -400,18 +400,17 @@ PRBool
 nsAccUtils::IsARIASelected(nsAccessible *aAccessible)
 {
   return aAccessible->GetContent()->
     AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::aria_selected,
                 nsAccessibilityAtoms::_true, eCaseMatters);
 }
 
 already_AddRefed<nsHyperTextAccessible>
-nsAccUtils::GetTextAccessibleFromSelection(nsISelection *aSelection,
-                                           nsINode **aNode)
+nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
 {
   // Get accessible from selection's focus DOM point (the DOM point where
   // selection is ended).
 
   nsCOMPtr<nsIDOMNode> focusDOMNode;
   aSelection->GetFocusNode(getter_AddRefs(focusDOMNode));
   if (!focusDOMNode)
     return nsnull;
@@ -430,22 +429,19 @@ nsAccUtils::GetTextAccessibleFromSelecti
   if (!accessible) {
     NS_NOTREACHED("No nsIAccessibleText for selection change event!");
     return nsnull;
   }
 
   do {
     nsHyperTextAccessible* textAcc = nsnull;
     CallQueryInterface(accessible, &textAcc);
-    if (textAcc) {
-      if (aNode)
-        NS_ADDREF(*aNode = accessible->GetNode());
+    if (textAcc)
+      return textAcc;
 
-      return textAcc;
-    }
   } while (accessible = accessible->GetParent());
 
   NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!");
   return nsnull;
 }
 
 nsresult
 nsAccUtils::ConvertToScreenCoords(PRInt32 aX, PRInt32 aY,
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -218,22 +218,20 @@ public:
    */
   static PRBool IsARIASelected(nsAccessible *aAccessible);
 
   /**
    * Return text accessible containing focus point of the given selection.
    * Used for normal and misspelling selection changes processing.
    *
    * @param aSelection  [in] the given selection
-   * @param aNode       [out, optional] the DOM node of text accessible
    * @return            text accessible
    */
   static already_AddRefed<nsHyperTextAccessible>
-    GetTextAccessibleFromSelection(nsISelection *aSelection,
-                                   nsINode **aNode = nsnull);
+    GetTextAccessibleFromSelection(nsISelection* aSelection);
 
   /**
    * Converts the given coordinates to coordinates relative screen.
    *
    * @param aX               [in] the given x coord
    * @param aY               [in] the given y coord
    * @param aCoordinateType  [in] specifies coordinates origin (refer to
    *                         nsIAccessibleCoordinateType)
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -441,17 +441,18 @@ static const char kRoleNames[][20] = {
   "combobox list",       //ROLE_COMBOBOX_LIST
   "combobox option",     //ROLE_COMBOBOX_OPTION
   "image map",           //ROLE_IMAGE_MAP
   "listbox option",      //ROLE_OPTION
   "listbox rich option", //ROLE_RICH_OPTION
   "listbox",             //ROLE_LISTBOX
   "flat equation",       //ROLE_FLAT_EQUATION
   "gridcell",            //ROLE_GRID_CELL
-  "embedded object"      //ROLE_EMBEDDED_OBJECT
+  "embedded object",     //ROLE_EMBEDDED_OBJECT
+  "note"                 //ROLE_NOTE
 };
 
 /**
  * Map nsIAccessibleEvents constants to strings. Used by
  * nsIAccessibleRetrieval::getStringEventType() method.
  */
 static const char kEventTypeNames[][40] = {
   "unknown",                                 //
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -39,17 +39,16 @@
 
 #include "nsAccessible.h"
 
 #include "nsIXBLAccessible.h"
 
 #include "AccGroupInfo.h"
 #include "AccIterator.h"
 #include "nsAccUtils.h"
-#include "nsARIAMap.h"
 #include "nsDocAccessible.h"
 #include "nsEventShell.h"
 
 #include "nsAccEvent.h"
 #include "nsAccessibilityService.h"
 #include "nsAccTreeWalker.h"
 #include "nsRelUtils.h"
 #include "nsTextEquivUtils.h"
@@ -1819,21 +1818,20 @@ nsAccessible::GetKeyBindings(PRUint8 aAc
   if (!defaultKey.IsEmpty())
     keyBindings->Add(defaultKey);
 
   NS_ADDREF(*aKeyBindings = keyBindings);
   return NS_OK;
 }
 
 PRUint32
-nsAccessible::Role()
+nsAccessible::ARIARoleInternal()
 {
-  // No ARIA role or it doesn't suppress role from native markup.
-  if (!mRoleMapEntry || mRoleMapEntry->roleRule != kUseMapRole)
-    return NativeRole();
+  NS_PRECONDITION(mRoleMapEntry && mRoleMapEntry->roleRule == kUseMapRole,
+                  "ARIARoleInternal should only be called when ARIA role overrides!");
 
   // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
   // where the accessible role depends on both the role and ARIA state.
   if (mRoleMapEntry->role == nsIAccessibleRole::ROLE_PUSHBUTTON) {
     if (nsAccUtils::HasDefinedARIAToken(mContent,
                                         nsAccessibilityAtoms::aria_pressed)) {
       // For simplicity, any existing pressed attribute except "" or "undefined"
       // indicates a toggle.
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -43,16 +43,17 @@
 
 #include "nsIAccessible.h"
 #include "nsIAccessibleHyperLink.h"
 #include "nsIAccessibleSelectable.h"
 #include "nsIAccessibleValue.h"
 #include "nsIAccessibleRole.h"
 #include "nsIAccessibleStates.h"
 
+#include "nsARIAMap.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
 
 class AccGroupInfo;
 class EmbeddedObjCollector;
 class nsAccessible;
 class AccEvent;
@@ -140,17 +141,35 @@ public:
    *                           explicitly (see nsIAccessible::name attribute for
    *                           details)
    */
   virtual nsresult GetNameInternal(nsAString& aName);
 
   /**
    * Return enumerated accessible role (see constants in nsIAccessibleRole).
    */
-  virtual PRUint32 Role();
+  inline PRUint32 Role()
+  {
+    if (!mRoleMapEntry || mRoleMapEntry->roleRule != kUseMapRole)
+      return NativeRole();
+
+    return ARIARoleInternal();
+  }
+
+  /**
+   * Return accessible role specified by ARIA (see constants in
+   * nsIAccessibleRole).
+   */
+  inline PRUint32 ARIARole()
+  {
+    if (!mRoleMapEntry || mRoleMapEntry->roleRule != kUseMapRole)
+      return nsIAccessibleRole::ROLE_NOTHING;
+
+    return ARIARoleInternal();
+  }
 
   /**
    * Returns enumerated accessible role from native markup (see constants in
    * nsIAccessibleRole). Doesn't take into account ARIA roles.
    */
   virtual PRUint32 NativeRole();
 
   /**
@@ -200,17 +219,16 @@ public:
   /**
    * Set the ARIA role map entry for a new accessible.
    * For a newly created accessible, specify which role map entry should be used.
    *
    * @param aRoleMapEntry The ARIA nsRoleMapEntry* for the accessible, or 
    *                      nsnull if none.
    */
   virtual void SetRoleMapEntry(nsRoleMapEntry *aRoleMapEntry);
-  const nsRoleMapEntry* GetRoleMapEntry() const { return mRoleMapEntry; }
 
   /**
    * Cache children if necessary. Return true if the accessible is defunct.
    */
   PRBool EnsureChildren();
 
   /**
    * Set the child count to -1 (unknown) and null out cached child pointers.
@@ -437,16 +455,21 @@ protected:
    * Return sibling accessible at the given offset.
    */
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
                                            nsresult *aError = nsnull);
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
 
+  /**
+   * Return ARIA role (helper method).
+   */
+  PRUint32 ARIARoleInternal();
+
   virtual nsIFrame* GetBoundsFrame();
   virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
   PRBool IsVisible(PRBool *aIsOffscreen); 
 
   //////////////////////////////////////////////////////////////////////////////
   // Name helpers
 
   /**
--- a/accessible/src/base/nsApplicationAccessible.cpp
+++ b/accessible/src/base/nsApplicationAccessible.cpp
@@ -374,22 +374,16 @@ nsApplicationAccessible::IsPrimaryForNod
 
 nsresult
 nsApplicationAccessible::GetARIAState(PRUint32 *aState, PRUint32 *aExtraState)
 {
   return NS_OK;
 }
 
 PRUint32
-nsApplicationAccessible::Role()
-{
-  return NativeRole();
-}
-
-PRUint32
 nsApplicationAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_APP_ROOT;
 }
 
 nsresult
 nsApplicationAccessible::GetStateInternal(PRUint32 *aState,
                                           PRUint32 *aExtraState)
--- a/accessible/src/base/nsApplicationAccessible.h
+++ b/accessible/src/base/nsApplicationAccessible.h
@@ -121,17 +121,16 @@ public:
   // nsAccessNode
   virtual PRBool IsDefunct();
   virtual PRBool Init();
   virtual void Shutdown();
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState);
-  virtual PRUint32 Role();
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
 
   virtual void InvalidateChildren();
 
 protected:
 
   // nsAccessible
--- a/accessible/src/base/nsCaretAccessible.cpp
+++ b/accessible/src/base/nsCaretAccessible.cpp
@@ -196,104 +196,102 @@ nsCaretAccessible::RemoveDocSelectionLis
                        getter_AddRefs(spellcheckSel));
   selPrivate = do_QueryInterface(spellcheckSel);
   NS_ENSURE_TRUE(selPrivate, NS_ERROR_FAILURE);
 
   return selPrivate->RemoveSelectionListener(this);
 }
 
 NS_IMETHODIMP
-nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc,
-                                          nsISelection *aSel,
+nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
+                                          nsISelection* aSelection,
                                           PRInt16 aReason)
 {
-  NS_ENSURE_ARG(aDoc);
+  NS_ENSURE_ARG(aDOMDocument);
+  NS_ENSURE_STATE(mRootAccessible);
 
-  nsCOMPtr<nsIDocument> document(do_QueryInterface(aDoc));
-  nsDocAccessible *docAccessible = GetAccService()->GetDocAccessible(document);
+  nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
+  nsDocAccessible* document = GetAccService()->GetDocAccessible(documentNode);
 
   // Don't fire events until document is loaded.
-  if (!docAccessible || !docAccessible->IsContentLoaded())
+  if (!document || !document->IsContentLoaded())
     return NS_OK;
 
-  nsCOMPtr<nsISelection2> sel2(do_QueryInterface(aSel));
+  nsCOMPtr<nsISelection2> sel2(do_QueryInterface(aSelection));
 
   PRInt16 type = 0;
   sel2->GetType(&type);
 
   if (type == nsISelectionController::SELECTION_NORMAL)
-    return NormalSelectionChanged(aDoc, aSel);
+    NormalSelectionChanged(document, aSelection);
 
-  if (type == nsISelectionController::SELECTION_SPELLCHECK)
-    return SpellcheckSelectionChanged(aDoc, aSel);
+  else if (type == nsISelectionController::SELECTION_SPELLCHECK)
+    SpellcheckSelectionChanged(document, aSelection);
 
   return NS_OK;
 }
 
-nsresult
-nsCaretAccessible::NormalSelectionChanged(nsIDOMDocument *aDoc,
-                                          nsISelection *aSel)
+void
+nsCaretAccessible::NormalSelectionChanged(nsDocAccessible* aDocument,
+                                          nsISelection* aSelection)
 {
-  NS_ENSURE_TRUE(mRootAccessible, NS_ERROR_FAILURE);
-
-  mLastUsedSelection = do_GetWeakReference(aSel);
+  mLastUsedSelection = do_GetWeakReference(aSelection);
 
   PRInt32 rangeCount = 0;
-  nsresult rv = aSel->GetRangeCount(&rangeCount);
-  NS_ENSURE_SUCCESS(rv, rv);
-
+  aSelection->GetRangeCount(&rangeCount);
   if (rangeCount == 0) {
     mLastTextAccessible = nsnull;
-    return NS_OK; // No selection
+    return; // No selection
   }
 
-  nsCOMPtr<nsINode> textNode;
   nsRefPtr<nsHyperTextAccessible> textAcc =
-    nsAccUtils::GetTextAccessibleFromSelection(aSel, getter_AddRefs(textNode));
-  NS_ENSURE_STATE(textAcc);
+    nsAccUtils::GetTextAccessibleFromSelection(aSelection);
+  if (!textAcc)
+    return;
 
-  PRInt32 caretOffset;
-  rv = textAcc->GetCaretOffset(&caretOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
+  PRInt32 caretOffset = -1;
+  nsresult rv = textAcc->GetCaretOffset(&caretOffset);
+  if (NS_FAILED(rv))
+    return;
 
   if (textAcc == mLastTextAccessible && caretOffset == mLastCaretOffset) {
-    PRInt32 selectionCount;
+    PRInt32 selectionCount = 0;
     textAcc->GetSelectionCount(&selectionCount);   // Don't swallow similar events when selecting text
-    if (!selectionCount) {
-      return NS_OK;  // Swallow duplicate caret event
-    }
+    if (!selectionCount)
+      return;  // Swallow duplicate caret event
   }
+
   mLastCaretOffset = caretOffset;
   mLastTextAccessible.swap(textAcc);
 
-  nsRefPtr<AccEvent> event = new AccCaretMoveEvent(textNode);
-  NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
-
-  return mRootAccessible->FireDelayedAccessibleEvent(event);
+  nsRefPtr<AccEvent> event =
+    new AccCaretMoveEvent(mLastTextAccessible->GetNode());
+  if (event)
+    aDocument->FireDelayedAccessibleEvent(event);
 }
 
-nsresult
-nsCaretAccessible::SpellcheckSelectionChanged(nsIDOMDocument *aDoc,
-                                              nsISelection *aSel)
+void
+nsCaretAccessible::SpellcheckSelectionChanged(nsDocAccessible* aDocument,
+                                              nsISelection* aSelection)
 {
   // XXX: fire an event for accessible of focus node of the selection. If
   // spellchecking is enabled then we will fire the number of events for
   // the same accessible for newly appended range of the selection (for every
   // misspelled word). If spellchecking is disabled (for example,
   // @spellcheck="false" on html:body) then we won't fire any event.
 
   nsRefPtr<nsHyperTextAccessible> textAcc =
-    nsAccUtils::GetTextAccessibleFromSelection(aSel);
-  NS_ENSURE_STATE(textAcc);
+    nsAccUtils::GetTextAccessibleFromSelection(aSelection);
+  if (!textAcc)
+    return;
 
   nsRefPtr<AccEvent> event =
     new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, textAcc);
-
-  nsEventShell::FireEvent(event);
-  return NS_OK;
+  if (event)
+    aDocument->FireDelayedAccessibleEvent(event);
 }
 
 nsIntRect
 nsCaretAccessible::GetCaretRect(nsIWidget **aOutWidget)
 {
   nsIntRect caretRect;
   NS_ENSURE_TRUE(aOutWidget, caretRect);
   *aOutWidget = nsnull;
--- a/accessible/src/base/nsCaretAccessible.h
+++ b/accessible/src/base/nsCaretAccessible.h
@@ -111,18 +111,28 @@ public:
    * the document, and in any case it is unavailable from the doc after a pagehide.
    * @param aShell   PresShell for document to no longer listen to selection events from.
    */
   nsresult RemoveDocSelectionListener(nsIPresShell *aShell);
 
   nsIntRect GetCaretRect(nsIWidget **aOutWidget);
 
 protected:
-  nsresult NormalSelectionChanged(nsIDOMDocument *aDoc, nsISelection *aSel);
-  nsresult SpellcheckSelectionChanged(nsIDOMDocument *aDoc, nsISelection *aSel);
+  /**
+   * Process normal selection change and fire caret move event.
+   */
+  void NormalSelectionChanged(nsDocAccessible* aDocument,
+                              nsISelection* aSelection);
+
+  /**
+   * Process spellcheck selection change and fire text attribute changed event
+   * for invalid text attribute.
+   */
+  void SpellcheckSelectionChanged(nsDocAccessible* aDocument,
+                                  nsISelection* aSelection);
 
   /**
    * Return selection controller for the given node.
    */
   already_AddRefed<nsISelectionController>
     GetSelectionControllerForNode(nsIContent *aNode);
 
 private:
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1492,18 +1492,17 @@ nsDocAccessible::UpdateTree(nsIContent* 
 
   // Check to see if change occurred inside an alert, and fire an EVENT_ALERT
   // if it did.
   if (aIsInsert && !(updateFlags & eAlertAccessible)) {
     // XXX: tree traversal is perf issue, accessible should know if they are
     // children of alert accessible to avoid this.
     nsAccessible* ancestor = container;
     while (ancestor) {
-      const nsRoleMapEntry* roleMapEntry = ancestor->GetRoleMapEntry();
-      if (roleMapEntry && roleMapEntry->role == nsIAccessibleRole::ROLE_ALERT) {
+      if (ancestor->ARIARole() == nsIAccessibleRole::ROLE_ALERT) {
         FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT,
                                    ancestor->GetNode(), AccEvent::eRemoveDupes,
                                    fromUserInput);
         break;
       }
 
       // Don't climb above this document.
       if (ancestor == this)
@@ -1918,42 +1917,59 @@ nsDocAccessible::UpdateTreeInternal(nsAc
       updateFlags |= UpdateTreeInternal(aContainer, node->GetFirstChild(),
                                         nsnull, aIsInsert, aFireAllEvents,
                                         aFromUserInput);
       continue;
     }
 
     updateFlags |= eAccessible;
 
+    if (!aIsInsert) {
+      // Fire menupopup end event before hide event if a menu goes away.
+
+      // XXX: We don't look into children of hidden subtree to find hiding
+      // menupopup (as we did prior bug 570275) because we don't do that when
+      // menu is showing (and that's impossible until bug 606924 is fixed).
+      // Nevertheless we should do this at least because layout coalesces
+      // the changes before our processing and we may miss some menupopup
+      // events. Now we just want to be consistent in content insertion/removal
+      // handling.
+      if (accessible->ARIARole() == nsIAccessibleRole::ROLE_MENUPOPUP) {
+        nsRefPtr<AccEvent> event =
+          new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, accessible);
+
+        if (event)
+          FireDelayedAccessibleEvent(event);
+      }
+    }
+
     // Fire show/hide event.
     if (aFireAllEvents) {
       nsRefPtr<AccEvent> event;
       if (aIsInsert)
         event = new AccShowEvent(accessible, node, aFromUserInput);
       else
         event = new AccHideEvent(accessible, node, aFromUserInput);
 
       if (event)
         FireDelayedAccessibleEvent(event);
     }
 
     if (aIsInsert) {
-      const nsRoleMapEntry* roleMapEntry = accessible->GetRoleMapEntry();
-      if (roleMapEntry) {
-        if (roleMapEntry->role == nsIAccessibleRole::ROLE_MENUPOPUP) {
-          // Fire EVENT_MENUPOPUP_START if ARIA menu appears.
-          FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
-                                     node, AccEvent::eRemoveDupes, aFromUserInput);
+      PRUint32 ariaRole = accessible->ARIARole();
+      if (ariaRole == nsIAccessibleRole::ROLE_MENUPOPUP) {
+        // Fire EVENT_MENUPOPUP_START if ARIA menu appears.
+        FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
+                                   node, AccEvent::eRemoveDupes, aFromUserInput);
 
-        } else if (roleMapEntry->role == nsIAccessibleRole::ROLE_ALERT) {
-          // Fire EVENT_ALERT if ARIA alert appears.
-          updateFlags = eAlertAccessible;
-          FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
-                                     AccEvent::eRemoveDupes, aFromUserInput);
-        }
+      } else if (ariaRole == nsIAccessibleRole::ROLE_ALERT) {
+        // Fire EVENT_ALERT if ARIA alert appears.
+        updateFlags = eAlertAccessible;
+        FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
+                                   AccEvent::eRemoveDupes, aFromUserInput);
       }
 
       // 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,
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -222,17 +222,17 @@ nsRootAccessible::GetStateInternal(PRUin
     *aExtraState |= nsIAccessibleStates::EXT_STATE_MODAL;
   }
 #endif
 
   return NS_OK;
 }
 
 const char* const docEvents[] = {
-#ifdef DEBUG
+#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 
@@ -616,23 +616,16 @@ nsRootAccessible::HandleEvent(nsIDOMEven
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION,
                               treeItemAccessible);
       return NS_OK;
     }
   }
   else
 #endif
   if (eventType.EqualsLiteral("focus")) {
-    if (targetNode == mDocument && mDocument != gLastFocusedNode) {
-      // Got focus event for the window, we will make sure that an accessible
-      // focus event for initial focus is fired. We do this on a short timer
-      // because the initial focus may not have been set yet.
-      NS_DISPATCH_RUNNABLEMETHOD(FireCurrentFocusEvent, this)
-    }
-
     // 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) {
@@ -739,17 +732,17 @@ nsRootAccessible::HandleEvent(nsIDOMEven
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
                             accessible, eFromUserInput);
     FireCurrentFocusEvent();
   }
   else if (eventType.EqualsLiteral("ValueChange")) {
     FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
                                targetNode, AccEvent::eRemoveDupes);
   }
-#ifdef DEBUG
+#ifdef DEBUG_DRAGDROPSTART
   else if (eventType.EqualsLiteral("mouseover")) {
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
                             accessible);
   }
 #endif
   return NS_OK;
 }
 
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -670,16 +670,25 @@ nsHTMLComboboxAccessible::
 
 PRUint32
 nsHTMLComboboxAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_COMBOBOX;
 }
 
 void
+nsHTMLComboboxAccessible::InvalidateChildren()
+{
+  nsAccessibleWrap::InvalidateChildren();
+
+  if (mListAccessible)
+    mListAccessible->InvalidateChildren();
+}
+
+void
 nsHTMLComboboxAccessible::CacheChildren()
 {
   nsIFrame* frame = GetFrame();
   if (!frame)
     return;
 
   nsIComboboxControlFrame *comboFrame = do_QueryFrame(frame);
   if (!comboFrame)
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -191,16 +191,17 @@ public:
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
+  virtual void InvalidateChildren();
 
 protected:
   // nsAccessible
   virtual void CacheChildren();
 
   // nsHTMLComboboxAccessible
 
   /**
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -129,30 +129,32 @@ nsresult nsHyperTextAccessible::QueryInt
 PRUint32
 nsHyperTextAccessible::NativeRole()
 {
   nsIAtom *tag = mContent->Tag();
 
   if (tag == nsAccessibilityAtoms::form)
     return nsIAccessibleRole::ROLE_FORM;
 
-  if (tag == nsAccessibilityAtoms::article ||
-      tag == nsAccessibilityAtoms::blockquote ||
+  if (tag == nsAccessibilityAtoms::blockquote ||
       tag == nsAccessibilityAtoms::div ||
       tag == nsAccessibilityAtoms::nav)
     return nsIAccessibleRole::ROLE_SECTION;
 
   if (tag == nsAccessibilityAtoms::h1 ||
       tag == nsAccessibilityAtoms::h2 ||
       tag == nsAccessibilityAtoms::h3 ||
       tag == nsAccessibilityAtoms::h4 ||
       tag == nsAccessibilityAtoms::h5 ||
       tag == nsAccessibilityAtoms::h6)
     return nsIAccessibleRole::ROLE_HEADING;
 
+  if (tag == nsAccessibilityAtoms::article)
+    return nsIAccessibleRole::ROLE_DOCUMENT;
+        
   // Deal with html landmark elements
   if (tag == nsAccessibilityAtoms::header)
     return nsIAccessibleRole::ROLE_HEADER;
 
   if (tag == nsAccessibilityAtoms::footer)
     return nsIAccessibleRole::ROLE_FOOTER;
 
   if (tag == nsAccessibilityAtoms::aside)
@@ -182,16 +184,19 @@ nsHyperTextAccessible::GetStateInternal(
   nsCOMPtr<nsIEditor> editor;
   GetAssociatedEditor(getter_AddRefs(editor));
   if (editor) {
     PRUint32 flags;
     editor->GetFlags(&flags);
     if (0 == (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
       *aExtraState |= nsIAccessibleStates::EXT_STATE_EDITABLE;
     }
+  } else if (mContent->Tag() == nsAccessibilityAtoms::article) {
+    // We want <article> to behave like a document in terms of readonly state.
+    *aState |= nsIAccessibleStates::STATE_READONLY;
   }
 
   PRInt32 childCount;
   GetChildCount(&childCount);
   if (childCount > 0) {
     *aExtraState |= nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT;
   }
 
@@ -1216,19 +1221,16 @@ nsHyperTextAccessible::GetAttributesInte
     nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
                            NS_LITERAL_STRING("navigation"));
   else if (mContent->Tag() == nsAccessibilityAtoms::header) 
     nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
                            NS_LITERAL_STRING("banner"));
   else if (mContent->Tag() == nsAccessibilityAtoms::footer) 
     nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
                            NS_LITERAL_STRING("contentinfo"));
-  else if (mContent->Tag() == nsAccessibilityAtoms::article) 
-    nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
-                           NS_LITERAL_STRING("main"));
   else if (mContent->Tag() == nsAccessibilityAtoms::aside) 
     nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
                            NS_LITERAL_STRING("note"));
 
   return  NS_OK;
 }
 
 /*
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -591,36 +591,28 @@ nsXULTreeAccessible::TreeViewInvalidated
 }
 
 void
 nsXULTreeAccessible::TreeViewChanged()
 {
   if (IsDefunct())
     return;
 
-  // Fire only notification destroy/create events on accessible tree to lie to
-  // AT because it should be expensive to fire destroy events for each tree item
-  // in cache.
-  nsRefPtr<AccEvent> eventDestroy =
-    new AccEvent(nsIAccessibleEvent::EVENT_HIDE, this);
-  if (!eventDestroy)
-    return;
-
-  FirePlatformEvent(eventDestroy);
+  // Fire reorder event on tree accessible on accessible tree (do not fire
+  // show/hide events on tree items because it can be expensive to fire them for
+  // each tree item.
+  nsRefPtr<AccEvent> reorderEvent =
+    new AccEvent(nsIAccessibleEvent::EVENT_REORDER, this, eAutoDetect,
+                 AccEvent::eCoalesceFromSameSubtree);
+  if (reorderEvent)
+    GetDocAccessible()->FireDelayedAccessibleEvent(reorderEvent);
 
+  // Clear cache.
   ClearCache(mAccessibleCache);
-
   mTree->GetView(getter_AddRefs(mTreeView));
-
-  nsRefPtr<AccEvent> eventCreate =
-    new AccEvent(nsIAccessibleEvent::EVENT_SHOW, this);
-  if (!eventCreate)
-    return;
-
-  FirePlatformEvent(eventCreate);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeAccessible: protected implementation
 
 already_AddRefed<nsAccessible>
 nsXULTreeAccessible::CreateTreeItemAccessible(PRInt32 aRow)
 {
--- a/accessible/tests/mochitest/actions/test_tree.xul
+++ b/accessible/tests/mochitest/actions/test_tree.xul
@@ -46,17 +46,16 @@
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     // gA11yEventDumpID = "debug";
 
     function doTestActions()
     {
       var treeNode = getNode("tree");
-      treeNode.removeEventListener("TreeViewChanged", doTestActions, false);
 
       var treeBodyNode = treeNode.boxObject.treeBody;
 
       var tree = getAccessible(treeNode);
       var expandedTreeItem = tree.getChildAt(2);
       var collapsedTreeItem = tree.getChildAt(5);
 
       var actions = [
@@ -95,17 +94,17 @@
       ];
 
       testActions(actions); // Will call SimpleTest.finish();
     }
 
     function doTest()
     {
       var treeNode = getNode("tree");
-      treeNode.addEventListener("TreeViewChanged", doTestActions, false);
+      waitForEvent(EVENT_REORDER, treeNode, doTestActions);
       treeNode.view = new nsTreeTreeView();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
--- a/accessible/tests/mochitest/actions/test_treegrid.xul
+++ b/accessible/tests/mochitest/actions/test_treegrid.xul
@@ -63,17 +63,16 @@
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     function doTestActions()
     {
       var treeNode = getNode("tabletree");
-      treeNode.removeEventListener("TreeViewChanged", doTestActions, false);
 
       var treeBodyNode = treeNode.boxObject.treeBody;
       treeNode.focus();
 
       var tree = getAccessible(treeNode);
 
       var expandedTreeItem = tree.getChildAt(2);
       var collapsedTreeItem = tree.getChildAt(5);
@@ -145,17 +144,17 @@
       testActions(actions); // Will call SimpleTest.finish();
     }
 
     // gA11yEventDumpID = "debug";
 
     function doTest()
     {
       var treeNode = getNode("tabletree");
-      treeNode.addEventListener("TreeViewChanged", doTestActions, false);
+      waitForEvent(EVENT_REORDER, treeNode, doTestActions);
       treeNode.view = new nsTreeTreeView();
     }
 
     function test1()
     {
       var boxObj = getNode("tabletree").treeBoxObject;
       boxObj.view.setCellValue(0, boxObj.columns.firstColumn, "false");
     }
--- a/accessible/tests/mochitest/attributes/test_obj_group_tree.xul
+++ b/accessible/tests/mochitest/attributes/test_obj_group_tree.xul
@@ -13,26 +13,27 @@
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../attributes.js" />
+  <script type="application/javascript"
+          src="../events.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     function doTestAttrs()
     {
       var treeNode = getNode("tree");
-      treeNode.removeEventListener("TreeViewChanged", doTestAttrs, false);
 
       var tree = getAccessible(treeNode);
       var treeitem1 = tree.firstChild.nextSibling;
       testGroupAttrs(treeitem1, 1, 4, 1);
 
       var treeitem2 = treeitem1.nextSibling;
       testGroupAttrs(treeitem2, 2, 4, 1);
 
@@ -49,17 +50,17 @@
       testGroupAttrs(treeitem6, 4, 4, 1);
 
       SimpleTest.finish();
     }
 
     function doTest()
     {
       var treeNode = getNode("tree");
-      treeNode.addEventListener("TreeViewChanged", doTestAttrs, false);
+      waitForEvent(EVENT_REORDER, treeNode, doTestAttrs);
       treeNode.view = new nsTreeTreeView();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
--- a/accessible/tests/mochitest/attributes/test_text.html
+++ b/accessible/tests/mochitest/attributes/test_text.html
@@ -12,71 +12,18 @@
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../attributes.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
-
-    const nsIDOMNSEditableElement =
-      Components.interfaces.nsIDOMNSEditableElement;
-
     var gComputedStyle = null;
 
-    var gTextAttrChangedEventHandler = {
-      handleEvent: function gTextAttrChangedEventHandler_handleEvent(aEvent)
-      {
-        this.eventNumber++;
-      },
-
-      eventNumber: 0
-    };
-
-    function testSpellTextAttrs()
-    {
-      registerA11yEventListener(nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED,
-                                gTextAttrChangedEventHandler);
-
-      ID = "area8";
-  
-      var node = document.getElementById(ID);
-      node.setAttribute("value", "valid text inalid tixt");
-      node.focus();
-
-      var editor = node.QueryInterface(nsIDOMNSEditableElement).editor;
-      var spellchecker = editor.getInlineSpellChecker(true);
-      spellchecker.enableRealTimeSpell = true;
-
-      window.setTimeout(function()
-        {
-          var defAttrs = buildDefaultTextAttrs(node, kInputFontSize);
-          testDefaultTextAttrs(ID, defAttrs);
-
-          var attrs = { };
-          var misspelledAttrs = {
-            "invalid": "spelling"
-          };
-
-          testTextAttrs(ID, 0, attrs, defAttrs, 0, 11);
-          testTextAttrs(ID, 11, misspelledAttrs, defAttrs, 11, 17);
-          testTextAttrs(ID, 17, attrs, defAttrs, 17, 18);
-          testTextAttrs(ID, 18, misspelledAttrs, defAttrs, 18, 22);
-
-          is(gTextAttrChangedEventHandler.eventNumber, 2,
-             "Wrong count of 'text attribute changed' events for " + ID);
-
-          unregisterA11yEventListener(nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED,
-                                      gTextAttrChangedEventHandler);
-
-          SimpleTest.finish();
-        }, 0);
-    }
-
     function doTest()
     {
       //////////////////////////////////////////////////////////////////////////
       // area1
       var ID = "area1";
       var defAttrs = buildDefaultTextAttrs(ID, "10pt");
       testDefaultTextAttrs(ID, defAttrs);
 
@@ -453,19 +400,17 @@
       // p
       testTextAttrs(ID, 18, { }, { }, 18, 19);
       // bold
       attrs = { "font-weight": kBoldFontWeight };
       testTextAttrs(ID, 19, attrs, defAttrs, 19, 23);
       // p
       testTextAttrs(ID, 23, { }, { }, 23, 24);
 
-      //////////////////////////////////////////////////////////////////////////
-      // test spelling text attributes
-      testSpellTextAttrs(); // Will call SimpleTest.finish();
+      SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body style="font-size: 12pt">
 
@@ -516,18 +461,16 @@
     <span style="background-color: blue">Blue BG color</span>
     <span lang="de">Ich bin/Du bist</span>
     <span lang="en">
       Normal
       <span style="color: magenta">Magenta<b>Bold</b>Magenta</span>
     </span>
   </p>
 
-  <input id="area8"/>
-
   <p id="area9" style="font-size: smaller">Small
     <span style="font-size: 120%">bigger</span> smaller
     <span style="background-color: blue;">background blue</span> normal
     <span style="font-style: italic;">Different styling</span> normal
     <span style="font-family: tahoma;">Different font</span> normal
     <span style="text-decoration: underline;">underlined</span> normal
     <span style="text-decoration: line-through;">strikethrough</span> normal
   </p>
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -3,24 +3,27 @@
 
 const EVENT_ALERT = nsIAccessibleEvent.EVENT_ALERT;
 const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
 const EVENT_DOCUMENT_RELOAD = nsIAccessibleEvent.EVENT_DOCUMENT_RELOAD;
 const EVENT_DOCUMENT_LOAD_STOPPED = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPPED;
 const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
 const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
 const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
+const EVENT_MENU_START = nsIAccessibleEvent.EVENT_MENU_START;
+const EVENT_MENU_END = nsIAccessibleEvent.EVENT_MENU_END;
 const EVENT_MENUPOPUP_START = nsIAccessibleEvent.EVENT_MENUPOPUP_START;
 const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
 const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
 const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
 const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
 const EVENT_SELECTION_WITHIN = nsIAccessibleEvent.EVENT_SELECTION_WITHIN;
 const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
 const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
+const EVENT_TEXT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED;
 const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
 const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
 const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
 const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE;
 
 ////////////////////////////////////////////////////////////////////////////////
 // General
 
@@ -30,16 +33,21 @@ const EVENT_VALUE_CHANGE = nsIAccessible
 var gA11yEventDumpID = "";
 
 /**
  * Set up this variable to dump event processing into console.
  */
 var gA11yEventDumpToConsole = false;
 
 /**
+ * Set up this variable to dump event processing into error console.
+ */
+var gA11yEventDumpToAppConsole = false;
+
+/**
  * Executes the function when requested event is handled.
  *
  * @param aEventType  [in] event type
  * @param aTarget     [in] event target
  * @param aFunc       [in] function to call when event is handled
  * @param aContext    [in, optional] object in which context the function is
  *                    called
  * @param aArg1       [in, optional] argument passed into the function
@@ -264,20 +272,22 @@ function eventQueue(aEventType)
         SimpleTest.finish();
 
       return;
     }
 
     // Start processing of next invoker.
     invoker = this.getNextInvoker();
 
-    this.setEventHandler(invoker);
+    if (gLogger.isEnabled()) {
+      gLogger.logToConsole("Event queue: \n  invoke: " + invoker.getID());
+      gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true);
+    }
 
-    if (gA11yEventDumpToConsole)
-      dump("\nEvent queue: \n  invoke: " + invoker.getID() + "\n");
+    this.setEventHandler(invoker);
 
     if (invoker.invoke() == INVOKER_ACTION_FAILED) {
       // Invoker failed to prepare action, fail and finish tests.
       this.processNextInvoker();
       return;
     }
 
     if (this.areAllEventsUnexpected())
@@ -396,16 +406,32 @@ function eventQueue(aEventType)
     this.mEventSeqIdx = -1;
 
     // Register event listeners
     if (this.mEventSeq) {
       aInvoker.wasCaught = new Array(this.mEventSeq.length);
 
       for (var idx = 0; idx < this.mEventSeq.length; idx++) {
         var eventType = this.getEventType(idx);
+
+        if (gLogger.isEnabled()) {
+          var strEventType = (typeof eventType == "string") ? eventType :
+            eventTypeToString(eventType);
+
+          var msg = "registered";
+          if (this.isEventUnexpected(idx))
+            msg += " unexpected";
+
+          msg += ": event type: " + strEventType + ", target: " +
+            prettyName(this.getEventTarget(idx));
+
+          gLogger.logToConsole(msg);
+          gLogger.logToDOM(msg, true);
+        }
+
         if (typeof eventType == "string") {
           // DOM event
           var target = this.getEventTarget(idx);
           var phase = this.getEventPhase(idx);
           target.ownerDocument.addEventListener(eventType, this, phase);
 
         } else {
           // A11y event
@@ -460,16 +486,21 @@ function eventQueue(aEventType)
     var eventItem = this.mEventSeq[aIdx];
     if ("getID" in eventItem)
       return eventItem.getID();
     
     var invoker = this.getInvoker();
     return invoker.getID();
   }
 
+  this.isEventUnexpected = function eventQueue_isEventUnexpected(aIdx)
+  {
+    return this.mEventSeq[aIdx].unexpected;
+  }
+
   this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
   {
     var eventType1 = this.getEventType(aIdx);
 
     var eventType2 = (aEvent instanceof nsIDOMEvent) ?
       aEvent.type : aEvent.eventType;
 
     if (eventType1 != eventType2)
@@ -520,64 +551,49 @@ function eventQueue(aEventType)
 
     return true;
   }
 
   this.dumpEventToDOM = function eventQueue_dumpEventToDOM(aOrigEvent,
                                                            aExpectedEventIdx,
                                                            aMatch)
   {
-    if (!gA11yEventDumpID) // debug stuff
+    if (!gLogger.isEnabled()) // debug stuff
       return;
 
     // Dump DOM event information. Skip a11y event since it is dumped by
     // gA11yEventObserver.
     if (aOrigEvent instanceof nsIDOMEvent) {
       var info = "Event type: " + aOrigEvent.type;
       info += ". Target: " + prettyName(aOrigEvent.originalTarget);
-      dumpInfoToDOM(info);
+      gLogger.logToDOM(info);
     }
 
     var currType = this.getEventType(aExpectedEventIdx);
     var currTarget = this.getEventTarget(aExpectedEventIdx);
 
-    var containerTagName = document instanceof nsIDOMHTMLDocument ?
-      "div" : "description";
-    var inlineTagName = document instanceof nsIDOMHTMLDocument ?
-      "span" : "description";
-
-    var container = document.createElement(containerTagName);
-    container.setAttribute("style", "padding-left: 10px;");
+    var msg = "EQ: ";
+    var emphText = "";
+    if (aMatch) {
+      emphText = "matched ";
 
-    var text1 = document.createTextNode("EQ: ");
-    container.appendChild(text1);
-
-    var styledNode = document.createElement(inlineTagName);
-    if (aMatch) {
-      styledNode.setAttribute("style", "color: blue;");
-      styledNode.textContent = "matched";
-
-      // Dump matched events into console.
-      if (gA11yEventDumpToConsole)
-        dump("\n*****\nEQ matched: " + eventTypeToString(currType) + "\n*****\n");
+      var consoleMsg =
+        "*****\nEQ matched: " + eventTypeToString(currType) + "\n*****";
+      gLogger.logToConsole(consoleMsg);
 
     } else {
-      styledNode.textContent = "expected";
+      msg += "expected";
     }
-    container.appendChild(styledNode);
 
-    var info = " event, type: ";
-    info += (typeof currType == "string") ?
+    msg += " event, type: ";
+    msg += (typeof currType == "string") ?
       currType : eventTypeToString(currType);
-    info += ". Target: " + prettyName(currTarget);
+    msg += ", target: " + prettyName(currTarget);
 
-    var text1 = document.createTextNode(info);
-    container.appendChild(text1);
-
-    dumpInfoToDOM(container);
+    gLogger.logToDOM(msg, true, emphText);
   }
 
   this.mDefEventType = aEventType;
 
   this.mInvokers = new Array();
   this.mIndex = -1;
 
   this.mEventSeq = null;
@@ -634,32 +650,33 @@ function sequence()
   this.idx = -1;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Event queue invokers
 
 /**
- * Invokers defined below take a checker object 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)
+ * 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.
  *
- * Note, checker object is optional.
+ * 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, aChecker, aEventType)
+function synthClick(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
 
   this.invoke = function synthClick_invoke()
   {
     // Scroll the node into view, otherwise synth click may fail.
     if (this.DOMNode instanceof nsIDOMNSHTMLElement)
       this.DOMNode.scrollIntoView(true);
 
     synthesizeMouse(this.DOMNode, 1, 1, {});
@@ -669,38 +686,38 @@ function synthClick(aNodeOrID, aChecker,
   {
     return prettyName(aNodeOrID) + " click"; 
   }
 }
 
 /**
  * Mouse move invoker.
  */
-function synthMouseMove(aNodeOrID, aChecker, aEventType)
+function synthMouseMove(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
 
   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"; 
   }
 }
 
 /**
  * General key press invoker.
  */
-function synthKey(aNodeOrID, aKey, aArgs, aChecker, aEventType)
+function synthKey(aNodeOrID, aKey, aArgs, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
 
   this.invoke = function synthKey_invoke()
   {
     synthesizeKey(this.mKey, this.mArgs);
   }
 
   this.getID = function synthKey_getID()
   {
@@ -709,126 +726,126 @@ function synthKey(aNodeOrID, aKey, aArgs
 
   this.mKey = aKey;
   this.mArgs = aArgs ? aArgs : {};
 }
 
 /**
  * Tab key invoker.
  */
-function synthTab(aNodeOrID, aChecker, aEventType)
+function synthTab(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
   this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: false },
-                                aChecker, aEventType);
+                                aCheckerOrEventSeq, aEventType);
 
   this.getID = function synthTab_getID() 
   { 
     return prettyName(aNodeOrID) + " tab";
   }
 }
 
 /**
  * Shift tab key invoker.
  */
-function synthShiftTab(aNodeOrID, aChecker, aEventType)
+function synthShiftTab(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
   this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: true },
-                                aChecker, aEventType);
+                                aCheckerOrEventSeq, aEventType);
 
   this.getID = function synthTabTest_getID() 
   { 
     return prettyName(aNodeOrID) + " shift tab";
   }
 }
 
 /**
  * Down arrow key invoker.
  */
-function synthDownKey(aNodeOrID, aChecker, aEventType)
+function synthDownKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthKey(aNodeOrID, "VK_DOWN", null, aChecker,
+  this.__proto__ = new synthKey(aNodeOrID, "VK_DOWN", null, aCheckerOrEventSeq,
                                 aEventType);
 
   this.getID = function synthDownKey_getID()
   {
     return prettyName(aNodeOrID) + " key down";
   }
 }
 
 /**
  * Right arrow key invoker.
  */
-function synthRightKey(aNodeOrID, aChecker, aEventType)
+function synthRightKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aChecker,
+  this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq,
                                 aEventType);
 
   this.getID = function synthRightKey_getID()
   {
     return prettyName(aNodeOrID) + " key right";
   }
 }
 
 /**
  * Home key invoker.
  */
-function synthHomeKey(aNodeOrID, aChecker, aEventType)
+function synthHomeKey(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthKey(aNodeOrID, "VK_HOME", null, aChecker,
+  this.__proto__ = new synthKey(aNodeOrID, "VK_HOME", null, aCheckerOrEventSeq,
                                 aEventType);
   
   this.getID = function synthHomeKey_getID()
   {
     return prettyName(aNodeOrID) + " key home";
   }
 }
 
 /**
  * Focus invoker.
  */
-function synthFocus(aNodeOrID, aChecker, aEventType)
+function synthFocus(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
 
   this.invoke = function synthFocus_invoke()
   {
     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, aChecker, aEventType)
+function synthFocusOnFrame(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
   this.__proto__ = new synthAction(getNode(aNodeOrID).contentDocument,
-                                   aChecker, aEventType);
+                                   aCheckerOrEventSeq, aEventType);
   
   this.invoke = function synthFocus_invoke()
   {
     this.DOMNode.body.focus();
   }
   
   this.getID = function synthFocus_getID() 
   { 
     return prettyName(aNodeOrID) + " frame document focus";
   }
 }
 
 /**
  * Select all invoker.
  */
-function synthSelectAll(aNodeOrID, aChecker, aEventType)
+function synthSelectAll(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
-  this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
+  this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType);
 
   this.invoke = function synthSelectAll_invoke()
   {
     if (this.DOMNode instanceof Components.interfaces.nsIDOMHTMLInputElement)
       this.DOMNode.select();
     else
       window.getSelection().selectAllChildren(this.DOMNode);
   }
@@ -905,46 +922,46 @@ var gA11yEventObserver =
       // Remove the leftover observer, otherwise it "leaks" to all the following tests.
       this.observerService.removeObserver(this, "accessible-event");
       // Forward the exception, with added explanation.
       throw "[accessible/events.js, gA11yEventObserver.observe] This is expected if a previous test has been aborted... Initial exception was: [ " + ex + " ]";
     }
     var listenersArray = gA11yEventListeners[event.eventType];
 
     var eventFromDumpArea = false;
-    if (gA11yEventDumpID) { // debug stuff
+    if (gLogger.isEnabled()) { // debug stuff
       eventFromDumpArea = true;
 
       var target = event.DOMNode;
-      var dumpElm = document.getElementById(gA11yEventDumpID);
+      var dumpElm = gA11yEventDumpID ?
+        document.getElementById(gA11yEventDumpID) : null;
 
-      var parent = target;
-      while (parent && parent != dumpElm)
-        parent = parent.parentNode;
+      if (dumpElm) {
+        var parent = target;
+        while (parent && parent != dumpElm)
+          parent = parent.parentNode;
+      }
 
-      if (parent != dumpElm) {
+      if (!dumpElm || parent != dumpElm) {
         var type = eventTypeToString(event.eventType);
         var info = "Event type: " + type;
 
         if (event instanceof nsIAccessibleTextChangeEvent) {
           info += ", start: " + event.start + ", length: " + event.length +
             ", " + (event.isInserted() ? "inserted" : "removed") +
             " text: " + event.modifiedText;
         }
 
         info += ". Target: " + prettyName(event.accessible);
 
         if (listenersArray)
           info += ". Listeners count: " + listenersArray.length;
 
         eventFromDumpArea = false;
-
-        if (gA11yEventDumpToConsole)
-          dump("\n" + info + "\n");
-        dumpInfoToDOM(info);
+        gLogger.log(info);
       }
     }
 
     // Do not notify listeners if event is result of event log changes.
     if (!listenersArray || eventFromDumpArea)
       return;
 
     for (var index = 0; index < listenersArray.length; index++)
@@ -992,45 +1009,103 @@ function removeA11yEventListener(aEventT
     gA11yEventListeners[aEventType] = null;
     delete gA11yEventListeners[aEventType];
   }
 
   return true;
 }
 
 /**
- * Dumps message to DOM.
- *
- * @param aInfo      [in] the message or DOM node to dump
- * @param aDumpNode  [in, optional] host DOM node for dumped message, if ommited
- *                    then global variable gA11yEventDumpID is used
+ * Used to dump debug information.
  */
-function dumpInfoToDOM(aInfo, aDumpNode)
+var gLogger =
 {
-  var dumpID = gA11yEventDumpID ? gA11yEventDumpID : aDumpNode;
-  if (!dumpID)
-    return;
-  
-  var dumpElm = document.getElementById(dumpID);
-  if (!dumpElm) {
-    ok(false, "No dump element '" + dumpID + "' within the document!");
-    return;
-  }
-  
-  var containerTagName = document instanceof nsIDOMHTMLDocument ?
-    "div" : "description";
+  /**
+   * Return true if dump is enabled.
+   */
+  isEnabled: function debugOutput_isEnabled()
+  {
+    return gA11yEventDumpID || gA11yEventDumpToConsole ||
+      gA11yEventDumpToAppConsole;
+  },
+
+  /**
+   * Dump information into DOM and console if applicable.
+   */
+  log: function logger_log(aMsg)
+  {
+    this.logToConsole(aMsg);
+    this.logToAppConsole(aMsg);
+    this.logToDOM(aMsg);
+  },
+
+  /**
+   * Log message to DOM.
+   *
+   * @param aMsg          [in] the primary message
+   * @param aHasIndent    [in, optional] if specified the message has an indent
+   * @param aPreEmphText  [in, optional] the text is colored and appended prior
+   *                        primary message
+   */
+  logToDOM: function logger_logToDOM(aMsg, aHasIndent, aPreEmphText)
+  {
+    if (gA11yEventDumpID == "")
+      return;
+
+    var dumpElm = document.getElementById(gA11yEventDumpID);
+    if (!dumpElm) {
+      ok(false,
+         "No dump element '" + gA11yEventDumpID + "' within the document!");
+      return;
+    }
+
+    var containerTagName = document instanceof nsIDOMHTMLDocument ?
+      "div" : "description";
 
-  var container = document.createElement(containerTagName);
-  if (aInfo instanceof nsIDOMNode)
-    container.appendChild(aInfo);
-  else
-    container.textContent = aInfo;
+    var container = document.createElement(containerTagName);
+    if (aHasIndent)
+      container.setAttribute("style", "padding-left: 10px;");
+
+    if (aPreEmphText) {
+      var inlineTagName = document instanceof nsIDOMHTMLDocument ?
+        "span" : "description";
+      var emphElm = document.createElement(inlineTagName);
+      emphElm.setAttribute("style", "color: blue;");
+      emphElm.textContent = aPreEmphText;
+
+      container.appendChild(emphElm);
+    }
+
+    var textNode = document.createTextNode(aMsg);
+    container.appendChild(textNode);
+
+    dumpElm.appendChild(container);
+  },
 
-  dumpElm.appendChild(container);
-}
+  /**
+   * Log message to console.
+   */
+  logToConsole: function logger_logToConsole(aMsg)
+  {
+    if (gA11yEventDumpToConsole)
+      dump("\n" + aMsg + "\n");
+  },
+
+  /**
+   * Log message to error console.
+   */
+  logToAppConsole: function logger_logToAppConsole(aMsg)
+  {
+    if (gA11yEventDumpToAppConsole)
+      consoleService.logStringMessage("events: " + aMsg);
+  },
+
+  consoleService: Components.classes["@mozilla.org/consoleservice;1"].
+    getService(Components.interfaces.nsIConsoleService)
+};
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Sequence
 
 /**
  * Base class of sequence item.
  */
@@ -1067,25 +1142,33 @@ function sequenceItem(aProcessor, aEvent
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Event queue invokers
 
 /**
  * Invoker base class for prepare an action.
  */
-function synthAction(aNodeOrID, aChecker, aEventType)
+function synthAction(aNodeOrID, aCheckerOrEventSeq, aEventType)
 {
   this.DOMNode = getNode(aNodeOrID);
-  if (aChecker)
-    aChecker.target = this.DOMNode;
+
+  this.checker = null;
+  if (aCheckerOrEventSeq) {
+    if (aCheckerOrEventSeq instanceof Array) {
+      this.eventSeq = aCheckerOrEventSeq;
+    } else {
+      this.checker = aCheckerOrEventSeq;
+      this.checker.target = this.DOMNode;
+    }
+  }
 
   if (aEventType)
     this.eventSeq = [ new invokerChecker(aEventType, this.DOMNode) ];
 
   this.check = function synthAction_check(aEvent)
   {
-    if (aChecker)
-      aChecker.check(aEvent);
+    if (this.checker)
+      this.checker.check(aEvent);
   }
 
   this.getID = function synthAction_getID() { return aNodeOrID + " action"; }
 }
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -60,19 +60,21 @@ include $(topsrcdir)/config/rules.mk
 		test_docload.html \
 		test_docload.xul \
 		test_dragndrop.html \
 		test_flush.html \
 		test_focus.html \
 		test_focus.xul \
 		test_focus_name.html \
 		test_focusdoc.html \
+		test_menu.xul \
 		test_mutation.html \
 		test_scroll.xul \
 		test_selection.html \
 		test_statechange.html \
 		test_text.html \
+		test_textattrchange.html \
 		test_tree.xul \
 		test_valuechange.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/events/test_aria_menu.html
+++ b/accessible/tests/mochitest/events/test_aria_menu.html
@@ -5,84 +5,211 @@
 
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <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">
-    function showMenu(aID, aViaDisplayStyle)
+    const kViaDisplayStyle = 0;
+    const kViaVisibilityStyle = 1;
+
+    function focusMenu(aMenuBarID, aMenuID)
     {
-      this.DOMNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_MENU_START, getNode(aMenuBarID)),
+        new invokerChecker(EVENT_FOCUS, getNode(aMenuID)),
+      ];
+
+      this.invoke = function focusMenu_invoke()
+      {
+        getNode(aMenuID).focus();
+      }
+
+      this.getID = function focusMenu_getID()
+      {
+        return "focus menu '" + aMenuID + "'";
+      }
+    }
+
+    function showMenu(aMenuID, aParentMenuID, aHow)
+    {
+      this.menuNode = getNode(aMenuID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, this.menuNode),
+        new invokerChecker(EVENT_MENUPOPUP_START, this.menuNode),
+        new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
+      ];
 
       this.invoke = function showMenu_invoke()
       {
-        if (aViaDisplayStyle)
-          this.DOMNode.style.display = "block";
+        if (aHow == kViaDisplayStyle)
+          this.menuNode.style.display = "block";
         else
-          this.DOMNode.style.visibility = "visible";
+          this.menuNode.style.visibility = "visible";
       };
 
       this.getID = function showMenu_getID()
       {
-        return "Show ARIA menu " + aID + " by " +
-          (aViaDisplayStyle ? "display" : "visibility") + " style tricks";
+        return "Show ARIA menu " + aMenuID + " by " +
+          (aHow == kViaDisplayStyle ? "display" : "visibility") +
+          " style tricks";
       };
     }
 
+    function closeMenu(aMenuID, aParentMenuID, aHow)
+    {
+      this.menuNode = getNode(aMenuID);
+      this.menu = null;
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_MENUPOPUP_END, getMenu, this),
+        new invokerChecker(EVENT_HIDE, getMenu, this),
+        new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
+      ];
+
+      this.invoke = function closeMenu_invoke()
+      {
+        // Store menu accessible reference while menu is still open.
+        this.menu = getAccessible(this.menuNode);
+
+        // Hide menu.
+        if (aHow == kViaDisplayStyle)
+          this.menuNode.style.display = "none";
+        else
+          this.menuNode.style.visibility = "hidden";
+      }
+
+      this.getID = function closeMenu_getID()
+      {
+        return "Close ARIA menu " + aMenuID + " by " +
+          (aHow == kViaDisplayStyle ? "display" : "visibility") +
+          " style tricks";
+      }
+
+      function getMenu(aThisObj)
+      {
+        return aThisObj.menu;
+      }
+    }
+
+    function focusInsideMenu(aMenuID, aMenuBarID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_FOCUS, getNode(aMenuID))
+      ];
+
+      this.unexpectedEventSeq = [
+        new invokerChecker(EVENT_MENU_END, getNode(aMenuBarID))
+      ];
+
+      this.invoke = function focusInsideMenu_invoke()
+      {
+        getNode(aMenuID).focus();
+      }
+
+      this.getID = function focusInsideMenu_getID()
+      {
+        return "focus menu '" + aMenuID + "'";
+      }
+    }
+
+    function blurMenu(aMenuBarID)
+    {
+      var eventSeq = [
+        new invokerChecker(EVENT_MENU_END, getNode(aMenuBarID)),
+        new invokerChecker(EVENT_FOCUS, getNode("outsidemenu"))
+      ];
+
+      this.__proto__ = new synthClick("outsidemenu", eventSeq);
+
+      this.getID = function blurMenu_getID()
+      {
+        return "blur menu";
+      }
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
 
     var gQueue = null;
 
     //gA11yEventDumpID = "eventdump";
+    //gA11yEventDumpToConsole = true;
 
     function doTests()
     {
-      gQueue = new eventQueue(EVENT_MENUPOPUP_START);
+      gQueue = new eventQueue();
 
-      gQueue.push(new showMenu("menu1", true));
-      gQueue.push(new showMenu("menu2", false));
+      gQueue.push(new focusMenu("menubar", "menu-file"));
+      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();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=606207"
      title="Dojo dropdown buttons are broken">
     Mozilla Bug 606207
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=614829"
+     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>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <div id="menu1" role="menu" style="display: none;">
-    <div role="menuitem">menuitem1.1</div>
-    <div role="menuitem">menuitem1.2</div>
+  <div id="menubar" role="menubar">
+    <div id="menu-file" role="menuitem" tabindex="0">
+      File
+      <div id="menupopup-file" role="menu" style="display: none;">
+        <div id="menuitem-newtab" role="menuitem" tabindex="0">New Tab</div>
+        <div id="menuitem-newwindow" role="menuitem" tabindex="0">New Window</div>
+      </div>
+    </div>
+    <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="menu2" role="menu" style="visibility: hidden;">
-    <div role="menuitem">menuitem2.1</div>
-    <div role="menuitem">menuitem2.2</div>
-  </div>
+  <div tabindex="0" id="outsidemenu">outsidemenu</div>
 
   <div id="eventdump"></div>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_menu.xul
@@ -0,0 +1,204 @@
+<?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 menu events testing for XUL menu">
+
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js" />
+  <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="../events.js" />
+
+  <script type="application/javascript">
+    function openFileMenu()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_MENU_START, getNode("menubar")),
+        new invokerChecker(EVENT_MENUPOPUP_START, getNode("menupopup-file"))
+        // new invokerChecker(EVENT_FOCUS, getNode("menuitem-newtab")) intermitent failure
+      ];
+
+      this.invoke = function openFileMenu_invoke()
+      {
+        synthesizeKey("F", { altKey: true, shiftKey: true });
+      }
+
+      this.getID = function openFileMenu_getID()
+      {
+        return "open file menu by alt+F press";
+      }
+    }
+
+    function openEditMenu()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_MENUPOPUP_END, getNode("menupopup-file")),
+        new invokerChecker(EVENT_MENUPOPUP_START, getNode("menupopup-edit"))
+        // new invokerChecker(EVENT_FOCUS, getNode("menuitem-undo")) intermitent failure
+      ];
+
+      this.invoke = function openEditMenu_invoke()
+      {
+        synthesizeKey("VK_RIGHT", { });
+      }
+
+      this.getID = function openEditMenu_getID()
+      {
+        return "open edit menu by lef arrow press";
+      }
+    }
+
+    function closeEditMenu()
+    {
+      this.eventSeq = [
+        //new invokerChecker(EVENT_FOCUS, document), intermitent failure
+        new invokerChecker(EVENT_MENUPOPUP_END, getNode("menupopup-edit")),
+        new invokerChecker(EVENT_MENU_END, getNode("menubar"))
+      ];
+
+      this.invoke = function closeEditMenu_invoke()
+      {
+        synthesizeKey("VK_ESCAPE", { });
+      }
+
+      this.getID = function closeEditMenu_getID()
+      {
+        return "close edit menu, leave menubar";
+      }
+    }
+
+    function focusFileMenu()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_MENU_START, getNode("menubar")),
+        new invokerChecker(EVENT_FOCUS, getNode("menuitem-file"))
+      ];
+
+      this.invoke = function focusFileMenu_invoke()
+      {
+        synthesizeKey("VK_ALT", { });
+      }
+
+      this.getID = function focusFileMenu_getID()
+      {
+        return "activate menubar, focus file menu (atl press)";
+      }
+    }
+
+    function focusEditMenu()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_FOCUS, getNode("menuitem-edit"))
+      ];
+
+      this.invoke = function focusEditMenu_invoke()
+      {
+        synthesizeKey("VK_RIGHT", { });
+      }
+
+      this.getID = function focusEditMenu_getID()
+      {
+        return "focus edit menu";
+      }
+    }
+
+    function leaveMenubar()
+    {
+      this.eventSeq = [
+        //new invokerChecker(EVENT_FOCUS, document), intermitent failure
+        new invokerChecker(EVENT_MENU_END, getNode("menubar"))
+      ];
+
+      this.invoke = function leaveMenubar_invoke()
+      {
+        synthesizeKey("VK_ESCAPE", { });
+      }
+
+      this.getID = function leaveMenubar_getID()
+      {
+        return "leave menubar";
+      }
+    }
+
+    /**
+     * Do tests.
+     */
+
+    //gA11yEventDumpID = "eventdump";
+    gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+
+    function doTests()
+    {
+      if (!WIN) {
+        todo(false, "Enable this test on other platforms.");
+        SimpleTest.finish();
+        return;
+      }
+
+      todo(false,
+           "Fix intermitent failures. Focus may randomly occur before or after menupopup events!");
+
+      gQueue = new eventQueue();
+
+      gQueue.push(new openFileMenu());
+      gQueue.push(new openEditMenu());
+      gQueue.push(new closeEditMenu());
+
+      // Alt key is used to active menubar and focus menu item on Windows,
+      // other platforms requires setting a ui.key.menuAccessKeyFocuses
+      // preference.
+      if (WIN) {
+        gQueue.push(new focusFileMenu());
+        gQueue.push(new focusEditMenu());
+        gQueue.push(new leaveMenubar());
+      }
+
+      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=615189"
+         title="Clean up FireAccessibleFocusEvent">
+        Mozilla Bug 615189
+      </a>
+      <p id="display"></p>
+      <div id="content" style="display: none"></div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+      <menubar id="menubar">
+        <menu id="menuitem-file" label="File" accesskey="F">
+          <menupopup id="menupopup-file">
+            <menuitem id="menuitem-newtab" label="New Tab"/>
+          </menupopup>
+        </menu>
+        <menu id="menuitem-edit" label="Edit" accesskey="E">
+          <menupopup id="menupopup-edit">
+            <menuitem id="menuitem-undo" label="Undo"/>
+          </menupopup>
+        </menu>
+      </menubar>
+
+      <vbox id="eventdump" role="log"/>
+    </vbox>
+  </hbox>
+</window>
--- a/accessible/tests/mochitest/events/test_statechange.html
+++ b/accessible/tests/mochitest/events/test_statechange.html
@@ -55,17 +55,17 @@
     }
 
     function invalidInput(aNodeOrID)
     {
       this.DOMNode = getNode(aNodeOrID);
 
       this.invoke = function invalidInput_invoke() {
         // Note: this should fire an EVENT_STATE_CHANGE
-        this.DOMNode.value = "I am too long";
+        this.DOMNode.value = "I am not an email";
       };
 
       this.check = function invalidInput_check() {
         testStates(aNodeOrID, STATE_INVALID);
       };
 
       this.getID = function invalidInput_getID() {
         return prettyName(aNodeOrID) + " became invalid";
@@ -83,17 +83,17 @@
     {
       gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
 
       // Test delayed editable state change
       var doc = document.getElementById("iframe").contentDocument;
       gQueue.push(new makeEditableDoc(doc));
 
       // invalid state change
-      gQueue.push(new invalidInput("maxlength"));
+      gQueue.push(new invalidInput("email"));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -115,13 +115,13 @@
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="testContainer">
     <iframe id="iframe"></iframe>
   </div>
 
-  <input id="maxlength" maxlength="1">
+  <input id="email" type='email'>
 
   <div id="eventdump"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_textattrchange.html
@@ -0,0 +1,102 @@
+<html>
+
+<head>
+  <title>Text attribute changed event for misspelled text</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <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="../attributes.js"></script>
+
+  <script type="application/javascript">
+
+    const nsIDOMNSEditableElement =
+      Components.interfaces.nsIDOMNSEditableElement;
+
+    function spelledTextInvoker(aID)
+    {
+      this.DOMNode = getNode(aID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_TEXT_ATTRIBUTE_CHANGED, this.DOMNode)
+      ];
+
+      this.invoke = function spelledTextInvoker_invoke()
+      {
+        this.DOMNode.setAttribute("value", "valid text inalid tixt");
+        this.DOMNode.focus();
+
+        var editor = this.DOMNode.QueryInterface(nsIDOMNSEditableElement).editor;
+        var spellchecker = editor.getInlineSpellChecker(true);
+        spellchecker.enableRealTimeSpell = true;
+      }
+
+      this.finalCheck = function spelledTextInvoker_finalCheck()
+      {
+        var defAttrs = buildDefaultTextAttrs(this.DOMNode, kInputFontSize);
+        testDefaultTextAttrs(aID, defAttrs);
+
+        var attrs = { };
+        var misspelledAttrs = {
+          "invalid": "spelling"
+        };
+
+        testTextAttrs(aID, 0, attrs, defAttrs, 0, 11);
+        testTextAttrs(aID, 11, misspelledAttrs, defAttrs, 11, 17);
+        testTextAttrs(aID, 17, attrs, defAttrs, 17, 18);
+        testTextAttrs(aID, 18, misspelledAttrs, defAttrs, 18, 22);
+      }
+
+      this.getID = function spelledTextInvoker_getID()
+      {
+        return "text attribute change for misspelled text";
+      }
+    }
+
+    /**
+     * Do tests.
+     */
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+
+    var gQueue = null;
+    function doTests()
+    {
+      gQueue = new eventQueue();
+      gQueue.push(new spelledTextInvoker("area8"));
+      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=345759"
+     title="Implement text attributes">
+    Mozilla Bug 345759
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <input id="area8"/>
+
+  <div id="eventdump"></div>
+</body>
+</html>
--- a/accessible/tests/mochitest/events/test_tree.xul
+++ b/accessible/tests/mochitest/events/test_tree.xul
@@ -33,20 +33,16 @@
     /**
      * Check TreeViewChanged event and run through accessible tree to ensure
      * it's created.
      */
     function treeViewChangedChecker(aMsg)
     {
       this.type = "TreeViewChanged";
       this.target = gTree;
-      this.check = function check(aEvent)
-      {
-        ensureAccessibleTree(gTree);
-      }
       this.getID = function getID()
       {
         return "TreeViewChanged";
       }
     }
 
     /**
      * Check TreeRowCountChanged event.
@@ -128,19 +124,27 @@
      */
     function setTreeView()
     {
       this.invoke = function setTreeView_invoke()
       {
         gTreeBox.view = gView;
       }
 
-      this.getID = function setTreeView_getID() { return "TreeViewChanged"; }
+      this.finalCheck = function setTreeView_finalCheck(aEvent)
+      {
+        ensureAccessibleTree(gTree);
+      }
 
-      this.eventSeq = [ new treeViewChangedChecker() ];
+      this.getID = function setTreeView_getID() { return "set tree view"; }
+
+      this.eventSeq = [
+        new treeViewChangedChecker(),
+        new invokerChecker(EVENT_REORDER, gTree)
+      ];
     };
 
     /**
      * Insert row at 0 index and checks TreeRowCountChanged and TreeInvalidated
      * event.
      */
     function insertRow()
     {
--- a/accessible/tests/mochitest/hyperlink/Makefile.in
+++ b/accessible/tests/mochitest/hyperlink/Makefile.in
@@ -41,14 +41,15 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/hyperlink
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
+		hyperlink.js \
 		test_general.html \
 		test_general.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/hyperlink/hyperlink.js
@@ -0,0 +1,48 @@
+/**
+ * Focus hyperlink invoker.
+ *
+ * @param aID             [in] hyperlink identifier
+ * @param aSelectedAfter  [in] specifies if hyperlink is selected/focused after
+ *                          the focus
+ */
+function focusLink(aID, aSelectedAfter)
+{
+  this.node = getNode(aID);
+  this.accessible = getAccessible(this.node);
+
+  this.eventSeq = [];
+  this.unexpectedEventSeq = [];
+
+  var checker = new invokerChecker(EVENT_FOCUS, this.accessible);
+  if (aSelectedAfter)
+    this.eventSeq.push(checker);
+  else
+    this.unexpectedEventSeq.push(checker);
+
+  this.invoke = function focusLink_invoke()
+  {
+    is(this.accessible.selected, false,
+       "Wrong selected state before focus for ID " + prettyName(aID) + "!");
+
+    var expectedStates = (aSelectedAfter ? STATE_FOCUSABLE : 0);
+    var unexpectedStates = (!aSelectedAfter ? STATE_FOCUSABLE : 0) | STATE_FOCUSED;
+    testStates(aID, expectedStates, 0, unexpectedStates, 0);
+
+    this.node.focus();
+  }
+
+  this.finalCheck = function focusLink_finalCheck()
+  {
+    is(this.accessible.selected, aSelectedAfter,
+       "Wrong seleccted state after focus for ID " + prettyName(aID) + "!");
+
+    var expectedStates = (aSelectedAfter ? STATE_FOCUSABLE | STATE_FOCUSED : 0);
+    var unexpectedStates = (!aSelectedAfter ? STATE_FOCUSABLE | STATE_FOCUSED : 0);
+    testStates(aID, expectedStates, 0, unexpectedStates, 0);
+  }
+
+  this.getID = function focusLink_getID()
+  {
+    return "focus hyperlink " + prettyName(aID);
+  }
+}
--- a/accessible/tests/mochitest/hyperlink/test_general.html
+++ b/accessible/tests/mochitest/hyperlink/test_general.html
@@ -13,16 +13,21 @@ https://bugzilla.mozilla.org/show_bug.cg
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript"
+          src="hyperlink.js"></script>
 
   <script type="application/javascript">
     function testThis(aID, aAcc, aRole, aAnchors, aName, aValid, aStartIndex,
                       aEndIndex)
     {
       testRole(aAcc, aRole);
       is(aAcc.anchorCount, aAnchors, "Wrong number of anchors for ID "
                                       + aID + "!");
@@ -31,84 +36,62 @@ https://bugzilla.mozilla.org/show_bug.cg
       is(aAcc.valid, aValid, "No correct valid state for ID "
                              + aID + "!");
       is(aAcc.startIndex, aStartIndex, "Wrong startIndex value for ID "
                                        + aID + "!");
       is(aAcc.endIndex, aEndIndex, "Wrong endIndex value for ID "
                                    + aID + "!");
     }
 
-    function testFocus(aID, aAcc, aSelectedBefore, aSelectedAfter)
-    {
-      is(aAcc.selected, aSelectedBefore,
-         "Wrong selected state before focus for ID " + aID + "!");
-      document.getElementById(aID).focus();
-      is(aAcc.selected, aSelectedAfter,
-         "Wrong seleccted state after focus for ID " + aID + "!");
-    }
-
     function testAction(aId, aAcc, aActionName)
     {
       var numActions = aActionName ? 1 : 0;
       is(aAcc.numActions, numActions,
          "Wrong actions number for ID " + aId);
       try {
         is(aAcc.getActionName(0), aActionName,
            "Wrong action name for ID " + aId);
       } catch (e) {
         if (numActions)
           ok(false, "Exception on action name getting for ID " + aId);
         else
           ok(true, "Correct action name for ID " + aId);
       }
     }
 
+    //gA11yEventDumpToConsole = true; // debug stuff
+
+    var gQueue = null;
     function doTest()
     {
       //////////////////////////////////////////////////////////////////////////
       // normal hyperlink
       var normalHyperlinkAcc = getAccessible("NormalHyperlink",
                                              [nsIAccessibleHyperLink]);
       testThis("NormalHyperlink", normalHyperlinkAcc, ROLE_LINK, 1,
                "Mozilla Foundation", true, 17, 18);
       is(normalHyperlinkAcc.getURI(0).spec, "http://www.mozilla.org/", 
          "URI wrong for normalHyperlinkElement!");
-      testStates(normalHyperlinkAcc,
-                 (STATE_FOCUSABLE | STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL));
-      testFocus("NormalHyperlink", normalHyperlinkAcc, false, true);
-      testStates(normalHyperlinkAcc,
-                 (STATE_FOCUSABLE | STATE_FOCUSED | STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL));
+      testStates(normalHyperlinkAcc, STATE_LINKED, EXT_STATE_HORIZONTAL);
 
       //////////////////////////////////////////////////////////////////////////
       // ARIA hyperlink
       var ariaHyperlinkAcc = getAccessible("AriaHyperlink",
                                            [nsIAccessibleHyperLink]);
       testThis("AriaHyperlink", ariaHyperlinkAcc, ROLE_LINK, 1,
                "Mozilla Foundation Home", true, 30, 31);
-      testStates(ariaHyperlinkAcc,
-                 (STATE_FOCUSABLE | STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL));
-      testFocus("AriaHyperlink", ariaHyperlinkAcc, false, true);
-      testStates(ariaHyperlinkAcc,
-                 (STATE_FOCUSABLE | STATE_FOCUSED | STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL));
+      testStates(ariaHyperlinkAcc, STATE_LINKED, EXT_STATE_HORIZONTAL);
       testAction("AriaHyperlink", ariaHyperlinkAcc, "click");
 
       //////////////////////////////////////////////////////////////////////////
       // ARIA hyperlink with status invalid
       var invalidAriaHyperlinkAcc = getAccessible("InvalidAriaHyperlink",
                                                   [nsIAccessibleHyperLink]);
       is(invalidAriaHyperlinkAcc.valid, false, "Should not be valid!");
-      testStates(invalidAriaHyperlinkAcc,
-                 (STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL), (STATE_FOCUSABLE));
-      testFocus("InvalidAriaHyperlink", invalidAriaHyperlinkAcc,
-                false, false);
+      testStates(invalidAriaHyperlinkAcc, STATE_LINKED, EXT_STATE_HORIZONTAL);
 
       //////////////////////////////////////////////////////////////////////////
       // image map and its link children
       var imageMapHyperlinkAcc = getAccessible("imgmap",
                                                [nsIAccessibleHyperLink]);
       testThis("imgmap", imageMapHyperlinkAcc, ROLE_IMAGE_MAP, 2, "b", true,
                79, 80);
       is(imageMapHyperlinkAcc.getURI(0).spec,
@@ -146,23 +129,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       //////////////////////////////////////////////////////////////////////////
       // normal hyperlink with embedded span
       var hyperlinkWithSpanAcc = getAccessible("LinkWithSpan",
                                                [nsIAccessibleHyperLink]);
       testThis("LinkWithSpan", hyperlinkWithSpanAcc, ROLE_LINK, 1,
                "Heise Online", true, 119, 120);
       is(hyperlinkWithSpanAcc.getURI(0).spec, "http://www.heise.de/", 
          "URI wrong for hyperlinkElementWithSpan!");
-      testStates(hyperlinkWithSpanAcc,
-                 (STATE_FOCUSABLE | STATE_LINKED),
-               (EXT_STATE_HORIZONTAL));
-      testFocus("LinkWithSpan", hyperlinkWithSpanAcc, false, true);
-      testStates(hyperlinkWithSpanAcc,
-                 (STATE_FOCUSABLE | STATE_FOCUSED | STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL));
+      testStates(hyperlinkWithSpanAcc, STATE_LINKED, EXT_STATE_HORIZONTAL);
       testAction("LinkWithSpan", hyperlinkWithSpanAcc, "jump");
 
       //////////////////////////////////////////////////////////////////////////
       // Named anchor, should never have state_linked
       var namedAnchorAcc = getAccessible("namedAnchor",
                                          [nsIAccessibleHyperLink]);
       testThis("namedAnchor", namedAnchorAcc, ROLE_LINK, 1,
                "This should never be of state_linked", true, 196, 197);
@@ -242,17 +219,26 @@ https://bugzilla.mozilla.org/show_bug.cg
       testAction(id, linkAcc, "jump");
 
       //////////////////////////////////////////////////////////////////////////
       // Text accessible shouldn't implement nsIAccessibleHyperLink
       var res = isAccessible(getNode("namedAnchor").firstChild,
                              [nsIAccessibleHyperLink]);
       ok(!res, "Text accessible shouldn't implement nsIAccessibleHyperLink");
 
-      SimpleTest.finish();
+      //////////////////////////////////////////////////////////////////////////
+      // Test focus
+      gQueue = new eventQueue();
+
+      gQueue.push(new focusLink("NormalHyperlink", true));
+      gQueue.push(new focusLink("AriaHyperlink", true));
+      gQueue.push(new focusLink("InvalidAriaHyperlink", false));
+      gQueue.push(new focusLink("LinkWithSpan", true));
+
+      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=418368">Mozilla Bug 418368</a
   ><p id="display"></p
--- a/accessible/tests/mochitest/hyperlink/test_general.xul
+++ b/accessible/tests/mochitest/hyperlink/test_general.xul
@@ -12,67 +12,70 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript"
+          src="hyperlink.js" />
 
   <script type="application/javascript">
   <![CDATA[
     function testThis(aID, aAcc, aRole, aAnchorCount, aAnchorName, aURI,
-                      aStartIndex, aEndIndex, aValid, aSelectedBefore,
-                      aSelectedAfter)
+                      aStartIndex, aEndIndex, aValid)
     {
       testRole(aID, aRole);
       is(aAcc.anchorCount, aAnchorCount, "Wrong number of anchors for ID " 
          + aID + "!");
       is(aAcc.getAnchor(0).name, aAnchorName, "Wrong name for ID " + aID + "!");
       is(aAcc.getURI(0).spec, aURI, "URI wrong for ID " + aID + "!");
       is(aAcc.startIndex, aStartIndex, "Wrong startIndex value for ID " + aID 
          + "!");
       is(aAcc.endIndex, aEndIndex, "Wrong endIndex value for ID " + aID + "!");
       is(aAcc.valid, aValid, "Wrong valid state for ID " + aID + "!");      
-
-      is(aAcc.selected, aSelectedBefore, "Wrong focused state before focus for "
-         + aID + "!");
-      document.getElementById(aID).focus();
-      is(aAcc.selected, aSelectedAfter, "Wrong selected state after focus for "
-         + aID + "!");
     }
 
+    var gQueue = null;
     function doTest()
     {
       var linkedLabelAcc = getAccessible("linkedLabel",
                                          [nsIAccessibleHyperLink]);
       testThis("linkedLabel", linkedLabelAcc, ROLE_LINK, 1,
                "Mozilla Foundation home", "http://www.mozilla.org/", 1, 2,
-               true, false, true);
-      testStates(linkedLabelAcc,
-                 (STATE_FOCUSABLE | STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL));
+               true);
+      testStates(linkedLabelAcc, STATE_LINKED, EXT_STATE_HORIZONTAL);
 
       var labelWithValueAcc = getAccessible("linkLabelWithValue",
                                             [nsIAccessibleHyperLink]);
       testThis("linkLabelWithValue", labelWithValueAcc, ROLE_LINK, 1,
                "Mozilla Foundation", "http://www.mozilla.org/", 2, 3, true,
                false, true);
-      testStates(labelWithValueAcc,
-                 (STATE_FOCUSABLE | STATE_LINKED),
-                 (EXT_STATE_HORIZONTAL));
+      testStates(labelWithValueAcc, STATE_LINKED, EXT_STATE_HORIZONTAL);
 
       var normalLabelAcc = getAccessible("normalLabel");
       testRole(normalLabelAcc, ROLE_LABEL);
       is(normalLabelAcc.name, "This label should not be a link",
          "Wrong name for normal label!");
       testStates(normalLabelAcc, 0, 0, (STATE_FOCUSABLE | STATE_LINKED));
 
-      SimpleTest.finish();
+      //////////////////////////////////////////////////////////////////////////
+      // Test focus
+
+      gQueue = new eventQueue();
+
+      gQueue.push(new focusLink("linkedLabel", true));
+      gQueue.push(new focusLink("linkLabelWithValue", true));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
   <body xmlns="http://www.w3.org/1999/xhtml">
--- a/accessible/tests/mochitest/name/markup.js
+++ b/accessible/tests/mochitest/name/markup.js
@@ -1,15 +1,19 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Name tests described by "markuprules.xml" file.
 
 var gNameRulesFileURL = "markuprules.xml";
 
 var gRuleDoc = null;
 
+// Debuggin stuff.
+var gDumpToConsole = false;
+gA11yEventDumpToConsole = gDumpToConsole;
+
 /**
  * Start name tests. Run through markup elements and test names for test
  * element (see namerules.xml for details).
  */
 function testNames()
 {
   var request = new XMLHttpRequest();
   request.open("get", gNameRulesFileURL, false);
@@ -56,20 +60,26 @@ var gTestIterator =
     this.ruleIdx++;
     if (this.ruleIdx == this.ruleElms.length) {
       this.markupIdx++;
       if (this.markupIdx == this.markupElms.length) {
         SimpleTest.finish();
         return;
       }
 
-      document.body.removeChild(this.container);
+      this.ruleIdx = -1;
 
-      this.ruleIdx = -1;
-      testNamesForMarkup(this.markupElms[this.markupIdx]);
+      if (gDumpToConsole) {
+        dump("\nPend next markup processing. Wait for reorder event on " +
+             prettyName(document) + "'\n");
+      }
+      waitForEvent(EVENT_REORDER, document, testNamesForMarkup,
+                   null, this.markupElms[this.markupIdx]);
+
+      document.body.removeChild(this.container);
       return;
     }
 
     testNameForRule(this.elm, this.ruleElms[this.ruleIdx]);
   },
 
   markupElms: null,
   markupIdx: -1,
@@ -80,34 +90,44 @@ var gTestIterator =
 };
 
 /**
  * Process every 'markup' element and test names for it. Used by testNames
  * function.
  */
 function testNamesForMarkup(aMarkupElm)
 {
+  if (gDumpToConsole)
+    dump("\nProcessing markup '" + aMarkupElm.getAttribute("id") + "'\n");
+
   var div = document.createElement("div");
   div.setAttribute("id", "test");
 
   var child = aMarkupElm.firstChild;
   while (child) {
     var newChild = document.importNode(child, true);
     div.appendChild(newChild);
     child = child.nextSibling;
   }
 
+  if (gDumpToConsole) {
+    dump("\nProcessing markup. Wait for reorder event on " +
+         prettyName(document) + "'\n");
+  }
   waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules,
                 null, aMarkupElm, div);
 
   document.body.appendChild(div);
 }
 
 function testNamesForMarkupRules(aMarkupElm, aContainer)
 {
+  if (gDumpToConsole)
+    dump("\nProcessing markup rules '" + aMarkupElm.getAttribute("id") + "'\n");
+
   ensureAccessibleTree(aContainer);
 
   var serializer = new XMLSerializer();
 
   var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref");
   var elms = evaluateXPath(document, expr, htmlDocResolver);
 
   var ruleId = aMarkupElm.getAttribute("ruleset");
@@ -117,22 +137,39 @@ function testNamesForMarkupRules(aMarkup
 }
 
 /**
  * Test name for current rule and current 'markup' element. Used by
  * testNamesForMarkup function.
  */
 function testNameForRule(aElm, aRuleElm)
 {
-  if (aRuleElm.hasAttribute("attr"))
+  if (aRuleElm.hasAttribute("attr")) {
+    if (gDumpToConsole) {
+      dump("\nProcessing rule { attr: " + aRuleElm.getAttribute("attr") +" }\n");
+    }
+
     testNameForAttrRule(aElm, aRuleElm);
-  else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr"))
+
+  } else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr")) {
+    if (gDumpToConsole) {
+      dump("\nProcessing rule { elm: " + aRuleElm.getAttribute("elm") +
+           ", elmattr: " + aRuleElm.getAttribute("elmattr") +" }\n");
+    }
+
     testNameForElmRule(aElm, aRuleElm);
-  else if (aRuleElm.getAttribute("fromsubtree") == "true")
+
+  } else if (aRuleElm.getAttribute("fromsubtree") == "true") {
+    if (gDumpToConsole) {
+      dump("\nProcessing rule { fromsubtree: " +
+           aRuleElm.getAttribute("fromsubtree") +" }\n");
+    }
+
     testNameForSubtreeRule(aElm, aRuleElm);
+  }
 }
 
 function testNameForAttrRule(aElm, aRule)
 {
   var name = "";
 
   var attr = aRule.getAttribute("attr");
   var attrValue = aElm.getAttribute(attr);
@@ -182,27 +219,36 @@ function testNameForElmRule(aElm, aRule)
   var treeWalker = document.createTreeWalker(document.body,
                                              NodeFilter.SHOW_ELEMENT,
                                              filter, false);
   var labelElm = treeWalker.nextNode();
   var msg = "Element '" + elm + "' test.";
   testName(aElm, labelElm.getAttribute("a11yname"), msg);
 
   var parentNode = labelElm.parentNode;
+
+  if (gDumpToConsole) {
+    dump("\nProcessed elm rule. Wait for reorder event on " +
+         prettyName(parentNode) + "'\n");
+  }
   waitForEvent(EVENT_REORDER, parentNode,
                gTestIterator.iterateNext, gTestIterator);
 
   parentNode.removeChild(labelElm);
 }
 
 function testNameForSubtreeRule(aElm, aRule)
 {
   var msg = "From subtree test.";
   testName(aElm, aElm.getAttribute("a11yname"), msg);
 
+  if (gDumpToConsole) {
+    dump("\nProcessed from subtree rule. Wait for reorder event on " +
+         prettyName(aElm) + "\n");
+  }
   waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator);
 
   while (aElm.firstChild)
     aElm.removeChild(aElm.firstChild);
 }
 
 /**
  * Return array of 'rule' elements. Used in conjunction with
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -119,57 +119,59 @@
       <rule fromsubtree="true"/>
       <rule attr="title" type="string"/>
     </ruleset>
 
   </ruledfn>
 
   <rulesample>
 
-    <markup ref="html:button" ruleset="htmlctrl">
+    <markup ref="html:button" ruleset="htmlctrl" id="markup1test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="btn" a11yname="test4">test4</html:label>
       <html:button id="btn"
                    aria-label="test1"
                    aria-labelledby="l1 l2"
                    title="test5"
                    a11yname="press me">press me</html:button>
     </markup>
 
-    <markup ref="html:input" ruleset="htmlinputbutton">
+    <markup ref="html:input" ruleset="htmlinputbutton" id="markup2test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="btn" a11yname="test4">test4</html:label>
       <html:input id="btn"
                   type="button"
                   aria-label="test1"
                   aria-labelledby="l1 l2"
                   value="test5"
                   alt="test6"
                   src="test7"
                   data="test8"
                   title="test9"/>
     </markup>
 
-    <markup ref="html:select/html:option[1]" ruleset="htmloption">
+    <markup ref="html:select/html:option[1]" ruleset="htmloption"
+            id="markup3test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:select>
         <html:option id="opt"
                      aria-label="test1"
                      aria-labelledby="l1 l2"
                      label="test4"
                      title="test5"
                      a11yname="option1">option1</html:option>
         <html:option>option2</html:option>
       </html:select>
     </markup>
 
-    <markup ref="html:table/html:tr/html:td" ruleset="htmlelm">
+    <markup ref="html:table/html:tr/html:td" ruleset="htmlelm"
+            id="markup4test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="tc" a11yname="test4">test4</html:label>
       <html:table>
         <html:tr>
           <html:td id="tc"
                    aria-label="test1"
                    aria-labelledby="l1 l2"
@@ -179,17 +181,18 @@
             <html:ul>
               <html:li>This is a list</html:li>
             </html:ul>
           </html:td>
         </html:tr>
       </html:table>
     </markup>
 
-    <markup ref="html:table/html:tr/html:td" ruleset="htmlctrl">
+    <markup ref="html:table/html:tr/html:td" ruleset="htmlctrl"
+            id="markup5test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="gc" a11yname="test4">test4</html:label>
       <html:table>
         <html:tr>
           <html:td id="gc"
                    role="gridcell"
                    aria-label="test1"
--- a/accessible/tests/mochitest/relations/test_tree.xul
+++ b/accessible/tests/mochitest/relations/test_tree.xul
@@ -12,27 +12,28 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../events.js" />
+  <script type="application/javascript"
           src="../relations.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     function doTestRelations()
     {
       var treeNode = getNode("tree");
-      treeNode.removeEventListener("TreeViewChanged", doTestRelations, false);
 
       var tree = getAccessible(treeNode);
       var treeitem1 = tree.firstChild.nextSibling;
       testRelation(treeitem1, RELATION_NODE_CHILD_OF, [tree]);
 
       var treeitem2 = treeitem1.nextSibling;
       testRelation(treeitem2, RELATION_NODE_CHILD_OF, [tree]);
 
@@ -49,17 +50,17 @@
       testRelation(treeitem6, RELATION_NODE_CHILD_OF, [tree]);
 
       SimpleTest.finish();
     }
 
     function doTest()
     {
       var treeNode = getNode("tree");
-      treeNode.addEventListener("TreeViewChanged", doTestRelations, false);
+      waitForEvent(EVENT_REORDER, treeNode, doTestRelations);
       treeNode.view = new nsTreeTreeView();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
--- a/accessible/tests/mochitest/selectable/test_tree.xul
+++ b/accessible/tests/mochitest/selectable/test_tree.xul
@@ -14,21 +14,21 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../events.js" />
+  <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
-  <script type="application/javascript"
-          src="../events.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     // gA11yEventDumpID = "debug";
 
@@ -78,17 +78,17 @@
         "tree processor for " + prettyName(aTreeID);
       }
     }
 
     var gQueue = null;
 
     function doTest()
     {
-      gQueue = new eventQueue("TreeViewChanged");
+      gQueue = new eventQueue(EVENT_REORDER);
       gQueue.push(new statesChecker("tree", new nsTreeTreeView()));
       gQueue.push(new statesChecker("treesingle", new nsTreeTreeView()));
       gQueue.push(new statesChecker("treecell", new nsTreeTreeView()));
       gQueue.push(new statesChecker("treetext", new nsTreeTreeView()));
       gQueue.push(new statesChecker("tabletree", new nsTreeTreeView()));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
--- a/accessible/tests/mochitest/states/test_doc.html
+++ b/accessible/tests/mochitest/states/test_doc.html
@@ -1,66 +1,103 @@
 <!DOCTYPE html>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=454997
-https://bugzilla.mozilla.org/show_bug.cgi?id=467387
-https://bugzilla.mozilla.org/show_bug.cgi?id=509696
--->
 <head>
   <title>states of document</title>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <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">
+    function designModeOn()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, document)
+      ];
+
+      this.invoke = function designModeOn_invoke()
+      {
+        document.designMode = "on";
+      }
+
+      this.finalCheck = function designModeOn_finalCheck()
+      {
+        testStates(document, 0, EXT_STATE_EDITABLE);
+        testStates("p", 0, EXT_STATE_EDITABLE);
+        testStates("document", 0, EXT_STATE_EDITABLE);
+        testStates("editable_document", 0, EXT_STATE_EDITABLE);
+      }
+
+      this.getID = function designModeOn_getID()
+      {
+        return "design mode on";
+      }
+    }
+
+    function designModeOff()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, document)
+      ];
+
+      this.invoke = function designModeOn_invoke()
+      {
+        document.designMode = "off";
+      }
+
+      this.finalCheck = function designModeOn_finalCheck()
+      {
+        testStates(document, STATE_READONLY);
+        testStates("document", STATE_READONLY);
+        testStates("editable_document", 0, EXT_STATE_EDITABLE);
+      }
+
+      this.getID = function designModeOn_getID()
+      {
+        return "design mode off";
+      }
+    }
+
+    var gQueue = null;
     function doTest()
     {
       // Bug 566542: root accesible should expose active state when focused.
       testStates(getRootAccessible(), 0, EXT_STATE_ACTIVE);
 
-      // Bug 509696
+      // Bug 509696, 607219.
       testStates(document, STATE_READONLY); // role=""
-      todo(false, "enable commented tests when we support body role changes");
-/*
+
       document.body.setAttribute("role","banner"); // no platform mapping
       testStates(document, STATE_READONLY);
       document.body.setAttribute("role","foo"); // bogus role
       testStates(document, STATE_READONLY);
       document.body.removeAttribute("role");
       testStates(document, STATE_READONLY);
-*/
-      
+
       // Bugs 454997 and 467387
       testStates(document, STATE_READONLY);
       testStates("document", STATE_READONLY);
       testStates("editable_document", 0, EXT_STATE_EDITABLE);
 
-      document.designMode = "on";
-
-      testStates(document, 0, EXT_STATE_EDITABLE);
-      testStates("p", 0, EXT_STATE_EDITABLE);
-      testStates("document", 0, EXT_STATE_EDITABLE);
-      testStates("editable_document", 0, EXT_STATE_EDITABLE);
-
-      document.designMode = "off";
-
-      testStates(document, STATE_READONLY);
-      testStates("document", STATE_READONLY);
-      testStates("editable_document", 0, EXT_STATE_EDITABLE);
-
-      SimpleTest.finish();
+      // Design mode on/off trigger document accessible subtree recreation.
+      gQueue = new eventQueue();
+      gQueue.push(new designModeOn());
+      gQueue.push(new designModeOff());
+      gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
 <body role="">
@@ -72,16 +109,19 @@ https://bugzilla.mozilla.org/show_bug.cg
      title="nsIAccessible states tests of editable document"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387">Mozilla Bug 467387</a>
    <a target="_blank"
      title="Role attribute on body with empty string causes DocAccessible not to have read-only state."
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=509696">Mozilla Bug 509696</a>
   <a target="_blank"
      title="Frame for firefox does not implement the state "active" when firefox is the active frame"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=566542">Mozilla Bug 566542</a>
+  <a target="_blank"
+     title="Dynamic role attribute change on body doesn't affect on document role"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=607219">Mozilla Bug 607219</a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <p id="p">hello</p>
 
--- a/accessible/tests/mochitest/states/test_docarticle.html
+++ b/accessible/tests/mochitest/states/test_docarticle.html
@@ -9,55 +9,113 @@
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <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">
-    function doTest()
+    function designModeOn()
     {
-      var docAcc = getAccessible(document, [nsIAccessibleDocument]);
-      if (docAcc) {
-        testStates(docAcc, STATE_READONLY);
-        testStates("article", STATE_READONLY);
-        testStates("editable_article", 0, EXT_STATE_EDITABLE);
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, document)
+      ];
 
+      this.invoke = function designModeOn_invoke()
+      {
         document.designMode = "on";
+      }
 
-        testStates(docAcc, 0, EXT_STATE_EDITABLE);
-        testStates("article", 0, EXT_STATE_EDITABLE);
+      this.finalCheck = function designModeOn_finalCheck()
+      {
+        testStates(document, 0, EXT_STATE_EDITABLE);
+        testStates("aria_article", 0, EXT_STATE_EDITABLE);
+        testStates("editable_aria_article", 0, EXT_STATE_EDITABLE);
         testStates("article", 0, EXT_STATE_EDITABLE);
         testStates("editable_article", 0, EXT_STATE_EDITABLE);
-  
+      }
+
+      this.getID = function designModeOn_getID()
+      {
+        return "design mode on";
+      }
+    }
+
+    function designModeOff()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, document)
+      ];
+
+      this.invoke = function designModeOn_invoke()
+      {
         document.designMode = "off";
+      }
 
-        testStates(docAcc, STATE_READONLY);
+      this.finalCheck = function designModeOn_finalCheck()
+      {
+        testStates(document, STATE_READONLY);
+        testStates("aria_article", STATE_READONLY);
+        testStates("editable_aria_article", 0, EXT_STATE_EDITABLE);
         testStates("article", STATE_READONLY);
         testStates("editable_article", 0, EXT_STATE_EDITABLE);
       }
-      SimpleTest.finish();
+
+      this.getID = function designModeOn_getID()
+      {
+        return "design mode off";
+      }
+    }
+
+    var gQueue = null;
+
+    function doTest()
+    {
+      testStates(document, STATE_READONLY);
+      testStates("aria_article", STATE_READONLY);
+      testStates("editable_aria_article", 0, EXT_STATE_EDITABLE);
+      testStates("article", STATE_READONLY);
+      testStates("editable_article", 0, EXT_STATE_EDITABLE);
+
+      // Design mode on/off trigger document accessible subtree recreation.
+      gQueue = new eventQueue();
+      gQueue.push(new designModeOn());
+      gQueue.push(new designModeOff());
+      gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
 <body role="article">
 
   <a target="_blank"
     href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387"
     title="Expose non-editable documents as readonly, regardless of role">
      Mozilla Bug 467387
+  </a><br/>
+  <a target="_blank"
+    href="https://bugzilla.mozilla.org/show_bug.cgi?id=613502"
+    title="Map <article> like we do aria role article">
+     Mozilla Bug 613502
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <div id="article" role="article">article</div>
-  <div id="editable_article" role="article" contentEditable="true">editable article</doc>
+  <div id="aria_article" role="article">aria article</div>
+  <div id="editable_aria_article" role="article" contentEditable="true">
+    editable aria article</div>
+
+  <article id="article">article</article>
+  <article id="editable_article" contentEditable="true">
+    editable article</article>
+
 </body>
 </html>
--- a/accessible/tests/mochitest/states/test_inputs.html
+++ b/accessible/tests/mochitest/states/test_inputs.html
@@ -33,27 +33,40 @@
       testStates(never_required[i], 0, 0, STATE_REQUIRED);
     }
 
     // inherited 'unavailable' state
     testStates("f", STATE_UNAVAILABLE);
     testStates("f_input", STATE_UNAVAILABLE);
     testStates("f_input_disabled", STATE_UNAVAILABLE);
 
+    /**
+     * maxlength doesn't make the element invalid until bug 613016 and bug 613019
+     * are fixed. Commenting out related lines and adding a todo to make sure
+     * it will be uncommented as soon as possible.
+     */
+    var todoInput = document.createElement("input");
+    todoInput.maxLength = '2';
+    todoInput.value = 'foo';
+    todo(!todoInput.validity.valid,
+         "input should be invalid because of maxlength");
+
     // invalid/valid state
-    var invalid = ["maxlength","pattern","email","url"];
-    document.getElementById("maxlength").value = "i am too long";
+    //var invalid = ["maxlength","pattern","email","url"];
+    //document.getElementById("maxlength").value = "i am too long";
+    var invalid = ["pattern","email","url"];
     for (i in invalid) {
       testStates(invalid[i], STATE_INVALID);
       testStates(invalid[i] + "2", 0, 0, STATE_INVALID);
     }
 
     // invalid/valid state
-    var invalid = ["maxlength","pattern","email","url"];
-    document.getElementById("maxlength").value = "i am too long";
+    //var invalid = ["maxlength","pattern","email","url"];
+    //document.getElementById("maxlength").value = "i am too long";
+    var invalid = ["pattern","email","url"];
     for (i in invalid) {
       testStates(invalid[i], STATE_INVALID);
       testStates(invalid[i] + "2", 0, 0, STATE_INVALID);
     }
 
     SimpleTest.finish();
   }
 
--- a/accessible/tests/mochitest/states/test_tree.xul
+++ b/accessible/tests/mochitest/states/test_tree.xul
@@ -79,17 +79,17 @@
         "tree processor for " + prettyName(aTreeID);
       }
     }
 
     var gQueue = null;
 
     function doTest()
     {
-      gQueue = new eventQueue("TreeViewChanged");
+      gQueue = new eventQueue(EVENT_REORDER);
       gQueue.push(new statesChecker("tree", new nsTreeTreeView()));
       gQueue.push(new statesChecker("treesingle", new nsTreeTreeView()));
       gQueue.push(new statesChecker("tabletree", new nsTreeTreeView()));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
--- a/accessible/tests/mochitest/table/test_headers_tree.xul
+++ b/accessible/tests/mochitest/table/test_headers_tree.xul
@@ -12,16 +12,18 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../events.js" />
+  <script type="application/javascript"
           src="../table.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     var gTree = null;
@@ -32,24 +34,22 @@
 
     function doTest()
     {
       // Initialize the tree
       gTree = document.getElementById("tree");
       gTreeBox = gTree.treeBoxObject;
       gView = new nsTableTreeView(3);
 
-      gTree.addEventListener("TreeViewChanged", continueTest, false);
+      waitForEvent(EVENT_REORDER, gTree, continueTest);
       gTreeBox.view = gView;
     }
 
     function continueTest()
     {
-      gTree.removeEventListener("TreeViewChanged", continueTest, false);
-
       var treeAcc = getAccessible(gTree, [nsIAccessibleTable]);
 
       var headerInfoMap = [
         {
           cell: treeAcc.getCellAt(0, 0),
           rowHeaderCells: [],
           columnHeaderCells: [ "col" ]
         },
--- a/accessible/tests/mochitest/table/test_indexes_tree.xul
+++ b/accessible/tests/mochitest/table/test_indexes_tree.xul
@@ -12,16 +12,18 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../events.js" />
+  <script type="application/javascript"
           src="../table.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     var gTree = null;
@@ -32,24 +34,22 @@
 
     function doTest()
     {
       // Initialize the tree
       gTree = document.getElementById("tree");
       gTreeBox = gTree.treeBoxObject;
       gView = new nsTableTreeView(3);
 
-      gTree.addEventListener("TreeViewChanged", continueTest, false);
+      waitForEvent(EVENT_REORDER, gTree, continueTest);
       gTreeBox.view = gView;
     }
 
     function continueTest()
     {
-      gTree.removeEventListener("TreeViewChanged", continueTest, false);
-
       var idxes = [
         [0, 1],
         [2, 3],
         [4, 5]
       ];
       testTableIndexes(gTree, idxes);
 
       SimpleTest.finish();
--- a/accessible/tests/mochitest/table/test_sels_tree.xul
+++ b/accessible/tests/mochitest/table/test_sels_tree.xul
@@ -12,16 +12,18 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../events.js" />
+  <script type="application/javascript"
           src="../states.js" />
   <script type="application/javascript"
           src="../table.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
@@ -34,24 +36,22 @@
 
     function doTest()
     {
       // Initialize the tree
       gTree = document.getElementById("tree");
       gTreeBox = gTree.treeBoxObject;
       gView = new nsTableTreeView(3);
 
-      gTree.addEventListener("TreeViewChanged", continueTest, false);
+      waitForEvent(EVENT_REORDER, gTree, continueTest);
       gTreeBox.view = gView;
     }
 
     function continueTest()
     {
-      gTree.removeEventListener("TreeViewChanged", continueTest, false);
-
       var cellsArray =
       [
         [false, false],
         [false, false],
         [false, false]
       ];
 
       testTableSelection(gTree, cellsArray);
--- a/accessible/tests/mochitest/table/test_struct_tree.xul
+++ b/accessible/tests/mochitest/table/test_struct_tree.xul
@@ -12,16 +12,18 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script type="application/javascript"
           src="../treeview.js" />
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
+          src="../events.js" />
+  <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../table.js" />
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
@@ -34,24 +36,22 @@
 
     function doTest()
     {
       // Initialize the tree
       gTree = document.getElementById("table");
       gTreeBox = gTree.treeBoxObject;
       gView = new nsTableTreeView(3);
 
-      gTree.addEventListener("TreeViewChanged", continueTest, false);
+      waitForEvent(EVENT_REORDER, gTree, continueTest);
       gTreeBox.view = gView;
     }
 
     function continueTest()
     {
-      gTree.removeEventListener("TreeViewChanged", continueTest, false);
-
       var cellsArray = [
         [kDataCell, kDataCell],
         [kDataCell, kDataCell],
         [kDataCell, kDataCell]
       ];
 
       testTableStruct(gTree, cellsArray, kTreeColumnHeader);
 
--- a/accessible/tests/mochitest/test_editabletext_1.html
+++ b/accessible/tests/mochitest/test_editabletext_1.html
@@ -1,25 +1,29 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=452161
 -->
 <head>
   <title>nsIAccessibleEditableText chrome tests</title>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <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="editabletext.js"></script>
+  <script type="application/javascript"
+          src="events.js"></script>
 
   <script type="application/javascript">
     var gParagraphAcc;
 
     function testEditable(aID)
     {
       var et = new nsEditableText(aID);
 
@@ -55,26 +59,32 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       //////////////////////////////////////////////////////////////////////////
 //      // cutNPasteText
 //      et.cutNPasteText(0, 1, 1, "ehhelloo");
 //      et.cutNPasteText(1, 2, 0, "hehelloo");
 //      et.cutNPasteText(7, 8, 8, "hehelloo");
     }
 
+    function testDocEditableNFinishTest(aDoc)
+    {
+      testEditable(aDoc);
+      SimpleTest.finish();
+    }
+
     function doTest()
     {
       testEditable("input");
       // testEditable("div"); XXX: bug 452599
 
-      var frame = document.getElementById("frame");
+      // Design mode on/off trigger document accessible subtree recreation.
+      var frame = getNode("frame");
+      waitForEvent(EVENT_REORDER, frame.contentDocument,
+                   testDocEditableNFinishTest, null, frame.contentDocument);
       frame.contentDocument.designMode = "on";
-      testEditable(frame.contentDocument);
-
-      SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
--- a/accessible/tests/mochitest/test_elm_landmarks.html
+++ b/accessible/tests/mochitest/test_elm_landmarks.html
@@ -19,54 +19,62 @@
 
   <script type="application/javascript">
 
     function doTest()
     {
       testRole("nav", ROLE_SECTION);
       testRole("header", ROLE_HEADER);
       testRole("footer", ROLE_FOOTER);
-      testRole("article", ROLE_SECTION);
+      testRole("article", ROLE_DOCUMENT);
       testRole("aside", ROLE_NOTE);
 
+      testRole("main", ROLE_DOCUMENT);
+
       // Some AT may look for this
       testAttrs("nav", {"xml-roles" : "navigation"}, true);
       testAttrs("header", {"xml-roles" : "banner"}, true);
       testAttrs("footer", {"xml-roles" : "contentinfo"}, true);
-      testAttrs("article", {"xml-roles" : "main"}, true);
       testAttrs("aside", {"xml-roles" : "note"}, true);
-      testAttrs("document", {"xml-roles" : "document"}, true); // ARIA override
+      testAttrs("main", {"xml-roles" : "main"}, true); // ARIA override
 
       // And some AT may look for this
       testAttrs("nav", {"tag" : "NAV"}, true);
       testAttrs("header", {"tag" : "HEADER"}, true);
       testAttrs("footer", {"tag" : "FOOTER"}, true);
       testAttrs("article", {"tag" : "ARTICLE"}, true);
       testAttrs("aside", {"tag" : "ASIDE"}, true);
-      testAttrs("document", {"tag" : "ARTICLE"}, true); // no override expected
+      testAttrs("main", {"tag" : "ARTICLE"}, true); // no override expected
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
-     title="Provide mappings for html5 <nav> <header> <footer> <article>"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=593368">Bug 593368</a>
+    title="Provide mappings for html5 <nav> <header> <footer> <article>"
+    href="https://bugzilla.mozilla.org/show_bug.cgi?id=593368">
+     Mozilla Bug 593368
+  </a><br/>
+  <a target="_blank"
+    href="https://bugzilla.mozilla.org/show_bug.cgi?id=613502"
+    title="Map <article> like we do aria role article">
+     Mozilla Bug 613502
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <nav id="nav">a nav</nav>
   <header id="header">a header</header>
   <footer id="footer">a footer</footer>
-  <article id="article">an article</article>
   <aside id="aside">by the way I am an aside</aside>
 
-  <article id="document" role="document">a document</article>
+  <article id="article">an article</article>
+  <article id="main" role="main">a main area</article>
 
 </body>
 </html>
--- a/accessible/tests/mochitest/test_keys.html
+++ b/accessible/tests/mochitest/test_keys.html
@@ -1,11 +1,12 @@
 <html>
 
 <head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
   <title>Keyboard shortcuts tests</title>
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
@@ -22,17 +23,17 @@
 
       is(acc.keyboardShortcut, aKey,
          "Wrong keyboard shortcut on " + prettyName(aAccOrElmOrID));
     }
 
     function doTest()
     {
       testKeyboardShortcut("input1", "");
-      testKeyboardShortcut("input2", "Alt+Shift+b");
+      testKeyboardShortcut("input2", MAC ? "⌃b" : "Alt+Shift+b");
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
--- a/accessible/tests/mochitest/test_text_caret.html
+++ b/accessible/tests/mochitest/test_text_caret.html
@@ -38,25 +38,26 @@
      */
     function setCaretOffsetInvoker(aID, aOffset)
     {
       this.target = getAccessible(aID, [nsIAccessibleText]);
 
       this.invoke = function setCaretOffsetInvoker_invoke()
       {
         this.target.caretOffset = aOffset;
+        todo(false, "Enable focus event handling when bug 422744 is fixed.");
       }
 
       this.getID = function setCaretOffsetInvoker_getID()
       {
-        "nsIAccessibleText::caretOffset test"
+        return "nsIAccessibleText::caretOffset test";
       }
 
       this.eventSeq = [
-        new invokerChecker(EVENT_FOCUS, this.target),
+        // new invokerChecker(EVENT_FOCUS, this.target),
         new caretMovedChecker(this.target, aOffset)
       ];
     }
 
     /**
      * Do tests.
      */
     var gQueue = null;
--- a/accessible/tests/mochitest/text.js
+++ b/accessible/tests/mochitest/text.js
@@ -119,16 +119,113 @@ function testTextBeforeOffset(aOffset, a
 
     testTextHelper(ID, aOffset, aBoundaryType,
                    aText, aStartOffset, aEndOffset,
                    toDoFlag1, toDoFlag2, toDoFlag3,
                    acc.getTextBeforeOffset, "getTextBeforeOffset ");
   }
 }
 
+/**
+ * Test word count for an element.
+ *
+ * @param aElement   [in] element identifier
+ * @param aCount     [in] Expected word count
+ * @param aToDoFlag  [in] kTodo or kOk for returned text
+ */
+function testWordCount(aElement, aCount, aToDoFlag)
+{
+  var isFunc = (aToDoFlag == kTodo) ? todo_is : is;
+  var acc = getAccessible(aElement, nsIAccessibleText);
+  var startOffsetObj = {}, endOffsetObj = {};
+  var length = acc.characterCount;
+  var offset = 0;
+  var wordCount = 0;
+  while (true) {
+    var text = acc.getTextAtOffset(offset, BOUNDARY_WORD_START,
+                                   startOffsetObj, endOffsetObj);
+    if (offset >= length)
+      break;
+
+    wordCount++;
+    offset = endOffsetObj.value;
+  }
+  isFunc(wordCount, aCount,  "wrong words count for '" + acc.getText(0, -1) + "': " +
+         wordCount);
+}
+
+/**
+ * Test word at a position for an element.
+ *
+ * @param aElement    [in] element identifier
+ * @param aWordIndex  [in] index of the word to test
+ * @param aText       [in] expected text for that word
+ * @param aToDoFlag   [in] kTodo or kOk for returned text
+ */
+function testWordAt(aElement, aWordIndex, aText, aToDoFlag)
+{
+  var isFunc = (aToDoFlag == kTodo) ? todo_is : is;
+  var acc = getAccessible(aElement, nsIAccessibleText);
+  var startOffsetObj = {}, endOffsetObj = {};
+  var length = acc.characterCount;
+  var offset = 0;
+  var wordIndex = -1;
+  var wordFountAtOffset = -1;
+  while (true) {
+    var text = acc.getTextAtOffset(offset, BOUNDARY_WORD_START,
+                                   startOffsetObj, endOffsetObj);
+    if (offset >= length)
+      break;
+
+    wordIndex++;
+    offset = endOffsetObj.value;
+    if (wordIndex == aWordIndex) {
+       wordFountAtOffset = startOffsetObj.value;
+       break;
+    }
+  } 
+  if (wordFountAtOffset >= 0) {
+    var text = acc.getTextAtOffset(wordFountAtOffset, BOUNDARY_WORD_END,
+                                   startOffsetObj, endOffsetObj);
+
+    if (endOffsetObj.value < wordFountAtOffset) {
+      todo(false,  "wrong start and end offset for word '" + aWordIndex + "': " +
+           " of text '" + acc.getText(0, -1) + "'");
+      return;
+    }
+
+    text = acc.getText(wordFountAtOffset, endOffsetObj.value);
+    isFunc(text, aText,  "wrong text for word at pos '" + aWordIndex + "': " +
+           " of text '" + acc.getText(0, -1) + "'");
+  }
+  else {
+    isFunc(false, "failed to find word " + aText + " at word pos " + aWordIndex +
+           " of text '" + acc.getText(0, -1) + "'");
+  }
+}
+
+/**
+ * Test words in a element.
+ *
+ * @param aElement   [in]           element identifier
+ * @param aWords     [in]           array of expected words
+ * @param aToDoFlag  [in, optional] kTodo or kOk for returned text
+ */
+function testWords(aElement, aWords, aToDoFlag)
+{
+  if (aToDoFlag == null)
+    aToDoFlag = kOk;
+
+  testWordCount(aElement, aWords.length, aToDoFlag);
+
+  for (var i = 0; i < aWords.length; i++) {
+    testWordAt(aElement, i, aWords[i], aToDoFlag);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Private
 
 function testTextHelper(aID, aOffset, aBoundaryType,
                         aText, aStartOffset, aEndOffset,
                         aToDoFlag1, aToDoFlag2, aToDoFlag3,
                         aTextFunc, aTextFuncName)
 {
--- a/accessible/tests/mochitest/text/Makefile.in
+++ b/accessible/tests/mochitest/text/Makefile.in
@@ -45,12 +45,13 @@ relativesrcdir  = accessible/text
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		doc.html \
 		test_doc.html \
 		test_singleline.html \
 		test_whitespaces.html \
+		test_words.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_words.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>nsIAccessibleText getText related function tests for html:input,html:div and html:textarea</title>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <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="../text.js"></script>
+  <script type="application/javascript">
+    
+    function doTest()
+    {
+      // "one two"
+      testWords("div1", ["one", "two"]);
+
+      // "one  two"
+      testWords("div2", ["one", "two"]);
+
+      // "one,two"
+      testWordCount("div3", 2, kOk);
+      testWordAt("div3", 0, "one", kTodo);
+      testWordAt("div3", 1, "two", kOk);
+
+      // "one, two"
+      testWordCount("div4", 2, kOk);
+      testWordAt("div4", 0, "one", kTodo);
+      testWordAt("div4", 1, "two", kOk);
+
+      // "one+two"
+      testWordCount("div5", 2, kOk);
+      testWordAt("div5", 0, "one", kTodo);
+      testWordAt("div5", 1, "two", kOk);
+
+      // "one+two   "
+      testWordCount("div6", 2, kOk);
+      testWordAt("div6", 0, "one", kTodo);
+      testWordAt("div6", 1, "two", kOk);
+
+      // "one\ntwo"
+      testWordCount("div7", 2, kOk);
+      testWordAt("div7", 0, "one", kOk);
+      testWordAt("div7", 1, "two", kTodo);
+
+      // "one.two"
+      testWordCount("div8", 2, kOk);
+      testWordAt("div8", 0, "one", kTodo);
+      testWordAt("div8", 1, "two", kOk);
+
+      // "345"
+      testWords("div9", ["345"]);
+
+      // "3a A4"
+      testWords("div10", ["3a", "A4"]);
+
+      // "3.1416"
+      testWords("div11", ["3.1416"], kTodo);
+
+      // "4,261.01"
+      testWords("div12", ["4,261.01"], kTodo);
+
+      // "カタカナ"
+      testWords("div13", ["カタカナ"], kTodo);
+
+      // "Peter's car"
+      testWords("div14", ["Peter's", "car"], kTodo);
+
+      // "N.A.T.O."
+      testWords("div15", ["N.A.T.O."], kTodo);
+
+      // "3+4*5=23"
+      testWordCount("div16", 4, kOk);
+      testWordAt("div15", 0, "3", kTodo);
+      testWordAt("div15", 1, "4", kTodo);
+      testWordAt("div15", 2, "5", kTodo);
+      testWordAt("div15", 3, "23", kTodo);
+
+      // "Hello. Friend, are you here?!"
+      testWordCount("div17", 5, kOk);
+      testWordAt("div17", 0, "Hello", kTodo);
+      testWordAt("div17", 1, "Friend", kTodo);
+      testWordAt("div17", 2, "are", kOk);
+      testWordAt("div17", 3, "you", kOk);
+      testWordAt("div17", 4, "here", kTodo);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="nsIAccessibleText test word boundaries"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">Mozilla Bug 452769</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  <div id="div1">one two</div>
+  <div id="div2">one  two</div>
+  <div id="div3">one,two</div>
+  <div id="div4">one, two</div>
+  <div id="div5">one+two</div>
+  <div id="div6">one+two   </div>
+  <div id="div7">one<br/>two</div>
+  <div id="div8">one.two</div>
+  <div id="div9">345</div>
+  <div id="div10">3a A4</div>
+  <div id="div11">3.1416</div>
+  <div id="div12">4,261.01</div>
+  <div id="div13">カタカナ</div>
+  <div id="div14">Peter's car</div>
+  <div id="div15">N.A.T.O.</div>
+  <div id="div16">3+4*5=23</div>
+  <div id="div17">Hello. Friend, are you here?!</div>
+  </pre>
+</body>
+</html>
--- a/accessible/tests/mochitest/tree/test_tree.xul
+++ b/accessible/tests/mochitest/tree/test_tree.xul
@@ -112,17 +112,17 @@
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     // gA11yEventDumpID = "debug";
     var gQueue = null;
 
     function doTest()
     {
-      var gQueue = new eventQueue("TreeViewChanged");
+      var gQueue = new eventQueue(EVENT_REORDER);
 
       gQueue.push(new treeChecker("list", new nsTableTreeView(3), ROLE_LIST));
       gQueue.push(new treeChecker("tree", new nsTreeTreeView(), ROLE_OUTLINE));
       gQueue.push(new treeChecker("table", new nsTableTreeView(3), ROLE_TABLE));
       gQueue.push(new treeChecker("treetable", new nsTreeTreeView(), ROLE_TREE_TABLE));
 
       gQueue.invoke(); // Will call SimpleTest.finish()
     }
--- a/accessible/tests/mochitest/treeupdate/Makefile.in
+++ b/accessible/tests/mochitest/treeupdate/Makefile.in
@@ -46,14 +46,15 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		test_ariadialog.html \
 		test_doc.html \
 		test_list_editabledoc.html \
 		test_list.html \
 		test_recreation.html \
+		test_select.html \
 		test_textleaf.html \
 		test_visibility.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_select.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Add select options test</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <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="../events.js"></script>
+
+  <script type="application/javascript">
+
+    function addOptions(aID)
+    {
+      this.selectNode = getNode(aID);
+      this.select = getAccessible(this.selectNode);
+
+      this.invoke = function addOptions_invoke()
+      {
+        for (i = 0; i < 2; i++) {
+          var opt = document.createElement("option");
+          opt.value = i;
+          opt.text = "Option: Value " + i;
+
+          this.selectNode.add(opt, null);
+        }
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.select)
+      ];
+
+      this.finalCheck = function addOptions_finalCheck()
+      {
+        var tree =
+          { COMBOBOX: [
+            { COMBOBOX_LIST: [
+              { COMBOBOX_OPTION: [
+                { TEXT_LEAF: [] }
+              ] },
+              { COMBOBOX_OPTION: [
+                { TEXT_LEAF: [] }
+              ] }
+            ] }
+          ] };
+        testAccessibleTree(this.select, tree);
+      }
+
+      this.getID = function addOptions_getID()
+      {
+        return "test elements insertion into a select";
+      }
+    }
+
+    //gA11yEventDumpID = "debug";
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new addOptions("select"));
+
+      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=616452"
+     title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree">
+    Mozilla Bug 616452</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <select id="select"></select>
+
+  <div id="debug"/>
+</body>
+</html>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -432,17 +432,16 @@ pref("dom.disable_window_open_feature.lo
 // allow JS to move and resize existing windows
 pref("dom.disable_window_move_resize",            false);
 // prevent JS from monkeying with window focus, etc
 pref("dom.disable_window_flip",                   true);
 
 // popups.policy 1=allow,2=reject
 pref("privacy.popups.policy",               1);
 pref("privacy.popups.usecustom",            true);
-pref("privacy.popups.firstTime",            true);
 pref("privacy.popups.showBrowserMessage",   true);
 
 pref("privacy.item.cookies",                false);
 
 pref("privacy.clearOnShutdown.history",     true);
 pref("privacy.clearOnShutdown.formdata",    true);
 pref("privacy.clearOnShutdown.passwords",   false);
 pref("privacy.clearOnShutdown.downloads",   true);
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -41,42 +41,36 @@
 <menupopup id="appmenu-popup"
 #ifdef MOZ_SERVICES_SYNC
            onpopupshowing="updateEditUIVisibility();gSyncUI.updateUI();">
 #else
            onpopupshowing="updateEditUIVisibility();">
 #endif
   <hbox>
     <vbox id="appmenuPrimaryPane">
-      <hbox flex="1"
-            class="split-menuitem">
-        <menuitem id="appmenu_newTab"
-                  class="menuitem-tooltip split-menuitem-item"
-                  flex="1"
-                  label="&tabCmd.label;"
-                  command="cmd_newNavigatorTab"
-                  key="key_newNavigatorTab"/>
-        <menu class="split-menuitem-menu">
+      <splitmenu id="appmenu_newTab"
+                 label="&tabCmd.label;"
+                 command="cmd_newNavigatorTab"
+                 key="key_newNavigatorTab">
           <menupopup>
             <menuitem id="appmenu_newTab_popup"
                       label="&tabCmd.label;"
                       command="cmd_newNavigatorTab"
                       key="key_newNavigatorTab"/>
             <menuitem id="appmenu_newNavigator"
                       label="&newNavigatorCmd.label;"
                       command="cmd_newNavigator"
                       key="key_newNavigator"/>
             <menuseparator/>
             <menuitem id="appmenu_openFile"
                       label="&openFileCmd.label;"
                       command="Browser:OpenFile"
                       key="openFileKb"/>
           </menupopup>
-        </menu>
-      </hbox>
+      </splitmenu>
       <menuitem id="appmenu_privateBrowsing"
                 class="menuitem-iconic menuitem-iconic-tooltip"
                 label="&privateBrowsingCmd.start.label;"
                 startlabel="&privateBrowsingCmd.start.label;"
                 stoplabel="&privateBrowsingCmd.stop.label;"
                 command="Tools:PrivateBrowsing"
                 key="key_privatebrowsing"/>
       <menuitem label="&goOfflineCmd.label;"
@@ -114,40 +108,35 @@
       <menuitem id="appmenu_savePage"
                 class="menuitem-tooltip"
                 label="&savePageCmd.label;"
                 command="Browser:SavePage"
                 key="key_savePage"/>
       <menuitem id="appmenu_sendLink"
                 label="&sendPageCmd.label;"
                 command="Browser:SendLink"/>
-      <hbox flex="1"
-            class="split-menuitem">
-        <menuitem id="appmenu_print"
-                  class="menuitem-iconic menuitem-iconic-tooltip split-menuitem-item"
-                  flex="1"
-                  label="&printCmd.label;"
-                  command="cmd_print"
-                  key="printKb"/>
-        <menu class="split-menuitem-menu">
+      <splitmenu id="appmenu_print"
+                 iconic="true"
+                 label="&printCmd.label;"
+                 command="cmd_print"
+                 key="printKb">
           <menupopup>
             <menuitem id="appmenu_print_popup"
                       class="menuitem-iconic"
                       label="&printCmd.label;"
                       command="cmd_print"
                       key="printKb"/>
             <menuitem id="appmenu_printPreview"
                       label="&printPreviewCmd.label;"
                       command="cmd_printPreview"/>
             <menuitem id="appmenu_printSetup"
                       label="&printSetupCmd.label;"
                       command="cmd_pageSetup"/>
           </menupopup>
-        </menu>
-      </hbox>
+      </splitmenu>
       <menuseparator class="appmenu-menuseparator"/>
       <menu id="appmenu_webDeveloper"
             label="&appMenuWebDeveloper.label;">
         <menupopup id="appmenu_webDeveloper_popup">
           <menuitem id="appmenu_webConsole"
                     label="&webConsoleCmd.label;"
                     oncommand="HUDConsoleUI.toggleHUD();"
                     key="key_webConsole"/>
@@ -188,38 +177,34 @@
 #ifdef XP_WIN
                 label="&quitApplicationCmdWin.label;"
 #else
                 label="&quitApplicationCmd.label;"
 #endif
                 command="cmd_quitApplication"/>
     </vbox>
     <vbox id="appmenuSecondaryPane">
-      <hbox class="split-menuitem">
-        <menuitem id="appmenu_bookmarks"
-                  class="menuitem-iconic menuitem-iconic-tooltip split-menuitem-item"
-                  flex="1"
-                  label="&bookmarksMenu.label;"
-                  command="Browser:ShowAllBookmarks"
-                  key="manBookmarkKb"/>
-        <menu id="appmenu_bookmarksMenu"
-              class="split-menuitem-menu">
+      <splitmenu id="appmenu_bookmarks"
+                 iconic="true"
+                 label="&bookmarksMenu.label;"
+                 command="Browser:ShowAllBookmarks"
+                 key="manBookmarkKb">
           <menupopup id="appmenu_bookmarksPopup"
                      placespopup="true"
                      context="placesContext"
                      openInTabs="children"
                      oncommand="BookmarksEventHandler.onCommand(event);"
                      onclick="BookmarksEventHandler.onClick(event);"
                      onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
                                      if (!this.parentNode._placesView)
                                        new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
                      tooltip="bhTooltip"
                      popupsinherittooltip="true">
             <menuitem id="appmenu_showAllBookmarks"
-                      label="&showAllBookmarks.label;"
+                      label="&showAllBookmarks2.label;"
                       command="Browser:ShowAllBookmarks"
                       context=""
                       key="manBookmarkKb"/>
             <menuseparator/>
             <menuitem id="appmenu_bookmarkThisPage"
                       class="menuitem-iconic"
                       label="&bookmarkThisPageCmd.label;"
                       command="Browser:AddBookmarkAs"
@@ -255,27 +240,22 @@
             <!-- Bookmarks menu items -->
             <menuseparator builder="end"
                            class="hide-if-empty-places-result"/>
             <menuitem id="appmenu_unsortedBookmarks"
                       label="&appMenuUnsorted.label;"
                       oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
                       class="menuitem-iconic"/>
           </menupopup>
-        </menu>
-      </hbox>
-      <hbox class="split-menuitem">
-        <menuitem id="appmenu_history"
-                  class="menuitem-iconic menuitem-iconic-tooltip split-menuitem-item"
-                  flex="1"
-                  label="&historyMenu.label;"
-                  command="Browser:ShowAllHistory"
-                  key="showAllHistoryKb"/>
-        <menu id="appmenu_historyMenu"
-              class="split-menuitem-menu">
+      </splitmenu>
+      <splitmenu id="appmenu_history"
+                 iconic="true"
+                 label="&historyMenu.label;"
+                 command="Browser:ShowAllHistory"
+                 key="showAllHistoryKb">
           <menupopup id="appmenu_historyMenupopup"
                      placespopup="true"
                      oncommand="this.parentNode._placesView._onCommand(event);"
                      onclick="checkForMiddleClick(this, event);"
                      onpopupshowing="if (!this.parentNode._placesView)
                                        new HistoryMenu(event);"
                      tooltip="bhTooltip"
                      popupsinherittooltip="true">
@@ -294,80 +274,69 @@
                       label="&historyRestoreLastSession.label;"
                       oncommand="restoreLastSession();"
                       disabled="true"/>
             <menu id="appmenu_recentlyClosedTabsMenu"
                   class="recentlyClosedTabsMenu"
                   label="&historyUndoMenu.label;"
                   disabled="true">
               <menupopup id="appmenu_recentlyClosedTabsMenupopup"
-                         onpopupshowing="document.getElementById('appmenu_historyMenu')._placesView.populateUndoSubmenu();"/>
+                         onpopupshowing="document.getElementById('appmenu_history')._placesView.populateUndoSubmenu();"/>
             </menu>
             <menu id="appmenu_recentlyClosedWindowsMenu"
                   class="recentlyClosedWindowsMenu"
                   label="&historyUndoWindowMenu.label;"
                   disabled="true">
               <menupopup id="appmenu_recentlyClosedWindowsMenupopup"
-                         onpopupshowing="document.getElementById('appmenu_historyMenu')._placesView.populateUndoWindowSubmenu();"/>
+                         onpopupshowing="document.getElementById('appmenu_history')._placesView.populateUndoWindowSubmenu();"/>
             </menu>
             <menuseparator/>
           </menupopup>
-        </menu>
-      </hbox>
+      </splitmenu>
       <menuitem id="appmenu_downloads"
                 class="menuitem-tooltip"
                 label="&downloads.label;"
                 command="Tools:Downloads"
                 key="key_openDownloads"/>
       <spacer id="appmenuSecondaryPane-spacer"/>
       <menuitem id="appmenu_addons"
                 class="menuitem-iconic menuitem-iconic-tooltip"
                 label="&addons.label;"
                 command="Tools:Addons"
                 key="key_openAddons"/>
-      <hbox class="split-menuitem">
-        <menuitem id="appmenu_customize"
+      <splitmenu id="appmenu_customize"
 #ifdef XP_UNIX
-                  label="&preferencesCmdUnix.label;"
+                 label="&preferencesCmdUnix.label;"
 #else
-                  label="&preferencesCmd.label;"
+                 label="&preferencesCmd2.label;"
 #endif
-                  class="split-menuitem-item"
-                  flex="1"
-                  oncommand="openPreferences();"/>
-        <menu class="split-menuitem-menu"
-              label="&preferencesCmd.label;">
+                 oncommand="openPreferences();">
           <menupopup id="appmenu_customizeMenu"
                      onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('appmenu_toggleTabsOnTop').previousSibling);">
             <menuitem id="appmenu_preferences"
 #ifdef XP_UNIX
                       label="&preferencesCmdUnix.label;"
 #else
-                      label="&preferencesCmd.label;"
+                      label="&preferencesCmd2.label;"
 #endif
                       oncommand="openPreferences();"/>
             <menuseparator/>
             <menuseparator/>
             <menuitem id="appmenu_toggleTabsOnTop"
                       label="&viewTabsOnTop.label;"
                       type="checkbox"
                       command="cmd_ToggleTabsOnTop"/>
             <menuitem id="appmenu_toolbarLayout"
                       label="&appMenuToolbarLayout.label;"
                       command="cmd_CustomizeToolbars"/>
           </menupopup>
-        </menu>
-      </hbox>
-      <hbox class="split-menuitem">
-        <menuitem id="appmenu_help"
-                  class="split-menuitem-item"
-                  flex="1"
-                  label="&helpMenu.label;"
-                  oncommand="openHelpLink('firefox-help')"/>
-        <menu class="split-menuitem-menu">
+      </splitmenu>
+      <splitmenu id="appmenu_help"
+                 label="&helpMenu.label;"
+                 oncommand="openHelpLink('firefox-help')">
           <menupopup id="appmenu_helpMenupopup">
             <menuitem id="appmenu_openHelp"
                       label="&helpMenu.label;"
                       oncommand="openHelpLink('firefox-help')"
                       onclick="checkForMiddleClick(this, event);"/>
             <menuitem id="appmenu_gettingStarted"
                       label="&appMenuGettingStarted.label;"
                       oncommand="gBrowser.loadOneTab('http://www.mozilla.com/firefox/central/', {inBackground: false});"
@@ -385,18 +354,17 @@
                       accesskey="&appMenuSafeMode.accesskey;"
                       label="&appMenuSafeMode.label;"
                       oncommand="safeModeRestart();"/>
             <menuseparator/>
             <menuitem id="appmenu_about"
                       label="&aboutProduct.label;"
                       oncommand="openAboutDialog();"/>
           </menupopup>
-        </menu>
-      </hbox>
+      </splitmenu>
 #ifdef MOZ_SERVICES_SYNC
       <spacer flex="1"/>
       <!-- only one of sync-setup or sync-syncnow will be showing at once -->
       <menuitem id="sync-setup-appmenu"
                 label="&syncSetup.label;"
                 observes="sync-setup-state"
                 oncommand="gSyncUI.openSetup()"/>
       <menuitem id="sync-syncnowitem-appmenu"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -457,17 +457,17 @@
                    oncommand="return FeedHandler.subscribeToFeed(null, event);"
                    onclick="checkForMiddleClick(this, event);"/>
       </menu>
       <menuitem id="menu_bookmarkAllTabs"
                 label="&addCurPagesCmd.label;"
                 command="Browser:BookmarkAllTabs"
                 key="bookmarkAllTabsKb"/>
       <menuitem id="bookmarksShowAll"
-                label="&showAllBookmarks.label;"
+                label="&showAllBookmarks2.label;"
                 command="Browser:ShowAllBookmarks"
                 key="manBookmarkKb"/>
       <menuseparator id="organizeBookmarksSeparator"/>
       <menu id="bookmarksToolbarFolderMenu"
             class="menu-iconic bookmark-item"
             label="&personalbarCmd.label;"
             container="true">
         <menupopup id="bookmarksToolbarFolderPopup"  
@@ -558,18 +558,18 @@
               <menuitem id="sanitizeItem"
                         accesskey="&clearRecentHistory.accesskey;"
                         label="&clearRecentHistory.label;"
                         key="key_sanitize"
                         command="Tools:Sanitize"/>
 #ifndef XP_UNIX
               <menuseparator id="prefSep"/>
               <menuitem id="menu_preferences"
-                        label="&preferencesCmd.label;"
-                        accesskey="&preferencesCmd.accesskey;"
+                        label="&preferencesCmd2.label;"
+                        accesskey="&preferencesCmd2.accesskey;"
                         oncommand="openPreferences();"/>
 #endif
               </menupopup>
             </menu>
 
 #ifdef XP_MACOSX
           <menu id="windowMenu" />
 #endif
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -224,17 +224,17 @@ var StarUI = {
     this._element("editBookmarkPanelStarIcon").removeAttribute("unstarred");
 
     this._itemId = aItemId !== undefined ? aItemId : this._itemId;
     this.beginBatch();
 
     // Consume dismiss clicks, see bug 400924
     this.panel.popupBoxObject
         .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
-    this.panel.openPopup(aAnchorElement, aPosition, -1, -1);
+    this.panel.openPopup(aAnchorElement, aPosition);
 
     gEditItemOverlay.initPanel(this._itemId,
                                { hiddenRows: ["description", "location",
                                               "loadInSidebar", "keyword"] });
   },
 
   panelShown:
   function SU_panelShown(aEvent) {
@@ -342,17 +342,18 @@ var PlacesCommandHook = {
 
     // dock the panel to the star icon when possible, otherwise dock
     // it to the content area
     if (aBrowser.contentWindow == window.content) {
       var starIcon = aBrowser.ownerDocument.getElementById("star-button");
       if (starIcon && isElementVisible(starIcon)) {
         // Make sure the bookmark properties dialog hangs toward the middle of
         // the location bar in RTL builds
-        var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'after_start' : 'after_end';
+        var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ?
+          'bottomcenter topleft' : 'bottomcenter topright';
         if (aShowEditUI)
           StarUI.showEditBookmarkPopup(itemId, starIcon, position);
         return;
       }
     }
 
     StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
   },
@@ -839,21 +840,21 @@ var PlacesMenuDNDHandler = {
       event.target.lastChild.setAttribute("autoopened", "true");
       event.target.lastChild.showPopup(event.target.lastChild);
     }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
     event.preventDefault();
     event.stopPropagation();
   },
 
   /**
-   * Handles dragleave on the <menu> element.
+   * Handles dragexit on the <menu> element.
    * @returns true if the element is a container element (menu or 
    *          menu-toolbarbutton), false otherwise.
    */
-  onDragLeave: function PMDH_onDragLeave(event) {
+  onDragExit: function PMDH_onDragExit(event) {
     // Closing menus in a Places popup is handled by the view itself.
     if (!this._isStaticContainer(event.target))
       return;
 
     if (this._loadTimer) {
       this._loadTimer.cancel();
       this._loadTimer = null;
     }
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -75,17 +75,17 @@
 
 
     <commandset id="editMenuCommands"/>
 
     <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(content.document);" observes="isImage"/>
     <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
     <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
     <command id="cmd_find"
-             oncommand="gFindBar.onFindCommand();"
+             oncommand="if (TabView.isVisible()) TabView.enableSearch(event); else gFindBar.onFindCommand();"
              observes="isImage"/>
     <command id="cmd_findAgain"
              oncommand="gFindBar.onFindAgainCommand(false);"
              observes="isImage"/>
     <command id="cmd_findPrevious"
              oncommand="gFindBar.onFindAgainCommand(true);"
              observes="isImage"/>
     <!-- work-around bug 392512 -->
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -559,16 +559,24 @@ var ctrlTab = {
     tabContainer[toggleEventListener]("TabClose", this, false);
 
     document[toggleEventListener]("keypress", this, false);
     gBrowser.mTabBox.handleCtrlTab = !enable;
 
     // If we're not running, hide the "Show All Tabs" menu item,
     // as Shift+Ctrl+Tab will be handled by the tab bar.
     document.getElementById("menu_showAllTabs").hidden = !enable;
+
+    // Also disable the <key> to ensure Shift+Ctrl+Tab never triggers
+    // Show All Tabs.
+    var key_showAllTabs = document.getElementById("key_showAllTabs");
+    if (enable)
+      key_showAllTabs.removeAttribute("disabled");
+    else
+      key_showAllTabs.setAttribute("disabled", "true");
   }
 };
 
 
 /**
  * All Tabs panel
  */
 var allTabs = {
@@ -623,22 +631,23 @@ var allTabs = {
     this._initiated = false;
   },
 
   prefName: "browser.allTabs.previews",
   readPref: function allTabs_readPref() {
     var allTabsButton = document.getElementById("alltabs-button");
     if (!allTabsButton)
       return;
-    if (gPrefService.getBoolPref(this.prefName)) {
-      allTabsButton.removeAttribute("type");
+
+    if (gPrefService.getBoolPref(this.prefName)) {
+      allTabsButton.removeAttribute("type");
       allTabsButton.setAttribute("command", "Browser:ShowAllTabs");
-    } else {
-      allTabsButton.setAttribute("type", "menu");
-      allTabsButton.removeAttribute("command");
+    } else {
+      allTabsButton.setAttribute("type", "menu");
+      allTabsButton.removeAttribute("command");
       allTabsButton.removeAttribute("oncommand");
     }
   },
   observe: function (aSubject, aTopic, aPrefName) {
     this.readPref();
   },
 
   pick: function allTabs_pick(aPreview) {
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -214,16 +214,22 @@ let TabView = {
 
   // ----------
   moveTabTo: function(tab, groupItemId) {
     if (this._window)
       this._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
   },
 
   // ----------
+  enableSearch: function Tabview_enableSearch(event) {
+    if (this._window)
+      this._window.UI.enableSearch(event);
+  },
+
+  // ----------
   // Adds new key commands to the browser, for invoking the Tab Candy UI
   // and for switching between groups of tabs when outside of the Tab Candy UI.
   _setBrowserKeyHandlers : function() {
     let self = this;
 
     window.addEventListener("keypress", function(event) {
       if (self.isVisible())
         return;
@@ -237,10 +243,17 @@ let TabView = {
 
         self._initFrame(function() {
           let tabItem = self._window.GroupItems.getNextGroupItemTab(event.shiftKey);
           if (tabItem)
             window.gBrowser.selectedTab = tabItem.tab;
         });
       }
     }, true);
+  },
+  
+  // ----------
+  // Prepares the tab view for undo close tab.
+  prepareUndoCloseTab: function() {
+    if (this._window)
+      this._window.UI.restoredClosedTab = true;
   }
 };
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -24,36 +24,42 @@ tabbrowser {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
 }
 
 .tabbrowser-tab:not([pinned]) {
   -moz-box-flex: 100;
   max-width: 250px;
   min-width: 100px;
   width: 0;
-  -moz-transition: min-width .2s ease-out, max-width .25s ease-out;
+  -moz-transition: min-width 200ms ease-out,
+                   max-width 250ms ease-out,
+                   opacity 50ms ease-out 20ms /* hide the tab for the first 20ms of the max-width transition */;
 }
 
 .tabbrowser-tab:not([pinned]):not([fadein]) {
-  max-width: 1px;
-  min-width: 1px;
+  max-width: 0.1px;
+  min-width: 0.1px;
+  opacity: 0 !important;
+  -moz-transition: min-width 200ms ease-out,
+                   max-width 250ms ease-out,
+                   opacity 50ms ease-out 180ms /* hide the tab for the last 20ms of the max-width transition */;
 }
 
 .tab-throbber:not([fadein]):not([pinned]),
 .tab-label:not([fadein]):not([pinned]),
 .tab-icon-image:not([fadein]):not([pinned]),
 .tab-close-button:not([fadein]):not([pinned]) {
   opacity: 0 !important;
 }
 
 .tab-throbber,
 .tab-label,
 .tab-icon-image,
 .tab-close-button {
-  -moz-transition: opacity .25s;
+  -moz-transition: opacity 250ms;
 }
 
 .tabbrowser-tabs:not([pinnedonly]) > .tabbrowser-tab[pinned] {
   position: fixed;
   display: block; /* position:fixed already does this (bug 579776), but let's be explicit */
 }
 
 #alltabs-popup {
@@ -89,16 +95,20 @@ toolbar[printpreview="true"] {
   -moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
 }
 %endif
 
 toolbarpaletteitem[place="palette"] > toolbaritem > hbox[type="places"] {
   display: none;
 }
 
+#main-window[disablechrome] #navigator-toolbox[tabsontop="true"] > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
+  visibility: collapse;
+}
+
 #wrapper-urlbar-container #urlbar-container > #urlbar > toolbarbutton,
 #urlbar-container:not([combined]) > #urlbar > toolbarbutton,
 #urlbar-container[combined] + #reload-button + #stop-button,
 #urlbar-container[combined] + #reload-button,
 toolbar:not([mode="icons"]) > #urlbar-container > #urlbar > toolbarbutton,
 toolbar[mode="icons"] > #urlbar-container > #urlbar > #urlbar-reload-button:not([displaystop]) + #urlbar-stop-button,
 toolbar[mode="icons"] > #urlbar-container > #urlbar > #urlbar-reload-button[displaystop],
 toolbar[mode="icons"] > #reload-button:not([displaystop]) + #stop-button,
@@ -125,16 +135,25 @@ toolbar[mode="icons"] > #reload-button[d
 }
 %endif
 
 #browser-bottombox[lwthemefooter="true"] {
   background-repeat: no-repeat;
   background-position: bottom left;
 }
 
+splitmenu {
+  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#splitmenu");
+}
+
+.split-menuitem-item {
+  list-style-image: inherit;
+  -moz-image-region: inherit;
+}
+
 .split-menuitem-menu > .menu-text,
 .split-menuitem-menu > .menu-accel-container {
   display: none;
 }
 
 .menuitem-tooltip {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-tooltip");
 }
@@ -433,17 +452,17 @@ window[chromehidden~="toolbar"] toolbar:
 
 #notification-popup-box[anchorid="geo-notification-icon"] > #geo-notification-icon,
 #notification-popup-box[anchorid="indexedDB-notification-icon"] > #indexedDB-notification-icon,
 #notification-popup-box[anchorid="addons-notification-icon"] > #addons-notification-icon,
 #notification-popup-box[anchorid="password-notification-icon"] > #password-notification-icon {
   display: -moz-box;
 }
 
-#invalid-form-popup {
+#invalid-form-popup > description {
   max-width: 280px;
 }
 
 #geolocation-notification {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#geolocation-notification");
 }
 
 /* override hidden="true" for the status bar compatibility shim
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -580,37 +580,19 @@ const gPopupBlockerObserver = {
     }
     else
       window.openDialog("chrome://browser/content/preferences/permissions.xul",
                         "_blank", "resizable,dialog=no,centerscreen", params);
   },
 
   dontShowMessage: function ()
   {
-#if 0 
-    // Disabled until bug 594294 is fixed.
     var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
-    var firstTime = gPrefService.getBoolPref("privacy.popups.firstTime");
-
-    // If the info message is showing at the top of the window, and the user has never
-    // hidden the message before, show an info box telling the user where the info
-    // will be displayed.
-    if (showMessage && firstTime)
-      this._displayPageReportFirstTime();
-
     gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
-#endif
-
     gBrowser.getNotificationBox().removeCurrentNotification();
-  },
-
-  _displayPageReportFirstTime: function ()
-  {
-    window.openDialog("chrome://browser/content/pageReportFirstTime.xul", "_blank",
-                      "dependent");
   }
 };
 
 const gXPInstallObserver = {
   _findChildShell: function (aDocShell, aSoughtShell)
   {
     if (aDocShell == aSoughtShell)
       return aDocShell;
@@ -652,59 +634,53 @@ const gXPInstallObserver = {
 
     var notificationID = aTopic;
     // Make notifications persist a minimum of 30 seconds
     var options = {
       timeout: Date.now() + 30000
     };
 
     switch (aTopic) {
-    case "addon-install-blocked":
-      var enabled = true;
-      try {
-        enabled = gPrefService.getBoolPref("xpinstall.enabled");
-      }
-      catch (e) {
-      }
-
-      if (!enabled) {
-        notificationID = "xpinstall-disabled"
-
-        if (gPrefService.prefIsLocked("xpinstall.enabled")) {
-          messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
-          buttons = [];
-        }
-        else {
-          messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
-
-          action = {
-            label: gNavigatorBundle.getString("xpinstallDisabledButton"),
-            accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
-            callback: function editPrefs() {
-              gPrefService.setBoolPref("xpinstall.enabled", true);
-            }
-          };
-        }
+    case "addon-install-disabled":
+      notificationID = "xpinstall-disabled"
+
+      if (gPrefService.prefIsLocked("xpinstall.enabled")) {
+        messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
+        buttons = [];
       }
       else {
-        messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
-                          [brandShortName, installInfo.originatingURI.host]);
+        messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
 
         action = {
-          label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
-          accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
-          callback: function() {
-            installInfo.install();
+          label: gNavigatorBundle.getString("xpinstallDisabledButton"),
+          accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
+          callback: function editPrefs() {
+            gPrefService.setBoolPref("xpinstall.enabled", true);
           }
         };
       }
 
       PopupNotifications.show(browser, notificationID, messageString, anchorID,
                               action, null, options);
       break;
+    case "addon-install-blocked":
+      messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
+                        [brandShortName, installInfo.originatingURI.host]);
+
+      action = {
+        label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
+        accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
+        callback: function() {
+          installInfo.install();
+        }
+      };
+
+      PopupNotifications.show(browser, notificationID, messageString, anchorID,
+                              action, null, options);
+      break;
     case "addon-install-failed":
       // TODO This isn't terribly ideal for the multiple failure case
       installInfo.installs.forEach(function(aInstall) {
         var host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
                    installInfo.originatingURI.host;
         if (!host)
           host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) &&
                  aInstall.sourceURI.host;
@@ -1247,16 +1223,19 @@ function BrowserStartup() {
   CombinedStopReload.init();
 
   allTabs.readPref();
 
   TabsOnTop.syncCommand();
 
   BookmarksMenuButton.init();
 
+  // initialize the private browsing UI
+  gPrivateBrowsingUI.init();
+
   setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
 }
 
 function HandleAppCommandEvent(evt) {
   evt.stopPropagation();
   switch (evt.command) {
   case "Back":
     BrowserBack();
@@ -1364,16 +1343,17 @@ function prepareForStartup() {
                             OfflineApps, false);
 
   // setup simple gestures support
   gGestureSupport.init(true);
 }
 
 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
   Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
   Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
 
   BrowserOffline.init();
   OfflineApps.init();
   IndexedDBPromptHelper.init();
@@ -1533,19 +1513,16 @@ function delayedStartup(isLoadingBlank, 
 
 #ifndef XP_MACOSX
   updateEditUIVisibility();
   let placesContext = document.getElementById("placesContext");
   placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
   placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
 #endif
 
-  // initialize the private browsing UI
-  gPrivateBrowsingUI.init();
-
   gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
   gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
   gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
 
   if (Win7Features)
     Win7Features.onOpenWindow();
 
   // called when we go into full screen, even if it is
@@ -1613,16 +1590,17 @@ function BrowserShutdown()
   try {
     FullZoom.destroy();
   }
   catch(ex) {
     Components.utils.reportError(ex);
   }
 
   Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
+  Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
   Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
   Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
   Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
   Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
   Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
 
   try {
     gBrowser.removeProgressListener(window.XULBrowserWindow);
@@ -2904,17 +2882,17 @@ var homeButtonObserver = {
       setTimeout(openHomeDialog, 0, browserDragAndDrop.drop(aEvent, { }));
     },
 
   onDragOver: function (aEvent)
     {
       browserDragAndDrop.dragOver(aEvent, "droponhomebutton");
       aEvent.dropEffect = "link";
     },
-  onDragLeave: function (aEvent)
+  onDragExit: function (aEvent)
     {
       XULWindowBrowser.setStatusText("");
     }
 }
 
 function openHomeDialog(aURL)
 {
   var promptTitle = gNavigatorBundle.getString("droponhometitle");
@@ -2947,29 +2925,29 @@ var bookmarksButtonObserver = {
   },
 
   onDragOver: function (aEvent)
   {
     browserDragAndDrop.dragOver(aEvent, "droponbookmarksbutton");
     aEvent.dropEffect = "link";
   },
 
-  onDragLeave: function (aEvent)
+  onDragExit: function (aEvent)
   {
     XULWindowBrowser.setStatusText("");
   }
 }
 
 var newTabButtonObserver = {
   onDragOver: function (aEvent)
   {
     browserDragAndDrop.dragOver(aEvent, "droponnewtabbutton");
   },
 
-  onDragLeave: function (aEvent)
+  onDragExit: function (aEvent)
   {
     XULWindowBrowser.setStatusText("");
   },
 
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
     var postData = {};
@@ -2981,17 +2959,17 @@ var newTabButtonObserver = {
   }
 }
 
 var newWindowButtonObserver = {
   onDragOver: function (aEvent)
   {
     browserDragAndDrop.dragOver(aEvent, "droponnewwindowbutton");
   },
-  onDragLeave: function (aEvent)
+  onDragExit: function (aEvent)
   {
     XULWindowBrowser.setStatusText("");
   },
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
     var postData = {};
     url = getShortcutOrURI(url, postData);
@@ -3008,17 +2986,17 @@ var DownloadsButtonDNDObserver = {
     XULWindowBrowser.setStatusText(gNavigatorBundle.getString("dropondownloadsbutton"));
     var types = aEvent.dataTransfer.types;
     if (types.contains("text/x-moz-url") ||
         types.contains("text/uri-list") ||
         types.contains("text/plain"))
       aEvent.preventDefault();
   },
 
-  onDragLeave: function (aEvent)
+  onDragExit: function (aEvent)
   {
     XULWindowBrowser.setStatusText("");
   },
 
   onDrop: function (aEvent)
   {
     let name = { };
     let url = browserDragAndDrop.drop(aEvent, name);
@@ -3918,16 +3896,17 @@ var XULBrowserWindow = {
   // Stored Status, Link and Loading values
   status: "",
   defaultStatus: "",
   jsStatus: "",
   jsDefaultStatus: "",
   startTime: 0,
   statusText: "",
   isBusy: false,
+  inContentWhitelist: ["about:addons"],
 
   QueryInterface: function (aIID) {
     if (aIID.equals(Ci.nsIWebProgressListener) ||
         aIID.equals(Ci.nsIWebProgressListener2) ||
         aIID.equals(Ci.nsISupportsWeakReference) ||
         aIID.equals(Ci.nsIXULBrowserWindow) ||
         aIID.equals(Ci.nsISupports))
       return this;
@@ -3994,28 +3973,39 @@ var XULBrowserWindow = {
 
   // Called before links are navigated to to allow us to retarget them if needed.
   onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
     // Don't modify non-default targets or targets that aren't in top-level app
     // tab docshells (isAppTab will be false for app tab subframes).
     if (originalTarget != "" || !isAppTab)
       return originalTarget;
 
-    let docURI = linkNode.ownerDocument.documentURIObject;
+    // External links from within app tabs should always open in new tabs
+    // instead of replacing the app tab's page (Bug 575561)
+    let linkHost;
+    let docHost;
     try {
-      let docURIDomain = Services.eTLD.getBaseDomain(docURI, 0);
-      let linkURIDomain = Services.eTLD.getBaseDomain(linkURI, 0);
-      // External links from within app tabs should always open in new tabs
-      // instead of replacing the app tab's page (Bug 575561)
-      if (docURIDomain != linkURIDomain)
-        return "_blank";
+      linkHost = linkURI.host;
+      docHost = linkNode.ownerDocument.documentURIObject.host;
     } catch(e) {
-      // If getBaseDomain fails, we return originalTarget below.
-    }
-    return originalTarget;
+      // nsIURI.host can throw for non-nsStandardURL nsIURIs.
+      // If we fail to get either host, just return originalTarget.
+      return originalTarget;
+    }
+
+    if (docHost == linkHost)
+      return originalTarget;
+
+    // Special case: ignore "www" prefix if it is part of host string
+    let [longHost, shortHost] =
+      linkHost.length > docHost.length ? [linkHost, docHost] : [docHost, linkHost];
+    if (longHost == "www." + shortHost)
+      return originalTarget;
+
+    return "_blank";
   },
 
   onLinkIconAvailable: function (aIconURL) {
     if (gProxyFavIcon && gBrowser.userTypedValue === null)
       PageProxySetIcon(aIconURL); // update the favicon in the URL bar
   },
 
   onProgressChange: function (aWebProgress, aRequest,
@@ -4145,16 +4135,26 @@ var XULBrowserWindow = {
             document.getElementById("aHTMLTooltip").hidePopup();
             document.tooltipNode = null;
             break;
           }
         }
       }
     }
 
+    // Show or hide browser chrome based on the whitelist
+    var disableChrome = this.inContentWhitelist.some(function(aSpec) {
+      return aSpec == location;
+    });
+
+    if (disableChrome)
+      document.documentElement.setAttribute("disablechrome", "true");
+    else
+      document.documentElement.removeAttribute("disablechrome");
+
     // This code here does not compare uris exactly when determining
     // whether or not the message should be hidden since the message
     // may be prematurely hidden when an install is invoked by a click
     // on a link that looks like this:
     //
     // <a href="#" onclick="return install();">Install Foo</a>
     //
     // - which fires a onLocationChange message to uri + '#'...
@@ -4322,16 +4322,18 @@ var XULBrowserWindow = {
     }
 
     // Don't pass in the actual location object, since it can cause us to
     // hold on to the window object too long.  Just pass in the fields we
     // care about. (bug 424829)
     var location = gBrowser.contentWindow.location;
     var locationObj = {};
     try {
+      // about:blank can be used by webpages so pretend it is http
+      locationObj.protocol = location == "about:blank" ? "http:" : location.protocol;
       locationObj.host = location.host;
       locationObj.hostname = location.hostname;
       locationObj.port = location.port;
     } catch (ex) {
       // Can sometimes throw if the URL being visited has no host/hostname,
       // e.g. about:blank. The _state for these pages means we won't need these
       // properties anyways, though.
     }
@@ -5443,33 +5445,36 @@ function stylesheetSwitchAll(frameset, t
 }
 
 function setStyleDisabled(disabled) {
   getMarkupDocumentViewer().authorStyleDisabled = disabled;
 }
 /* End of the Page Style functions */
 
 var BrowserOffline = {
+  _inited: false,
+
   /////////////////////////////////////////////////////////////////////////////
   // BrowserOffline Public Methods
   init: function ()
   {
     if (!this._uiElement)
       this._uiElement = document.getElementById("workOfflineMenuitemState");
 
     Services.obs.addObserver(this, "network:offline-status-changed", false);
 
     this._updateOfflineUI(Services.io.offline);
+
+    this._inited = true;
   },
 
   uninit: function ()
   {
-    try {
+    if (this._inited) {
       Services.obs.removeObserver(this, "network:offline-status-changed");
-    } catch (ex) {
     }
   },
 
   toggleOfflineStatus: function ()
   {
     var ioService = Services.io;
 
     // Stop automatic management of the offline status
@@ -6787,16 +6792,17 @@ function undoCloseTab(aIndex) {
       !gPrefService.getBoolPref("browser.tabs.autoHide") &&
       isTabEmpty(gBrowser.selectedTab))
     blankTabToRemove = gBrowser.selectedTab;
 
   var tab = null;
   var ss = Cc["@mozilla.org/browser/sessionstore;1"].
            getService(Ci.nsISessionStore);
   if (ss.getClosedTabCount(window) > (aIndex || 0)) {
+    TabView.prepareUndoCloseTab();
     tab = ss.undoCloseTab(window, aIndex || 0);
 
     if (blankTabToRemove)
       gBrowser.removeTab(blankTabToRemove);
   }
 
   return tab;
 }
@@ -6887,20 +6893,22 @@ var gBookmarkAllTabsHandler = {
  * Utility object to handle manipulations of the identity indicators in the UI
  */
 var gIdentityHandler = {
   // Mode strings used to control CSS display
   IDENTITY_MODE_IDENTIFIED       : "verifiedIdentity", // High-quality identity information
   IDENTITY_MODE_DOMAIN_VERIFIED  : "verifiedDomain",   // Minimal SSL CA-signed domain verification
   IDENTITY_MODE_UNKNOWN          : "unknownIdentity",  // No trusted identity information
   IDENTITY_MODE_MIXED_CONTENT    : "unknownIdentity mixedContent",  // SSL with unauthenticated content
+  IDENTITY_MODE_CHROMEUI         : "chromeUI",         // Part of the product's UI
 
   // Cache the most recent SSLStatus and Location seen in checkIdentity
   _lastStatus : null,
   _lastLocation : null,
+  _mode : "unknownIdentity",
 
   // smart getters
   get _encryptionLabel () {
     delete this._encryptionLabel;
     this._encryptionLabel = {};
     this._encryptionLabel[this.IDENTITY_MODE_DOMAIN_VERIFIED] =
       gNavigatorBundle.getString("identity.encrypted");
     this._encryptionLabel[this.IDENTITY_MODE_IDENTIFIED] =
@@ -7030,17 +7038,19 @@ var gIdentityHandler = {
   checkIdentity : function(state, location) {
     var currentStatus = gBrowser.securityUI
                                 .QueryInterface(Components.interfaces.nsISSLStatusProvider)
                                 .SSLStatus;
     this._lastStatus = currentStatus;
     this._lastLocation = location;
 
     let nsIWebProgressListener = Ci.nsIWebProgressListener;
-    if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
+    if (location.protocol == "chrome:" || location.protocol == "about:")
+      this.setMode(this.IDENTITY_MODE_CHROMEUI);
+    else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
       this.setMode(this.IDENTITY_MODE_IDENTIFIED);
     else if (state & nsIWebProgressListener.STATE_SECURE_HIGH)
       this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
     else if (state & nsIWebProgressListener.STATE_IS_BROKEN)
       this.setMode(this.IDENTITY_MODE_MIXED_CONTENT);
     else
       this.setMode(this.IDENTITY_MODE_UNKNOWN);
   },
@@ -7079,16 +7089,18 @@ var gIdentityHandler = {
     }
 
     this._identityBox.className = newMode;
     this.setIdentityMessages(newMode);
 
     // Update the popup too, if it's open
     if (this._identityPopup.state == "open")
       this.setPopupMessages(newMode);
+
+    this._mode = newMode;
   },
 
   /**
    * Set up the messages for the primary identity UI based on the specified mode,
    * and the details of the SSL cert, where applicable
    *
    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
@@ -7145,16 +7157,22 @@ var gIdentityHandler = {
       // swap the positions of the organization and country code labels.
       // The Unicode ranges reflect the definition of the UCS2_CHAR_IS_BIDI
       // macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
       // fixed, this test should be replaced by one adhering to the
       // Unicode Bidirectional Algorithm proper (at the paragraph level).
       icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/.test(icon_label) ?
                         "rtl" : "ltr";
     }
+    else if (newMode == this.IDENTITY_MODE_CHROMEUI) {
+      icon_label = "";
+      tooltip = "";
+      icon_country_label = "";
+      icon_labels_dir = "ltr";
+    }
     else {
       tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");
       icon_label = "";
       icon_country_label = "";
       icon_labels_dir = "ltr";
     }
 
     // Push the appropriate strings out to the UI
@@ -7239,30 +7257,33 @@ var gIdentityHandler = {
     if ((event.type == "click" && event.button != 0) ||
         (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
          event.keyCode != KeyEvent.DOM_VK_RETURN))
       return; // Left click, space or enter only
 
     // Revert the contents of the location bar, see bug 406779
     gURLBar.handleRevert();
 
+    if (this._mode == this.IDENTITY_MODE_CHROMEUI)
+      return;
+
     // Make sure that the display:none style we set in xul is removed now that
     // the popup is actually needed
     this._identityPopup.hidden = false;
 
     // Tell the popup to consume dismiss clicks, to avoid bug 395314
     this._identityPopup.popupBoxObject
         .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
 
     // Update the popup strings
     this.setPopupMessages(this._identityBox.className);
 
     // Make sure the identity popup hangs toward the middle of the location bar
     // in RTL builds
-    var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'after_end' : 'after_start';
+    var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'bottomcenter topright' : 'bottomcenter topleft';
 
     // Add the "open" attribute to the identity box for styling
     this._identityBox.setAttribute("open", "true");
     var self = this;
     this._identityPopup.addEventListener("popuphidden", function (e) {
       e.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
       self._identityBox.removeAttribute("open");
     }, false);
@@ -7279,17 +7300,17 @@ var gIdentityHandler = {
     var urlString = value + "\n" + content.document.title;
     var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
 
     var dt = event.dataTransfer;
     dt.setData("text/x-moz-url", urlString);
     dt.setData("text/uri-list", value);
     dt.setData("text/plain", value);
     dt.setData("text/html", htmlString);
-    dt.setDragImage(event.currentTarget, 0, 0);
+    dt.addElement(event.currentTarget);
   }
 };
 
 let DownloadMonitorPanel = {
   //////////////////////////////////////////////////////////////////////////////
   //// DownloadMonitorPanel Member Variables
 
   _panel: null,
@@ -7439,29 +7460,35 @@ function getTabModalPromptBox(aWindow) {
 /* DEPRECATED */
 function getBrowser() gBrowser;
 function getNavToolbox() gNavToolbox;
 
 let gPrivateBrowsingUI = {
   _privateBrowsingService: null,
   _searchBarValue: null,
   _findBarValue: null,
+  _inited: false,
 
   init: function PBUI_init() {
     Services.obs.addObserver(this, "private-browsing", false);
     Services.obs.addObserver(this, "private-browsing-transition-complete", false);
 
     this._privateBrowsingService = Cc["@mozilla.org/privatebrowsing;1"].
                                    getService(Ci.nsIPrivateBrowsingService);
 
     if (this.privateBrowsingEnabled)
       this.onEnterPrivateBrowsing(true);
+
+    this._inited = true;
   },
 
   uninit: function PBUI_unint() {
+    if (!this._inited)
+      return;
+
     Services.obs.removeObserver(this, "private-browsing");
     Services.obs.removeObserver(this, "private-browsing-transition-complete");
   },
 
   get _disableUIOnToggle() {
     if (this._privateBrowsingService.autoStarted)
       return false;
 
@@ -7477,21 +7504,18 @@ let gPrivateBrowsingUI = {
     if (aTopic == "private-browsing") {
       if (aData == "enter")
         this.onEnterPrivateBrowsing();
       else if (aData == "exit")
         this.onExitPrivateBrowsing();
     }
     else if (aTopic == "private-browsing-transition-complete") {
       if (this._disableUIOnToggle) {
-        // use setTimeout here in order to make the code testable
-        setTimeout(function() {
-          document.getElementById("Tools:PrivateBrowsing")
-                  .removeAttribute("disabled");
-        }, 0);
+        document.getElementById("Tools:PrivateBrowsing")
+                .removeAttribute("disabled");
       }
     }
   },
 
   _shouldEnter: function PBUI__shouldEnter() {
     try {
       // Never prompt if the session is not going to be closed, or if user has
       // already requested not to be prompted.
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -164,21 +164,22 @@
 
     <!-- for search and content formfill/pw manager -->
     <panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for url bar autocomplete -->
     <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
 
     <!-- for invalid form error message -->
-    <panel id="invalid-form-popup" noautofocus="true" hidden="true" level="parent">
+    <panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
       <description/>
     </panel>
 
     <panel id="editBookmarkPanel"
+           type="arrow"
            orient="vertical"
            ignorekeys="true"
            hidden="true"
            onpopupshown="StarUI.panelShown(event);"
            aria-labelledby="editBookmarkPanelTitle">
       <row id="editBookmarkPanelHeader" align="center" hidden="true">
         <vbox align="center">
           <image id="editBookmarkPanelStarIcon"/>
@@ -329,20 +330,23 @@
                                return gContextMenu.shouldDisplay;"
                onpopuphiding="if (event.target == this) { gContextMenu = null; updateEditUIVisibility(); }">
 #include browser-context.inc
     </menupopup>
 
     <menupopup id="placesContext"/>
 
     <panel id="notification-popup" type="arrow" position="after_start"
-           noautofocus="true" hidden="true" orient="vertical"/>
+           hidden="true" orient="vertical"/>
 
     <!-- Popup for site identity information -->
-    <panel id="identity-popup" position="after_start" hidden="true" noautofocus="true"
+    <panel id="identity-popup"
+           type="arrow"
+           hidden="true"
+           noautofocus="true"
            onpopupshown="document.getElementById('identity-popup-more-info-button').focus();"
            level="top">
       <hbox id="identity-popup-container" align="top">
         <image id="identity-popup-icon"/>
         <vbox id="identity-popup-content-box">
           <label id="identity-popup-connectedToLabel"
                  class="identity-popup-label"
                  value="&identity.connectedTo;"/>
@@ -510,17 +514,17 @@
       </toolbaritem>
 
       <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      persist="class" removable="true"
                      label="&homeButton.label;"
                      ondragover="homeButtonObserver.onDragOver(event)"
                      ondragenter="homeButtonObserver.onDragOver(event)"
                      ondrop="homeButtonObserver.onDrop(event)"
-                     ondragleave="homeButtonObserver.onDragLeave(event)"
+                     ondragexit="homeButtonObserver.onDragExit(event)"
                      onclick="BrowserGoHome(event);"
                      aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
 
       <toolbaritem id="urlbar-container" align="center" flex="400" persist="width" combined="true"
                    title="&locationItem.title;" class="chromeclass-location" removable="true">
         <textbox id="urlbar" flex="1"
                  placeholder="&urlbar.placeholder;"
                  type="autocomplete"
@@ -619,17 +623,17 @@
                    title="&bookmarksMenuButton.label;">
         <toolbarbutton id="bookmarks-menu-button"
                        type="menu"
                        class="toolbarbutton-1"
                        label="&bookmarksMenuButton.label;"
                        tooltiptext="&bookmarksMenuButton.tooltip;"
                        ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
                        ondragover="PlacesMenuDNDHandler.onDragOver(event);"
-                       ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
+                       ondragexit="PlacesMenuDNDHandler.onDragExit(event);"
                        ondrop="PlacesMenuDNDHandler.onDrop(event);">
           <menupopup id="BMB_bookmarksPopup"
                      placespopup="true"
                      context="placesContext"
                      openInTabs="children"
                      oncommand="BookmarksEventHandler.onCommand(event);"
                      onclick="BookmarksEventHandler.onClick(event);"
                      onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
@@ -639,17 +643,17 @@
             <menuitem id="BMB_viewBookmarksToolbar"
                       placesanonid="view-toolbar"
                       toolbarId="PersonalToolbar"
                       type="checkbox"
                       oncommand="onViewToolbarCommand(event)"
                       label="&viewBookmarksToolbar.label;"/>
             <menuseparator/>
             <menuitem id="BMB_bookmarksShowAll"
-                      label="&showAllBookmarks.label;"
+                      label="&showAllBookmarks2.label;"
                       command="Browser:ShowAllBookmarks"
                       key="manBookmarkKb"/>
             <menuseparator/>
             <menuitem id="BMB_bookmarkThisPage"
 #ifndef XP_MACOSX
                       class="menuitem-iconic"
 #endif
                       label="&bookmarkThisPageCmd.label;"
@@ -808,17 +812,17 @@
                      class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&tabCmd.label;"
                      command="cmd_newNavigatorTab"
                      onclick="checkForMiddleClick(this, event);"
                      tooltiptext="&newTabButton.tooltip;"
                      ondrop="newTabButtonObserver.onDrop(event)"
                      ondragover="newTabButtonObserver.onDragOver(event)"
                      ondragenter="newTabButtonObserver.onDragOver(event)"
-                     ondragleave="newTabButtonObserver.onDragLeave(event)"
+                     ondragexit="newTabButtonObserver.onDragExit(event)"
                      removable="true"/>
 
       <toolbarbutton id="alltabs-button"
                      class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button"
                      type="menu"
                      label="&listAllTabs.label;"
                      tooltiptext="&listAllTabs.label;"
                      removable="true">
@@ -855,40 +859,40 @@
         <image/>
       </toolbaritem>
 
       <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      observes="Tools:Downloads"
                      ondrop="DownloadsButtonDNDObserver.onDrop(event)"
                      ondragover="DownloadsButtonDNDObserver.onDragOver(event)"
                      ondragenter="DownloadsButtonDNDObserver.onDragOver(event)"
-                     ondragleave="DownloadsButtonDNDObserver.onDragLeave(event)"
+                     ondragexit="DownloadsButtonDNDObserver.onDragExit(event)"
                      label="&downloads.label;"
                      tooltiptext="&downloads.tooltip;"/>
 
       <toolbarbutton id="history-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      observes="viewHistorySidebar" label="&historyButton.label;"
                      tooltiptext="&historyButton.tooltip;"/>
 
       <toolbarbutton id="bookmarks-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      observes="viewBookmarksSidebar"
                      tooltiptext="&bookmarksButton.tooltip;"
                      ondrop="bookmarksButtonObserver.onDrop(event)"
                      ondragover="bookmarksButtonObserver.onDragOver(event)"
                      ondragenter="bookmarksButtonObserver.onDragOver(event)"
-                     ondragleave="bookmarksButtonObserver.onDragLeave(event)"/>
+                     ondragexit="bookmarksButtonObserver.onDragExit(event)"/>
 
       <toolbarbutton id="new-window-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&newNavigatorCmd.label;"
                      command="key_newNavigator"
                      tooltiptext="&newWindowButton.tooltip;"
                      ondrop="newWindowButtonObserver.onDrop(event)"
                      ondragover="newWindowButtonObserver.onDragOver(event)"
                      ondragenter="newWindowButtonObserver.onDragOver(event)"
-                     ondragleave="newWindowButtonObserver.onDragLeave(event)"/>
+                     ondragexit="newWindowButtonObserver.onDragExit(event)"/>
 
       <toolbarbutton id="cut-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&cutCmd.label;"
                      command="cmd_cut"
                      tooltiptext="&cutButton.tooltip;"/>
 
       <toolbarbutton id="copy-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&copyCmd.label;"
@@ -964,16 +968,17 @@
   <vbox id="browser-bottombox" layer="true">
     <toolbar id="addon-bar"
              toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
              collapsed="true"
              class="toolbar-primary chromeclass-toolbar"
              context="toolbar-context-menu" toolboxid="navigator-toolbox"
              mode="icons" iconsize="small" defaulticonsize="small"
              lockiconsize="true"
+             defaultset="status-bar"
              customizable="true" align="right">
       <statusbar id="status-bar"/>
     </toolbar>
   </vbox>
 
 #ifndef XP_UNIX
   <svg:svg height="0">
     <svg:mask id="winstripe-keyhole-forward-mask" maskContentUnits="objectBoundingBox">
@@ -981,16 +986,52 @@
       <svg:circle cx="-0.46" cy="0.5" r="0.63"/>
     </svg:mask>
     <svg:mask id="winstripe-keyhole-forward-mask-hover" maskContentUnits="objectBoundingBox">
       <svg:rect x="0" y="0" width="1" height="1" fill="white"/>
       <svg:circle cx="-0.35" cy="0.5" r="0.58"/>
     </svg:mask>
   </svg:svg>
 #endif
+#ifdef XP_MACOSX
+  <svg:svg height="0">
+    <svg:mask id="pinstripe-tab-ontop-left-curve-mask" maskContentUnits="userSpaceOnUse">
+      <svg:circle cx="9" cy="3" r="3" fill="white"/>
+      <svg:rect x="9" y="0" width="3" height="3" fill="white"/>
+      <svg:rect x="6" y="3" width="6" height="19" fill="white"/>
+      <svg:rect x="1" y="17" width="5" height="5" fill="white"/>
+      <svg:circle cx="1" cy="17" r="5"/>
+      <svg:rect x="0" y="22" width="12" height="1" fill="white"/>
+    </svg:mask>
+    <svg:mask id="pinstripe-tab-ontop-right-curve-mask" maskContentUnits="userSpaceOnUse">
+      <svg:circle cx="3" cy="3" r="3" fill="white"/>
+      <svg:rect x="0" y="0" width="3" height="3" fill="white"/>
+      <svg:rect x="0" y="3" width="6" height="19" fill="white"/>
+      <svg:rect x="6" y="17" width="5" height="5" fill="white"/>
+      <svg:circle cx="11" cy="17" r="5"/>
+      <svg:rect x="0" y="22" width="12" height="1" fill="white"/>
+    </svg:mask>
+    <svg:mask id="pinstripe-tab-onbottom-left-curve-mask" maskContentUnits="userSpaceOnUse">
+      <svg:circle cx="9" cy="20" r="3" fill="white"/>
+      <svg:rect x="9" y="20" width="3" height="3" fill="white"/>
+      <svg:rect x="6" y="1" width="6" height="19" fill="white"/>
+      <svg:rect x="1" y="1" width="5" height="5" fill="white"/>
+      <svg:circle cx="1" cy="6" r="5"/>
+      <svg:rect x="0" y="0" width="12" height="1" fill="white"/>
+    </svg:mask>
+    <svg:mask id="pinstripe-tab-onbottom-right-curve-mask" maskContentUnits="userSpaceOnUse">
+      <svg:circle cx="3" cy="20" r="3" fill="white"/>
+      <svg:rect x="0" y="20" width="3" height="3" fill="white"/>
+      <svg:rect x="0" y="1" width="6" height="19" fill="white"/>
+      <svg:rect x="6" y="1" width="5" height="5" fill="white"/>
+      <svg:circle cx="11" cy="6" r="5"/>
+      <svg:rect x="0" y="0" width="12" height="1" fill="white"/>
+    </svg:mask>
+  </svg:svg>
+#endif
 
 </vbox>
 # <iframe id="tab-view"> is dynamically appended as the 2nd child of #tab-view-deck.
 #     Introducing the iframe dynamically, as needed, was found to be better than
 #     starting with an empty iframe here in browser.xul from a Ts standpoint.
 </deck>
 
 </window>
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -1420,23 +1420,23 @@ XPCOMUtils.defineLazyGetter(InspectorUI,
 });
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
   return Services.strings.createBundle("chrome://browser/locale/inspector.properties");
 });
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "PropertyTreeView", function () {
   var obj = {};
-  Cu.import("resource://gre/modules/PropertyPanel.jsm", obj);
+  Cu.import("resource:///modules/PropertyPanel.jsm", obj);
   return obj.PropertyTreeView;
 });
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "PropertyPanel", function () {
   var obj = {};
-  Cu.import("resource://gre/modules/PropertyPanel.jsm", obj);
+  Cu.import("resource:///modules/PropertyPanel.jsm", obj);
   return obj.PropertyPanel;
 });
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "style", function () {
   var obj = {};
   Cu.import("resource:///modules/stylePanel.jsm", obj);
   obj.style.initialize();
   return obj.style;
deleted file mode 100644
--- a/browser/base/content/pageReportFirstTime.xul
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0"?> 
-# ***** 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 Communicator client code, released
-# March 31, 1998.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998-1999
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   David Hyatt (hyatt@apple.com)
-#
-# 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 *****
-
-<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
-
-<!DOCTYPE dialog [
-  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-  %brandDTD;
-  <!ENTITY % pageReportFirstTimeDTD SYSTEM "chrome://browser/locale/pageReportFirstTime.dtd" >
-  %pageReportFirstTimeDTD;
-]>            
- 
-<dialog id="pageReportFirstTime"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="&caption.label;"
-        onload="setTimeout(function() { window.sizeToContent(); }, 100);"
-        style="width: 40em;"
-        buttons="accept"
-        persist="screenX screenY"
-        screenX="24" screenY="24">
-              
-  <description>
-    &startDescriptionText.label;
-  </description>
-
-  <separator class="thin"/>
-
-  <hbox pack="center">
-    <!-- insert example here! (bug 594294) -->
-  </hbox>
-
-  <separator class="thin"/>
-
-  <description>
-    &endDescription.label;
-  </description>
-
- </dialog>
--- a/browser/base/content/syncGenericChange.js
+++ b/browser/base/content/syncGenericChange.js
@@ -94,19 +94,20 @@ let Change = {
           document.getElementById("passphraseBackupButtons").hidden = true;
           document.getElementById("generatePassphraseButton").hidden = true;
           document.title = this._str("new.synckey.title");
           introText.textContent = this._str("new.synckey.introText");
           this._dialog.getButton("accept")
               .setAttribute("label", this._str("new.synckey.acceptButton"));
         }
         else {
+          this._passphraseBox.setAttribute("readonly", "true");
           let pp = Weave.Service.passphrase;
-          if (pp.length == 20)
-            pp = gSyncUtils.hyphenatePassphrase(pp);
+          if (Weave.Utils.isPassphrase(pp))
+             pp = Weave.Utils.hyphenatePassphrase(pp);
           document.getElementById("passphraseBox").value = pp;
           document.title = this._str("change.synckey.title");
           introText.innerHTML = this._str("change.synckey.introText");
           introText2.innerHTML = this._str("change.synckey.introText2");
           warningText.innerHTML = this._str("change.synckey2.warningText");
           this._dialog.getButton("accept")
               .setAttribute("label", this._str("change.synckey.acceptButton"));
         }
@@ -143,17 +144,16 @@ let Change = {
   },
 
   _updateStatus: function Change__updateStatus(str, state) {
      this._updateStatusWithString(this._str(str), state);
   },
   
   _updateStatusWithString: function Change__updateStatusWithString(string, state) {
     this._statusRow.hidden = false;
-    document.getElementById("passphraseStrengthRow").hidden = true;
     this._status.value = string;
     this._statusIcon.setAttribute("status", state);
 
     let error = state == "error";
     this._dialog.getButton("cancel").setAttribute("disabled", !error);
     this._dialog.getButton("accept").setAttribute("disabled", !error);
     document.getElementById("printSyncKeyButton").disabled = !error;
     document.getElementById("saveSyncKeyButton").disabled = !error;
@@ -170,25 +170,24 @@ let Change = {
         break;
       case "ChangePassword":
         return this.doChangePassword();
         break;
     }
   },
 
   doGeneratePassphrase: function () {
-    let passphrase = gSyncUtils.generatePassphrase();
+    let passphrase = Weave.Utils.generatePassphrase();
     let el = document.getElementById("passphraseBox");
-    el.value = gSyncUtils.hyphenatePassphrase(passphrase);
-    document.getElementById("passphraseStrengthRow").hidden = true;
+    el.value = Weave.Utils.hyphenatePassphrase(passphrase);
     this._dialog.getButton("accept").disabled = false;
   },
 
   doChangePassphrase: function Change_doChangePassphrase() {
-    let pp = gSyncUtils.normalizePassphrase(this._passphraseBox.value);
+    let pp = Weave.Utils.normalizePassphrase(this._passphraseBox.value);
     if (this._updatingPassphrase) {
       Weave.Service.passphrase = pp;
       if (Weave.Service.login()) {
         this._updateStatus("change.synckey2.success", "success");
         Weave.Service.persistLogin();
       }
       else {
         this._updateStatus("new.passphrase.status.incorrect", "error");
@@ -224,56 +223,42 @@ let Change = {
         this._updateStatus("change.password.status.success", "success");
       else
         this._updateStatus("change.password.status.error", "error");
     }
 
     return false;
   },
 
-  validate: function () {
+  validate: function (event) {
     let valid = false;
     let errorString = "";
 
     if (this._dialogType == "ChangePassword") {
       if (this._currentPasswordInvalid)
         [valid, errorString] = gSyncUtils.validatePassword(this._firstBox);
       else
         [valid, errorString] = gSyncUtils.validatePassword(this._firstBox, this._secondBox);
     }
     else {
-      if (this._updatingPassphrase) {
-        [valid, errorString] = gSyncUtils.validatePassphrase(this._passphraseBox);
-      } else {
-        [valid, errorString] = gSyncUtils.validatePassphrase(this._passphraseBox, true);
-        if (valid)
-          this.displayPassphraseStrength();
-      }
+      if (!this._updatingPassphrase)
+        return;
+
+      if (event.keyCode != event.DOM_VK_BACK_SPACE) {
+        this._passphraseBox.value = Weave.Utils.hyphenatePartialPassphrase(
+          this._passphraseBox.value);
+       }
+      valid = Weave.Utils.isPassphrase(this._passphraseBox.value);
     }
 
     if (errorString == "")
       this._clearStatus();
     else
       this._updateStatusWithString(errorString, "error");
 
     this._statusRow.hidden = valid;
     this._dialog.getButton("accept").disabled = !valid;
   },
 
-  displayPassphraseStrength: function () {
-    let bits = Weave.Utils.passphraseStrength(this._passphraseBox.value);
-    let meter = document.getElementById("passphraseStrength");
-    meter.value = bits;
-    // The generated 20 character passphrase has an entropy of 94 bits
-    // which we consider "strong".
-    if (bits > 94)
-      meter.className = "strong";
-    else if (bits > 47)
-      meter.className = "medium";
-    else
-      meter.className = "";
-    document.getElementById("passphraseStrengthRow").hidden = false;
-  },
-
   _str: function Change__string(str) {
     return this._stringBundle.GetStringFromName(str);
   }
 };
--- a/browser/base/content/syncGenericChange.xul
+++ b/browser/base/content/syncGenericChange.xul
@@ -93,50 +93,31 @@
           <label id="textBox2Label" control="textBox2"/>
           <textbox id="textBox2" type="password" oninput="Change.validate()"/>
           <spacer/>
         </row>
         <row id="passphraseRow" align="center">
           <label id="passphraseLabel" control="passphraseBox"/>
           <textbox id="passphraseBox"
                    onfocus="this.select()"
-                   oninput="Change.validate()"/>
+                   onkeyup="Change.validate(event)"/>
           <label id="generatePassphraseButton"
                  value="&syncKeyGenerate.label;"
                  class="text-link inline-link"
                  onclick="event.stopPropagation();
                           Change.doGeneratePassphrase();"/>
         </row>
       </rows>
     </grid>
 
     <vbox id="feedback" pack="center">
       <hbox id="statusRow" align="center">
         <image id="statusIcon" class="statusIcon"/>
         <label id="status" class="status" value=" "/>
       </hbox>
-
-      <vbox id="passphraseStrengthRow" hidden="true" pack="end">
-        <hbox>
-          <label id="passphraseStrengthLabel"
-                 control="passphraseStrength"
-                 value="&syncKeyStrength.label;"/>
-          <progressmeter id="passphraseStrength"
-                         aria-labelledby="passphraseStrengthLabel"
-                         max="128"
-                         value="0"
-                         flex="1"/>
-        </hbox>
-        <hbox align="right" flex="1">
-          <label class="text-link inline-link"
-                 onclick="event.stopPropagation();
-                          gSyncUtils.openSyncKeyHelp();"
-                 value="&syncKeyHelp.label;"/>
-        </hbox>
-      </vbox>
     </vbox>
 
     <hbox id="passphraseBackupButtons" pack="center">
       <button id="printSyncKeyButton"
               label="&button.syncKeyBackup.print.label;"
               accesskey="&button.syncKeyBackup.print.accesskey;"
               oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/>
       <button id="saveSyncKeyButton"
--- a/browser/base/content/syncSetup.js
+++ b/browser/base/content/syncSetup.js
@@ -70,17 +70,16 @@ var gSyncSetup = {
   _disabledSites: [],
   _remoteSites: [Weave.Service.serverURL, "https://api-secure.recaptcha.net"],
 
   status: {
     password: false,
     email: false,
     server: false
   },
-  _haveSyncKeyBackup: false,
 
   get _usingMainServers() {
     if (this._settingUpNew)
       return document.getElementById("serverType").selectedItem.value == "main";
 
     return document.getElementById("existingServerType").selectedItem.value == "main";
   },
 
@@ -138,17 +137,18 @@ var gSyncSetup = {
   },
 
   useExistingAccount: function () {
     this._settingUpNew = false;
     this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
   },
 
   onResetPassphrase: function () {
-    document.getElementById("existingPassphrase").value = Weave.Service.passphrase;
+    document.getElementById("existingPassphrase").value = 
+      Weave.Utils.hyphenatePassphrase(Weave.Service.passphrase);
     this.wizard.advance();
   },
 
   onLoginStart: function () {
     this.toggleLoginFeedback(false);
   },
 
   onLoginEnd: function () {
@@ -194,16 +194,24 @@ var gSyncSetup = {
         // otherwise, fall through
       case "wipeClient":
       case "wipeRemote":
         Weave.Svc.Prefs.set("firstSync", action);
         break;
     }
   },
 
+  onPassphraseKeyUp: function (event) {
+    if (event.keyCode != event.DOM_VK_BACK_SPACE) {
+      let el = event.target;
+      el.value = Weave.Utils.hyphenatePartialPassphrase(el.value);
+    }
+    this.checkFields();
+  },
+
   // fun with validation!
   checkFields: function () {
     this.wizard.canAdvance = this.readyToAdvance();
   },
 
   readyToAdvance: function () {
     switch (this.wizard.pageIndex) {
       case INTRO_PAGE:
@@ -212,31 +220,30 @@ var gSyncSetup = {
         for (i in this.status) {
           if (!this.status[i])
             return false;
         }
         if (this._usingMainServers)
           return document.getElementById("tos").checked;
 
         return true;
-      case NEW_ACCOUNT_PP_PAGE:
-        return this._haveSyncKeyBackup && this.checkPassphrase();
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         let hasUser = document.getElementById("existingAccountName").value != "";
         let hasPass = document.getElementById("existingPassword").value != "";
         if (hasUser && hasPass) {
           if (this._usingMainServers)
             return true;
 
           if (this._validateServer(document.getElementById("existingServerURL"), false))
             return true;
         }
         return false;
       case EXISTING_ACCOUNT_PP_PAGE:
-        return document.getElementById("existingPassphrase").value != "";
+        let el = document.getElementById("existingPassphrase");
+        return Weave.Utils.isPassphrase(el.value);
     }
     // we probably shouldn't get here
     return true;
   },
 
   onEmailInput: function () {
     // Check account validity when the user stops typing for 1 second.
     if (this._checkAccountTimer)
@@ -295,90 +302,21 @@ var gSyncSetup = {
 
     let feedback = document.getElementById("passwordFeedbackRow");
     this._setFeedback(feedback, valid, errorString);
 
     this.status.password = valid;
     this.checkFields();
   },
 
-  onPassphraseChange: function () {
-    // Ignore if there's no actual change from the generated one.
-    let el = document.getElementById("weavePassphrase");
-    if (gSyncUtils.normalizePassphrase(el.value) == Weave.Service.passphrase) {
-      el = document.getElementById("generatePassphraseButton");
-      el.hidden = true;
-      this._haveCustomSyncKey = false;
-      return;
-    }
-
-    this._haveSyncKeyBackup = true;
-    this._haveCustomSyncKey = true;
-    el = document.getElementById("generatePassphraseButton");
-    el.hidden = false;
-    this.checkFields();
-  },
-
   onPassphraseGenerate: function () {
-    let passphrase = gSyncUtils.generatePassphrase();
+    let passphrase = Weave.Utils.generatePassphrase();
     Weave.Service.passphrase = passphrase;
     let el = document.getElementById("weavePassphrase");
-    el.value = gSyncUtils.hyphenatePassphrase(passphrase);
-
-    el = document.getElementById("generatePassphraseButton");
-    el.hidden = true;
-    document.getElementById("passphraseStrengthRow").hidden = true;
-    let feedback = document.getElementById("passphraseFeedbackRow");
-    this._setFeedback(feedback, true, "");
-  },
-
-  afterBackup: function () {
-    this._haveSyncKeyBackup = true;
-    this.checkFields();
-  },
-
-  checkPassphrase: function () {
-    let el1 = document.getElementById("weavePassphrase");
-    let valid, str;
-    // xxxmpc - hack, sigh
-    if (el1.value == document.getElementById("weavePassword").value) {
-      valid = false;
-      str = Weave.Utils.getErrorString("change.synckey.sameAsPassword");
-    }
-    else {
-      [valid, str] = gSyncUtils.validatePassphrase(el1);
-    }
-
-    let feedback = document.getElementById("passphraseFeedbackRow");
-    this._setFeedback(feedback, valid, str);
-    if (!valid) {
-      // Hide strength meter if we're displaying an error.
-      document.getElementById("passphraseStrengthRow").hidden = true;
-      return valid;
-    }
-
-    // No passphrase strength meter for the generated key.
-    if (!this._haveCustomSyncKey)
-      return valid;
-
-    // Display passphrase strength
-    let pp = document.getElementById("weavePassphrase").value;
-    let bits = Weave.Utils.passphraseStrength(pp);
-    let meter = document.getElementById("passphraseStrength");
-    meter.value = bits;
-    // The generated 20 character passphrase has an entropy of 94 bits
-    // which we consider "strong".
-    if (bits > 94)
-      meter.className = "strong";
-    else if (bits > 47)
-      meter.className = "medium";
-    else
-      meter.className = "";
-    document.getElementById("passphraseStrengthRow").hidden = false;
-    return valid;
+    el.value = Weave.Utils.hyphenatePassphrase(passphrase);
   },
 
   onPageShow: function() {
     switch (this.wizard.pageIndex) {
       case INTRO_PAGE:
         this.wizard.getButton("next").hidden = true;
         this.wizard.getButton("back").hidden = true;
         this.wizard.getButton("extra1").hidden = true;
@@ -396,16 +334,19 @@ var gSyncSetup = {
         // fall through
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         this.checkFields();
         this.wizard.getButton("next").hidden = false;
         this.wizard.getButton("back").hidden = false;
         this.wizard.getButton("extra1").hidden = false;
         this.wizard.canRewind = true;
         break;
+      case EXISTING_ACCOUNT_PP_PAGE:
+        this.checkFields();
+        break;
       case SETUP_SUCCESS_PAGE:
         this.wizard.canRewind = false;
         this.wizard.getButton("back").hidden = true;
         this.wizard.getButton("next").hidden = true;
         this.wizard.getButton("cancel").hidden = true;
         this.wizard.getButton("finish").hidden = false;
         this._handleSuccess();
         break;
@@ -481,41 +422,41 @@ var gSyncSetup = {
           this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
           return false;
         }
 
         image.setAttribute("status", "error");
         label.value = Weave.Utils.getErrorString(error);
         return false;
       case NEW_ACCOUNT_PP_PAGE:
-        if (this._haveCustomSyncKey)
-          Weave.Service.passphrase = document.getElementById("weavePassphrase").value;
         // Time to load the captcha.
         // First check for NoScript and whitelist the right sites.
         this._handleNoScript(true);
         this.captchaBrowser.loadURI(Weave.Service.miscAPI + "captcha_html");
         break;
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         Weave.Service.account = document.getElementById("existingAccountName").value;
         Weave.Service.password = document.getElementById("existingPassword").value;
-        Weave.Service.passphrase = document.getElementById("existingPassphrase").value;
+        Weave.Service.passphrase = Weave.Utils.normalizePassphrase(
+            document.getElementById("existingPassphrase").value);
+
         // verifyLogin() will likely return false because we probably don't
         // have a passphrase yet (unless the user already entered it
         // and hit the back button).
         if (!Weave.Service.verifyLogin()
             && Weave.Status.login != Weave.LOGIN_FAILED_NO_PASSPHRASE
             && Weave.Status.login != Weave.LOGIN_FAILED_INVALID_PASSPHRASE) {
           let feedback = document.getElementById("existingPasswordFeedbackRow");
           this._setFeedbackMessage(feedback, false, Weave.Status.login);
           return false;
         }
         break;
       case EXISTING_ACCOUNT_PP_PAGE:
         let pp = document.getElementById("existingPassphrase").value;
-        Weave.Service.passphrase = gSyncUtils.normalizePassphrase(pp);
+        Weave.Service.passphrase = Weave.Utils.normalizePassphrase(pp);
         if (Weave.Service.login())
           this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
         return false;
       case OPTIONS_PAGE:
         let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
         // No confirmation needed on new account setup or merge option
         // with existing account.
         if (this._settingUpNew || (!this._resettingSync && desc == 0))
--- a/browser/base/content/syncSetup.xul
+++ b/browser/base/content/syncSetup.xul
@@ -198,76 +198,35 @@
               onextra1="gSyncSetup.onSyncOptions()"
               onpageshow="gSyncSetup.onPageShow();">
     <description>
       &setup.newSyncKeyPage.description.label;
     </description>
     <spacer/>
 
     <groupbox>
-      <hbox>
-        <label value="&syncKeyEntry.label;"
-               accesskey="&syncKeyEntry.accesskey;"
-               control="weavePassphrase"/>
-        <spacer flex="1" />
-        <label id="generatePassphraseButton"
-               value="&syncKeyGenerate.label;"
-               class="text-link inline-link"
-               onclick="event.stopPropagation();
-                        gSyncSetup.onPassphraseGenerate();"/>
-      </hbox>
+      <label value="&syncKeyEntry.label;"
+             accesskey="&syncKeyEntry.accesskey;"
+             control="weavePassphrase"/>
       <textbox id="weavePassphrase"
-               onkeyup="gSyncSetup.onPassphraseChange()"
-               onchange="gSyncSetup.onPassphraseChange()"
-               onfocus="this.select();
-                        gSyncSetup.afterBackup();"/>
-
-      <vbox id="passphraseFeedback" pack="center">
-        <hbox id="passphraseFeedbackRow" hidden="true" align="center">
-          <spacer/>
-          <hbox>
-            <image class="statusIcon"/>
-            <label class="status" value=" "/>
-          </hbox>
-        </hbox>
-
-        <vbox id="passphraseStrengthRow" hidden="true" pack="end">
-          <hbox>
-            <label id="passphraseStrengthLabel"
-                   control="passphraseStrength"
-                   value="&syncKeyStrength.label;"/>
-            <progressmeter id="passphraseStrength"
-                           aria-labelledby="passphraseStrengthLabel"
-                           max="128"
-                           value="0"
-                           flex="1"/>
-          </hbox>
-          <hbox align="right" flex="1">
-            <label class="text-link inline-link"
-                   onclick="event.stopPropagation();
-                            gSyncUtils.openSyncKeyHelp();"
-                   value="&syncKeyHelp.label;"/>
-          </hbox>
-        </vbox>
-      </vbox>
+               readonly="true"
+               onfocus="this.select();"/>
     </groupbox>
 
     <groupbox align="center">
       <description>&syncKeyBackup.description;</description>
       <hbox>
         <button id="printSyncKeyButton"
                 label="&button.syncKeyBackup.print.label;"
                 accesskey="&button.syncKeyBackup.print.accesskey;"
-                oncommand="gSyncUtils.passphrasePrint('weavePassphrase');
-                           gSyncSetup.afterBackup();"/>
+                oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/>
         <button id="saveSyncKeyButton"
                 label="&button.syncKeyBackup.save.label;"
                 accesskey="&button.syncKeyBackup.save.accesskey;"
-                oncommand="gSyncUtils.passphraseSave('weavePassphrase');
-                           gSyncSetup.afterBackup();"/>
+                oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/>
       </hbox>
     </groupbox>
   </wizardpage>
 
   <wizardpage label="&setup.captchaPage2.title.label;"
               onextra1="gSyncSetup.onSyncOptions()">
     <vbox flex="1" align="center">
       <browser height="150"
@@ -355,22 +314,23 @@
               <label value="&connecting.label;"/>
           </hbox>
           </row>
         </rows>
       </grid>
   </wizardpage>
 
   <wizardpage id="existingPassphraseEntry"
+              label="&setup.existingSyncKeyPage.title;"
               onextra1="gSyncSetup.onSyncOptions()"
-              label="&setup.existingSyncKeyPage.title;">
+              onpageshow="gSyncSetup.onPageShow()">
     <description>&setup.existingSyncKeyPage.description;</description>
     <textbox id="existingPassphrase"
-             onkeyup="gSyncSetup.checkFields(event)"
-             onchange="gSyncSetup.checkFields(event)"/>
+             onkeyup="gSyncSetup.onPassphraseKeyUp(event)"
+             onchange="gSyncSetup.checkFields()"/>
     <hbox id="passphrase-throbber" hidden="true">
       <image/>
       <label value="&verifying.label;"/>
     </hbox>
     <hbox align="left" id="existingPassphraseFeedbackBox">
       <spacer/>
       <hbox>
         <image class="statusIcon"/>
--- a/browser/base/content/syncUtils.js
+++ b/browser/base/content/syncUtils.js
@@ -100,65 +100,30 @@ let gSyncUtils = {
   openToS: function () {
     this._openLink(Weave.Svc.Prefs.get("termsURL"));
   },
 
   openPrivacyPolicy: function () {
     this._openLink(Weave.Svc.Prefs.get("privacyURL"));
   },
 
-  openSyncKeyHelp: function () {
-    this._openLink(Weave.Svc.Prefs.get("syncKeyHelpURL"));
-  },
-
   // xxxmpc - fix domain before 1.3 final (bug 583652)
   _baseURL: "http://www.mozilla.com/firefox/sync/",
 
   openFirstClientFirstrun: function () {
     let url = this._baseURL + "firstrun.html";
     this._openLink(url);
   },
 
   openAddedClientFirstrun: function () {
     let url = this._baseURL + "secondrun.html";
     this._openLink(url);
   },
 
   /**
-   * Generate 20 random characters a-z
-   */
-  generatePassphrase: function() {
-    let rng = Cc["@mozilla.org/security/random-generator;1"]
-                .createInstance(Ci.nsIRandomGenerator);
-    let bytes = rng.generateRandomBytes(20);
-    return [String.fromCharCode(97 + Math.floor(byte * 26 / 256))
-            for each (byte in bytes)].join("");
-  },
-
-  /**
-   * Hyphenate a 20 character passphrase in 4 groups of 5.
-   */
-  hyphenatePassphrase: function(passphrase) {
-    return passphrase.slice(0, 5) + '-'
-         + passphrase.slice(5, 10) + '-'
-         + passphrase.slice(10, 15) + '-'
-         + passphrase.slice(15, 20);
-  },
-
-  /**
-   * Remove hyphens as inserted by hyphenatePassphrase().
-   */
-  normalizePassphrase: function(pp) {
-    if (pp.length == 23 && pp[5] == '-' && pp[11] == '-' && pp[17] == '-')
-      return pp.slice(0, 5) + pp.slice(6, 11)
-           + pp.slice(12, 17) + pp.slice(18, 23);
-    return pp;
-  },
-
-  /**
    * Prepare an invisible iframe with the passphrase backup document.
    * Used by both the print and saving methods.
    *
    * @param elid : ID of the form element containing the passphrase.
    * @param callback : Function called once the iframe has loaded.
    */
   _preparePPiframe: function(elid, callback) {
     let pp = document.getElementById(elid).value;
@@ -279,41 +244,10 @@ let gSyncUtils = {
         valid = true;
       else if (val1.length < Weave.MIN_PASS_LENGTH)
         error = "change.password.tooShort";
       else if (val1 != val2)
         error = "change.password.mismatch";
     }
     let errorString = error ? Weave.Utils.getErrorString(error) : "";
     return [valid, errorString];
-  },
-
-  /**
-   * validatePassphrase
-   *
-   * @param el : the textbox element
-   * @param change : indicate whether this signifies a passphrase change
-   * 
-   * returns [valid, errorString]
-   */
-  validatePassphrase: function (el, change) {
-    let valid = false;
-    let val = el.value;
-    let error = "";
-
-    if (val.length < Weave.MIN_PP_LENGTH)
-      error = "change.synckey.tooShort";
-    else if (val == Weave.Service.username)
-      error = "change.synckey.sameAsUsername";
-    else if (val == Weave.Service.account)
-      error = "change.synckey.sameAsEmail";
-    else if (val == Weave.Service.password)
-      error = "change.synckey.sameAsPassword";
-    else if (change && val == Weave.Service.passphrase)
-      error = "change.synckey.sameAsSyncKey";
-    else
-      valid = true;
-
-    let errorString = error ? Weave.Utils.getErrorString(error) : "";
-    return [valid, errorString];
   }
-}
-
+};
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -35,15 +35,15 @@
 }
 
 tabpanels {
   background-color: white;
 }
 
 .tab-drop-indicator {
   position: relative;
-  z-index: 1;
+  z-index: 2;
 }
 
 .tab-throbber:not([busy]),
 .tab-throbber[busy] + .tab-icon-image {
   display: none;
 }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1305,23 +1305,24 @@
               let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
               if (aAllowThirdPartyFixup)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
               if (aFromExternal)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
               try {
                 b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
 
-                // We start our browsers out as inactive, and then maintain
-                // activeness in the tab switcher.
-                b.docShell.isActive = false;
               }
               catch (ex) { }
             }
 
+            // We start our browsers out as inactive, and then maintain
+            // activeness in the tab switcher.
+            b.docShell.isActive = false;
+
             // Check if we're opening a tab related to the current tab and
             // move it to after the current tab.
             // aReferrerURI is null or undefined if the tab is opened from
             // an external application or bookmark, i.e. somewhere other
             // than the current tab.
             if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
                 Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
               let newTabPos = (this._lastRelatedTab ||
@@ -1441,17 +1442,17 @@
             if (!this._beginRemoveTab(aTab, false, null, true))
               return;
 
             if (!animate /* the caller didn't opt in */ ||
                 isLastTab ||
                 aTab.pinned ||
                 this._removingTabs.length > 3 /* don't want lots of concurrent animations */ ||
                 aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
-                window.getComputedStyle(aTab).maxWidth == "1px" /* fade-in transition hasn't moved yet */ ||
+                window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ ||
                 !Services.prefs.getBoolPref("browser.tabs.animate")) {
               this._endRemoveTab(aTab);
               return;
             }
 
             this._blurTab(aTab);
             aTab.removeAttribute("fadein");
           ]]>
@@ -1889,31 +1890,33 @@
       </method>
 
       <method name="showTab">
         <parameter name="aTab"/>
         <body>
         <![CDATA[
           if (aTab.hidden) {
             aTab.removeAttribute("hidden");
+            this.tabContainer.adjustTabstrip();
             let event = document.createEvent("Events");
             event.initEvent("TabShow", true, false);
             aTab.dispatchEvent(event);
           }
         ]]>
         </body>
       </method>
 
       <method name="hideTab">
         <parameter name="aTab"/>
         <body>
         <![CDATA[
           if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
               this._removingTabs.indexOf(aTab) == -1) {
             aTab.setAttribute("hidden", "true");
+            this.tabContainer.adjustTabstrip();
             let event = document.createEvent("Events");
             event.initEvent("TabHide", true, false);
             aTab.dispatchEvent(event);
           }
         ]]>
         </body>
       </method>
 
@@ -2777,22 +2780,30 @@
 
           if (pinnedOnly)
             this.tabbrowser.tabContainer.setAttribute("pinnedonly", "true");
           else
             this.tabbrowser.tabContainer.removeAttribute("pinnedonly");
 
           var scrollButtonWidth = (this.getAttribute("overflow") != "true" || pinnedOnly) ? 0 :
                                   this.mTabstrip._scrollButtonDown.scrollWidth;
+          var paddingStart = this.mTabstrip.scrollboxPaddingStart;
+
           for (var i = this.tabbrowser._numPinnedTabs - 1; i >= 0; i--) {
             let tab = this.childNodes[i];
             width += pinnedOnly ? 0 : tab.scrollWidth;
-            tab.style.MozMarginStart = - (width + scrollButtonWidth) + "px";
+            if (this.getAttribute("overflow") != "true")
+              tab.style.MozMarginStart = - (width + scrollButtonWidth) + "px";
+            else
+              tab.style.MozMarginStart = - (width + scrollButtonWidth + paddingStart) + "px";
           }
-          this.style.MozMarginStart = width + "px";
+          if (width == 0 || this.getAttribute("overflow") != "true")
+            this.style.MozMarginStart = width + "px";
+          else
+            this.style.MozMarginStart = width + paddingStart + "px";
           this.mTabstrip.ensureElementIsVisible(this.selectedItem, false);
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
@@ -3278,17 +3289,17 @@
             return;
         }
 
         var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
         this.tabbrowser.replaceTabWithWindow(draggedTab);
         event.stopPropagation();
       ]]></handler>
 
-      <handler event="dragleave"><![CDATA[
+      <handler event="dragexit"><![CDATA[
         this._dragTime = 0;
 
         // This does not work at all (see bug 458613)
         var target = event.relatedTarget;
         while (target && target != this)
           target = target.parentNode;
         if (target)
           return;
@@ -3373,17 +3384,27 @@
   <binding id="tabbrowser-tab" display="xul:hbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content context="tabContextMenu" closetabtext="&closeTab.label;">
       <xul:stack class="tab-stack" flex="1">
-        <xul:hbox class="tab-content" align="center">
+        <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+                  class="tab-background">
+          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+                    class="tab-background-start"/>
+          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+                    class="tab-background-middle"/>
+          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+                    class="tab-background-end"/>
+        </xul:hbox>
+        <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+                  class="tab-content" align="center">
           <xul:image xbl:inherits="fadein,pinned,busy,progress,selected"
                      class="tab-throbber"
                      role="presentation"/>
           <xul:image xbl:inherits="validate,src=image,fadein,pinned,selected"
                      class="tab-icon-image"
                      role="presentation"/>
           <xul:label flex="1"
                      xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected"
--- a/browser/base/content/tabview/drag.js
+++ b/browser/base/content/tabview/drag.js
@@ -62,23 +62,21 @@ var resize = {
 // ----------
 // Constructor: Drag
 // Called to create a Drag in response to an <Item> draggable "start" event.
 // Note that it is also used partially during <Item>'s resizable method as well.
 //
 // Parameters:
 //   item - The <Item> being dragged
 //   event - The DOM event that kicks off the drag
-//   isResizing - (boolean) is this a resizing instance? or (if false) dragging?
 //   isFauxDrag - (boolean) true if a faux drag, which is used when simply snapping.
-function Drag(item, event, isResizing, isFauxDrag) {
+function Drag(item, event, isFauxDrag) {
   Utils.assert(item && (item.isAnItem || item.isAFauxItem), 
       'must be an item, or at least a faux item');
 
-  this.isResizing = isResizing || false;
   this.item = item;
   this.el = item.container;
   this.$el = iQ(this.el);
   this.parent = this.item.parent;
   this.startPosition = new Point(event.clientX, event.clientY);
   this.startTime = Date.now();
 
   this.item.isDragging = true;
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -85,24 +85,16 @@ function GroupItem(listOfEls, options) {
   this.fadeAwayUndoButtonDuration = 300;
 
   this.keepProportional = false;
 
   // Variable: _activeTab
   // The <TabItem> for the groupItem's active tab.
   this._activeTab = null;
 
-  // Variables: xDensity, yDensity
-  // "density" ranges from 0 to 1, with 0 being "not dense" = "squishable" and 1 being "dense"
-  // = "not squishable". For example, if there is extra space in the vertical direction,
-  // yDensity will be < 1. These are set by <GroupItem.arrange>, as it is dependent on the tab items
-  // inside the groupItem.
-  this.xDensity = 0;
-  this.yDensity = 0;
-
   if (Utils.isPoint(options.userSize))
     this.userSize = new Point(options.userSize);
 
   var self = this;
 
   var rectToBe;
   if (options.bounds) {
     Utils.assert(Utils.isRect(options.bounds), "options.bounds must be a Rect");
@@ -143,17 +135,17 @@ function GroupItem(listOfEls, options) {
   this.$resizer = iQ("<div>")
     .addClass('resizer')
     .appendTo($container)
     .hide();
 
   // ___ Titlebar
   var html =
     "<div class='title-container'>" +
-      "<input class='name'/>" +
+      "<input class='name' />" +
       "<div class='title-shield' />" +
     "</div>";
 
   this.$titlebar = iQ('<div>')
     .addClass('titlebar')
     .html(html)
     .appendTo($container);
 
@@ -560,35 +552,38 @@ GroupItem.prototype = Utils.extend(new I
   },
 
   // ----------
   // Function: close
   // Closes the groupItem, removing (but not closing) all of its children.
   close: function GroupItem_close() {
     this.removeAll();
     GroupItems.unregister(this);
-    this._sendToSubscribers("close");
-    this.removeTrenches();
 
     if (this.hidden) {
       iQ(this.container).remove();
       if (this.$undoContainer) {
         this.$undoContainer.remove();
         this.$undoContainer = null;
        }
+      this.removeTrenches();
       Items.unsquish();
+      this._sendToSubscribers("close");
     } else {
+      let self = this;
       iQ(this.container).animate({
         opacity: 0,
         "-moz-transform": "scale(.3)",
       }, {
         duration: 170,
         complete: function() {
           iQ(this).remove();
+          self.removeTrenches();
           Items.unsquish();
+          self._sendToSubscribers("close");
         }
       });
     }
     this.deleteData();
   },
 
   // ----------
   // Function: closeAll
@@ -981,27 +976,41 @@ GroupItem.prototype = Utils.extend(new I
   // Removes all of the groupItem's children.
   removeAll: function GroupItem_removeAll() {
     var self = this;
     var toRemove = this._children.concat();
     toRemove.forEach(function(child) {
       self.remove(child, {dontArrange: true});
     });
   },
+  
+  // ----------
+  // Handles error event for loading app tab's fav icon.
+  _onAppTabError : function(event) {
+    iQ(".appTabIcon", this.$appTabTray).each(function(icon) {
+      let $icon = iQ(icon);
+      if ($icon.data("xulTab") == event.target) {
+        $icon.attr("src", Utils.defaultFaviconURL);
+        return true;
+      }
+    });
+  },
 
   // ----------
   // Adds the given xul:tab as an app tab in this group's apptab tray
   addAppTab: function GroupItem_addAppTab(xulTab) {
     let self = this;
 
+    xulTab.addEventListener("error", this._onAppTabError, false);
+
     // add the icon
-    let icon = xulTab.image || Utils.defaultFaviconURL;
+    let iconUrl = xulTab.image || Utils.defaultFaviconURL;
     let $appTab = iQ("<img>")
       .addClass("appTabIcon")
-      .attr("src", icon)
+      .attr("src", iconUrl)
       .data("xulTab", xulTab)
       .appendTo(this.$appTabTray)
       .click(function(event) {
         if (Utils.isRightClick(event))
           return;
 
         GroupItems.setActiveGroupItem(self);
         UI.goToTab(iQ(this).data("xulTab"));
@@ -1027,16 +1036,18 @@ GroupItem.prototype = Utils.extend(new I
       $icon.remove();
     });
     
     // adjust the tray
     if (!iQ(".appTabIcon", this.$appTabTray).length) {
       this.$appTabTray.css({width: 0});
       this.arrange();
     }
+
+    xulTab.removeEventListener("error", this._onAppTabError, false);
   },
 
   // ----------
   // Function: hideExpandControl
   // Hide the control which expands a stacked groupItem into a quick-look view.
   hideExpandControl: function GroupItem_hideExpandControl() {
     this.$expander.hide();
   },
@@ -1095,57 +1106,46 @@ GroupItem.prototype = Utils.extend(new I
       Items.arrange(this._children, box, Utils.extend({}, options, {z: 99999}));
     } else {
       var bb = this.getContentBounds();
       if (!this.shouldStack()) {
         if (!options)
           options = {};
 
         this._children.forEach(function(child) {
-            child.removeClass("stacked")
+          child.removeClass("stacked")
         });
 
         this.topChild = null;
 
-        if (!this._children.length) {
-          this.xDensity = 0;
-          this.yDensity = 0;
+        if (!this._children.length)
           return;
-        }
 
         var arrangeOptions = Utils.copy(options);
         Utils.extend(arrangeOptions, {
           columns: this._columns
         });
 
         // Items.arrange will rearrange the children, but also return an array
         // of the Rect's used.
 
         var rects = Items.arrange(this._children, bb, arrangeOptions);
 
-        // yDensity = (the distance of the bottom of the last tab to the top of the content area)
-        // / (the total available content height)
-        this.yDensity = (rects[rects.length - 1].bottom - bb.top) / (bb.height);
-
-        // xDensity = (the distance from the left of the content area to the right of the rightmost
-        // tab) / (the total available content width)
-
         // first, find the right of the rightmost tab! luckily, they're in order.
         var rightMostRight = 0;
         if (UI.rtl) {
           rightMostRight = rects[0].right;
         } else {
           for each (var rect in rects) {
             if (rect.right > rightMostRight)
               rightMostRight = rect.right;
             else
               break;
           }
         }
-        this.xDensity = (rightMostRight - bb.left) / (bb.width);
 
         this._isStacked = false;
       } else
         this._stackArrange(bb, options);
     }
 
     if (this._isStacked && !this.expanded) this.showExpandControl();
     else this.hideExpandControl();
@@ -1186,23 +1186,19 @@ GroupItem.prototype = Utils.extend(new I
     var bbAspect = bb.height / bb.width;
 
     // compute h and w. h and w are the dimensions of each of the tabs... in other words, the
     // height and width of the entire stack, modulo rotation.
     if (bbAspect > itemAspect) { // Tall, thin groupItem
       w = bb.width * scale;
       h = w * itemAspect;
       // let's say one, because, even though there's more space, we're enforcing that with scale.
-      this.xDensity = 1;
-      this.yDensity = h / (bb.height * scale);
     } else { // Short, wide groupItem
       h = bb.height * scale;
       w = h * (1 / itemAspect);
-      this.yDensity = 1;
-      this.xDensity = h / (bb.width * scale);
     }
 
     // x is the left margin that the stack will have, within the content area (bb)
     // y is the vertical margin
     var x = (bb.width - w) / 2;
 
     var y = Math.min(x, (bb.height - h) / 2);
     var box = new Rect(bb.left + x, bb.top + y, w, h);
@@ -1454,42 +1450,44 @@ GroupItem.prototype = Utils.extend(new I
     this.arrange({animate: false});
     // this.arrange calls this.save for us
   },
 
   // Function: reorderTabsBasedOnTabItemOrder
   // Reorders the tabs in the tab bar based on the arrangment of the tabs
   // shown in the groupItem.
   reorderTabsBasedOnTabItemOrder: function GroupItem_reorderTabsBasedOnTabItemOrder() {
-    var tabBarTabs = Array.slice(gBrowser.tabs);
-    var currentIndex;
+    let targetIndices = null;
 
-    // ToDo: optimisation is needed to further reduce the tab move.
-    // Bug 586553
-    this._children.forEach(function(tabItem) {
-      tabBarTabs.some(function(tab, i) {
-        if (tabItem.tab == tab) {
-          if (!currentIndex)
-            currentIndex = i;
-          else if (tab.pinned)
-            currentIndex++;
-          else {
-            var removed;
-            if (currentIndex < i)
-              currentIndex = i;
-            else if (currentIndex > i) {
-              removed = tabBarTabs.splice(i, 1);
-              tabBarTabs.splice(currentIndex, 0, removed);
-              gBrowser.moveTabTo(tabItem.tab, currentIndex);
-            }
-          }
+    let self = this;
+    this._children.some(function(tabItem, index) {
+      // if no targetIndices, or it was reset, recompute
+      if (!targetIndices) {
+        let currentIndices = [tabItem.tab._tPos for each (tabItem in self._children)];
+        targetIndices = currentIndices.concat().sort();
+        // if no resorting is required, we're done!
+        if (currentIndices == targetIndices)
           return true;
-        }
-        return false;
-      });
+      }
+    
+      // Compute a target range for this tab's index
+      let originalIndex = tabItem.tab._tPos;
+      let targetRange = new Range(index ? targetIndices[index - 1] : -1,
+                                  targetIndices[index + 1] || Infinity);
+
+      // If the originalIndex of this tab is not within its target,
+      // let's move it to the targetIndex.
+      if (!targetRange.contains(originalIndex)) {
+        let targetIndex = targetIndices[index];
+        gBrowser.moveTabTo(tabItem.tab, targetIndex);
+        // force recomputing targetIndices
+        targetIndices = null;
+      }
+      
+      return false;
     });
   },
 
   // ----------
   // Function: setTopChild
   // Sets the <Item> that should be displayed on top when in stack mode.
   setTopChild: function GroupItem_setTopChild(topChild) {
     this.topChild = topChild;
@@ -1796,51 +1794,16 @@ let GroupItems = {
       if (candidate.id == a)
         result = candidate;
     });
 
     return result;
   },
 
   // ----------
-  // Function: arrange
-  // Arranges all of the groupItems into a grid.
-  arrange: function GroupItems_arrange() {
-    var bounds = Items.getPageBounds();
-    bounds.bottom -= 20; // for the dev menu
-
-    var count = this.groupItems.length - 1;
-    var columns = Math.ceil(Math.sqrt(count));
-    var rows = ((columns * columns) - count >= columns ? columns - 1 : columns);
-    var padding = 12;
-    var startX = bounds.left + padding;
-    var startY = bounds.top + padding;
-    var totalWidth = bounds.width - padding;
-    var totalHeight = bounds.height - padding;
-    var box = new Rect(startX, startY,
-        (totalWidth / columns) - padding,
-        (totalHeight / rows) - padding);
-
-    var i = 0;
-    this.groupItems.forEach(function(groupItem) {
-      if (groupItem.locked.bounds)
-        return;
-
-      groupItem.setBounds(box, true);
-
-      box.left += box.width + padding;
-      i++;
-      if (i % columns == 0) {
-        box.left = startX;
-        box.top += box.height + padding;
-      }
-    });
-  },
-
-  // ----------
   // Function: removeAll
   // Removes all tabs from all groupItems (which automatically closes all unnamed groupItems).
   removeAll: function GroupItems_removeAll() {
     var toRemove = this.groupItems.concat();
     toRemove.forEach(function(groupItem) {
       groupItem.removeAll();
     });
   },
@@ -1873,17 +1836,17 @@ let GroupItems = {
       gBrowser.visibleTabs.some(function(tab) {
         if (!tab.pinned && tab != tabItem.tab) {
           otherTab = tab;
           return true;
         }
         return false;
       });
 
-      if (otherTab) {
+      if (otherTab && otherTab.tabItem) {
         // the first visible tab belongs to a group, add the new tabItem into 
         // that group
         if (otherTab.tabItem.parent) {
           let groupItem = otherTab.tabItem.parent;
           groupItem.add(tabItem);
           this.setActiveGroupItem(groupItem);
           return;
         }
--- a/browser/base/content/tabview/items.js
+++ b/browser/base/content/tabview/items.js
@@ -218,17 +218,17 @@ Item.prototype = {
     var self = this;
     this.resizeOptions = {
       aspectRatio: self.keepProportional,
       minWidth: 90,
       minHeight: 90,
       start: function(e,ui) {
         if (this.isAGroupItem)
           GroupItems.setActiveGroupItem(this);
-        resize.info = new Drag(this, e, true); // true = isResizing
+        resize.info = new Drag(this, e);
       },
       resize: function(e,ui) {
         resize.info.snap(UI.rtl ? 'topright' : 'topleft', false, self.keepProportional);
       },
       stop: function() {
         self.setUserSize();
         self.pushAway();
         resize.info.stop();
@@ -563,19 +563,19 @@ Item.prototype = {
   // Parameters:
   //  immediately - bool for having the drag do the final positioning without animation
   snap: function Item_snap(immediately) {
     // make the snapping work with a wider range!
     var defaultRadius = Trenches.defaultRadius;
     Trenches.defaultRadius = 2 * defaultRadius; // bump up from 10 to 20!
 
     var event = {startPosition:{}}; // faux event
-    var FauxDragInfo = new Drag(this,event,false,true);
-    // false == isDragging, true == isFauxDrag
-    FauxDragInfo.snap('none',false);
+    var FauxDragInfo = new Drag(this, event, true);
+    // true == isFauxDrag
+    FauxDragInfo.snap('none', false);
     FauxDragInfo.stop(immediately);
 
     Trenches.defaultRadius = defaultRadius;
   },
 
   // ----------
   // Function: draggable
   // Enables dragging on this item. Note: not to be called multiple times on the same item!
--- a/browser/base/content/tabview/search.js
+++ b/browser/base/content/tabview/search.js
@@ -124,16 +124,26 @@ var TabUtils = {
     // because we have to deal with both tabs represented inside
     // of active Panoramas as well as for windows in which
     // Panorama has yet to be activated. We uses object sniffing to
     // determine the type of tab and then returns its name.     
     return tab.label != undefined ? tab.label : tab.nameEl.innerHTML;
   },
   
   // ---------
+  // Function: URLOf
+  // Given a <TabItem> or a <xul:tab> returns the URL of tab
+  URLOf: function TabUtils_URLOf(tab) {
+    // Convert a <TabItem> to <xul:tab>
+    if(tab.tab != undefined)
+      tab = tab.tab;
+    return tab.linkedBrowser.currentURI.spec;
+  },
+
+  // ---------
   // Function: favURLOf
   // Given a <TabItem> or a <xul:tab> returns the URL of tab's favicon.
   faviconURLOf: function TabUtils_faviconURLOf(tab) {
     return tab.image != undefined ? tab.image : tab.favImgEl.src;
   },
   
   // ---------
   // Function: focus
@@ -160,18 +170,19 @@ TabMatcher.prototype = {
   // ---------
   // Function: _filterAndSortMatches
   // Given an array of <TabItem>s and <xul:tab>s returns a new array
   // of tabs whose name matched the search term, sorted by lexical
   // closeness.  
   _filterAndSortForMatches: function TabMatcher__filterAndSortForMatches(tabs) {
     var self = this;
     tabs = tabs.filter(function(tab){
-      var name = TabUtils.nameOf(tab);
-      return name.match(self.term, "i");
+      let name = TabUtils.nameOf(tab);
+      let url = TabUtils.URLOf(tab);
+      return name.match(self.term, "i") || url.match(self.term, "i");
     });
 
     tabs.sort(function sorter(x, y){
       var yScore = scorePatternMatch(self.term, TabUtils.nameOf(y));
       var xScore = scorePatternMatch(self.term, TabUtils.nameOf(x));
       return yScore - xScore; 
     });
     
@@ -181,17 +192,18 @@ TabMatcher.prototype = {
   // ---------
   // Function: _filterForUnmatches
   // Given an array of <TabItem>s returns an unsorted array of tabs whose name
   // does not match the the search term.
   _filterForUnmatches: function TabMatcher__filterForUnmatches(tabs) {
     var self = this;
     return tabs.filter(function(tab) {
       var name = tab.nameEl.innerHTML;
-      return !name.match(self.term, "i");
+      let url = TabUtils.URLOf(tab);
+      return !name.match(self.term, "i") && !url.match(self.term, "i");
     });
   },
   
   // ---------
   // Function: _getTabsForOtherWindows
   // Returns an array of <TabItem>s and <xul:tabs>s representing that
   // tabs from all windows but the currently focused window. <TabItem>s
   // will be returned for windows in which Panorama has been activated at
@@ -254,17 +266,17 @@ TabMatcher.prototype = {
     return tabs;    
   },
   
   // ----------
   // Function: unmatched
   // Returns all of <TabItem>s that .matched() doesn't return.
   unmatched: function TabMatcher_unmatched() {
     var tabs = TabItems.getItems();
-    if ( this.term.length < 2 )
+    if (this.term.length < 2)
       return tabs;
       
     return this._filterForUnmatches(tabs);
   },
 
   // ----------
   // Function: doSearch
   // Performs the search. Lets you provide three functions.
@@ -279,17 +291,17 @@ TabMatcher.prototype = {
     var matches = this.matched();
     var unmatched = this.unmatched();
     var otherMatches = this.matchedTabsFromOtherWindows();
     
     matches.forEach(function(tab, i) {
       matchFunc(tab, i);
     });
 
-    otherMatches.forEach(function(tab,i){
+    otherMatches.forEach(function(tab,i) {
       otherFunc(tab, i+matches.length);      
     });
     
     unmatched.forEach(function(tab, i) {
       unmatchFunc(tab, i);
     });    
   }
 };
@@ -307,17 +319,18 @@ SearchEventHandlerClass.prototype = {
   // ----------
   // Function: init
   // Initializes the searchbox to be focused, and everything
   // else to be hidden, and to have everything have the appropriate
   // event handlers;
   init: function () {
     var self = this;
     iQ("#searchbox")[0].focus(); 
-    iQ("#search").hide().click(function(event) {
+    iQ("#search").hide();
+    iQ("#searchshade").hide().click(function(event) {
       if ( event.target.id != "searchbox")
         hideSearch();
     });
     
     iQ("#searchbox").keyup(function() {
       performSearch();
     });
     
@@ -347,54 +360,55 @@ SearchEventHandlerClass.prototype = {
     this.switchToInMode();
     ensureSearchShown(event);
   },
 
   // ----------
   // Function: inSearchKeyHandler
   // Handles all keypresses while search mode.
   inSearchKeyHandler: function (event) {
-    var term = iQ("#searchbox").val();
-
+    let term = iQ("#searchbox").val();
     if ((event.keyCode == event.DOM_VK_ESCAPE) || 
         (event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1)) {
       hideSearch(event);
       return;
     }
 
-    var matcher = new TabMatcher(term);
-    var matches = matcher.matched();
-    var others =  matcher.matchedTabsFromOtherWindows();
-    if (event.keyCode == event.DOM_VK_RETURN && (matches.length > 0 || others.length > 0)) {
+    let matcher = createSearchTabMacher();
+    let matches = matcher.matched();
+    let others =  matcher.matchedTabsFromOtherWindows();
+    if ((event.keyCode == event.DOM_VK_RETURN || 
+         event.keyCode == event.DOM_VK_ENTER) && 
+         (matches.length > 0 || others.length > 0)) {
       hideSearch(event);
       if (matches.length > 0) 
         matches[0].zoomIn();
       else
         TabUtils.focus(others[0]);
     }
   },
 
   // ----------
   // Function: switchToBeforeMode
   // Make sure the event handlers are appropriate for
   // the before-search mode. 
   switchToBeforeMode: function switchToBeforeMode() {
-    var self = this;
+    let self = this;
     if (this.currentHandler)
       iQ(window).unbind("keypress", this.currentHandler);
     this.currentHandler = function(event) self.beforeSearchKeyHandler(event);
     iQ(window).keypress(this.currentHandler);
   },
   
   // ----------
   // Function: switchToInMode
   // Make sure the event handlers are appropriate for
   // the in-search mode.   
   switchToInMode: function switchToInMode() {
-    var self = this;
+    let self = this;
     if (this.currentHandler)
       iQ(window).unbind("keypress", this.currentHandler);
     this.currentHandler = function(event) self.inSearchKeyHandler(event);
     iQ(window).keypress(this.currentHandler);
   }
 };
 
 var TabHandlers = {
@@ -423,17 +437,17 @@ var TabHandlers = {
      .unbind("mouseup", TabHandlers._showHandler);
   },
   
   onOther: function(tab, index){
     // Unlike the other on* functions, in this function tab can
     // either be a <TabItem> or a <xul:tab>. In other functions
     // it is always a <TabItem>. Also note that index is offset
     // by the number of matches within the window.
-    var item = iQ("<div/>")
+    let item = iQ("<div/>")
       .addClass("inlineMatch")
       .click(function(event){
         hideSearch(event);
         TabUtils.focus(tab);
       });
     
     iQ("<img/>")
       .attr("src", TabUtils.faviconURLOf(tab) )
@@ -445,45 +459,52 @@ var TabHandlers = {
       
     index != 0 ? item.addClass("notMainMatch") : item.removeClass("notMainMatch");      
     item.appendTo("#results");
     iQ("#otherresults").show();    
   },
   
   _hideHandler: function(event){
     iQ("#search").fadeOut();
+    iQ("#searchshade").fadeOut();
     TabHandlers._mouseDownLocation = {x:event.clientX, y:event.clientY};
   },
   
   _showHandler: function(event){
     // If the user clicks on a tab without moving the mouse then
     // they are zooming into the tab and we need to exit search
     // mode.
     if (TabHandlers._mouseDownLocation.x == event.clientX &&
         TabHandlers._mouseDownLocation.y == event.clientY){
-        hideSearch();
-        return;
+      hideSearch();
+      return;
     }
-    
+
+    iQ("#searchshade").show();    
     iQ("#search").show();
     iQ("#searchbox")[0].focus();
     // Marshal the search.
     setTimeout(performSearch, 0);
   },
   
   _mouseDownLocation: null
 };
 
+function createSearchTabMacher() {
+  return new TabMatcher(iQ("#searchbox").val());
+}
+
 function hideSearch(event){
   iQ("#searchbox").val("");
+  iQ("#searchshade").hide();
   iQ("#search").hide();
-  
+
   iQ("#searchbutton").css({ opacity:.8 });
-  
-  var mainWindow = gWindow.document.getElementById("main-window");    
+
+  let mainWindow = gWindow.document.getElementById("main-window");
   mainWindow.setAttribute("activetitlebarcolor", "#C4C4C4");
 
   performSearch();
   SearchEventHandler.switchToBeforeMode();
 
   if (event){
     event.preventDefault();
     event.stopPropagation();
@@ -494,47 +515,47 @@ function hideSearch(event){
   gTabViewFrame.contentWindow.focus();
 
   let newEvent = document.createEvent("Events");
   newEvent.initEvent("tabviewsearchdisabled", false, false);
   dispatchEvent(newEvent);
 }
 
 function performSearch() {
-  var matcher = new TabMatcher(iQ("#searchbox").val());
+  let matcher = new TabMatcher(iQ("#searchbox").val());
 
   // Remove any previous other-window search results and
   // hide the display area.
   iQ("#results").empty();
   iQ("#otherresults").hide();
   iQ("#otherresults>.label").text(tabviewString("search.otherWindowTabs"));
 
   matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch, TabHandlers.onOther);
 }
 
 function ensureSearchShown(event){
   var $search = iQ("#search");
+  var $searchShade = iQ("#searchshade");
   var $searchbox = iQ("#searchbox");
   iQ("#searchbutton").css({ opacity: 1 });
 
-
   if (!isSearchEnabled()) {
+    $searchShade.show();
     $search.show();
     var mainWindow = gWindow.document.getElementById("main-window");
     mainWindow.setAttribute("activetitlebarcolor", "#717171");       
 
     // Marshal the focusing, otherwise you end up with
     // a race condition where only sometimes would the
     // first keystroke be registered by the search box.
     // When you marshal it never gets registered, so we
     // manually 
     setTimeout(function focusSearch() {
       $searchbox[0].focus();
       $searchbox[0].val = '0';
-      $searchbox.css({"z-index":"1015"});
       if (event != null)
         $searchbox.val(String.fromCharCode(event.charCode));        
 
       let newEvent = document.createEvent("Events");
       newEvent.initEvent("tabviewsearchenabled", false, false);
       dispatchEvent(newEvent);
     }, 0);
   }
@@ -543,10 +564,9 @@ function ensureSearchShown(event){
 function isSearchEnabled() {
   return iQ("#search").css("display") != "none";
 }
 
 var SearchEventHandler = new SearchEventHandlerClass();
 
 // Features to add:
 // (1) Make sure this looks good on Windows. Bug 594429
-// (2) Make sure that we don't put the matched tab over the search box. Bug 594433
-// (3) Group all of the highlighted tabs into a group? Bug 594434
+// (2) Group all of the highlighted tabs into a group? Bug 594434
\ No newline at end of file
--- a/browser/base/content/tabview/tabitems.js
+++ b/browser/base/content/tabview/tabitems.js
@@ -63,18 +63,19 @@ function TabItem(tab, options) {
     .addClass('tab')
     .html("<div class='thumb'>" +
           "<img class='cached-thumb' style='display:none'/><canvas/></div>" +
           "<div class='favicon'><img/></div>" +
           "<span class='tab-title'>&nbsp;</span>"
     )
     .appendTo('body');
 
+  this._cachedImageData = null;
+  this.shouldHideCachedData = false;
   this.canvasSizeForced = false;
-  this.isShowingCachedData = false;
   this.favEl = (iQ('.favicon', $div))[0];
   this.favImgEl = (iQ('.favicon>img', $div))[0];
   this.nameEl = (iQ('.tab-title', $div))[0];
   this.thumbEl = (iQ('.thumb', $div))[0];
   this.canvasEl = (iQ('.thumb canvas', $div))[0];
   this.cachedThumbEl = (iQ('img.cached-thumb', $div))[0];
 
   this.tabCanvas = new TabCanvas(this.tab, this.canvasEl);
@@ -182,19 +183,20 @@ function TabItem(tab, options) {
   });
 
   $div.mouseup(function(e) {
     var same = (e.target == self.lastMouseDownTarget);
     self.lastMouseDownTarget = null;
     if (!same)
       return;
 
-    if (iQ(e.target).hasClass("close"))
+    // press close button or middle mouse click
+    if (iQ(e.target).hasClass("close") || e.button == 1) {
       self.close();
-    else {
+    } else {
       if (!Items.item(this).isDragging)
         self.zoomIn();
     }
   });
 
   iQ("<div>")
     .addClass('close')
     .appendTo($div);
@@ -237,54 +239,85 @@ TabItem.prototype = Utils.extend(new Ite
   // size of thumbnail on screen. Note that this call does not nest, unlike
   // <TabItems.resumePainting>; if you call forceCanvasSize multiple
   // times, you just need a single unforce to clear them all.
   unforceCanvasSize: function TabItem_unforceCanvasSize() {
     this.canvasSizeForced = false;
   },
 
   // ----------
+  // Function: isShowingCachedData
+  // Returns a boolean indicates whether the cached data is being displayed or
+  // not. 
+  isShowingCachedData: function() {
+    return (this._cachedImageData != null);
+  },
+
+  // ----------
   // Function: showCachedData
   // Shows the cached data i.e. image and title.  Note: this method should only
   // be called at browser startup with the cached data avaliable.
+  //
+  // Parameters:
+  //   tabData - the tab data
   showCachedData: function TabItem_showCachedData(tabData) {
-    this.isShowingCachedData = true;
-    var $nameElement = iQ(this.nameEl);
-    var $canvasElement = iQ(this.canvasEl);
-    var $cachedThumbElement = iQ(this.cachedThumbEl);
-    $cachedThumbElement.attr("src", tabData.imageData).show();
+    if (!this._cachedImageData) {
+      TabItems.cachedDataCounter++;
+      this.tab.linkedBrowser._tabViewTabItemWithCachedData = this;
+      if (TabItems.cachedDataCounter == 1)
+        gBrowser.addTabsProgressListener(TabItems.tabsProgressListener);
+    }
+    this._cachedImageData = tabData.imageData;
+    let $nameElement = iQ(this.nameEl);
+    let $canvasElement = iQ(this.canvasEl);
+    let $cachedThumbElement = iQ(this.cachedThumbEl);
+    $cachedThumbElement.attr("src", this._cachedImageData).show();
     $canvasElement.css({opacity: 0.0});
     $nameElement.text(tabData.title ? tabData.title : "");
   },
 
   // ----------
   // Function: hideCachedData
   // Hides the cached data i.e. image and title and show the canvas.
   hideCachedData: function TabItem_hideCachedData() {
-    var $canvasElement = iQ(this.canvasEl);
-    var $cachedThumbElement = iQ(this.cachedThumbEl);
+    let $canvasElement = iQ(this.canvasEl);
+    let $cachedThumbElement = iQ(this.cachedThumbEl);
     $cachedThumbElement.hide();
     $canvasElement.css({opacity: 1.0});
-    this.isShowingCachedData = false;
+    if (this._cachedImageData) {
+      TabItems.cachedDataCounter--;
+      this._cachedImageData = null;
+      this.tab.linkedBrowser._tabViewTabItemWithCachedData = null;
+      if (TabItems.cachedDataCounter == 0)
+        gBrowser.removeTabsProgressListener(TabItems.tabsProgressListener);
+    }
   },
 
   // ----------
   // Function: getStorageData
   // Get data to be used for persistent storage of this object.
   //
   // Parameters:
   //   getImageData - true to include thumbnail pixels (and page title as well); default false
   getStorageData: function TabItem_getStorageData(getImageData) {
+    let imageData = null;
+
+    if (getImageData) { 
+      if (this._cachedImageData)
+        imageData = this._cachedImageData;
+      else if (this.tabCanvas)
+        imageData = this.tabCanvas.toImageData();
+    }
+
     return {
       bounds: this.getBounds(),
       userSize: (Utils.isPoint(this.userSize) ? new Point(this.userSize) : null),
       url: this.tab.linkedBrowser.currentURI.spec,
       groupID: (this.parent ? this.parent.id : 0),
-      imageData: (getImageData && this.tabCanvas ?
-                  this.tabCanvas.toImageData() : null),
+      imageData: imageData,
       title: getImageData && this.tab.label || null
     };
   },
 
   // ----------
   // Function: save
   // Store persistent for this object.
   //
@@ -545,53 +578,39 @@ TabItem.prototype = Utils.extend(new Ite
     var self = this;
     var $tabEl = iQ(this.container);
     var childHitResult = { shouldZoom: true };
     if (this.parent)
       childHitResult = this.parent.childHit(this);
 
     if (childHitResult.shouldZoom) {
       // Zoom in!
+      var tab = this.tab;
       var orig = $tabEl.bounds();
-      var scale = window.innerWidth/orig.width;
-      var tab = this.tab;
 
       function onZoomDone() {
         UI.goToTab(tab);
 
         // tab might not be selected because hideTabView() is invoked after 
         // UI.goToTab() so we need to setup everything for the gBrowser.selectedTab
         if (tab != gBrowser.selectedTab) {
           UI.onTabSelect(gBrowser.selectedTab);
         } else { 
           if (isNewBlankTab)
             gWindow.gURLBar.focus();
         }
         if (childHitResult.callback)
           childHitResult.callback();
       }
 
-      // The scaleCheat is a clever way to speed up the zoom-in code.
-      // Because image scaling is slowest on big images, we cheat and stop the image
-      // at scaled-down size and placed accordingly. Because the animation is fast, you can't
-      // see the difference but it feels a lot zippier. The only trick is choosing the
-      // right animation function so that you don't see a change in percieved
-      // animation speed.
-      var scaleCheat = 1.7;
-
       let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
       if (animateZoom) {
         TabItems.pausePainting();
         $tabEl.addClass("front")
-        .animate({
-          top:    orig.top    * (1 - 1/scaleCheat),
-          left:   orig.left   * (1 - 1/scaleCheat),
-          width:  orig.width  * scale/scaleCheat,
-          height: orig.height * scale/scaleCheat
-        }, {
+        .animate(this.getZoomRect(), {
           duration: 230,
           easing: 'fast',
           complete: function() {
             TabItems.resumePainting();
     
             $tabEl
               .css(orig.css())
               .removeClass("front");
@@ -647,47 +666,69 @@ TabItem.prototype = Utils.extend(new Ite
         }
       });
     } else {
       onZoomDone();
     }
   },
 
   // ----------
+  // Function: getZoomRect
+  // Returns a faux rect (just an object with top, left, width, height)
+  // which represents the maximum bounds of the tab thumbnail in the zoom
+  // animation. Note that this is not just the rect of the window itself,
+  // due to scaleCheat.
+  getZoomRect: function TabItem_getZoomRect(scaleCheat) {
+    let $tabEl = iQ(this.container);
+    let orig = $tabEl.bounds();
+    // The scaleCheat is a clever way to speed up the zoom-in code.
+    // Because image scaling is slowest on big images, we cheat and stop
+    // the image at scaled-down size and placed accordingly. Because the
+    // animation is fast, you can't see the difference but it feels a lot
+    // zippier. The only trick is choosing the right animation function so
+    // that you don't see a change in percieved animation speed.
+    if (!scaleCheat)
+      scaleCheat = 1.7;
+
+    let zoomWidth = orig.width + (window.innerWidth - orig.width) / scaleCheat;
+    return {
+      top:    orig.top    * (1 - 1/scaleCheat),
+      left:   orig.left   * (1 - 1/scaleCheat),
+      width:  zoomWidth,
+      height: orig.height * zoomWidth / orig.width
+    };
+  },
+
+  // ----------
   // Function: setZoomPrep
   // Either go into or return from (depending on <value>) "zoom prep" mode,
   // where the tab fills a large portion of the screen in anticipation of
   // the zoom out animation.
   setZoomPrep: function TabItem_setZoomPrep(value) {
     let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
 
     var $div = iQ(this.container);
     var data;
 
     var box = this.getBounds();
     if (value && animateZoom) {
       this._zoomPrep = true;
 
-      // The divide by two part here is a clever way to speed up the zoom-out code.
+      // The scaleCheat of 2 here is a clever way to speed up the zoom-out code.
       // Because image scaling is slowest on big images, we cheat and start the image
       // at half-size and placed accordingly. Because the animation is fast, you can't
       // see the difference but it feels a lot zippier. The only trick is choosing the
       // right animation function so that you don't see a change in percieved
       // animation speed from frame #1 (the tab) to frame #2 (the half-size image) to
       // frame #3 (the first frame of real animation). Choosing an animation that starts
       // fast is key.
-      var scaleCheat = 2;
+
       $div
         .addClass('front')
-        .css({
-          left: box.left * (1-1/scaleCheat),
-          top: box.top * (1-1/scaleCheat),
-          width: window.innerWidth/scaleCheat,
-          height: box.height * (window.innerWidth / box.width)/scaleCheat
-        });
+        .css(this.getZoomRect(2));
     } else {
       this._zoomPrep = false;
       $div.removeClass('front');
 
       this.setBounds(box, true, {force: true});
     }
   }
 });
@@ -697,39 +738,53 @@ TabItem.prototype = Utils.extend(new Ite
 // Singleton for managing <TabItem>s
 let TabItems = {
   minTabWidth: 40,
   tabWidth: 160,
   tabHeight: 120,
   fontSize: 9,
   items: [],
   paintingPaused: 0,
+  cachedDataCounter: 0,  // total number of cached data being displayed.
+  tabsProgressListener: null,
   _tabsWaitingForUpdate: [],
   _heartbeatOn: false, // see explanation at startHeartbeat() below
   _heartbeatTiming: 100, // milliseconds between _checkHeartbeat() calls
   _lastUpdateTime: Date.now(),
   _eventListeners: [],
   tempCanvas: null,
 
   // ----------
   // Function: init
   // Set up the necessary tracking to maintain the <TabItems>s.
   init: function TabItems_init() {
     Utils.assert(window.AllTabs, "AllTabs must be initialized first");
-    var self = this;
+    let self = this;
 
     let $canvas = iQ("<canvas>");
     $canvas.appendTo(iQ("body"));
     $canvas.hide();
     this.tempCanvas = $canvas[0];
     // 150 pixels is an empirical size, below which FF's drawWindow()
     // algorithm breaks down
     this.tempCanvas.width = 150;
     this.tempCanvas.height = 150;
 
+    this.tabsProgressListener = {
+      onStateChange: function(browser, webProgress, request, stateFlags, status) {
+        if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
+            (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)) {
+          // browser would only has _tabViewTabItemWithCachedData if 
+          // it's showing cached data.
+          if (browser._tabViewTabItemWithCachedData)
+            browser._tabViewTabItemWithCachedData.shouldHideCachedData = true;
+        }
+      }
+    };
+
     // When a tab is opened, create the TabItem
     this._eventListeners["open"] = function(tab) {
       if (tab.ownerDocument.defaultView != gWindow || tab.pinned)
         return;
 
       self.link(tab);
     }
     // When a tab's content is loaded, show the canvas and hide the cached data
@@ -759,16 +814,19 @@ let TabItems = {
       self.link(tab, {immediately: true});
       self.update(tab);
     });
   },
 
   // ----------
   // Function: uninit
   uninit: function TabItems_uninit() {
+    if (this.tabsProgressListener)
+      gBrowser.removeTabsProgressListener(this.tabsProgressListener);
+
     for (let name in this._eventListeners) {
       AllTabs.unregister(name, this._eventListeners[name]);
     }
     this.items.forEach(function(tabItem) {
       for (let x in tabItem) {
         if (typeof tabItem[x] == "object")
           tabItem[x] = null;
       }
@@ -791,17 +849,17 @@ let TabItems = {
 
       let shouldDefer = (
         this.isPaintingPaused() ||
         this._tabsWaitingForUpdate.length ||
         Date.now() - this._lastUpdateTime < this._heartbeatTiming
       );
 
       let isCurrentTab = (
-        !UI._isTabViewVisible() &&
+        !UI.isTabViewVisible() &&
         tab == gBrowser.selectedTab
       );
 
       if (shouldDefer && !isCurrentTab) {
         if (this._tabsWaitingForUpdate.indexOf(tab) == -1)
           this._tabsWaitingForUpdate.push(tab);
         this.startHeartbeat();
       } else
@@ -824,17 +882,17 @@ let TabItems = {
         this._tabsWaitingForUpdate.splice(index, 1);
 
       // ___ get the TabItem
       Utils.assertThrow(tab.tabItem, "must already be linked");
       let tabItem = tab.tabItem;
 
       // ___ icon
       let iconUrl = tab.image;
-      if (iconUrl == null)
+      if (!iconUrl)
         iconUrl = Utils.defaultFaviconURL;
 
       if (iconUrl != tabItem.favImgEl.src)
         tabItem.favImgEl.src = iconUrl;
 
       // ___ URL
       let tabUrl = tab.linkedBrowser.currentURI.spec;
       if (tabUrl != tabItem.url) {
@@ -845,17 +903,17 @@ let TabItems = {
           this.reconnect(tabItem);
 
         tabItem.save();
       }
 
       // ___ label
       let label = tab.label;
       let $name = iQ(tabItem.nameEl);
-      if (!tabItem.isShowingCachedData && $name.text() != label)
+      if (!tabItem.isShowingCachedData() && $name.text() != label)
         $name.text(label);
 
       // ___ thumbnail
       let $canvas = iQ(tabItem.canvasEl);
       if (!tabItem.canvasSizeForced) {
         let w = $canvas.width();
         let h = $canvas.height();
         if (w != tabItem.canvasEl.width || h != tabItem.canvasEl.height) {
@@ -865,18 +923,17 @@ let TabItems = {
       }
 
       this._lastUpdateTime = Date.now();
       tabItem._lastTabUpdateTime = this._lastUpdateTime;
 
       tabItem.tabCanvas.paint();
 
       // ___ cache
-      // TODO: this logic needs to be better; hiding too soon now
-      if (tabItem.isShowingCachedData && !tab.hasAttribute("busy"))
+      if (tabItem.isShowingCachedData() && tabItem.shouldHideCachedData)
         tabItem.hideCachedData();
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: link
@@ -1085,26 +1142,18 @@ let TabItems = {
             // if it matches the selected tab or no active tab and the browser 
             // tab is hidden, the active group item would be set.
             if (item.tab == gBrowser.selectedTab || 
                 (!GroupItems.getActiveGroupItem() && !item.tab.hidden))
               GroupItems.setActiveGroupItem(item.parent);
           }
         }
 
-        if (tabData.imageData) {
+        if (tabData.imageData)
           item.showCachedData(tabData);
-          // the code in the progress listener doesn't fire sometimes because
-          // tab is being restored so need to catch that.
-          setTimeout(function() {
-            if (item && item.isShowingCachedData) {
-              item.hideCachedData();
-            }
-          }, 15000);
-        }
 
         item.reconnected = true;
         found = {addedToGroup: tabData.groupID};
       } else {
         // We should never have any orphaned tabs. Therefore, item is not 
         // connected if it has no parent and GroupItems.newTab() would handle 
         // the group creation.
         item.reconnected = (item.parent != null);
--- a/browser/base/content/tabview/tabview.css
+++ b/browser/base/content/tabview/tabview.css
@@ -194,46 +194,52 @@ body {
 ----------------------------------*/
 #exit-button {
   position: absolute;
   z-index: 1000;
 }
 
 /* Search
 ----------------------------------*/
+#searchshade{
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  z-index: 1000;
+}
 
 #search{
   position: absolute;
   top: 0px;
   left: 0px;
-  z-index: 1000;  
+  pointer-events: none;
+  z-index: 1050;
 }
 
 html[dir=rtl] #search {
   left: auto;
   right: 0;
 }
 
 #searchbox{
   position: absolute;
   right: 20px;
   top: 20px;
-  z-index: 1050;
 }
 
 html[dir=rtl] #searchbox {
   right: auto;
   left: 20px;
 }
 
 #actions{
   position: absolute;
   top: 100px;
   right: 0px;
-  z-index:10;
+  z-index: 10;
 }
 
 html[dir=rtl] #actions {
   right: auto;
   left: 0;
 }
 
 #actions #searchbutton{
--- a/browser/base/content/tabview/tabview.html
+++ b/browser/base/content/tabview/tabview.html
@@ -5,23 +5,25 @@
   <title>&nbsp;</title>
   <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
   <link rel="stylesheet" href="tabview.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/tabview/tabview.css" type="text/css"/>
 </head>
 
 <body transparent="true">
   <div id="content">
-    <input id="exit-button" type="image" alt=""/>
+    <div id="bg">
+    </div>
+    <input id="exit-button" type="image" alt="" groups="0" />
     <div id="actions">
       <input id="searchbutton" type="button"/>
     </div>
-    <div id="bg" />
-  </div>
-  
+  </div>  
+
+  <div id="searchshade"></div>
   <div id="search">
     <input id="searchbox" type="text"/>
     <div id="otherresults">
       <span class="label"></span>
       <span id="results"></span>
     </div>
   </div>
 
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -60,16 +60,20 @@ let UI = {
   // Variable: _closedLastVisibleTab
   // If true, the last visible tab has just been closed in the tab strip.
   _closedLastVisibleTab : false,
 
   // Variable: _closedSelectedTabInTabView
   // If true, a select tab has just been closed in TabView.
   _closedSelectedTabInTabView : false,
 
+  // Variable: restoredClosedTab
+  // If true, a closed tab has just been restored.
+  restoredClosedTab : false,
+
   // Variable: _reorderTabItemsOnShow
   // Keeps track of the <GroupItem>s which their tab items' tabs have been moved
   // and re-orders the tab items when switching to TabView.
   _reorderTabItemsOnShow : [],
 
   // Variable: _reorderTabsOnHide
   // Keeps track of the <GroupItem>s which their tab items have been moved in
   // TabView UI and re-orders the tabs when switcing back to main browser.
@@ -129,31 +133,24 @@ let UI = {
       // ___ currentTab
       this._currentTab = gBrowser.selectedTab;
 
       // ___ exit button
       iQ("#exit-button").click(function() {
         self.exit();
         self.blurAll();
       });
-        
-      // ___ Dev Menu
-      // This dev menu is not meant for shipping, nor is it of general
-      // interest, but we still need it for the time being. Change the
-      // false below to enable; just remember to change back before
-      // committing. Bug 586721 will track the ultimate removal.
-      if (false)
-        this._addDevMenu();
 
       // When you click on the background/empty part of TabView,
       // we create a new groupItem.
       iQ(gTabViewFrame.contentDocument).mousedown(function(e) {
         if (iQ(":focus").length > 0) {
           iQ(":focus").each(function(element) {
-            if (element.nodeName == "INPUT")
+            // don't fire blur event if the same input element is clicked.
+            if (e.target != element && element.nodeName == "INPUT")
               element.blur();
           });
         }
         if (e.originalTarget.id == "content")
           self._createGroupItemOnDrag(e)
       });
 
       iQ(window).bind("beforeunload", function() {
@@ -171,17 +168,16 @@ let UI = {
 
       // ___ setup key handlers
       this._setTabViewFrameKeyHandlers();
 
       // ___ add tab action handlers
       this._addTabActionHandlers();
 
       // ___ Storage
-
       GroupItems.pauseArrange();
       GroupItems.init();
 
       let firstTime = true;
       if (gPrefBranch.prefHasUserValue("experienced_first_run"))
         firstTime = !gPrefBranch.getBoolPref("experienced_first_run");
       let groupItemsData = Storage.readGroupItemsData(gWindow);
       let groupItemData = Storage.readGroupItemData(gWindow);
@@ -205,17 +201,17 @@ let UI = {
       iQ(window).resize(function() {
         self._resize();
       });
 
       // ___ setup observer to save canvas images
       var observer = {
         observe : function(subject, topic, data) {
           if (topic == "quit-application-requested") {
-            if (self._isTabViewVisible()) {
+            if (self.isTabViewVisible()) {
               GroupItems.removeHiddenGroups();
               TabItems.saveAll(true);
             }
             self._save();
           }
         }
       };
       Services.obs.addObserver(observer, "quit-application-requested", false);
@@ -374,19 +370,19 @@ let UI = {
         self._activeTab = null;
       });
 
       this._activeTab.makeActive();
     }
   },
 
   // ----------
-  // Function: _isTabViewVisible
+  // Function: isTabViewVisible
   // Returns true if the TabView UI is currently shown.
-  _isTabViewVisible: function UI__isTabViewVisible() {
+  isTabViewVisible: function UI_isTabViewVisible() {
     return gTabViewDeck.selectedIndex == 1;
   },
 
   // ---------
   // Function: _initPageDirection
   // Initializes the page base direction
   _initPageDirection: function UI__initPageDirection() {
     let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
@@ -397,17 +393,17 @@ let UI = {
   },
 
   // ----------
   // Function: showTabView
   // Shows TabView and hides the main browser UI.
   // Parameters:
   //   zoomOut - true for zoom out animation, false for nothing.
   showTabView: function UI_showTabView(zoomOut) {
-    if (this._isTabViewVisible())
+    if (this.isTabViewVisible())
       return;
 
     // initialize the direction of the page
     this._initPageDirection();
 
     var self = this;
     var currentTab = this._currentTab;
     var item = null;
@@ -463,17 +459,17 @@ let UI = {
 
     TabItems.resumePainting();
   },
 
   // ----------
   // Function: hideTabView
   // Hides TabView and shows the main browser UI.
   hideTabView: function UI_hideTabView() {
-    if (!this._isTabViewVisible())
+    if (!this.isTabViewVisible())
       return;
 
     // another tab might be select if user decides to stay on a page when
     // a onclose confirmation prompts.
     GroupItems.removeHiddenGroups();
     TabItems.pausePainting();
 
     this._reorderTabsOnHide.forEach(function(groupItem) {
@@ -485,19 +481,16 @@ let UI = {
     // Push the top of TabView frame to behind the tabbrowser, so glass can show
     // XXX bug 586679: avoid shrinking the iframe and squishing iframe contents
     // as well as avoiding the flash of black as we animate out
     gTabViewFrame.style.marginTop = gBrowser.boxObject.y + "px";
 #endif
     gTabViewDeck.selectedIndex = 0;
     gBrowser.contentWindow.focus();
 
-    // set the close button on tab
-    gBrowser.tabContainer.adjustTabstrip();
-
     gBrowser.updateTitlebar();
 #ifdef XP_MACOSX
     this._setActiveTitleColor(false);
 #endif
     let event = document.createEvent("Events");
     event.initEvent("tabviewhidden", true, false);
     dispatchEvent(event);
   },
@@ -562,18 +555,18 @@ let UI = {
     // don't reenter Panorama due to all of the session restore tab
     // manipulation (which otherwise we might). When transitioning away from
     // PB, we reenter Panorama if we had been there directly before PB.
     function pbObserver(aSubject, aTopic, aData) {
       if (aTopic == "private-browsing") {
         self._privateBrowsing.transitionStage = 3;
         if (aData == "enter") {
           // If we are in Tab View, exit. 
-          self._privateBrowsing.wasInTabView = self._isTabViewVisible();
-          if (self._isTabViewVisible())
+          self._privateBrowsing.wasInTabView = self.isTabViewVisible();
+          if (self.isTabViewVisible())
             self.goToTab(gBrowser.selectedTab);
         }
       } else if (aTopic == "private-browsing-change-granted") {
         if (aData == "enter" || aData == "exit") {
           self._privateBrowsing.transitionStage = 1;
           self._privateBrowsing.transitionMode = aData;
         }
       }
@@ -601,17 +594,17 @@ let UI = {
     this._eventListeners.close = function(tab) {
       if (tab.ownerDocument.defaultView != gWindow)
         return;
 
       // if it's an app tab, remove it from all the group items
       if (tab.pinned)
         GroupItems.removeAppTab(tab);
         
-      if (self._isTabViewVisible()) {
+      if (self.isTabViewVisible()) {
         // just closed the selected tab in the TabView interface.
         if (self._currentTab == tab)
           self._closedSelectedTabInTabView = true;
       } else {
         // If we're currently in the process of entering private browsing,
         // we don't want to go to the Tab View UI. 
         if (self._privateBrowsing.transitionStage > 0)
           return; 
@@ -714,42 +707,53 @@ let UI = {
   // ----------
   // Function: onTabSelect
   // Called when the user switches from one tab to another outside of the TabView UI.
   onTabSelect: function UI_onTabSelect(tab) {
     let currentTab = this._currentTab;
     this._currentTab = tab;
 
     // if the last visible tab has just been closed, don't show the chrome UI.
-    if (this._isTabViewVisible() &&
-        (this._closedLastVisibleTab || this._closedSelectedTabInTabView)) {
+    if (this.isTabViewVisible() &&
+        (this._closedLastVisibleTab || this._closedSelectedTabInTabView ||
+         this.restoredClosedTab)) {
+      if (this.restoredClosedTab) {
+        // when the tab view UI is being displayed, update the thumb for the 
+        // restored closed tab after the page load
+        tab.linkedBrowser.addEventListener("load", function (event) {
+          tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+          TabItems._update(tab);
+        }, true);
+      }
       this._closedLastVisibleTab = false;
       this._closedSelectedTabInTabView = false;
+      this.restoredClosedTab = false;
       return;
     }
     // reset these vars, just in case.
     this._closedLastVisibleTab = false;
     this._closedSelectedTabInTabView = false;
+    this.restoredClosedTab = false;
 
     // if TabView is visible but we didn't just close the last tab or
     // selected tab, show chrome.
-    if (this._isTabViewVisible())
+    if (this.isTabViewVisible())
       this.hideTabView();
 
     // another tab might be selected when hideTabView() is invoked so a
     // validation is needed.
     if (this._currentTab != tab)
       return;
 
     let oldItem = null;
     let newItem = null;
 
     if (currentTab && currentTab.tabItem)
       oldItem = currentTab.tabItem;
-      
+
     // update the tab bar for the new tab's group
     if (tab && tab.tabItem) {
       newItem = tab.tabItem;
       GroupItems.updateActiveGroupItemAndTabBar(newItem);
     } else {
       // No tabItem; must be an app tab. Base the tab bar on the current group.
       // If no current group or orphan tab, figure it out based on what's
       // already in the tab bar.
@@ -784,42 +788,45 @@ let UI = {
 
   // ----------
   // Function: setReorderTabsOnHide
   // Sets the groupItem which the tab items' tabs should be re-ordered when
   // switching to the main browser UI.
   // Parameters:
   //   groupItem - the groupItem which would be used for re-ordering tabs.
   setReorderTabsOnHide: function UI_setReorderTabsOnHide(groupItem) {
-    if (this._isTabViewVisible()) {
+    if (this.isTabViewVisible()) {
       var index = this._reorderTabsOnHide.indexOf(groupItem);
       if (index == -1)
         this._reorderTabsOnHide.push(groupItem);
     }
   },
 
   // ----------
   // Function: setReorderTabItemsOnShow
   // Sets the groupItem which the tab items should be re-ordered when
   // switching to the tab view UI.
   // Parameters:
   //   groupItem - the groupItem which would be used for re-ordering tab items.
   setReorderTabItemsOnShow: function UI_setReorderTabItemsOnShow(groupItem) {
-    if (!this._isTabViewVisible()) {
+    if (!this.isTabViewVisible()) {
       var index = this._reorderTabItemsOnShow.indexOf(groupItem);
       if (index == -1)
         this._reorderTabItemsOnShow.push(groupItem);
     }
   },
   
   // ----------
-  updateTabButton: function UI__updateTabButton(){
+  updateTabButton: function UI__updateTabButton() {
     let groupsNumber = gWindow.document.getElementById("tabviewGroupsNumber");
+    let exitButton = document.getElementById("exit-button");
     let numberOfGroups = GroupItems.groupItems.length;
+
     groupsNumber.setAttribute("groups", numberOfGroups);
+    exitButton.setAttribute("groups", numberOfGroups);
   },
 
   // ----------
   // Function: getClosestTab
   // Convenience function to get the next tab closest to the entered position
   getClosestTab: function UI_getClosestTab(tabCenter) {
     let cl = null;
     let clDist;
@@ -846,17 +853,18 @@ let UI = {
       if (!event.metaKey) 
         Keys.meta = false;
     });
 
     iQ(window).keydown(function(event) {
       if (event.metaKey) 
         Keys.meta = true;
 
-      if (isSearchEnabled())
+      if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") || 
+          isSearchEnabled())
         return;
 
       function getClosestTabBy(norm) {
         if (!self.getActiveTab())
           return null;
         var centers =
           [[item.bounds.center(), item]
              for each(item in TabItems.getItems()) if (!item.parent || !item.parent.hidden)];
@@ -934,21 +942,42 @@ let UI = {
               else
                 newIndex = (currentIndex + 1);
             }
             self.setActiveTab(tabItems[newIndex]);
           }
         }
         event.stopPropagation();
         event.preventDefault();
+      } else if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
+        // the / event handler for find bar is defined in the findbar.xml
+        // binding.  To keep things in its own module, we handle our slash here.
+        self.enableSearch(event);
       }
     });
   },
 
   // ----------
+  // Function: enableSearch
+  // Enables the search feature.
+  // Parameters:
+  //   event - the event triggers this action.
+  enableSearch: function UI_enableSearch(event) {
+    if (!isSearchEnabled()) {
+      ensureSearchShown(null);
+      SearchEventHandler.switchToInMode();
+      
+      if (event) {
+        event.stopPropagation();
+        event.preventDefault();
+      }
+    }
+  },
+
+  // ----------
   // Function: _createGroupItemOnDrag
   // Called in response to a mousedown in empty space in the TabView UI;
   // creates a new groupItem based on the user's drag.
   _createGroupItemOnDrag: function UI__createGroupItemOnDrag(e) {
     const minSize = 60;
     const minMinSize = 15;
 
     let lastActiveGroupItem = GroupItems.getActiveGroupItem();
@@ -982,17 +1011,17 @@ let UI = {
         this.container.css("opacity", opacity);
       },
       // we don't need to pushAway the phantom item at the end, because
       // when we create a new GroupItem, it'll do the actual pushAway.
       pushAway: function () {},
     };
     item.setBounds(new Rect(startPos.y, startPos.x, 0, 0));
 
-    var dragOutInfo = new Drag(item, e, true); // true = isResizing
+    var dragOutInfo = new Drag(item, e);
 
     function updateSize(e) {
       var box = new Rect();
       box.left = Math.min(startPos.x, e.clientX);
       box.right = Math.max(startPos.x, e.clientX);
       box.top = Math.min(startPos.y, e.clientY);
       box.bottom = Math.max(startPos.y, e.clientY);
       item.setBounds(box);
@@ -1078,17 +1107,17 @@ let UI = {
     if (typeof force == "undefined")
       force = false;
 
     if (!this._pageBounds)
       return;
 
     // If TabView isn't focused and is not showing, don't perform a resize.
     // This resize really slows things down.
-    if (!force && !this._isTabViewVisible())
+    if (!force && !this.isTabViewVisible())
       return;
 
     var oldPageBounds = new Rect(this._pageBounds);
     var newPageBounds = Items.getPageBounds();
     if (newPageBounds.equals(oldPageBounds))
       return;
 
     var items = Items.getTopLevelItems();
@@ -1159,93 +1188,75 @@ let UI = {
     this._save();
   },
 
   // ----------
   // Function: exit
   // Exits TabView UI.
   exit: function UI_exit() {
     let self = this;
-    
-    // If there's an active TabItem, zoom into it. If not (for instance when the
-    // selected tab is an app tab), just go there. 
-    let activeTabItem = this.getActiveTab();
-    if (!activeTabItem)
-      activeTabItem = gBrowser.selectedTab.tabItem;
-      
-    if (activeTabItem)
-      activeTabItem.zoomIn(); 
-    else
-      self.goToTab(gBrowser.selectedTab);
-  },
+    let zoomedIn = false;
+
+    if (isSearchEnabled()) {
+      let matcher = createSearchTabMacher();
+      let matches = matcher.matched();
+
+      if (matches.length > 0) {
+        matches[0].zoomIn();
+        zoomedIn = true;
+      }
+      hideSearch(null);
+    }
 
-  // ----------
-  // Function: _addDevMenu
-  // Fills out the "dev menu" in the TabView UI.
-  _addDevMenu: function UI__addDevMenu() {
-    try {
-      var self = this;
-
-      var $select = iQ("<select>")
-        .css({
-          position: "absolute",
-          bottom: 5,
-          right: 5,
-          zIndex: 99999,
-          opacity: .2
-        })
-        .appendTo("#content")
-        .change(function () {
-          var index = iQ(this).val();
-          try {
-            commands[index].code.apply(commands[index].element);
-          } catch(e) {
-            Utils.log("dev menu error", e);
-          }
-          iQ(this).val(0);
-        });
+    if (!zoomedIn) {
+      let unhiddenGroups = GroupItems.groupItems.filter(function(groupItem) {
+        return (!groupItem.hidden && groupItem.getChildren().length > 0);
+      });
+      // no visible groups, no orphaned tabs and no apps tabs, open a new group
+      // with a blank tab
+      if (unhiddenGroups.length == 0 && GroupItems.getOrphanedTabs().length == 0 &&
+          gBrowser._numPinnedTabs == 0) {
+        let box = new Rect(20, 20, 250, 200);
+        let groupItem = new GroupItem([], { bounds: box, immediately: true });
+        groupItem.newTab();
+        return;
+      }
 
-      var commands = [{
-        name: "dev menu",
-        code: function() { }
-      }, {
-        name: "show trenches",
-        code: function() {
-          Trenches.toggleShown();
-          iQ(this).html((Trenches.showDebug ? "hide" : "show") + " trenches");
-        }
-      }, {
-/*
-        name: "refresh",
-        code: function() {
-          location.href = "tabview.html";
+      // If there's an active TabItem, zoom into it. If not (for instance when the
+      // selected tab is an app tab), just go there.
+      let activeTabItem = this.getActiveTab();
+      if (!activeTabItem) {
+        let tabItem = gBrowser.selectedTab.tabItem;
+        if (tabItem) {
+          if (!tabItem.parent || !tabItem.parent.hidden) {
+            activeTabItem = tabItem;
+          } else { // set active tab item if there is at least one unhidden group
+            if (unhiddenGroups.length > 0)
+              activeTabItem = unhiddenGroups[0].getActiveTab();
+          }
         }
-      }, {
-        name: "reset",
-        code: function() {
-          self.reset();
-        }
-      }, {
-*/
-        name: "save",
-        code: function() {
-          self._saveAll();
+      }
+
+      if (activeTabItem) {
+        activeTabItem.zoomIn();
+      } else {
+        if (gBrowser._numPinnedTabs > 0) {
+          if (gBrowser.selectedTab.pinned) {
+            self.goToTab(gBrowser.selectedTab);
+          } else {
+            Array.some(gBrowser.tabs, function(tab) {
+              if (tab.pinned) {
+                self.goToTab(tab);
+                return true;
+              }
+              return false
+            });
+          }
         }
-      }];
-
-      var count = commands.length;
-      var a;
-      for (a = 0; a < count; a++) {
-        commands[a].element = (iQ("<option>")
-          .val(a)
-          .html(commands[a].name)
-          .appendTo($select))[0];
       }
-    } catch(e) {
-      Utils.log(e);
     }
   },
 
   // ----------
   // Function: storageSanity
   // Given storage data for this object, returns true if it looks valid.
   _storageSanity: function UI__storageSanity(data) {
     if (Utils.isEmptyObject(data))
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -150,26 +150,28 @@ endif
                  browser_bug577121.js \
                  browser_bug579872.js \
                  browser_bug580956.js \
                  browser_bug581242.js \
                  browser_bug581253.js \
                  browser_bug581947.js \
                  browser_bug585785.js \
                  browser_bug585830.js \
+                 browser_bug590206.js \
                  browser_bug592338.js \
                  browser_bug594131.js \
                  browser_bug595507.js \
                  browser_bug596687.js \
                  browser_bug597218.js \
                  browser_bug598923.js \
                  browser_bug599325.js \
                  browser_bug609700.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
+                 browser_disablechrome.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_hide_removing.js \
                  browser_inspector_initialization.js \
                  browser_inspector_treeSelection.js \
                  browser_inspector_highlighter.js \
@@ -192,23 +194,26 @@ endif
                  browser_sanitize-sitepermissions.js \
                  browser_sanitize-timespans.js \
                  browser_sanitizeDialog.js \
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
                  browser_tabfocus.js \
+                 browser_tabs_isActive.js \
                  browser_tabs_owner.js \
+                 browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
                  browser_visibleTabs_bookmarkAllTabs.js \
                  browser_visibleTabs_tabPreview.js \
                  bug592338.html \
+                 disablechrome.html \
                  discovery.html \
                  domplate_test.js \
                  moz.png \
                  test_bug435035.html \
                  test_bug462673.html \
                  page_style_sample.html \
                  feed_tab.html \
                  plugin_unknown.html \
--- a/browser/base/content/test/app_bug575561.html
+++ b/browser/base/content/test/app_bug575561.html
@@ -6,11 +6,14 @@ https://bugzilla.mozilla.org/show_bug.cg
   <head>
     <title>Test for links in app tabs</title>
   </head>
   <body>
     <a href="http://example.com/browser/browser/base/content/test/dummy_page.html">same domain</a>
     <a href="http://test1.example.com/browser/browser/base/content/test/dummy_page.html">same domain (different subdomain)</a>
     <a href="http://example.org/browser/browser/base/content/test/dummy_page.html">different domain</a>
     <a href="http://example.org/browser/browser/base/content/test/dummy_page.html" target="foo">different domain (with target)</a>
+    <a href="http://www.example.com/browser/browser/base/content/test/dummy_page.html">same domain (www prefix)</a>
+    <a href="data:text/html,<!DOCTYPE html><html><body>Another Page</body></html>">data: URI</a>
+    <a href="about:mozilla">about: URI</a>
     <iframe src="app_subframe_bug575561.html"></iframe>
   </body>
 </html>
--- a/browser/base/content/test/browser_bug553455.js
+++ b/browser/base/content/test/browser_bug553455.js
@@ -54,16 +54,55 @@ function wait_for_install_dialog(aCallba
     },
 
     onWindowTitleChange: function(aXULWindow, aNewTitle) {
     }
   });
 }
 
 var TESTS = [
+function test_disabled_install() {
+  Services.prefs.setBoolPref("xpinstall.enabled", false);
+
+  // Wait for the disabled notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "xpinstall-disabled-notification", "Should have seen installs disabled");
+    is(notification.button.label, "Enable", "Should have seen the right button");
+    is(notification.getAttribute("label"),
+       "Software installation is currently disabled. Click Enable and try again.");
+
+    // Click on Enable
+    EventUtils.synthesizeMouseAtCenter(notification.button, {});
+
+    try {
+      Services.prefs.getBoolPref("xpinstall.disabled");
+      ok(false, "xpinstall.disabled should not be set");
+    }
+    catch (e) {
+      ok(true, "xpinstall.disabled should not be set");
+    }
+
+    gBrowser.removeTab(gBrowser.selectedTab);
+
+    AddonManager.getAllInstalls(function(aInstalls) {
+      is(aInstalls.length, 1, "Should have been one install created");
+      aInstalls[0].cancel();
+
+      runNextTest();
+    });
+  });
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "XPI": "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+},
+
 function test_blocked_install() {
   // Wait for the blocked notification
   wait_for_notification(function(aPanel) {
     let notification = aPanel.childNodes[0];
     is(notification.id, "addon-install-blocked-notification", "Should have seen the install blocked");
     is(notification.button.label, "Allow", "Should have seen the right button");
     is(notification.getAttribute("label"),
        gApp + " prevented this site (example.com) from asking you to install " +
--- a/browser/base/content/test/browser_bug575561.js
+++ b/browser/base/content/test/browser_bug575561.js
@@ -1,29 +1,41 @@
 function test() {
   waitForExplicitFinish();
 
   // Pinned: Link to the same domain should not open a new tab
   // Tests link to http://example.com/browser/browser/base/content/test/dummy_page.html  
   testLink(0, true, false, function() {
-    // Pinned: Link to the same domain should not open a new tab
+    // Pinned: Link to a different subdomain should open a new tab
     // Tests link to http://test1.example.com/browser/browser/base/content/test/dummy_page.html
-    testLink(1, true, false, function() {
+    testLink(1, true, true, function() {
       // Pinned: Link to a different domain should open a new tab
       // Tests link to http://example.org/browser/browser/base/content/test/dummy_page.html
       testLink(2, true, true, function() {
         // Not Pinned: Link to a different domain should not open a new tab
         // Tests link to http://example.org/browser/browser/base/content/test/dummy_page.html
         testLink(2, false, false, function() {
           // Pinned: Targetted link should open a new tab
           // Tests link to http://example.org/browser/browser/base/content/test/dummy_page.html with target="foo"
           testLink(3, true, true, function() {
             // Pinned: Link in a subframe should not open a new tab
             // Tests link to http://example.org/browser/browser/base/content/test/dummy_page.html in subframe
-            testLink(0, true, false, finish, true);
+            testLink(0, true, false, function() {
+              // Pinned: Link to the same domain (with www prefix) should not open a new tab
+              // Tests link to http://www.example.com/browser/browser/base/content/test/dummy_page.html      
+              testLink(4, true, false, function() {
+                // Pinned: Link to a data: URI should not open a new tab
+                // Tests link to data:text/html,<!DOCTYPE html><html><body>Another Page</body></html>
+                testLink(5, true, false, function() {
+                  // Pinned: Link to an about: URI should not open a new tab
+                  // Tests link to about:mozilla
+                  testLink(6, true, false, finish);
+                });
+              });
+            }, true);
           });
         });
       });
     });
   });
 }
 
 function testLink(aLinkIndex, pinTab, expectNewTab, nextTest, testSubFrame) {
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug590206.js
@@ -0,0 +1,136 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const DUMMY = "browser/browser/base/content/test/dummy_page.html";
+
+function loadNewTab(aURL, aCallback) {
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(aURL);
+
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    if (gBrowser.selectedBrowser.currentURI.spec != aURL)
+      return;
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    aCallback(gBrowser.selectedTab);
+  }, true);
+}
+
+function getIdentityMode() {
+  return document.getElementById("identity-box").className;
+}
+
+var TESTS = [
+function test_webpage() {
+  let oldTab = gBrowser.selectedTab;
+
+  loadNewTab("http://example.com/" + DUMMY, function(aNewTab) {
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.selectedTab = oldTab;
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.selectedTab = aNewTab;
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.removeTab(aNewTab);
+
+    runNextTest();
+  });
+},
+
+function test_blank() {
+  let oldTab = gBrowser.selectedTab;
+
+  loadNewTab("about:blank", function(aNewTab) {
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.selectedTab = oldTab;
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.selectedTab = aNewTab;
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.removeTab(aNewTab);
+
+    runNextTest();
+  });
+},
+
+function test_chrome() {
+  let oldTab = gBrowser.selectedTab;
+
+  loadNewTab("chrome://mozapps/content/extensions/extensions.xul", function(aNewTab) {
+    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+    gBrowser.selectedTab = oldTab;
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.selectedTab = aNewTab;
+    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+    gBrowser.removeTab(aNewTab);
+
+    runNextTest();
+  });
+},
+
+function test_https() {
+  let oldTab = gBrowser.selectedTab;
+
+  loadNewTab("https://example.com/" + DUMMY, function(aNewTab) {
+    is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
+
+    gBrowser.selectedTab = oldTab;
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.selectedTab = aNewTab;
+    is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
+
+    gBrowser.removeTab(aNewTab);
+
+    runNextTest();
+  });
+},
+
+function test_addons() {
+  let oldTab = gBrowser.selectedTab;
+
+  loadNewTab("about:addons", function(aNewTab) {
+    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+    gBrowser.selectedTab = oldTab;
+    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+    gBrowser.selectedTab = aNewTab;
+    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+    gBrowser.removeTab(aNewTab);
+
+    runNextTest();
+  });
+}
+];
+
+var gTestStart = null;
+
+function runNextTest() {
+  if (gTestStart)
+    info("Test part took " + (Date.now() - gTestStart) + "ms");
+
+  if (TESTS.length == 0) {
+    finish();
+    return;
+  }
+
+  info("Running " + TESTS[0].name);
+  gTestStart = Date.now();
+  TESTS.shift()();
+};
+
+function test() {
+  waitForExplicitFinish();
+
+  runNextTest();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_disablechrome.js
@@ -0,0 +1,141 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the disablechrome attribute gets propogated to the main UI
+
+const HTTPSRC = "http://example.com/browser/browser/base/content/test/";
+
+function is_element_hidden(aElement) {
+  var style = window.getComputedStyle(document.getElementById("nav-bar"), "");
+  if (style.visibility != "visible" || style.display == "none")
+    return true;
+
+  if (aElement.ownerDocument != aElement.parentNode)
+    return is_element_hidden(aElement.parentNode);
+
+  return false;
+}
+
+function is_chrome_hidden() {
+  is(document.documentElement.getAttribute("disablechrome"), "true", "Attribute should be set");
+  if (TabsOnTop.enabled)
+    ok(is_element_hidden(document.getElementById("nav-bar")), "Toolbar should be hidden");
+  else
+    ok(!is_element_hidden(document.getElementById("nav-bar")), "Toolbar should not be hidden");
+}
+
+function is_chrome_visible() {
+  isnot(document.getElementById("main-window").getAttribute("disablechrome"), "true", "Attribute should not be set");
+  ok(!is_element_hidden(document.getElementById("nav-bar")), "Toolbar should not be hidden");
+}
+
+function load_page(aURL, aCallback) {
+  gNewBrowser.addEventListener("pageshow", function() {
+    // Filter out about:blank loads
+    if (gNewBrowser.currentURI.spec != aURL)
+      return;
+
+    gNewBrowser.removeEventListener("pageshow", arguments.callee, false);
+    executeSoon(aCallback);
+  }, false);
+  gNewBrowser.loadURI(aURL);
+}
+
+var gOldTab;
+var gNewTab;
+var gNewBrowser;
+
+function test() {
+  var gOldTabsOnTop = TabsOnTop.enabled;
+  registerCleanupFunction(function() {
+    TabsOnTop.enabled = gOldTabsOnTop;
+  });
+
+  waitForExplicitFinish();
+
+  gOldTab = gBrowser.selectedTab;
+  gNewTab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
+  gNewBrowser = gBrowser.selectedBrowser;
+
+  info("Tabs on top");
+  TabsOnTop.enabled = true;
+
+  run_http_test_1();
+}
+
+function end_test() {
+  gBrowser.removeTab(gNewTab);
+  finish();
+}
+
+function test_url(aURL, aCanHide, aNextTest) {
+  is_chrome_visible();
+
+  info("Page load");
+  load_page(aURL, function() {
+    if (aCanHide)
+      is_chrome_hidden();
+    else
+      is_chrome_visible();
+
+    info("Switch away");
+    gBrowser.selectedTab = gOldTab;
+    is_chrome_visible();
+
+    info("Switch back");
+    gBrowser.selectedTab = gNewTab;
+    if (aCanHide)
+      is_chrome_hidden();
+    else
+      is_chrome_visible();
+
+    gBrowser.removeTab(gNewTab);
+    gNewTab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
+    gNewBrowser = gBrowser.selectedBrowser;
+
+    gBrowser.selectedTab = gOldTab;
+
+    info("Background load");
+    load_page(aURL, function() {
+      is_chrome_visible();
+
+      info("Switch back");
+      gBrowser.selectedTab = gNewTab;
+      if (aCanHide)
+        is_chrome_hidden();
+      else
+        is_chrome_visible();
+
+      load_page("about:blank", aNextTest);
+    });
+  });
+}
+
+// Should never hide the chrome
+function run_http_test_1() {
+  info("HTTP tests");
+  test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test);
+}
+
+// Should hide the chrome
+function run_chrome_about_test() {
+  info("Chrome about: tests");
+  test_url("about:addons", true, function() {
+    info("Tabs on bottom");
+    TabsOnTop.enabled = false;
+    run_http_test_2();
+  });
+}
+
+// Should never hide the chrome
+function run_http_test_2() {
+  info("HTTP tests");
+  test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_2);
+}
+
+// Should not hide the chrome
+function run_chrome_about_test_2() {
+  info("Chrome about: tests");
+  test_url("about:addons", true, end_test);
+}
--- a/browser/base/content/test/browser_overflowScroll.js
+++ b/browser/base/content/test/browser_overflowScroll.js
@@ -10,16 +10,17 @@ function right(ele)          rect(ele).r
 function isLeft(ele, msg)    is(left(ele), left(scrollbox), msg);
 function isRight(ele, msg)   is(right(ele), right(scrollbox), msg);
 function elementFromPoint(x) tabstrip._elementFromPoint(x);
 function nextLeftElement()   elementFromPoint(left(scrollbox) - 1);
 function nextRightElement()  elementFromPoint(right(scrollbox) + 1);
 function firstScrollable()   tabs[gBrowser._numPinnedTabs];
 
 function test() {
+  requestLongerTimeout(2);
   waitForExplicitFinish();
 
   // If the previous (or more) test finished with cleaning up the tabs,
   // there may be some pending animations. That can cause a failure of
   // this tests, so, we should test this in another stack.
   setTimeout(doTest, 0);
 }
 
@@ -41,39 +42,43 @@ function runOverflowTests(aEvent) {
 
   tabstrip.removeEventListener("overflow", runOverflowTests, false);
 
   var upButton = tabstrip._scrollButtonUp;
   var downButton = tabstrip._scrollButtonDown;
   var element;
 
   gBrowser.selectedTab = firstScrollable();
-  isLeft(firstScrollable(), "Selecting the first tab scrolls it into view");
+  ok(left(scrollbox) <= left(firstScrollable()), "Selecting the first tab scrolls it into view " +
+     "(" + left(scrollbox) + " <= " + left(firstScrollable()) + ")");
 
   element = nextRightElement();
   EventUtils.synthesizeMouse(downButton, 1, 1, {});
   isRight(element, "Scrolled one tab to the right with a single click");
 
   gBrowser.selectedTab = tabs[tabs.length - 1];
-  isRight(gBrowser.selectedTab, "Selecting the last tab scrolls it into view");
+  ok(right(gBrowser.selectedTab) <= right(scrollbox), "Selecting the last tab scrolls it into view " +
+     "(" + right(gBrowser.selectedTab) + " <= " + right(scrollbox) + ")");
 
   element = nextLeftElement();
   EventUtils.synthesizeMouse(upButton, 1, 1, {});
   isLeft(element, "Scrolled one tab to the left with a single click");
 
   element = elementFromPoint(left(scrollbox) - width(scrollbox));
   EventUtils.synthesizeMouse(upButton, 1, 1, {clickCount: 2});
   isLeft(element, "Scrolled one page of tabs with a double click");
 
   EventUtils.synthesizeMouse(upButton, 1, 1, {clickCount: 3});
-  isLeft(firstScrollable(), "Scrolled to the start with a triple click");
+  var firstScrollableLeft = left(firstScrollable());
+  ok(left(scrollbox) <= firstScrollableLeft, "Scrolled to the start with a triple click " +
+     "(" + left(scrollbox) + " <= " + firstScrollableLeft + ")");
 
   for (var i = 2; i; i--)
     EventUtils.synthesizeMouseScroll(scrollbox, 1, 1, {axis: "horizontal", delta: -1});
-  isLeft(firstScrollable(), "Remained at the start with the mouse wheel");
+  is(left(firstScrollable()), firstScrollableLeft, "Remained at the start with the mouse wheel");
 
   element = nextRightElement();
   EventUtils.synthesizeMouseScroll(scrollbox, 1, 1, {axis: "horizontal", delta: 1});
   isRight(element, "Scrolled one tab to the right with the mouse wheel");
 
   while (tabs.length > 1)
     gBrowser.removeTab(tabs[0]);
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_tabs_isActive.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  test_tab("about:blank");
+  test_tab("about:license");
+}
+
+function test_tab(url) {
+  let originalTab = gBrowser.selectedTab;
+  let newTab = gBrowser.addTab(url, {skipAnimation: true});
+  is(tabIsActive(newTab), false, "newly added " + url + " tab is not active");
+  is(tabIsActive(originalTab), true, "original tab is active initially");
+
+  gBrowser.selectedTab = newTab;
+  is(tabIsActive(newTab), true, "newly added " + url + " tab is active after selection");
+  is(tabIsActive(originalTab), false, "original tab is not active while unselected");
+
+  gBrowser.selectedTab = originalTab;
+  is(tabIsActive(newTab), false, "newly added " + url + " tab is not active after switch back");
+  is(tabIsActive(originalTab), true, "original tab is active again after switch back");
+  
+  gBrowser.removeTab(newTab);
+}
+
+function tabIsActive(tab) {
+  let browser = tab.linkedBrowser;
+  return browser.docShell.isActive;
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_visibleFindSelection.js
@@ -0,0 +1,39 @@
+
+function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab();
+  gBrowser.selectedTab = tab;
+  tab.linkedBrowser.addEventListener("load", function(aEvent) {
+    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    ok(true, "Load listener called");
+    waitForFocus(onFocus, content);
+  }, true);
+
+  content.location = "data:text/html,<div style='position: absolute; left: 2200px; background: green; width: 200px; height: 200px;'>div</div><div style='position: absolute; left: 0px; background: red; width: 200px; height: 200px;'><span id='s'>div</span></div>";
+}
+
+function onFocus() {
+  EventUtils.synthesizeKey("f", { accelKey: true });
+  ok(gFindBarInitialized, "find bar is now initialized");
+
+  EventUtils.synthesizeKey("d", {});
+  EventUtils.synthesizeKey("i", {});
+  EventUtils.synthesizeKey("v", {});
+  // finds the div in the green box
+
+  EventUtils.synthesizeKey("g", { accelKey: true });
+  // finds the div in the red box
+
+  var rect = content.document.getElementById("s").getBoundingClientRect();
+  ok(rect.left >= 0, "scroll should include find result");
+
+  // clear the find bar
+  EventUtils.synthesizeKey("a", { accelKey: true });
+  EventUtils.synthesizeKey("VK_DELETE", { });
+
+  gFindBar.close();
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/disablechrome.html
@@ -0,0 +1,4 @@
+<html>
+<body>
+</body>
+</html>
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -42,32 +42,40 @@ relativesrcdir  = browser/base/content/t
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
                  browser_tabview_alltabs.js \
                  browser_tabview_apptabs.js \
                  browser_tabview_bug580412.js \
+                 browser_tabview_bug586553.js \
                  browser_tabview_bug587043.js \
                  browser_tabview_bug587231.js \
+                 browser_tabview_bug587351.js \
                  browser_tabview_bug587990.js \
                  browser_tabview_bug589324.js \
                  browser_tabview_bug590606.js \
                  browser_tabview_bug591706.js \
                  browser_tabview_bug594176.js \
                  browser_tabview_bug595191.js \
                  browser_tabview_bug595518.js \
                  browser_tabview_bug595521.js \
+                 browser_tabview_bug595560.js \
                  browser_tabview_bug595804.js \
                  browser_tabview_bug595930.js \
                  browser_tabview_bug595943.js \
+                 browser_tabview_bug597248.js \
                  browser_tabview_bug597399.js \
                  browser_tabview_bug598600.js \
                  browser_tabview_bug599626.js \
+                 browser_tabview_bug600645.js \
+                 browser_tabview_bug606905.js \
+                 browser_tabview_bug608037.js \
+                 browser_tabview_bug608158.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_orphaned_tabs.js \
                  browser_tabview_privatebrowsing.js \
                  browser_tabview_rtl.js \
                  browser_tabview_search.js \
@@ -75,15 +83,16 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_startup_transitions.js \
                  browser_tabview_undo_group.js \
                  browser_tabview_firstrun_pref.js \
                  dummy_page.html \
                  head.js \
                  search1.html \
                  search2.html \
                  test_bug599626.html \
+                 test_bug600645.html \
                  $(NULL)
 
 # compartments: test disabled
 #                 browser_tabview_multiwindow_search.js \
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug586553.js
@@ -0,0 +1,95 @@
+/* ***** 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 tabview bug 586553 test.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Michael Yoshitaka Erlewine <mitcho@mitcho.com>
+ *
+ * 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 ***** */
+
+function test() {
+  waitForExplicitFinish();
+
+  window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
+  TabView.toggle();
+}
+
+let moves = 0;
+let contentWindow = null;
+let newTabs = [];
+let originalTab = null;
+
+function onTabMove(e) {
+  let tab = e.target;
+  moves++;
+}
+
+function onTabViewWindowLoaded() {
+  window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
+  
+  contentWindow = document.getElementById("tab-view").contentWindow;
+  
+  originalTab = gBrowser.selectedTab;
+  newTabs = [gBrowser.addTab("about:robots"), gBrowser.addTab("about:mozilla"), gBrowser.addTab("about:credits")];
+
+  is(originalTab._tPos, 0, "Original tab is in position 0");
+  is(newTabs[0]._tPos, 1, "Robots is in position 1");
+  is(newTabs[1]._tPos, 2, "Mozilla is in position 2");
+  is(newTabs[2]._tPos, 3, "Credits is in position 3");
+  
+  gBrowser.tabContainer.addEventListener("TabMove", onTabMove, false);
+    
+  groupItem = contentWindow.GroupItems.getActiveGroupItem();
+  
+  // move 3 > 0 (and therefore 0 > 1, 1 > 2, 2 > 3)
+  groupItem._children.splice(0, 0, groupItem._children.splice(3, 1)[0]);
+  groupItem.arrange();
+  
+  window.addEventListener("tabviewhidden", onTabViewWindowHidden, false);
+  TabView.toggle();
+}
+
+function onTabViewWindowHidden() {
+  window.removeEventListener("tabviewhidden", onTabViewWindowHidden, false);
+  gBrowser.tabContainer.removeEventListener("TabMove", onTabMove, false);
+  
+  is(moves, 1, "Only one move should be necessary for this basic move.");
+
+  is(originalTab._tPos, 1, "Original tab is in position 0");
+  is(newTabs[0]._tPos, 2, "Robots is in position 1");
+  is(newTabs[1]._tPos, 3, "Mozilla is in position 2");
+  is(newTabs[2]._tPos, 0, "Credits is in position 3");
+  
+  gBrowser.removeTab(newTabs[0]);
+  gBrowser.removeTab(newTabs[1]);
+  gBrowser.removeTab(newTabs[2]);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug587351.js
@@ -0,0 +1,72 @@
+/* ***** 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 a test for bug 587351.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Raymond Lee <raymond@appcoast.com>
+ *
+ * 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 ***** */
+
+let newTab;
+
+function test() {
+  waitForExplicitFinish();
+
+  newTab = gBrowser.addTab();
+
+  window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
+  TabView.toggle();
+}
+
+function onTabViewWindowLoaded() {
+  window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
+
+  let contentWindow = document.getElementById("tab-view").contentWindow;
+  is(contentWindow.GroupItems.groupItems.length, 1, "Has one group only");
+
+  let tabItems = contentWindow.GroupItems.groupItems[0].getChildren();
+  ok(tabItems.length, 2, "There are two tabItems in the group");
+
+  is(tabItems[1].tab, newTab, "The second tabItem is linked to the new tab");
+
+  EventUtils.sendMouseEvent({ type: "mousedown", button: 1 }, tabItems[1].container, contentWindow);
+  EventUtils.sendMouseEvent({ type: "mouseup", button: 1 }, tabItems[1].container, contentWindow);
+
+  ok(tabItems.length, 1, "There is only one tabItem in the group");
+
+  let endGame = function() {
+    window.removeEventListener("tabviewhidden", endGame, false);
+
+    finish();
+  };
+  window.addEventListener("tabviewhidden", endGame, false);
+  TabView.toggle();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug595560.js
@@ -0,0 +1,162 @@
+/* ***** 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 bug 595560 test.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Raymond Lee <raymond@appcoast.com>
+ *
+ * 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 ***** */
+
+let newTabOne;
+let originalTab;
+
+function test() {
+  waitForExplicitFinish();
+
+  originalTab = gBrowser.visibleTabs[0];
+  newTabOne = gBrowser.addTab("http://mochi.test:8888/");
+
+  let browser = gBrowser.getBrowserForTab(newTabOne);
+  let onLoad = function() {
+    browser.removeEventListener("load", onLoad, true);
+    
+    // show the tab view
+    window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
+    TabView.toggle();
+  }
+  browser.addEventListener("load", onLoad, true);
+}
+
+function onTabViewWindowLoaded() {
+  window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
+  ok(TabView.isVisible(), "Tab View is visible");
+
+  let contentWindow = document.getElementById("tab-view").contentWindow;
+  testOne(contentWindow);
+}
+
+function testOne(contentWindow) {
+  onSearchEnabledAndDisabled(contentWindow, function() {
+    testTwo(contentWindow); 
+  });
+  // execute a find command (i.e. press cmd/ctrl F)
+  document.getElementById("cmd_find").doCommand();
+}
+
+function testTwo(contentWindow) {
+  onSearchEnabledAndDisabled(contentWindow, function() { 
+    testThree(contentWindow);
+  });
+  // press /
+  EventUtils.synthesizeKey("VK_SLASH", { type: "keydown" }, contentWindow);
+}
+
+function testThree(contentWindow) {
+  let groupItem = createEmptyGroupItem(contentWindow, 200);
+
+  let onTabViewHidden = function() {
+    window.removeEventListener("tabviewhidden", onTabViewHidden, false);
+    TabView.toggle();
+  };
+  let onTabViewShown = function() {
+    window.removeEventListener("tabviewshown", onTabViewShown, false);
+
+    is(contentWindow.UI.getActiveTab(), groupItem.getChild(0), 
+       "The active tab is newly created tab item");
+
+    let onSearchEnabled = function() {
+      contentWindow.removeEventListener(
+        "tabviewsearchenabled", onSearchEnabled, false);
+
+      let searchBox = contentWindow.iQ("#searchbox");
+      searchBox.val(newTabOne.tabItem.nameEl.innerHTML);
+
+      contentWindow.performSearch();
+
+      let checkSelectedTab = function() {
+        window.removeEventListener("tabviewhidden", checkSelectedTab, false);
+        is(newTabOne, gBrowser.selectedTab, "The search result tab is shown");
+        cleanUpAndFinish(groupItem.getChild(0), contentWindow);
+      };
+      window.addEventListener("tabviewhidden", checkSelectedTab, false);
+
+      // use the tabview menu (the same as pressing cmd/ctrl + e)
+      document.getElementById("menu_tabview").doCommand();
+   };
+   contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
+   EventUtils.synthesizeKey("VK_SLASH", { type: "keydown" }, contentWindow);
+  };
+  window.addEventListener("tabviewhidden", onTabViewHidden, false);
+  window.addEventListener("tabviewshown", onTabViewShown, false);
+  
+  // click on the + button
+  let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
+  ok(newTabButton[0], "New tab button exists");
+
+  EventUtils.sendMouseEvent({ type: "click" }, newTabButton[0], contentWindow);
+}
+
+function onSearchEnabledAndDisabled(contentWindow, callback) {
+  let onSearchEnabled = function() {
+    contentWindow.removeEventListener(
+      "tabviewsearchenabled", onSearchEnabled, false);
+    contentWindow.addEventListener("tabviewsearchdisabled", onSearchDisabled, false);
+    contentWindow.hideSearch();
+  }
+  let onSearchDisabled = function() {
+    contentWindow.removeEventListener(
+      "tabviewsearchdisabled", onSearchDisabled, false);
+    callback();
+  }
+  contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
+}
+
+function cleanUpAndFinish(tabItem, contentWindow) {
+  gBrowser.selectedTab = originalTab;
+  gBrowser.removeTab(newTabOne);
+  gBrowser.removeTab(tabItem.tab);
+  
+  finish();
+}
+
+function createEmptyGroupItem(contentWindow, padding) {
+  let pageBounds = contentWindow.Items.getPageBounds();
+  pageBounds.inset(padding, padding);
+
+  let box = new contentWindow.Rect(pageBounds);
+  box.width = 300;
+  box.height = 300;
+
+  let emptyGroupItem = new contentWindow.GroupItem([], { bounds: box });
+
+  return emptyGroupItem;
+}
+
--- a/browser/base/content/test/tabview/browser_tabview_bug595930.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595930.js
@@ -6,17 +6,17 @@
  * 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 tabview test for bug 587040.
+ * The Original Code is tabview test for bug 595930.
  *
  * The Initial Developer of the Original Code is
  * Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  * Raymond Lee <raymond@appcoast.com>
@@ -66,20 +66,29 @@ function onTabViewWindowLoaded() {
     let onTabViewHidden = function() {
       window.removeEventListener("tabviewhidden", onTabViewHidden, false);
       // assert that we're no longer in tab view
       ok(!TabView.isVisible(), "Tab View is hidden");
       finish();
     };
     window.addEventListener("tabviewhidden", onTabViewHidden, false);
 
-    EventUtils.synthesizeKey("e", {accelKey : true}, contentWindow);
+    // delay to give time for hidden group DOM element to be removed so
+    // the appropriate group would get selected when the key
+    // combination is pressed
+    executeSoon(function() { 
+      EventUtils.synthesizeKey("e", {accelKey : true}, contentWindow);
+    });
+  });
+
+  group1.addSubscriber(group1, "groupHidden", function() {
+    group1.removeSubscriber(group1, "groupHidden");
+
+    // close undo group
+    let closeButton = group1.$undoContainer.find(".close");
+    EventUtils.sendMouseEvent(
+      { type: "click" }, closeButton[0], contentWindow);
   });
 
   // Get rid of the group and its children
   group1.closeAll();
-  
-  // close undo group
-  let closeButton = group1.$undoContainer.find(".close");
-  EventUtils.sendMouseEvent(
-    { type: "click" }, closeButton[0], contentWindow);
 }
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug597248.js
@@ -0,0 +1,214 @@
+/* ***** 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 tabview bug 597248 test.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Raymond Lee <raymond@appcoast.com>
+ *
+ * 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 ***** */
+
+let newWin;
+let restoredWin;
+let newTabOne;
+let newTabTwo;
+let restoredNewTabOneLoaded = false;
+let restoredNewTabTwoLoaded = false;
+let frameInitialized = false;
+
+function test() {
+  waitForExplicitFinish();
+
+  // open a new window 
+  newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
+  newWin.addEventListener("load", function(event) {
+    newWin.removeEventListener("load", arguments.callee, false);
+    setupOne();
+  }, false);
+}
+
+function setupOne() {
+  let loadedCount = 0;
+  let allLoaded = function() {
+    if (++loadedCount == 2) {
+      newWin.addEventListener("tabviewshown", setupTwo, false);
+      newWin.TabView.tog