Merge from tracemonkey.
authorDavid Anderson <danderson@mozilla.com>
Thu, 07 Oct 2010 09:55:00 -0700
changeset 74597 2bb1ec3b12a46e520748c11478424757815c1b5c
parent 74596 66356ff98dc2d6a1d7a52049dd7125720460f13d (current diff)
parent 55541 c4c67e451175f753ae334d2ed78111d96e6cfc90 (diff)
child 74598 8ccce3eba5c1e3a64b264937dc15f9c0e1dcd73d
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone2.0b8pre
Merge from tracemonkey.
browser/base/content/test/browser_overLinkInLocationBar.html
build/macosx/universal/mozconfig-64
content/base/test/file_bug543870_doc.html
content/base/test/file_bug543870_img.jpg
content/base/test/file_bug543870_inner.html
content/base/test/file_bug543870_text.txt
content/base/test/test_bug543870.html
content/html/content/test/583288_redirect_server.sjs
content/html/content/test/583288_submit_server.sjs
content/html/content/test/test_bug583288-1.html
content/html/content/test/test_bug583288-2.html
content/html/content/test/test_bug583288-3.html
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsopcode.tbl
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jsreflect.cpp
js/src/jsscope.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jstracer.cpp
js/src/jsxdrapi.h
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/CompilerBase.h
js/src/methodjit/FastArithmetic.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/FrameEntry.h
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/ImmutableSync.cpp
js/src/methodjit/ImmutableSync.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MachineRegs.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/MonoIC.h
js/src/methodjit/StubCalls.h
js/src/methodjit/TrampolineCompiler.cpp
js/src/tests/js1_8_1/trace/trace-test.js
js/src/trace-test/trace-test.py
layout/reftests/svg/image-fill-01.svg
layout/reftests/svg/image-scaling-01.svg
layout/reftests/svg/image-scaling-02.svg
layout/reftests/svg/image/background-image-rect-1png.html
layout/reftests/svg/image/background-image-rect-1svg.html
layout/reftests/svg/image/background-image-rect-2.html
layout/reftests/svg/image/background-simple-1.html
layout/reftests/svg/image/background-simple-2.html
layout/reftests/svg/image/background-viewBox-1.html
layout/reftests/svg/image/border-image-simple-1.html
layout/reftests/svg/image/border-image-simple-2.html
layout/reftests/svg/image/content-outside-viewBox-1-helper.svg
layout/reftests/svg/image/img-content-outside-viewBox-1-ref.html
layout/reftests/svg/image/img-content-outside-viewBox-1.html
layout/reftests/svg/image/img-dyn-1-ref.html
layout/reftests/svg/image/img-dyn-1.html
layout/reftests/svg/image/img-height-meet-1-ref.html
layout/reftests/svg/image/img-height-meet-1.html
layout/reftests/svg/image/img-height-meet-2-ref.html
layout/reftests/svg/image/img-height-meet-2.html
layout/reftests/svg/image/img-height-slice-1-ref.html
layout/reftests/svg/image/img-height-slice-1.html
layout/reftests/svg/image/img-height-slice-2-ref.html
layout/reftests/svg/image/img-height-slice-2.html
layout/reftests/svg/image/img-simple-1.html
layout/reftests/svg/image/img-simple-2.html
layout/reftests/svg/image/img-simple-3.html
layout/reftests/svg/image/img-simple-4.html
layout/reftests/svg/image/img-simple-5.html
layout/reftests/svg/image/img-simple-6.html
layout/reftests/svg/image/img-simple-7.html
layout/reftests/svg/image/img-width-meet-1-ref.html
layout/reftests/svg/image/img-width-meet-1.html
layout/reftests/svg/image/img-width-meet-2-ref.html
layout/reftests/svg/image/img-width-meet-2.html
layout/reftests/svg/image/img-width-slice-1-ref.html
layout/reftests/svg/image/img-width-slice-1.html
layout/reftests/svg/image/img-width-slice-2-ref.html
layout/reftests/svg/image/img-width-slice-2.html
layout/reftests/svg/image/img-widthAndHeight-meet-1-ref.html
layout/reftests/svg/image/img-widthAndHeight-meet-1.html
layout/reftests/svg/image/img-widthAndHeight-meet-2-ref.html
layout/reftests/svg/image/img-widthAndHeight-meet-2.html
layout/reftests/svg/image/img-widthAndHeight-slice-1-ref.html
layout/reftests/svg/image/img-widthAndHeight-slice-1.html
layout/reftests/svg/image/img-widthAndHeight-slice-2-ref.html
layout/reftests/svg/image/img-widthAndHeight-slice-2.html
layout/reftests/svg/image/lime100x100-noSVGDimensions.svg
layout/reftests/svg/image/lime100x100-ref.html
layout/reftests/svg/image/lime100x100.png
layout/reftests/svg/image/lime100x100.svg
layout/reftests/svg/image/lime200x100.svg
layout/reftests/svg/image/lime200x200.svg
layout/reftests/svg/image/limeInRed100x100-viewBox.svg
layout/reftests/svg/image/limeInRed100x100.png
layout/reftests/svg/image/limeInRed100x100.svg
layout/reftests/svg/image/list-simple-1-ref.html
layout/reftests/svg/image/list-simple-1.html
layout/reftests/svg/image/reftest.list
layout/reftests/svg/image/squaredCircle-viewBox-noSize.svg
layout/reftests/svg/image/svg-image-util.css
layout/reftests/svg/image/svg-image-util.js
layout/reftests/svg/image/zoom/img-zoomIn-1.html
layout/reftests/svg/image/zoom/img-zoomOut-1.html
layout/reftests/svg/image/zoom/reftest.list
layout/reftests/svg/image/zoom/squaredCircle-150x150-ref.html
layout/reftests/svg/image/zoom/squaredCircle-50x50-ref.html
layout/reftests/svg/image/zoom/squaredCircle.svg
testing/mochitest/tests/MochiKit-1.4.2/tests/SimpleTest/SimpleTest.js
testing/mozmill/mozmill-1.5.0/mozmill/extension/resource/modules/controller.js.orig
testing/mozmill/mozmill-1.5.0/mozmill/extension/resource/modules/utils.js.orig
testing/mozmill/mozmill-1.5.0/mozmill/extension/resource/modules/utils.js.rej
toolkit/crashreporter/google-breakpad/src/common/linux/memory.h
toolkit/themes/pinstripe/mozapps/extensions/navigation.png
toolkit/themes/winstripe/mozapps/extensions/navigation.png
--- a/Makefile.in
+++ b/Makefile.in
@@ -84,50 +84,61 @@ DIST_GARBAGE = config.cache config.log c
    netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
    $(topsrcdir)/.mozconfig.mk $(topsrcdir)/.mozconfig.out
 
 ifdef WINCE
 check::
 	$(PYTHON) $(topsrcdir)/build/mobile/devicemanager-utils.py copy $(DIST)/bin
 endif
 
-default alldep all:: $(topsrcdir)/configure config.status
+default alldep all:: $(topsrcdir)/configure $(topsrcdir)/js/src/configure config.status js/src/config.status
 	$(RM) -rf $(DIST)/sdk
 	$(RM) -rf $(DIST)/include
 	$(RM) -rf $(DIST)/private
 	$(RM) -rf $(DIST)/public
 	$(RM) -rf $(DIST)/bin/components
 	$(RM) -rf _tests
 
 $(topsrcdir)/configure: $(topsrcdir)/configure.in
 	@echo "STOP!  configure.in has changed, and your configure is out of date."
 	@echo "Please rerun autoconf and re-configure your build directory."
 	@echo "To ignore this message, touch 'configure' in the source directory,"
 	@echo "but your build might not succeed."
 	@exit 1
 
+$(topsrcdir)/js/src/configure: $(topsrcdir)/js/src/configure.in
+	@echo "STOP!  js/src/configure.in has changed, and your js/src/configure is out of date."
+	@echo "Please rerun autoconf in js/src and re-configure your build directory."
+	@echo "To ignore this message, touch 'js/src/configure' in the source directory,"
+	@echo "but your build might not succeed."
+	@exit 1
+
 config.status: $(topsrcdir)/configure
 	@echo "STOP!  configure has changed and needs to be run in this build directory."
 	@echo "Please rerun configure."
 	@echo "To ignore this message, touch 'config.status' in the build directory,"
 	@echo "but your build might not succeed."
 	@exit 1
 
+js/src/config.status: $(topsrcdir)/js/src/configure
+	@echo "STOP!  js/src/configure has changed and needs to be run in this build directory."
+	@echo "Please rerun js/src/configure."
+	@echo "To ignore this message, touch 'js/src/config.status' in the build directory,"
+	@echo "but your build might not succeed."
+	@exit 1
+
 # Build pseudo-external modules first when export is explicitly called
 export::
 	$(RM) -rf $(DIST)/sdk
 	$(MAKE) -C config export
 	$(MAKE) tier_nspr
 
 ifdef ENABLE_TESTS
 # Additional makefile targets to call automated test suites
 include $(topsrcdir)/testing/testsuite-targets.mk
-else
-# OS X Universal builds will want to call this, so stub it out
-package-tests:
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 distclean::
 	cat unallmakefiles | $(XARGS) rm -f
 	rm -f unallmakefiles $(DIST_GARBAGE)
 
@@ -143,17 +154,17 @@ MAKE_SYM_STORE_ARGS += --vcs-info
 endif
 DUMP_SYMS_BIN ?= $(topsrcdir)/toolkit/crashreporter/tools/win32/dump_syms.exe
 # PDB files don't get moved to dist, so we need to scan the whole objdir
 MAKE_SYM_STORE_PATH := .
 endif
 ifeq ($(OS_ARCH),Darwin)
 # need to pass arch flags for universal builds
 ifdef UNIVERSAL_BINARY
-MAKE_SYM_STORE_ARGS := -c -a "ppc i386" --vcs-info
+MAKE_SYM_STORE_ARGS := -c -a "i386 x86_64" --vcs-info
 MAKE_SYM_STORE_PATH := $(DIST)/universal
 else
 MAKE_SYM_STORE_ARGS := -c -a $(OS_TEST) --vcs-info
 MAKE_SYM_STORE_PATH := $(DIST)/bin
 endif
 DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
 endif
 ifeq (,$(filter-out Linux SunOS,$(OS_ARCH)))
--- a/accessible/public/nsIAccessibilityService.h
+++ b/accessible/public/nsIAccessibilityService.h
@@ -70,16 +70,27 @@ public:
    * @param  aNode      [in] the DOM node to get an accessible for
    * @param  aPresShell [in] the presentation shell which contains layout info
    *                         for the DOM node
    */
   virtual nsAccessible* GetAccessibleInShell(nsINode* aNode,
                                              nsIPresShell* aPresShell) = 0;
 
   /**
+   * Return root document accessible that is or contains a document accessible
+   * for the given presshell.
+   *
+   * @param aPresShell  [in] the presshell
+   * @param aCanCreate  [in] points whether the root document accessible
+   *                        should be returned from the cache or can be created
+   */
+  virtual nsAccessible* GetRootDocumentAccessible(nsIPresShell* aPresShell,
+                                                  PRBool aCanCreate) = 0;
+
+  /**
    * Creates accessible for the given DOM node or frame.
    */
   virtual already_AddRefed<nsAccessible>
     CreateHTMLBRAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0;
   virtual already_AddRefed<nsAccessible>
     CreateHTML4ButtonAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0;
   virtual already_AddRefed<nsAccessible>
     CreateHTMLButtonAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0;
@@ -165,18 +176,17 @@ public:
   virtual void PresShellDestroyed(nsIPresShell *aPresShell) = 0;
 
   /**
    * Fire accessible event of the given type for the given target.
    *
    * @param aEvent   [in] accessible event type
    * @param aTarget  [in] target of accessible event
    */
-  virtual nsresult FireAccessibleEvent(PRUint32 aEvent,
-                                       nsIAccessible *aTarget) = 0;
+  virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIAccessibilityService,
                               NS_IACCESSIBILITYSERVICE_IID)
 
 // for component registration
 // {DE401C37-9A7F-4278-A6F8-3DE2833989EF}
 #define NS_ACCESSIBILITY_SERVICE_CID \
--- a/accessible/src/base/nsAccessibilityAtomList.h
+++ b/accessible/src/base/nsAccessibilityAtomList.h
@@ -133,16 +133,17 @@ ACCESSIBILITY_ATOM(listhead, "listhead")
 ACCESSIBILITY_ATOM(listheader, "listheader") // XUL
 ACCESSIBILITY_ATOM(map, "map")
 ACCESSIBILITY_ATOM(math, "math")
 ACCESSIBILITY_ATOM(menupopup, "menupopup")     // XUL
 ACCESSIBILITY_ATOM(object, "object")
 ACCESSIBILITY_ATOM(ol, "ol")
 ACCESSIBILITY_ATOM(optgroup, "optgroup")
 ACCESSIBILITY_ATOM(option, "option")
+ACCESSIBILITY_ATOM(output, "output")
 ACCESSIBILITY_ATOM(panel, "panel") // XUL
 ACCESSIBILITY_ATOM(q, "q")
 ACCESSIBILITY_ATOM(select, "select")
 ACCESSIBILITY_ATOM(select1, "select1") // XForms
 ACCESSIBILITY_ATOM(svg, "svg")
 ACCESSIBILITY_ATOM(table, "table")
 ACCESSIBILITY_ATOM(tabpanels, "tabpanels") // XUL
 ACCESSIBILITY_ATOM(tbody, "tbody")
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -160,28 +160,51 @@ nsAccessibilityService::NotifyOfAnchorJu
   // XXX note in rare cases the node could go away before we flush the queue,
   // for example if the node becomes inaccessible, or is removed from the DOM.
   GetDocAccessible(document)->
     FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
                                targetNode);
 }
 
 // nsIAccessibilityService
-nsresult
+void
 nsAccessibilityService::FireAccessibleEvent(PRUint32 aEvent,
-                                            nsIAccessible *aTarget)
+                                            nsAccessible* aTarget)
 {
-  nsRefPtr<nsAccessible> accessible(do_QueryObject(aTarget));
-  nsEventShell::FireEvent(aEvent, accessible);
-  return NS_OK;
+  nsEventShell::FireEvent(aEvent, aTarget);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibilityService
 
+nsAccessible*
+nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell,
+                                                  PRBool aCanCreate)
+{
+  nsIDocument* documentNode = aPresShell->GetDocument();
+  if (documentNode) {
+    nsCOMPtr<nsISupports> container = documentNode->GetContainer();
+    nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
+    if (treeItem) {
+      nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
+      treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
+      if (treeItem != rootTreeItem) {
+        nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(rootTreeItem));
+        nsCOMPtr<nsIPresShell> presShell;
+        docShell->GetPresShell(getter_AddRefs(presShell));
+        documentNode = presShell->GetDocument();
+      }
+
+      return aCanCreate ?
+        GetDocAccessible(documentNode) : GetDocAccessibleFromCache(documentNode);
+    }
+  }
+  return nsnull;
+}
+
 already_AddRefed<nsAccessible>
 nsAccessibilityService::CreateOuterDocAccessible(nsIContent* aContent,
                                                  nsIPresShell* aPresShell)
 {
   nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell));
   nsAccessible* accessible = new nsOuterDocAccessible(aContent, weakShell);
   NS_IF_ADDREF(accessible);
   return accessible;
@@ -1643,16 +1666,22 @@ nsAccessibilityService::CreateHTMLAccess
 
   if (nsCoreUtils::IsHTMLTableHeader(aContent)) {
     nsAccessible* accessible = new nsHTMLTableHeaderCellAccessibleWrap(aContent,
                                                                        aWeakShell);
     NS_IF_ADDREF(accessible);
     return accessible;
   }
 
+  if (tag == nsAccessibilityAtoms::output) {
+    nsAccessible* accessible = new nsHTMLOutputAccessible(aContent, aWeakShell);
+    NS_IF_ADDREF(accessible);
+    return accessible;
+  }
+
   return nsnull;
  }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibilityService (DON'T put methods here)
 
 nsAccessible*
 nsAccessibilityService::AddNativeRootAccessible(void* aAtkAccessible)
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -55,16 +55,18 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLERETRIEVAL
   NS_DECL_NSIOBSERVER
 
   // nsIAccessibilityService
   virtual nsAccessible* GetAccessibleInShell(nsINode* aNode,
                                              nsIPresShell* aPresShell);
+  virtual nsAccessible* GetRootDocumentAccessible(nsIPresShell* aPresShell,
+                                                  PRBool aCanCreate);
 
   virtual already_AddRefed<nsAccessible>
     CreateHTMLBRAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
   virtual already_AddRefed<nsAccessible>
     CreateHTML4ButtonAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
   virtual already_AddRefed<nsAccessible>
     CreateHTMLButtonAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
   virtual already_AddRefed<nsAccessible>
@@ -112,17 +114,17 @@ public:
   virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell,
                                         nsIContent *aContent,
                                         PRUint32 aChangeType);
 
   virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
 
   virtual void PresShellDestroyed(nsIPresShell* aPresShell);
 
-  virtual nsresult FireAccessibleEvent(PRUint32 aEvent, nsIAccessible *aTarget);
+  virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget);
 
   // nsAccessibiltiyService
 
   /**
    * Return true if accessibility service has been shutdown.
    */
   static PRBool IsShutdown() { return gIsShutdown; }
 
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -2153,19 +2153,28 @@ nsAccessible::GetRelationByType(PRUint32
     {
       return nsRelUtils::
         AddTargetFromNeighbour(aRelationType, aRelation, mContent,
                                nsAccessibilityAtoms::aria_controls);
     }
 
   case nsIAccessibleRelation::RELATION_CONTROLLER_FOR:
     {
-      return nsRelUtils::
+      nsresult rv = nsRelUtils::
         AddTargetFromIDRefsAttr(aRelationType, aRelation, mContent,
                                 nsAccessibilityAtoms::aria_controls);
+      NS_ENSURE_SUCCESS(rv,rv);
+
+      if (rv != NS_OK_NO_RELATION_TARGET)
+        return NS_OK; // XXX bug 381599, avoid performance problems      
+
+      return nsRelUtils::
+        AddTargetFromNeighbour(aRelationType, aRelation, mContent,
+                               nsAccessibilityAtoms::_for,
+                               nsAccessibilityAtoms::output);
     }
 
   case nsIAccessibleRelation::RELATION_FLOWS_TO:
     {
       return nsRelUtils::
         AddTargetFromIDRefsAttr(aRelationType, aRelation, mContent,
                                 nsAccessibilityAtoms::aria_flowto);
     }
@@ -2718,17 +2727,17 @@ nsAccessible::InvalidateChildren()
 }
 
 PRBool
 nsAccessible::AppendChild(nsAccessible* aChild)
 {
   if (!mChildren.AppendElement(aChild))
     return PR_FALSE;
 
-  if (nsAccUtils::IsText(aChild))
+  if (!nsAccUtils::IsEmbeddedObject(aChild))
     mChildrenFlags = eMixedChildren;
 
   aChild->BindToParent(this, mChildren.Length() - 1);
   return PR_TRUE;
 }
 
 PRBool
 nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
--- a/accessible/src/html/nsHTMLTextAccessible.cpp
+++ b/accessible/src/html/nsHTMLTextAccessible.cpp
@@ -35,16 +35,18 @@
  * 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 "nsHTMLTextAccessible.h"
 
 #include "nsDocAccessible.h"
+#include "nsAccUtils.h"
+#include "nsRelUtils.h"
 #include "nsTextEquivUtils.h"
 
 #include "nsIFrame.h"
 #include "nsPresContext.h"
 #include "nsISelection.h"
 #include "nsISelectionController.h"
 #include "nsComponentManagerUtils.h"
 
@@ -191,16 +193,66 @@ nsHTMLLabelAccessible::GetNameInternal(n
 
 PRUint32
 nsHTMLLabelAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_LABEL;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsHTMLOuputAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+nsHTMLOutputAccessible::
+  nsHTMLOutputAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
+  nsHyperTextAccessibleWrap(aContent, aShell)
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLOutputAccessible, nsHyperTextAccessible)
+
+NS_IMETHODIMP
+nsHTMLOutputAccessible::GetRelationByType(PRUint32 aRelationType,
+                                          nsIAccessibleRelation** aRelation)
+{
+  nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType, aRelation);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (rv != NS_OK_NO_RELATION_TARGET)
+    return NS_OK; // XXX bug 381599, avoid performance problems
+
+  if (aRelationType == nsIAccessibleRelation::RELATION_CONTROLLED_BY) {
+    return nsRelUtils::
+      AddTargetFromIDRefsAttr(aRelationType, aRelation, mContent,
+                              nsAccessibilityAtoms::_for);
+  }
+
+  return NS_OK;
+}
+
+PRUint32
+nsHTMLOutputAccessible::NativeRole()
+{
+  return nsIAccessibleRole::ROLE_SECTION;
+}
+
+nsresult
+nsHTMLOutputAccessible::GetAttributesInternal(nsIPersistentProperties* aAttributes)
+{
+  nsresult rv = nsAccessibleWrap::GetAttributesInternal(aAttributes);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::live,
+                         NS_LITERAL_STRING("polite"));
+  
+  return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 // nsHTMLLIAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLLIAccessible::
   nsHTMLLIAccessible(nsIContent *aContent, nsIWeakReference *aShell,
                      const nsAString& aBulletText) :
   nsHyperTextAccessibleWrap(aContent, aShell)
 {
--- a/accessible/src/html/nsHTMLTextAccessible.h
+++ b/accessible/src/html/nsHTMLTextAccessible.h
@@ -101,16 +101,35 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint32 NativeRole();
 };
 
 /**
+ * Used for HTML output element.
+ */
+class nsHTMLOutputAccessible : public nsHyperTextAccessibleWrap
+{
+public:
+  nsHTMLOutputAccessible(nsIContent* aContent, nsIWeakReference* aShell);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIAccessible
+  NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
+                               nsIAccessibleRelation** aRelation);
+
+  // nsAccessible
+  virtual PRUint32 NativeRole();
+  virtual nsresult GetAttributesInternal(nsIPersistentProperties* aAttributes);
+};
+
+/**
  * Used for bullet of HTML list item element (for example, HTML li).
  */
 class nsHTMLListBulletAccessible : public nsLeafAccessible
 {
 public:
   nsHTMLListBulletAccessible(nsIContent *aContent, nsIWeakReference *aShell,
                              const nsAString& aBulletText);
 
--- a/accessible/src/msaa/nsAccessNodeWrap.cpp
+++ b/accessible/src/msaa/nsAccessNodeWrap.cpp
@@ -129,23 +129,69 @@ STDMETHODIMP nsAccessNodeWrap::QueryInte
    
   (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); 
   return S_OK;
 }
 
 STDMETHODIMP
 nsAccessNodeWrap::QueryService(REFGUID guidService, REFIID iid, void** ppv)
 {
+  *ppv = nsnull;
+
   static const GUID IID_SimpleDOMDeprecated = {0x0c539790,0x12e4,0x11cf,0xb6,0x61,0x00,0xaa,0x00,0x4c,0xd6,0xd8};
+
+  // Provide a special service ID for getting the accessible for the browser tab
+  // document that contains this accessible object. If this accessible object
+  // is not inside a browser tab then the service fails with E_NOINTERFACE.
+  // A use case for this is for screen readers that need to switch context or
+  // 'virtual buffer' when focus moves from one browser tab area to another.
+  static const GUID SID_IAccessibleContentDocument = {0xa5d8e1f3,0x3571,0x4d8f,0x95,0x21,0x07,0xed,0x28,0xfb,0x07,0x2e};
+
   if (guidService != IID_ISimpleDOMNode &&
       guidService != IID_SimpleDOMDeprecated &&
       guidService != IID_IAccessible &&  guidService != IID_IAccessible2 &&
-      guidService != IID_IAccessibleApplication)
+      guidService != IID_IAccessibleApplication &&
+      guidService != SID_IAccessibleContentDocument)
     return E_INVALIDARG;
 
+  if (guidService == SID_IAccessibleContentDocument) {
+    if (iid != IID_IAccessible)
+      return E_NOINTERFACE;
+
+    nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = 
+      nsCoreUtils::GetDocShellTreeItemFor(mContent);
+    if (!docShellTreeItem)
+      return E_UNEXPECTED;
+
+    // Walk up the parent chain without crossing the boundary at which item
+    // types change, preventing us from walking up out of tab content.
+    nsCOMPtr<nsIDocShellTreeItem> root;
+    docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
+    if (!root)
+      return E_UNEXPECTED;
+
+
+    // If the item type is typeContent, we assume we are in browser tab content.
+    // Note this includes content such as about:addons, for consistency.
+    PRInt32 itemType;
+    root->GetItemType(&itemType);
+    if (itemType != nsIDocShellTreeItem::typeContent)
+      return E_NOINTERFACE;
+
+    // Make sure this is a document.
+    nsDocAccessible* docAcc = nsAccUtils::GetDocAccessibleFor(root);
+    if (!docAcc)
+      return E_UNEXPECTED;
+
+    *ppv = static_cast<IAccessible*>(docAcc);
+
+    (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
+    return NS_OK;
+  }
+
   // Can get to IAccessibleApplication from any node via QS
   if (iid == IID_IAccessibleApplication) {
     nsApplicationAccessible *applicationAcc = GetApplicationAccessible();
     if (!applicationAcc)
       return E_NOINTERFACE;
 
     nsresult rv = applicationAcc->QueryNativeInterface(iid, ppv);
     return NS_SUCCEEDED(rv) ? S_OK : E_NOINTERFACE;
--- a/accessible/src/msaa/nsWinUtils.cpp
+++ b/accessible/src/msaa/nsWinUtils.cpp
@@ -59,17 +59,17 @@ nsWinUtils::ConvertToIA2Array(nsIArray *
   nsresult rv = aGeckoArray->GetLength(&length);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
   if (length == 0)
     return S_FALSE;
 
   *aIA2Array =
-    static_cast<IUnknown**>(nsMemory::Alloc((length) * sizeof(IUnknown*)));
+    static_cast<IUnknown**>(::CoTaskMemAlloc((length) * sizeof(IUnknown*)));
   if (!*aIA2Array)
     return E_OUTOFMEMORY;
 
   PRUint32 idx = 0;
   for (; idx < length; ++idx) {
     nsCOMPtr<nsIWinAccessNode> winAccessNode =
       do_QueryElementAt(aGeckoArray, idx, &rv);
     if (NS_FAILED(rv))
@@ -85,17 +85,17 @@ nsWinUtils::ConvertToIA2Array(nsIArray *
   }
 
   if (NS_FAILED(rv)) {
     for (PRUint32 idx2 = 0; idx2 < idx; idx2++) {
       (*aIA2Array)[idx2]->Release();
       (*aIA2Array)[idx2] = NULL;
     }
 
-    nsMemory::Free(*aIA2Array);
+    ::CoTaskMemFree(*aIA2Array);
     return GetHRESULT(rv);
   }
 
   *aIA2ArrayLen = length;
   return S_OK;
 }
 
 void
--- a/accessible/tests/mochitest/attributes/test_obj.html
+++ b/accessible/tests/mochitest/attributes/test_obj.html
@@ -1,13 +1,14 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=475006
 https://bugzilla.mozilla.org/show_bug.cgi?id=391829
 https://bugzilla.mozilla.org/show_bug.cgi?id=581952
+https://bugzilla.mozilla.org/show_bug.cgi?id=558036
 -->
 <head>
   <title>Group attributes 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>
@@ -34,16 +35,21 @@ https://bugzilla.mozilla.org/show_bug.cg
       testAttrs("grabbed", {"grabbed" : "true"}, true);
       testAttrs("dropeffect", {"dropeffect" : "copy"}, true);
       testAttrs("sortAscending", {"sort" : "ascending"}, true);
       testAttrs("sortDescending", {"sort" : "descending"}, true);
       testAttrs("sortNone", {"sort" : "none"}, true);
       testAttrs("sortOther", {"sort" : "other"}, true);
 
       // live object attribute
+      
+      // HTML
+      testAttrs("output", {"live" : "polite"}, true);
+
+      // ARIA
       testAttrs("live", {"live" : "polite"}, true);
       testAttrs("live2", {"live" : "polite"}, true);
       testAbsentAttrs("live3", {"live" : ""});
       testAttrs("log", {"live" : "polite"}, true);
       testAttrs("logAssertive", {"live" : "assertive"}, true);
       testAttrs("marquee", {"live" : "off"}, true);
       testAttrs("status", {"live" : "polite"}, true);
       testAttrs("timer", {"live" : "off"}, true);
@@ -103,16 +109,21 @@ https://bugzilla.mozilla.org/show_bug.cg
      title="Add support for container-live-role to object attributes">
     Mozilla Bug 391829
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=581952"
      title="Make explicit that aria-label is not an object attribute">
     Mozilla Bug 475006
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
+     title="make HTML <output> accessible">
+    Mozilla Bug 558036
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- aria -->
   <div id="atomic" aria-atomic="true"></div>
@@ -125,16 +136,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div id="checkedTreeitem" role="treeitem" aria-checked="true"></div>
   <div id="grabbed" aria-grabbed="true"></div>
   <div id="dropeffect" aria-dropeffect="copy"></div>
   <div id="sortAscending" role="columnheader" aria-sort="ascending"></div>
   <div id="sortDescending" role="columnheader" aria-sort="descending"></div>
   <div id="sortNone" role="columnheader" aria-sort="none"></div>
   <div id="sortOther" role="columnheader" aria-sort="other"></div>
 
+  <!-- html -->
+  <output id="output"></output>
+
+  <!-- back to aria -->
   <div id="live" aria-live="polite">excuse <div id="liveChild">me</div></div>
   <div id="live2" role="marquee" aria-live="polite">excuse <div id="live2Child">me</div></div>
   <div id="live3" role="region">excuse</div>
   <div id="log" role="log">excuse <div id="logChild">me</div></div>
   <div id="logAssertive" role="log" aria-live="assertive">excuse <div id="logAssertiveChild">me</div></div>
   <div id="marquee" role="marquee">excuse <div id="marqueeChild">me</div></div>
   <div id="status" role="status">excuse <div id="statusChild">me</div></div>
   <div id="timer" role="timer">excuse <div id="timerChild">me</div></div>
--- a/accessible/tests/mochitest/events/test_scroll.xul
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -68,20 +68,20 @@
       /*
        * When tests are packed in a .jar, we need to extract them so we 
        * can access the specific url with a file:// protocol which appears
        * to be required by loadURI() (at least a file without an embedded .jar)
        */
       var jar = getJar(rootDir);
       if (jar) {
         var tmpdir = extractJarToTmp(jar);
-        rootDir = "file://" + tmpdir.path;
+        rootDir = "file://" + tmpdir.path + '/';
       }
 
-      var url = rootDir + "/scroll.html#link1";
+      var url = rootDir + "scroll.html#link1";
       var tabBrowser = document.getElementById("tabBrowser");
       tabBrowser.loadURI(url);
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -80,16 +80,21 @@
       // aria-flowto, multiple relations
       testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
       testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
       testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
 
       // 'default button' relation
       testRelation("input", RELATION_DEFAULT_BUTTON, "submit");
 
+      // output 'for' relations
+      testRelation("output", RELATION_CONTROLLED_BY, ["input", "input2"]);
+      testRelation("input", RELATION_CONTROLLER_FOR, "output");
+      testRelation("input2", RELATION_CONTROLLER_FOR, "output");
+
       // 'described by'/'description for' relation for html:table and
       // html:caption
       testRelation("caption", RELATION_DESCRIPTION_FOR, "table");
       testRelation("table", RELATION_DESCRIBED_BY, "caption");
 
       // 'labelled by'/'label for' relation for html:fieldset and
       // html:legend
       testRelation("legend", RELATION_LABEL_FOR, "fieldset");
@@ -120,16 +125,21 @@
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=475298"
      title="mochitests for accessible relations">
     Mozilla Bug 475298
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
+     title="make HTML <output> accessible">
+    Mozilla Bug 558036
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <label id="label1" for="checkbox1">label</label>
   <input id="checkbox1" />
 
@@ -184,19 +194,21 @@
 
   <span id="flowto" aria-flowto="flowfrom">flow to</span>
   <span id="flowfrom">flow from</span>
 
   <span id="flowto1" aria-flowto="flowfrom1 flowfrom2">flow to</span>
   <span id="flowfrom1">flow from</span>
   <span id="flowfrom2">flow from</span>
 
-  <form>
+  <form id="form">
     <input id="input" />
+    <input id="input2" />
     <input type="submit" id="submit" />
+    <output id="output" style="display:block" for="input input2"></output>
   </form>
 
   <table id="table">
     <caption id="caption">tabple caption</caption>
     <tr>
       <td>cell1</td><td>cell2</td>
     </tr>
   </table>
--- a/accessible/tests/mochitest/test_value.html
+++ b/accessible/tests/mochitest/test_value.html
@@ -34,17 +34,17 @@
       {
         var acc = getAccessible(aID);
         if (!acc)
           return;
         is(acc.value, aValue, "Wrong value for " + aID + "!");
       }
 
       var rootDir = getRootDirectory(window.location.href);
-      var href = rootDir.path + "/foo";
+      var href = rootDir.path + "foo";
 
       // roles that can't live as nsHTMLLinkAccessibles
       testValue("aria_menuitem_link", "");
       testValue("aria_button_link", "");
       testValue("aria_checkbox_link", "");
       testValue("aria_application_link", "");
 
       // roles that can live as nsHTMLLinkAccessibles
--- a/accessible/tests/mochitest/tree/test_formctrl.html
+++ b/accessible/tests/mochitest/tree/test_formctrl.html
@@ -56,16 +56,26 @@
         children: [
           {
             role: ROLE_STATICTEXT
           }
         ]
       };
       testAccessibleTree("image_submit", accTree);
 
+      accTree = {
+        role: ROLE_SECTION,
+        children: [
+          {
+            role: ROLE_TEXT_LEAF
+          }
+        ]
+      };
+      testAccessibleTree("output", accTree);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -75,22 +85,28 @@
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045">
     Mozilla Bug 342045
   </a>
   <a target="_blank"
     title="add test for role of input type='image'"
     href="https://bugzilla.mozilla.org/show_bug.cgi?id=524521">
      Mozilla Bug 524521
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
+     title="make HTML <output> accessible">
+    Mozilla Bug 558036
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <input type="checkbox" id="checkbox">
   <input type="radio" id="radio">
   <input type="button" value="button" id="btn1">
   <button id="btn2">button</button>
 
   <input type="submit" id="submit">
   <input type="image" id="image_submit">
+  <output id="output">1337</output>
 </body>
 </html>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -65,17 +65,17 @@ pref("extensions.getAddons.search.url", 
 pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/%APP%/discovery/%VERSION%/%OS%");
 
 // Blocklist preferences
 pref("extensions.blocklist.enabled", true);
 pref("extensions.blocklist.interval", 86400);
 // Controls what level the blocklist switches from warning about items to forcibly
 // blocking them.
 pref("extensions.blocklist.level", 2);
-pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
+pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/");
 pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
 
 pref("extensions.update.autoUpdateDefault", true);
 
 // Dictionary download preference
 pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/%APP%/dictionaries/");
 
 // Update Timer Manager preferences
@@ -83,16 +83,21 @@ pref("browser.dictionaries.download.url"
 //           default=10 minutes
 pref("app.update.timer", 600000);
 
 // App-specific update preferences
 
 // The interval to check for updates (app.update.interval) is defined in
 // firefox-branding.js
 
+// Alternative windowtype for an application update user interface window. When
+// a window with this windowtype is open the application update service won't
+// open the normal application update user interface window.
+pref("app.update.altwindowtype", "Browser:About");
+
 // Enables some extra Application Update Logging (can reduce performance)
 pref("app.update.log", false);
 
 // The number of general background check failures to allow before notifying the
 // user of the failure. User initiated update checks always notify the user of
 // the failure.
 pref("app.update.backgroundMaxErrors", 10);
 
@@ -410,25 +415,22 @@ pref("javascript.options.showInConsole",
 pref("general.warnOnAboutConfig",                 false);
 #endif
 
 #ifdef WINCE
 // Set the threshold higher to avoid some slow script warnings
 pref("dom.max_script_run_time",                   20);
 #endif
 
-// Make the status bar reliably present and unaffected by pages
-pref("dom.disable_window_open_feature.status",    true);
 // This is the pref to control the location bar, change this to true to 
-// force this instead of or in addition to the status bar - this makes 
-// the origin of popup windows more obvious to avoid spoofing. We would 
-// rather not do it by default because it affects UE for web applications, but
-// without it there isn't a really good way to prevent chrome spoofing, see bug 337344
+// force this - this makes the origin of popup windows more obvious to avoid
+// spoofing. We would rather not do it by default because it affects UE for web
+// applications, but without it there isn't a really good way to prevent chrome
+// spoofing, see bug 337344
 pref("dom.disable_window_open_feature.location",  true);
-pref("dom.disable_window_status_change",          true);
 // 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);
@@ -483,18 +485,18 @@ pref("intl.charset.detector", "chrome://
 pref("intl.charset.default",  "chrome://global-platform/locale/intl.properties");
 pref("font.language.group", "chrome://global/locale/intl.properties");
 pref("intl.menuitems.alwaysappendaccesskeys","chrome://global/locale/intl.properties");
 pref("intl.menuitems.insertseparatorbeforeaccesskeys","chrome://global/locale/intl.properties");
 
 // simple gestures support
 pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
 pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate");
-pref("browser.gesture.swipe.up", "cmd_scrollTop");
-pref("browser.gesture.swipe.down", "cmd_scrollBottom");
+pref("browser.gesture.swipe.up", "Browser:HideTabView");
+pref("browser.gesture.swipe.down", "Browser:ShowTabView");
 #ifdef XP_MACOSX
 pref("browser.gesture.pinch.latched", true);
 pref("browser.gesture.pinch.threshold", 150);
 #else
 pref("browser.gesture.pinch.latched", false);
 pref("browser.gesture.pinch.threshold", 25);
 #endif
 pref("browser.gesture.pinch.out", "cmd_fullZoomEnlarge");
@@ -931,24 +933,23 @@ pref("browser.sessionstore.interval", 60
 pref("toolbar.customization.usesheet", true);
 #else
 pref("toolbar.customization.usesheet", false);
 #endif
 
 // The default for this pref reflects whether the build is capable of IPC.
 // (Turning it on in a no-IPC build will have no effect.)
 #ifdef XP_MACOSX
-// OSX still has only partial support for IPC.  Note that the PowerPC
-// and x86 builds must generate identical copies of this file, so we
-// can't make the prefs indicate that IPC is not available at all in
-// PowerPC builds.
-pref("dom.ipc.plugins.enabled", false);
-// These plug-ins will run OOP by default
-pref("dom.ipc.plugins.enabled.flash player.plugin", true);
-pref("dom.ipc.plugins.enabled.javaplugin2_npapi.plugin", true);
+// i386 ipc preferences
+pref("dom.ipc.plugins.enabled.i386", false);
+pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true);
+pref("dom.ipc.plugins.enabled.i386.javaplugin2_npapi.plugin", true);
+// x86_64 ipc preferences
+pref("dom.ipc.plugins.enabled.x86_64", true);
+pref("dom.ipc.plugins.enabled.x86_64.test.plugin", false);
 #elifdef MOZ_IPC
 pref("dom.ipc.plugins.enabled", true);
 #else
 pref("dom.ipc.plugins.enabled", false);
 #endif
 
 #ifdef XP_WIN
 #ifndef WINCE
@@ -993,18 +994,16 @@ pref("services.sync.prefs.sync.browser.t
 pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
 pref("services.sync.prefs.sync.browser.urlbar.autocomplete.enabled", true);
 pref("services.sync.prefs.sync.browser.urlbar.autoFill", true);
 pref("services.sync.prefs.sync.browser.urlbar.default.behavior", true);
 pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
 pref("services.sync.prefs.sync.dom.disable_open_during_load", true);
 pref("services.sync.prefs.sync.dom.disable_window_flip", true);
 pref("services.sync.prefs.sync.dom.disable_window_move_resize", true);
-pref("services.sync.prefs.sync.dom.disable_window_open_feature.status", true);
-pref("services.sync.prefs.sync.dom.disable_window_status_change", true);
 pref("services.sync.prefs.sync.dom.event.contextmenu.enabled", true);
 pref("services.sync.prefs.sync.extensions.personas.current", true);
 pref("services.sync.prefs.sync.extensions.update.enabled", true);
 pref("services.sync.prefs.sync.general.autoScroll", true);
 pref("services.sync.prefs.sync.general.smoothScroll", true);
 pref("services.sync.prefs.sync.intl.accept_languages", true);
 pref("services.sync.prefs.sync.javascript.enabled", true);
 pref("services.sync.prefs.sync.layout.spellcheckDefault", true);
@@ -1038,13 +1037,16 @@ pref("services.sync.prefs.sync.security.
 pref("services.sync.prefs.sync.security.warn_leaving_secure", true);
 pref("services.sync.prefs.sync.security.warn_submit_insecure", true);
 pref("services.sync.prefs.sync.security.warn_viewing_mixed", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
-// Disable the Error Console
+// Disable the error console and inspector
 pref("devtools.errorconsole.enabled", false);
+pref("devtools.inspector.enabled", false);
 
-// disable the Inspector
-pref("devtools.inspector.enabled", false);
+// Whether the character encoding menu is under the main Firefox button. This
+// preference is a string so that localizers can alter it.
+pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
+
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -36,38 +36,55 @@
 }
 
 #bottomBox {
   padding: 15px 10px 0;
 }
 
 #version {
   margin-top: 5px;
-}
-
-#released {
-  color: #666666;
+  -moz-margin-start: 0;
 }
 
 #distribution,
 #distributionId {
   font-weight: bold;
   display: none;
   margin-top: 0;
   margin-bottom: 0;
 }
 
-#checkForUpdatesButton,
 .text-blurb {
   margin-bottom: 10px;
   -moz-margin-start: 0;
   -moz-padding-start: 0;
 }
 
-.version-label,
+#updateBox {
+  margin-bottom: 10px;
+}
+
+#updateButton,
+#updateDeck > hbox > label {
+  -moz-margin-start: 0;
+  -moz-padding-start: 0;
+}
+
+#updateDeck > hbox > label:not([class="text-link"]) {
+  color: #909090;
+  font-style:italic;
+}
+
+.update-throbber {
+  width: 16px;
+  min-height: 16px;
+  -moz-margin-end: 3px;
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
+
 .trademark-label,
 .text-link,
 .text-link:focus {
   margin: 0px;
   padding: 0px;
 }
 
 .bottom-link,
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -16,16 +16,17 @@
 # The Initial Developer of the Original Code is
 # Blake Ross (blaker@netscape.com).
 # Portions created by the Initial Developer are Copyright (C) 2002
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Margaret Leibovic <margaret.leibovic@gmail.com>
+#   Robert Strong <robert.bugzilla@gmail.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
@@ -60,42 +61,479 @@ function init(aEvent)
       distroIdField.style.display = "block";
     }
   }
   catch (e) {
     // Pref is unset
   }
 
 #ifdef MOZ_UPDATER
-  initUpdates();
+  gAppUpdater = new appUpdater();
 #endif
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
 #endif
 }
 
 #ifdef MOZ_UPDATER
-/**
- * Creates "Last Updated" message and sets up "Check for Updates..." button.
- */
-function initUpdates()
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+var gAppUpdater;
+
+function onUnload(aEvent) {
+  if (gAppUpdater.isChecking)
+    gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
+  // Safe to call even when there isn't a download in progress.
+  gAppUpdater.removeDownloadListener();
+  gAppUpdater = null;
+}
+
+
+function appUpdater()
 {
-  var um = Components.classes["@mozilla.org/updates/update-manager;1"].
-           getService(Components.interfaces.nsIUpdateManager);
-  var browserBundle = Services.strings.
-                      createBundle("chrome://browser/locale/browser.properties");
+  this.updateDeck = document.getElementById("updateDeck");
+
+  // Hide the update deck when there is already an update window open to avoid
+  // syncing issues between them.
+  if (Services.wm.getMostRecentWindow("Update:Wizard")) {
+    this.updateDeck.hidden = true;
+    return;
+  }
+
+  XPCOMUtils.defineLazyServiceGetter(this, "aus",
+                                     "@mozilla.org/updates/update-service;1",
+                                     "nsIApplicationUpdateService");
+  XPCOMUtils.defineLazyServiceGetter(this, "checker",
+                                     "@mozilla.org/updates/update-checker;1",
+                                     "nsIUpdateChecker");
+  XPCOMUtils.defineLazyServiceGetter(this, "um",
+                                     "@mozilla.org/updates/update-manager;1",
+                                     "nsIUpdateManager");
+  XPCOMUtils.defineLazyServiceGetter(this, "bs",
+                                     "@mozilla.org/extensions/blocklist;1",
+                                     "nsIBlocklistService");
+
+  this.bundle = Services.strings.
+                createBundle("chrome://browser/locale/browser.properties");
 
-  if (um.updateCount) {
-    let buildID = um.getUpdateAt(0).buildID;
-    let released = browserBundle.formatStringFromName("aboutdialog.released", 
-                                                      [buildID.substring(0, 4), 
-                                                       buildID.substring(4, 6), 
-                                                       buildID.substring(6, 8)], 3);
-    document.getElementById("released").setAttribute("value", released);
+  this.updateBtn = document.getElementById("updateButton");
+
+  // The button label value must be set so its height is correct.
+  this.setupUpdateButton("update.checkInsideButton");
+
+  let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
+  let manualLink = document.getElementById("manualLink");
+  manualLink.value = manualURL;
+  manualLink.href = manualURL;
+  document.getElementById("failedLink").href = manualURL;
+
+  if (this.updateDisabledAndLocked) {
+    this.selectPanel("adminDisabled");
+    return;
+  }
+
+  if (this.isPending) {
+    this.setupUpdateButton("update.restart." +
+                           (this.isMajor ? "upgradeButton" : "applyButton"));
+    return;
+  }
+
+  if (this.isDownloading) {
+    this.startDownload();
+    return;
   }
 
-  var checkForUpdates = document.getElementById("checkForUpdatesButton");
-  setupCheckForUpdates(checkForUpdates, browserBundle);
+  if (this.updateEnabled && this.updateAuto) {
+    this.selectPanel("checkingForUpdates");
+    this.isChecking = true;
+    this.checker.checkForUpdates(this.updateCheckListener, true);
+    return;
+  }
 }
+
+appUpdater.prototype =
+{
+  // true when there is an update check in progress.
+  isChecking: false,
+
+  // true when there is an update already staged / ready to be applied.
+  get isPending() {
+    if (this.update)
+      return this.update.state == "pending";
+    return this.um.activeUpdate && this.um.activeUpdate.state == "pending";
+  },
+
+  // true when there is an update download in progress.
+  get isDownloading() {
+    if (this.update)
+      return this.update.state == "downloading";
+    return this.um.activeUpdate &&
+           this.um.activeUpdate.state == "downloading";
+  },
+
+  // true when the update type is major.
+  get isMajor() {
+    if (this.update)
+      return this.update.type == "major";
+    return this.um.activeUpdate.type == "major";
+  },
+
+  // true when updating is disabled by an administrator.
+  get updateDisabledAndLocked() {
+    return !this.updateEnabled &&
+           Services.prefs.prefIsLocked("app.update.enabled");
+  },
+
+  // true when updating is enabled.
+  get updateEnabled() {
+    try {
+      return Services.prefs.getBoolPref("app.update.enabled");
+    }
+    catch (e) { }
+    return true; // Firefox default is true
+  },
+
+  // true when updating is automatic.
+  get updateAuto() {
+    try {
+      return Services.prefs.getBoolPref("app.update.auto");
+    }
+    catch (e) { }
+    return true; // Firefox default is true
+  },
+
+  /**
+   * Sets the deck's selected panel.
+   *
+   * @param  aChildID
+   *         The id of the deck's child to select.
+   */
+  selectPanel: function(aChildID) {
+    this.updateDeck.selectedPanel = document.getElementById(aChildID);
+    this.updateBtn.disabled = (aChildID != "updateButtonBox");
+  },
+
+  /**
+   * Sets the update button's label and accesskey.
+   *
+   * @param  aKeyPrefix
+   *         The prefix for the properties file entry to use for setting the
+   *         label and accesskey.
+   */
+  setupUpdateButton: function(aKeyPrefix) {
+    this.updateBtn.label = this.bundle.GetStringFromName(aKeyPrefix + ".label");
+    this.updateBtn.accessKey = this.bundle.GetStringFromName(aKeyPrefix + ".accesskey");
+    if (!document.commandDispatcher.focusedElement ||
+        document.commandDispatcher.focusedElement.isSameNode(this.updateBtn))
+      this.updateBtn.focus();
+  },
+
+  /**
+   * Handles oncommand for the update button.
+   */
+  buttonOnCommand: function() {
+    if (this.isPending) {
+      // Notify all windows that an application quit has been requested.
+      let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
+                       createInstance(Components.interfaces.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+
+      // Something aborted the quit process.
+      if (cancelQuit.data)
+        return;
+
+      // If already in safe mode restart in safe mode (bug 327119)
+      if (Services.appinfo.inSafeMode) {
+        let env = Components.classes["@mozilla.org/process/environment;1"].
+                  getService(Components.interfaces.nsIEnvironment);
+        env.set("MOZ_SAFE_MODE_RESTART", "1");
+      }
+
+      Components.classes["@mozilla.org/toolkit/app-startup;1"].
+      getService(Components.interfaces.nsIAppStartup).
+      quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
+           Components.interfaces.nsIAppStartup.eRestart);
+      return;
+    }
+
+    const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
+    // Firefox no longer displays a license for updates and the licenseURL check
+    // is just in case a distibution does.
+    if (this.update && (this.update.billboardURL || this.update.licenseURL ||
+        this.addons.length != 0)) {
+      var ary = null;
+      ary = Components.classes["@mozilla.org/supports-array;1"].
+            createInstance(Components.interfaces.nsISupportsArray);
+      ary.AppendElement(this.update);
+      var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
+      Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary);
+      window.close();
+      return;
+    }
+
+    this.selectPanel("checkingForUpdates");
+    this.isChecking = true;
+    this.checker.checkForUpdates(this.updateCheckListener, true);
+  },
+
+  /**
+   * Implements nsIUpdateCheckListener. The methods implemented by
+   * nsIUpdateCheckListener have to be in a different scope from
+   * nsIIncrementalDownload because both nsIUpdateCheckListener and
+   * nsIIncrementalDownload implement onProgress.
+   */
+  updateCheckListener: {
+    /**
+     * See nsIUpdateService.idl
+     */
+    onProgress: function(aRequest, aPosition, aTotalSize) {
+    },
+
+    /**
+     * See nsIUpdateService.idl
+     */
+    onCheckComplete: function(aRequest, aUpdates, aUpdateCount) {
+      gAppUpdater.isChecking = false;
+      gAppUpdater.update = gAppUpdater.aus.
+                           selectUpdate(aUpdates, aUpdates.length);
+      if (!gAppUpdater.update) {
+        gAppUpdater.selectPanel("noUpdatesFound");
+        return;
+      }
+
+      if (!gAppUpdater.aus.canApplyUpdates) {
+        gAppUpdater.selectPanel("manualUpdate");
+        return;
+      }
+
+      // Firefox no longer displays a license for updates and the licenseURL
+      // check is just in case a distibution does.
+      if (gAppUpdater.update.billboardURL || gAppUpdater.update.licenseURL) {
+        gAppUpdater.selectPanel("updateButtonBox");
+        gAppUpdater.setupUpdateButton("update.openUpdateUI." +
+                                      (this.isMajor ? "upgradeButton"
+                                                    : "applyButton"));
+        return;
+      }
+
+      if (!gAppUpdater.update.appVersion ||
+          Services.vc.compare(gAppUpdater.update.appVersion,
+                              Services.appinfo.version) == 0) {
+        gAppUpdater.startDownload();
+        return;
+      }
+
+      gAppUpdater.checkAddonCompatibility();
+    },
+
+    /**
+     * See nsIUpdateService.idl
+     */
+    onError: function(aRequest, aUpdate) {
+      // Errors in the update check are treated as no updates found. If the
+      // update check fails repeatedly without a success the user will be
+      // notified with the normal app update user interface so this is safe.
+      gAppUpdater.isChecking = false;
+      gAppUpdater.selectPanel("noUpdatesFound");
+      return;
+    },
+
+    /**
+     * See nsISupports.idl
+     */
+    QueryInterface: function(aIID) {
+      if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) &&
+          !aIID.equals(Components.interfaces.nsISupports))
+        throw Components.results.NS_ERROR_NO_INTERFACE;
+      return this;
+    }
+  },
+
+  /**
+   * Checks the compatibility of add-ons for the application update.
+   */
+  checkAddonCompatibility: function() {
+    var self = this;
+    AddonManager.getAllAddons(function(aAddons) {
+      self.addons = [];
+      self.addonsCheckedCount = 0;
+      aAddons.forEach(function(aAddon) {
+        // If an add-on isn't appDisabled and isn't userDisabled then it is
+        // either active now or the user expects it to be active after the
+        // restart. If that is the case and the add-on is not installed by the
+        // application and is not compatible with the new application version
+        // then the user should be warned that the add-on will become
+        // incompatible. If an addon's type equals plugin it is skipped since
+        // checking plugins compatibility information isn't supported and
+        // getting the scope property of a plugin breaks in some environments
+        // (see bug 566787).
+        if (aAddon.type != "plugin" &&
+            !aAddon.appDisabled && !aAddon.userDisabled &&
+            aAddon.scope != AddonManager.SCOPE_APPLICATION &&
+            aAddon.isCompatible &&
+            !aAddon.isCompatibleWith(self.update.appVersion,
+                                     self.update.platformVersion))
+          self.addons.push(aAddon);
+      });
+      self.addonsTotalCount = self.addons.length;
+      if (self.addonsTotalCount == 0) {
+        self.startDownload();
+        return;
+      }
+
+      self.checkAddonsForUpdates();
+    });
+  },
+
+  /**
+   * Checks if there are updates for add-ons that are incompatible with the
+   * application update.
+   */
+  checkAddonsForUpdates: function() {
+    this.addons.forEach(function(aAddon) {
+      aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
+                         this.update.appVersion,
+                         this.update.platformVersion);
+    }, this);
+  },
+
+  /**
+   * See XPIProvider.jsm
+   */
+  onCompatibilityUpdateAvailable: function(aAddon) {
+    for (var i = 0; i < this.addons.length; ++i) {
+      if (this.addons[i].id == aAddon.id) {
+        this.addons.splice(i, 1);
+        break;
+      }
+    }
+  },
+
+  /**
+   * See XPIProvider.jsm
+   */
+  onUpdateAvailable: function(aAddon, aInstall) {
+    if (!this.bs.isAddonBlocklisted(aAddon.id, aInstall.version,
+                                    this.update.appVersion,
+                                    this.update.platformVersion)) {
+      // Compatibility or new version updates mean the same thing here.
+      this.onCompatibilityUpdateAvailable(aAddon);
+    }
+  },
+
+  /**
+   * See XPIProvider.jsm
+   */
+  onUpdateFinished: function(aAddon) {
+    ++this.addonsCheckedCount;
+
+    if (this.addonsCheckedCount < this.addonsTotalCount)
+      return;
+
+    if (this.addons.length == 0) {
+      // Compatibility updates or new version updates were found for all add-ons
+      this.startDownload();
+      return;
+    }
+
+    this.selectPanel("updateButtonBox");
+    this.setupUpdateButton("update.openUpdateUI." +
+                           (this.isMajor ? "upgradeButton" : "applyButton"));
+  },
+
+  /**
+   * Starts the download of an update mar.
+   */
+  startDownload: function() {
+    if (!this.update)
+      this.update = this.um.activeUpdate;
+    this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
+    this.update.setProperty("foregroundDownload", "true");
+
+    this.aus.pauseDownload();
+    let state = this.aus.downloadUpdate(this.update, false);
+    if (state == "failed") {
+      this.selectPanel("downloadFailed");      
+      return;
+    }
+
+    this.downloadStatus = document.getElementById("downloadStatus");
+    this.downloadStatus.value =
+      DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
+    this.selectPanel("downloading");
+    this.aus.addDownloadListener(this);
+  },
+
+  removeDownloadListener: function() {
+    this.aus.removeDownloadListener(this);
+  },
+
+  /**
+   * See nsIRequestObserver.idl
+   */
+  onStartRequest: function(aRequest, aContext) {
+  },
+
+  /**
+   * See nsIRequestObserver.idl
+   */
+  onStopRequest: function(aRequest, aContext, aStatusCode) {
+    switch (aStatusCode) {
+    case Components.results.NS_ERROR_UNEXPECTED:
+      if (this.update.selectedPatch.state == "download-failed" &&
+          (this.update.isCompleteUpdate || this.update.patchCount != 2)) {
+        // Verification error of complete patch, informational text is held in
+        // the update object.
+        this.removeDownloadListener();
+        this.selectPanel("downloadFailed");
+        break;
+      }
+      // Verification failed for a partial patch, complete patch is now
+      // downloading so return early and do NOT remove the download listener!
+      break;
+    case Components.results.NS_BINDING_ABORTED:
+      // Do not remove UI listener since the user may resume downloading again.
+      break;
+    case Components.results.NS_OK:
+      this.removeDownloadListener();
+      this.selectPanel("updateButtonBox");
+      this.setupUpdateButton("update.restart." +
+                             (this.isMajor ? "upgradeButton" : "applyButton"));
+      break;
+    default:
+      this.removeDownloadListener();
+      this.selectPanel("downloadFailed");
+      break;
+    }
+
+  },
+
+  /**
+   * See nsIProgressEventSink.idl
+   */
+  onStatus: function(aRequest, aContext, aStatus, aStatusArg) {
+  },
+
+  /**
+   * See nsIProgressEventSink.idl
+   */
+  onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
+    this.downloadStatus.value =
+      DownloadUtils.getTransferTotal(aProgress, aProgressMax);
+  },
+
+  /**
+   * See nsISupports.idl
+   */
+  QueryInterface: function(aIID) {
+    if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
+        !aIID.equals(Components.interfaces.nsIRequestObserver) &&
+        !aIID.equals(Components.interfaces.nsISupports))
+      throw Components.results.NS_ERROR_NO_INTERFACE;
+    return this;
+  }
+};
 #endif
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -18,16 +18,17 @@
 # The Initial Developer of the Original Code is
 # Blake Ross (blaker@netscape.com).
 # Portions created by the Initial Developer are Copyright (C) 2002
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Margaret Leibovic <margaret.leibovic@gmail.com>
+#   Robert Strong <robert.bugzilla@gmail.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
@@ -52,41 +53,67 @@
 <?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
 #endif
 
 <window xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="aboutDialog"
         windowtype="Browser:About"
         onload="init(event);"
+#ifdef MOZ_UPDATER
+        onunload="onUnload(event);"
+#endif
 #ifdef XP_MACOSX
         inwindowmenu="false"
 #else
         title="&aboutDialog.title;"
 #endif
         >
 
-  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript" src="chrome://browser/content/aboutDialog.js"/>
 
   <vbox>
     <hbox id="clientBox">
       <vbox id="leftBox" flex="1"/>
       <vbox id="rightBox" flex="1">
-        <description id="version">
-#expand <label class="version-label" value="__MOZ_APP_VERSION__"/> <label class="version-label" id="released"/>
-        </description>
+#expand <label id="version" value="__MOZ_APP_VERSION__"/>
         <label id="distribution" class="text-blurb"/>
         <label id="distributionId" class="text-blurb"/>
+        <vbox id="updateBox">
 #ifdef MOZ_UPDATER
-        <hbox>
-          <button id="checkForUpdatesButton" oncommand="checkForUpdates();" align="start"/>
-          <spacer flex="1"/>
-        </hbox>
+          <deck id="updateDeck" orient="vertical">
+            <hbox id="updateButtonBox" align="center">
+              <button id="updateButton" align="start"
+                      oncommand="gAppUpdater.buttonOnCommand();"/>
+              <spacer flex="1"/>
+            </hbox>
+            <hbox id="checkingForUpdates" align="center">
+              <image class="update-throbber"/><label>&update.checkingForUpdates;</label>
+            </hbox>
+            <hbox id="checkingAddonCompat" align="center">
+              <image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
+            </hbox>
+            <hbox id="downloading" align="center">
+              <image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
+            </hbox>
+            <hbox id="downloadFailed" align="center">
+              <label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
+            </hbox>
+            <hbox id="adminDisabled" align="center">
+              <label>&update.adminDisabled;</label>
+            </hbox>
+            <hbox id="noUpdatesFound" align="center">
+              <label>&update.noUpdatesFound;</label>
+            </hbox>
+            <hbox id="manualUpdate" align="center">
+              <label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
+            </hbox>
+          </deck>
 #endif
+        </vbox>
         <description class="text-blurb">
           &community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" href="about:credits">&community.creditsLink;</label>&community.end2;
         </description>
         <description class="text-blurb">
           &contribute.start;<label class="text-link" href="http://www.mozilla.org/contribute/">&contribute.getInvolvedLink;</label>&contribute.end;
         </description>
       </vbox>
     </hbox>
--- a/browser/base/content/baseMenuOverlay.xul
+++ b/browser/base/content/baseMenuOverlay.xul
@@ -103,16 +103,21 @@
                   label="&helpReleaseNotes.label;"
                   oncommand="openReleaseNotes()"
                   onclick="checkForMiddleClick(this, event);"/>
         <menuitem id="feedbackPage"
                   accesskey="&helpFeedbackPage.accesskey;"
                   label="&helpFeedbackPage.label;"
                   oncommand="openFeedbackPage()"
                   onclick="checkForMiddleClick(this, event);"/>
+        <menuitem id="helpSafeMode"
+                  accesskey="&helpSafeMode.accesskey;"
+                  label="&helpSafeMode.label;"
+                  oncommand="safeModeRestart();"/>
+        <menuseparator/>
         <menuseparator id="updateSeparator"/>
 #ifdef XP_MACOSX
 #ifdef MOZ_UPDATER
         <menuitem id="checkForUpdates"
                   label="&updateCmd.label;"
                   class="menuitem-iconic"
                   oncommand="checkForUpdates();"/>
 #endif
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -108,16 +108,17 @@
 #ifndef XP_MACOSX
                 <menuseparator/>
 #endif
 #endif
                 <menuitem id="goOfflineMenuitem"
                           label="&goOfflineCmd.label;"
                           accesskey="&goOfflineCmd.accesskey;"
                           type="checkbox"
+                          observes="workOfflineMenuitemState"
                           oncommand="BrowserOffline.toggleOfflineStatus();"/>
                 <menuitem id="menu_FileQuitItem"
 #ifdef XP_WIN
                           label="&quitApplicationCmdWin.label;"
                           accesskey="&quitApplicationCmdWin.accesskey;"
 #else
 #ifdef XP_MACOSX
                           label="&quitApplicationCmdMac.label;"
@@ -204,16 +205,17 @@
 #endif
               </menupopup>
             </menu>
 
             <menu id="view-menu" label="&viewMenu.label;"
                   accesskey="&viewMenu.accesskey;">
               <menupopup id="menu_viewPopup">
                 <menuitem id="menu_tabview"
+                          key="key_tabview"
                           label="&viewTabGroups.label;"
                           accesskey="&viewTabGroups.accesskey;"
                           command="Browser:ToggleTabView"/>
                 <menu id="viewToolbarsMenu"
                       label="&viewToolbarsMenu.label;"
                       accesskey="&viewToolbarsMenu.accesskey;">
                   <menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
                     <menuseparator/>
@@ -224,26 +226,16 @@
                               accesskey="&viewTabsOnTop.accesskey;"/>
                     <menuseparator/>
                     <menuitem id="menu_customizeToolbars"
                               label="&viewCustomizeToolbar.label;"
                               accesskey="&viewCustomizeToolbar.accesskey;"
                               command="cmd_CustomizeToolbars"/>
                   </menupopup>
                 </menu>
-                <menuitem id="toggle_taskbar"
-                          label="&taskbarCmd.label;"
-                          accesskey="&taskbarCmd.accesskey;"
-                          type="checkbox"
-                          command="cmd_toggleTaskbar"
-#ifndef WINCE
-                          checked="true" />
-#else
-                          checked="false" />
-#endif
                 <menu id="viewSidebarMenuMenu"
                       label="&viewSidebarMenu.label;"
                       accesskey="&viewSidebarMenu.accesskey;">
                   <menupopup id="viewSidebarMenu">
                     <menuitem id="menu_bookmarksSidebar"
                               key="viewBookmarksSidebarKb"
                               observes="viewBookmarksSidebar"
                               accesskey="&bookmarksButton.accesskey;"/>
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -630,18 +630,19 @@ HistoryMenu.prototype = {
         if (/^https?:/.test(iconURL))
           iconURL = "moz-anno:favicon:" + iconURL;
         m.setAttribute("image", iconURL);
       }
       m.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
       m.setAttribute("value", i);
       m.setAttribute("oncommand", "undoCloseTab(" + i + ");");
 
-      // Set the targetURI attribute so it will be shown in tooltip and statusbar.
-      // SessionStore uses one-based indexes, so we need to normalize them.
+      // Set the targetURI attribute so it will be shown in tooltip and trigger
+      // onLinkHovered. SessionStore uses one-based indexes, so we need to
+      // normalize them.
       let tabData = undoItems[i].state;
       let activeIndex = (tabData.index || tabData.entries.length) - 1;
       if (activeIndex >= 0 && tabData.entries[activeIndex])
         m.setAttribute("targetURI", tabData.entries[activeIndex].url);
 
       m.addEventListener("click", this._undoCloseMiddleClick, false);
       if (i == 0)
         m.setAttribute("key", "key_undoCloseTab");
@@ -711,17 +712,17 @@ HistoryMenu.prototype = {
         // don't initiate a connection just to fetch a favicon (see bug 467828)
         if (/^https?:/.test(iconURL))
           iconURL = "moz-anno:favicon:" + iconURL;
         m.setAttribute("image", iconURL);
       }
       m.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
       m.setAttribute("oncommand", "undoCloseWindow(" + i + ");");
 
-      // Set the targetURI attribute so it will be shown in tooltip and statusbar.
+      // Set the targetURI attribute so it will be shown in tooltip.
       // SessionStore uses one-based indexes, so we need to normalize them.
       let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
       if (activeIndex >= 0 && selectedTab.entries[activeIndex])
         m.setAttribute("targetURI", selectedTab.entries[activeIndex].url);
 
       if (i == 0)
         m.setAttribute("key", "key_undoCloseWindow");
       undoPopup.appendChild(m);
@@ -1147,51 +1148,59 @@ let BookmarksMenuButton = {
 
   init: function BMB_init() {
     this.updatePosition();
 
     // Any other stuff that does not regard the button itself should be
     // handled in the onPopupShowing handler, so it does not hit Ts.
   },
 
-  _popupInitialized: false,
-  _popupNeedsUpdating: true,
+  _popupNeedsUpdate: {},
   onPopupShowing: function BMB_onPopupShowing(event) {
-    if (!this._popupNeedsUpdating)
+    // Don't handle events for submenus.
+    if (event.target != event.currentTarget)
       return;
-    this._popupNeedsUpdating = false;
+
+    let popup = event.target;
+    let needsUpdate = this._popupNeedsUpdate[popup.id];
 
-    let viewToolbar = document.getElementById("BMB_viewBookmarksToolbar");
-    if (!this._popupInitialized) {
-      // First popupshowing event, initialize immutable attributes.
-      this._popupInitialized = true;
+    // Check if popup contents need to be updated.  Note that if needsUpdate is
+    // undefined we have never seen the popup, thus it should be updated.
+    if (needsUpdate === false)
+      return;
+    this._popupNeedsUpdate[popup.id] = false;
+
+    function getPlacesAnonymousElement(aAnonId)
+      document.getAnonymousElementByAttribute(popup.parentNode,
+                                              "placesanonid",
+                                              aAnonId);
+
+    let viewToolbarMenuitem = getPlacesAnonymousElement("view-toolbar");
+    if (viewToolbarMenuitem) {
       // Update View bookmarks toolbar checkbox menuitem.
-      viewToolbar.setAttribute("toolbarindex",
-                               Array.indexOf(gNavToolbox.childNodes,
-                                             this.personalToolbar));
-
-      // Need to set the label on Unsorted Bookmarks menu.
-      let unsortedBookmarksElt =
-        document.getElementById("BMB_unsortedBookmarksFolderMenu");
-      unsortedBookmarksElt.label =
-        PlacesUtils.getString("UnsortedBookmarksFolderTitle");
+      viewToolbarMenuitem.setAttribute("checked",
+                                       !this.personalToolbar.collapsed);
     }
 
-    // Update View Bookmarks Toolbar checkbox menuitem.
-    viewToolbar.setAttribute("checked", !this.personalToolbar.collapsed);
-
-    // Hide Bookmarks Toolbar menu if the button is next to the bookmarks
-    // toolbar item, show them otherwise.
-    let button = this.button;
-    document.getElementById("BMB_bookmarksToolbarFolderMenu").collapsed =
-      button && button.parentNode == this.bookmarksToolbarItem;
+    let toolbarMenuitem = getPlacesAnonymousElement("toolbar-autohide");
+    if (toolbarMenuitem) {
+      // If bookmarks items are visible, hide Bookmarks Toolbar menu and the
+      // separator after it.
+      toolbarMenuitem.collapsed = toolbarMenuitem.nextSibling.collapsed =
+        isElementVisible(this.bookmarksToolbarItem);
+    }
   },
 
   updatePosition: function BMB_updatePosition() {
-    this._popupNeedsUpdating = true;
+    // Popups will have to be updated when the user customizes the UI, or
+    // changes personal toolbar collapsed status.  Both of those location call
+    // updatePosition(), so this is the only point asking for popup updates.
+    for (let popupId in this._popupNeedsUpdate) {
+      this._popupNeedsUpdate[popupId] = true;
+    }
 
     let button = this.button;
     if (!button)
       return;
 
     // If the toolbar containing bookmarks is visible, we want to move the
     // button to bookmarksToolbarItem.
     let bookmarksToolbarItem = this.bookmarksToolbarItem;
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -64,17 +64,16 @@
     <command id="Browser:SendLink"
              oncommand="MailIntegration.sendLinkForWindow(window.content);"/>
 
     <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
     <command id="cmd_print" oncommand="PrintUtils.print();"/>
     <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
     <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
     <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
-    <command id="cmd_toggleTaskbar" oncommand="goToggleToolbar('status-bar','toggle_taskbar');"/>
     <command id="cmd_ToggleTabsOnTop" oncommand="TabsOnTop.toggle()"/>
     <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
     <command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
 
 
     <commandset id="editMenuCommands"/>
 
     <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(content.document);" observes="isImage"/>
@@ -113,16 +112,18 @@
     </command>
     <command id="Browser:ReloadSkipCache" oncommand="BrowserReloadSkipCache()" disabled="true">
       <observes element="Browser:Reload" attribute="disabled"/>
     </command>
     <command id="Browser:NextTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(1, true);"/>
     <command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);"/>
     <command id="Browser:ShowAllTabs" oncommand="allTabs.open();"/>
     <command id="Browser:ToggleTabView" oncommand="TabView.toggle();"/>
+    <command id="Browser:ShowTabView" oncommand="TabView.show();"/>
+    <command id="Browser:HideTabView" oncommand="TabView.hide();"/>    
     <command id="cmd_fullZoomReduce"  oncommand="FullZoom.reduce()"/>
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
@@ -191,16 +192,17 @@
     <broadcaster id="isFrameImage"/>
     <broadcaster id="singleFeedMenuitemState" disabled="true"/>
     <broadcaster id="multipleFeedsMenuState" hidden="true"/>
     <broadcaster id="tabviewGroupsNumber" groups="0"/>
 #ifdef MOZ_SERVICES_SYNC
     <broadcaster id="sync-setup-state"/>
     <broadcaster id="sync-syncnow-state"/>
 #endif
+    <broadcaster id="workOfflineMenuitemState"/>
   </broadcasterset>
 
   <keyset id="mainKeyset">
     <key id="key_newNavigator"
          key="&newNavigatorCmd.key;"
          command="cmd_newNavigator"
          modifiers="accel"/>
     <key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTab"/>
@@ -211,34 +213,31 @@
          modifiers="alt"/>
 #endif
 
 #
 # Search Command Key Logic works like this:
 # 
 # Unix: Ctrl+K (cross platform binding)
 #       Ctrl+J (in case of emacs Ctrl-K conflict)
-# Mac:  Ctrl+K (cross platform binding)
+# Mac:  Cmd+K (cross platform binding)
+#       Cmd+Opt+F (platform convention)
 # Win:  Ctrl+K (cross platform binding)
-#       Ctrl+E (IE compat)
 #
 # We support Ctrl+K on all platforms now and advertise it in the menu since it is
 # our standard - it is a "safe" choice since it is near no harmful keys like "W" as
 # "E" is. People mourning the loss of Ctrl+K for emacs compat can switch their GTK
 # system setting to use emacs emulation, and we should respect it. Focus-Search-Box
 # is a fundamental keybinding and we are maintaining a XP binding so that it is easy
 # for people to switch to Linux.
 #
     <key id="key_search" key="&searchFocus.commandkey;" command="Tools:Search" modifiers="accel"/>
 #ifdef XP_MACOSX
     <key id="key_search2" key="&findOnCmd.commandkey;" command="Tools:Search" modifiers="accel,alt"/>
 #endif
-#ifdef XP_WIN
-    <key id="key_search2" key="&searchFocus.commandkey2;" command="Tools:Search" modifiers="accel"/>
-#endif
 #ifdef XP_GNOME
     <key id="key_search2" key="&searchFocusUnix.commandkey;" command="Tools:Search" modifiers="accel"/>
     <key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/>
 #else
     <key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
 #endif
     <key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
     <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/>
@@ -343,16 +342,18 @@
     <key                          key="&fullZoomEnlargeCmd.commandkey3;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
     <key id="key_fullZoomReset"   key="&fullZoomResetCmd.commandkey;"    command="cmd_fullZoomReset"   modifiers="accel"/>
     <key                          key="&fullZoomResetCmd.commandkey2;"   command="cmd_fullZoomReset"   modifiers="accel"/>
 
     <key id="key_showAllTabs" command="Browser:ShowAllTabs" keycode="VK_TAB" modifiers="control,shift"/>
 
     <key id="key_switchTextDirection" key="&bidiSwitchTextDirectionItem.commandkey;" command="cmd_switchTextDirection" modifiers="accel,shift" />
 
+    <key id="key_tabview" key="&tabView.commandkey;" command="Browser:ToggleTabView" modifiers="accel"/>
+
     <key id="key_privatebrowsing" command="Tools:PrivateBrowsing" key="&privateBrowsingCmd.commandkey;" modifiers="accel,shift"/>
     <key id="key_sanitize" command="Tools:Sanitize" keycode="VK_DELETE" modifiers="accel,shift"/>
 #ifdef XP_MACOSX
     <key id="key_sanitize_mac" command="Tools:Sanitize" keycode="VK_BACK" modifiers="accel,shift"/>
 #endif
 #ifdef XP_UNIX
     <key id="key_quitApplication" key="&quitApplicationCmdMac.key;" command="cmd_quitApplication" modifiers="accel"/>
 #endif
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -62,34 +62,53 @@ let gSyncUI = {
                "weave:service:login:start",
                "weave:service:login:finish",
                "weave:service:login:error",
                "weave:service:logout:finish",
                "weave:service:start-over"];
 
     // If this is a browser window?
     if (gBrowser) {
-      obs.push("weave:notification:added", "weave:notification:removed");
+      obs.push("weave:notification:added");
     }
 
     let self = this;
     obs.forEach(function(topic) {
       Services.obs.addObserver(self, topic, true);
     });
 
     // Find the alltabs-popup, only if there is a gBrowser
     if (gBrowser) {
       let popup = document.getElementById("alltabs-popup");
       let self = this;
       popup.addEventListener("popupshowing", function() {
         self.alltabsPopupShowing();
       }, true);
+
+      if (Weave.Notifications.notifications.length)
+        this.initNotifications();
     }
     this.updateUI();
   },
+  
+  initNotifications: function SUI_initNotifications() {
+    const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+    let notificationbox = document.createElementNS(XULNS, "notificationbox");
+    notificationbox.id = "sync-notifications";
+    notificationbox.setAttribute("flex", "1");
+
+    let bottombox = document.getElementById("browser-bottombox");
+    bottombox.insertBefore(notificationbox, bottombox.firstChild);
+
+    // Force a style flush to ensure that our binding is attached.
+    notificationbox.clientTop;
+
+    // notificationbox will listen to observers from now on.
+    Services.obs.removeObserver(this, "weave:notification:added");
+  },
 
   _wasDelayed: false,
 
   _needsSetup: function SUI__needsSetup() {
     let firstSync = "";
     try {
       firstSync = Services.prefs.getCharPref("services.sync.firstSync");
     } catch (e) { }
@@ -135,17 +154,17 @@ let gSyncUI = {
     menuitem.setAttribute("class", "alltabs-item");
     menuitem.setAttribute("oncommand", "BrowserOpenSyncTabs();");
 
     let sep = document.createElement("menuseparator");
     sep.setAttribute("id", "sync-tabs-sep");
 
     // Fake the tab object on the menu entries, so that we don't have to worry
     // about removing them ourselves. They will just get cleaned up by popup
-    // binding. This also makes sure the statusbar updates with the URL.
+    // binding.
     menuitem.tab = { "linkedBrowser": { "currentURI": { "spec": label } } };
     sep.tab = { "linkedBrowser": { "currentURI": { "spec": " " } } };
 
     popup.insertBefore(sep, popup.firstChild);
     popup.insertBefore(menuitem, sep);
   },
 
 
@@ -168,17 +187,23 @@ let gSyncUI = {
   onSyncError: function SUI_onSyncError() {
     this._onSyncEnd(false);
   },
 
   onSyncDelay: function SUI_onSyncDelay() {
     // basically, we want to just inform users that stuff is going to take a while
     let title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
     let description = this._stringBundle.GetStringFromName("error.sync.no_node_found");
-    let notification = new Weave.Notification(title, description, null, Weave.Notifications.PRIORITY_INFO);
+    let buttons = [new Weave.NotificationButton(
+      this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
+      this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
+      function() { gWeaveWin.openServerStatus(); return true; }
+    )];
+    let notification = new Weave.Notification(
+      title, description, null, Weave.Notifications.PRIORITY_INFO, buttons);
     Weave.Notifications.replaceTitle(notification);
     this._wasDelayed = true;
   },
 
   onLoginFinish: function SUI_onLoginFinish() {
     // Clear out any login failure notifications
     let title = this._stringBundle.GetStringFromName("error.login.title");
     Weave.Notifications.removeAll(title);
@@ -232,44 +257,19 @@ let gSyncUI = {
       function() { gSyncUI.openQuotaDialog(); return true; }
     ));
 
     let notification = new Weave.Notification(
       title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
     Weave.Notifications.replaceTitle(notification);
   },
 
-  onNotificationAdded: function SUI_onNotificationAdded() {
-    if (!gBrowser)
-      return;
-
-    let notificationsButton = document.getElementById("sync-notifications-button");
-    notificationsButton.hidden = false;
-    let notification = Weave.Notifications.notifications.reduce(function(prev, cur) {
-      return prev.priority > cur.priority ? prev : cur;
-    });
-
-    let image = notification.priority >= Weave.Notifications.PRIORITY_WARNING ?
-                "chrome://global/skin/icons/warning-16.png" :
-                "chrome://global/skin/icons/information-16.png";
-    notificationsButton.image = image;
-    notificationsButton.label = notification.title;
-  },
-
-  onNotificationRemoved: function SUI_onNotificationRemoved() {
-    if (!gBrowser)
-      return;
-
-    if (Weave.Notifications.notifications.length == 0) {
-      document.getElementById("sync-notifications-button").hidden = true;
-    }
-    else {
-      // Display remaining notifications
-      this.onNotificationAdded();
-    }
+  openServerStatus: function () {
+    let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
+    window.openUILinkIn(statusURL, "tab");
   },
 
   // Commands
   doLogin: function SUI_doLogin() {
     Weave.Service.login();
   },
 
   doLogout: function SUI_doLogout() {
@@ -351,28 +351,50 @@ let gSyncUI = {
       }
       let error = Weave.Utils.getErrorString(Weave.Status.sync);
       let description =
         this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
 
       let priority = Weave.Notifications.PRIORITY_WARNING;
       let buttons = [];
 
-      if (Weave.Status.sync == Weave.OVER_QUOTA) {
+      // Check if the client is outdated in some way
+      let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
+      for (let [engine, reason] in Iterator(Weave.Status.engines))
+        outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
+
+      if (outdated) {
+        description = this._stringBundle.GetStringFromName(
+          "error.sync.needUpdate.description");
+        buttons.push(new Weave.NotificationButton(
+          this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
+          this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
+          function() { window.openUILinkIn("https://services.mozilla.com/update/", "tab"); return true; }
+        ));
+      }
+      else if (Weave.Status.sync == Weave.OVER_QUOTA) {
         description = this._stringBundle.GetStringFromName(
           "error.sync.quota.description");
         buttons.push(new Weave.NotificationButton(
           this._stringBundle.GetStringFromName(
             "error.sync.viewQuotaButton.label"),
           this._stringBundle.GetStringFromName(
             "error.sync.viewQuotaButton.accesskey"),
           function() { gSyncUI.openQuotaDialog(); return true; } )
         );
       }
-      else if (!Weave.Status.enforceBackoff) {
+      else if (Weave.Status.enforceBackoff) {
+        priority = Weave.Notifications.PRIORITY_INFO;
+        buttons.push(new Weave.NotificationButton(
+          this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
+          this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
+          function() { gSyncUI.openServerStatus(); return true; }
+        ));
+      }
+      else {
         priority = Weave.Notifications.PRIORITY_INFO;
         buttons.push(new Weave.NotificationButton(
           this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
           this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
           function() { gSyncUI.doSync(); return true; }
         ));
       }
 
@@ -429,20 +451,17 @@ let gSyncUI = {
         break;
       case "weave:service:start-over":
         this.onStartOver();
         break;
       case "weave:service:ready":
         this.initUI();
         break;
       case "weave:notification:added":
-        this.onNotificationAdded();
-        break;
-      case "weave:notification:removed":
-        this.onNotificationRemoved();
+        this.initNotifications();
         break;
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -96,16 +96,21 @@ let TabView = {
           let data = (self.isVisible() ? "true" : "false");
           self._sessionstore.setWindowValue(window, self._visibilityID, data);
         }
       }
       
       Services.obs.addObserver(observer, "quit-application-requested", false);
     }
   },
+  
+  // ----------
+  getContentWindow: function TabView_getContentWindow() {
+    return this._window;
+  },
 
   // ----------
   isVisible: function() {
     return (this._deck ? this._deck.selectedIndex == 1 : false);
   },
 
   // ----------
   show: function() {
@@ -202,57 +207,16 @@ let TabView = {
   _setBrowserKeyHandlers : function() {
     let self = this;
 
     window.addEventListener("keypress", function(event) {
       if (self.isVisible())
         return;
 
       let charCode = event.charCode;
-#ifdef XP_MACOSX
-      // if a text box in a webpage has the focus, the event.altKey would
-      // return false so we are depending on the charCode here.
-      if (!event.ctrlKey && !event.metaKey && !event.shiftKey &&
-          charCode == 160) { // alt + space
-#else
-      if (event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey && 
-          charCode == KeyEvent.DOM_VK_SPACE) { // ctrl + space
-#endif
-
-        // Don't handle this event if it's coming from a node that might allow
-        // multiple keyboard selection like selects or trees
-        let node = event.target;
-        switch (node.namespaceURI) {
-          case "http://www.w3.org/1999/xhtml":
-            // xhtml:select only allows multiple when the attr is set
-            if (node.localName == "select" && node.hasAttribute("multiple"))
-              return;
-            break;
-
-          case "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul":
-            switch (node.localName) {
-              case "listbox":
-                // xul:listbox is by default single
-                if (node.getAttribute("seltype") == "multiple")
-                  return;
-                break;
-              case "tree":
-                // xul:tree is by default multiple
-                if (node.getAttribute("seltype") != "single")
-                  return;
-                break;
-            }
-        }
-
-        event.stopPropagation();
-        event.preventDefault();
-        self.show();
-        return;
-      }
-
       // Control (+ Shift) + `
       if (event.ctrlKey && !event.metaKey && !event.altKey &&
           (charCode == 96 || charCode == 126)) {
         event.stopPropagation();
         event.preventDefault();
 
         self._initFrame(function() {
           let tabItem = self._window.GroupItems.getNextGroupItemTab(event.shiftKey);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -132,69 +132,77 @@ toolbar[mode="icons"] > #reload-button[d
 }
 
 .menuitem-iconic-tooltip,
 .menuitem-tooltip[type="checkbox"],
 .menuitem-tooltip[type="radio"] {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-iconic-tooltip");
 }
 
+#appmenu_offlineModeRecovery:not([checked=true]) {
+  display: none;
+}
+
 /* ::::: location bar ::::: */
 #urlbar {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
 #urlbar-progress {
-  -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter");
+  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-progress");
 }
 
 /* Some child nodes want to be ordered based on the locale's direction, while
    everything else should be ltr. */
+#urlbar-progress:-moz-locale-dir(rtl),
 .urlbar-input-box:-moz-locale-dir(rtl) {
   direction: rtl;
 }
 
 html|*.urlbar-input {
   direction: ltr;
 }
 
 /* over-link in location bar */
 
+/* Delay transitions on mouseout.  (Mouseover transitions are delayed by a
+   timeout in urlbarBindings.xml.) */
+.urlbar-textbox-container:not([overlinkstate]),
+.urlbar-over-link-layer:not([overlinkstate]),
+.urlbar-textbox-container-children:not([overlinkstate]),
+.urlbar-over-link-box:not([overlinkstate]) {
+  -moz-transition-delay: 100ms;
+}
+
 .urlbar-over-link-layer[overlinkstate="fade-in"],
 .urlbar-textbox-container:not([overlinkstate]) {
   -moz-transition-property: color;
   -moz-transition-duration: 150ms;
-  -moz-transition-delay: 50ms;
   -moz-transition-timing-function: cubic-bezier(0.0, 0.6, 1.0, 1.0);
 }
 
 .urlbar-textbox-container[overlinkstate="fade-in"],
 .urlbar-over-link-layer:not([overlinkstate]) {
   -moz-transition-property: color;
   -moz-transition-duration: 150ms;
-  -moz-transition-delay: 50ms;
   -moz-transition-timing-function: linear;
   color: transparent;
 }
 
 .urlbar-over-link-box[overlinkstate="fade-in"],
 .urlbar-textbox-container-children:not([overlinkstate]) {
   -moz-transition-property: opacity;
   -moz-transition-duration: 150ms;
-  -moz-transition-delay: 50ms;
-  -moz-transition-timing-function: cubic-bezier(0.0, 0.6, 1.0, 1.0);
   opacity: 1;
 }
 
 .urlbar-textbox-container-children[overlinkstate="fade-in"],
 .urlbar-over-link-box:not([overlinkstate]) {
   -moz-transition-property: opacity;
   -moz-transition-duration: 150ms;
-  -moz-transition-delay: 50ms;
-  -moz-transition-timing-function: linear;
   opacity: 0;
 }
 
 .urlbar-textbox-container[overlinkstate="showing"] {
   color: transparent;
 }
 
 .urlbar-over-link-box[overlinkstate="showing"] {
@@ -310,23 +318,24 @@ window[chromehidden~="toolbar"] toolbar:
 }
 
 #navigator-toolbox ,
 #status-bar ,
 #mainPopupSet {
   min-width: 1px;
 }
 
-/* Sync statusbar UI */
 %ifdef MOZ_SERVICES_SYNC
-#sync-notifications-box {
+/* Sync notification UI */
+#sync-notifications {
   -moz-binding: url("chrome://browser/content/syncNotification.xml#notificationbox");
+  overflow-y: visible !important;
 }
 
-#sync-notifications-box notification {
+#sync-notifications notification {
   -moz-binding: url("chrome://browser/content/syncNotification.xml#notification");
 }
 %endif
 
 /* Identity UI */
 #identity-popup-content-box.unknownIdentity > #identity-popup-connectedToLabel ,
 #identity-popup-content-box.unknownIdentity > #identity-popup-runByLabel ,
 #identity-popup-content-box.unknownIdentity > #identity-popup-content-host ,
@@ -412,8 +421,19 @@ window[chromehidden~="toolbar"] toolbar:
 
 #invalid-form-popup {
   max-width: 280px;
 }
 
 #geolocation-notification {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#geolocation-notification");
 }
+
+/* override hidden="true" for the status bar compatibility shim
+   in case it was persisted for the real status bar */
+#status-bar {
+  display: -moz-box;
+}
+
+/* Remove the resizer from the statusbar compatibility shim */
+#status-bar > .statusbar-resizerpanel {
+  display: none;
+}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -47,16 +47,18 @@
 #   Edward Lee <edward.lee@engineering.uiuc.edu>
 #   Paul O’Shannessy <paul@oshannessy.com>
 #   Nils Maier <maierman@web.de>
 #   Rob Arnold <robarnold@cmu.edu>
 #   Dietrich Ayala <dietrich@mozilla.com>
 #   Gavin Sharp <gavin@gavinsharp.com>
 #   Justin Dolske <dolske@mozilla.com>
 #   Rob Campbell <rcampbell@mozilla.com>
+#   David Dahl <ddahl@mozilla.com>
+#   Patrick Walton <pcwalton@mozilla.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
@@ -368,34 +370,24 @@ function findChildShell(aDocument, aDocS
     docShell = findChildShell(aDocument, docShell, aSoughtURI);
     if (docShell)
       return docShell;
   }
   return null;
 }
 
 const gPopupBlockerObserver = {
-  _reportButton: null,
 
   onUpdatePageReport: function (aEvent)
   {
     if (aEvent.originalTarget != gBrowser.selectedBrowser)
       return;
 
-    if (!this._reportButton)
-      this._reportButton = document.getElementById("page-report-button");
-
-    if (!gBrowser.pageReport) {
-      // Hide the popup blocker statusbar button
-      this._reportButton.hidden = true;
-
+    if (!gBrowser.pageReport)
       return;
-    }
-
-    this._reportButton.hidden = false;
 
     // Only show the notification again if we've not already shown it. Since
     // notifications are per-browser, we don't need to worry about re-adding
     // it.
     if (!gBrowser.pageReport.reported) {
       if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
         var brandBundle = document.getElementById("bundle_brand");
         var brandShortName = brandBundle.getString("brandShortName");
@@ -543,20 +535,17 @@ const gPopupBlockerObserver = {
     if (foundUsablePopupURI)
       blockedPopupsSeparator.removeAttribute("hidden");
     else
       blockedPopupsSeparator.setAttribute("hidden", true);
 
     var blockedPopupDontShowMessage = document.getElementById("blockedPopupDontShowMessage");
     var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
     blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
-    if (aEvent.target.localName == "popup")
-      blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));
-    else
-      blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromStatusbar"));
+    blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));
   },
 
   showBlockedPopup: function (aEvent)
   {
     var target = aEvent.target;
     var popupWindowURI = target.getAttribute("popupWindowURI");
     var features = target.getAttribute("popupWindowFeatures");
     var name = target.getAttribute("popupWindowName");
@@ -593,26 +582,29 @@ 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");
@@ -811,18 +803,18 @@ const gFormSubmitObserver = {
     // We are going to handle invalid form submission attempt by focusing the
     // first invalid element and show the corresponding validation message in a
     // panel attached to the element.
     if (!aInvalidElements.length) {
       return;
     }
 
     // Don't show the popup if the current tab doesn't contain the invalid form.
-    if (gBrowser.selectedTab.linkedBrowser.contentDocument !=
-        aFormElement.ownerDocument) {
+    if (gBrowser.contentDocument !=
+        aFormElement.ownerDocument.defaultView.top.document) {
       return;
     }
 
     let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
 
     if (!(element instanceof HTMLInputElement ||
           element instanceof HTMLTextAreaElement ||
           element instanceof HTMLSelectElement ||
@@ -1531,19 +1523,16 @@ function delayedStartup(isLoadingBlank, 
   // auto-resume downloads begin (such as after crashing or quitting with
   // active downloads) and speeds up the first-load of the download manager UI.
   // If the user manually opens the download manager before the timeout, the
   // downloads will start right away, and getting the service again won't hurt.
   setTimeout(function() {
     gDownloadMgr = Cc["@mozilla.org/download-manager;1"].
                    getService(Ci.nsIDownloadManager);
 
-    // Initialize the downloads monitor panel listener
-    DownloadMonitorPanel.init();
-
     if (Win7Features) {
       let tempScope = {};
       Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
                 tempScope);
       tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
     }
   }, 10000);
 
@@ -1588,22 +1577,35 @@ function delayedStartup(isLoadingBlank, 
     document.getElementById("menu_pageinspect").setAttribute("hidden", false);
     document.getElementById("Tools:Inspect").removeAttribute("disabled");
     let appMenuInspect = document.getElementById("appmenu_pageInspect");
     if (appMenuInspect)
       appMenuInspect.setAttribute("hidden", false);
   }
 
   // Enable Error Console?
-  let consoleEnabled = gPrefService.getBoolPref("devtools.errorconsole.enabled");
+  // XXX Temporarily always-enabled, see bug 601201
+  let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled");
   if (consoleEnabled) {
     document.getElementById("javascriptConsole").hidden = false;
     document.getElementById("key_errorConsole").removeAttribute("disabled");
   }
 
+  // If the user (or the locale) hasn't enabled the top-level "Character
+  // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
+  // hide it.
+  const showCharacterEncodingPref = "browser.menu.showCharacterEncoding";
+  let extraCharacterEncodingMenuEnabled = gPrefService.
+    getComplexValue(showCharacterEncodingPref, Ci.nsIPrefLocalizedString).data;
+  if (extraCharacterEncodingMenuEnabled !== "true") {
+    let charsetMenu = document.getElementById("appmenu_charsetMenu");
+    if (charsetMenu)
+      charsetMenu.setAttribute("hidden", "true");
+  }
+
   Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
 }
 
 function BrowserShutdown()
 {
   if (Win7Features)
     Win7Features.onCloseWindow();
 
@@ -1643,17 +1645,16 @@ function BrowserShutdown()
   try {
     gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
   } catch (ex) {
     Components.utils.reportError(ex);
   }
 
   BrowserOffline.uninit();
   OfflineApps.uninit();
-  DownloadMonitorPanel.uninit();
   gPrivateBrowsingUI.uninit();
   IndexedDBPromptHelper.uninit();
 
   var enumerator = Services.wm.getEnumerator(null);
   enumerator.getNext();
   if (!enumerator.hasMoreElements()) {
     document.persist("sidebar-box", "sidebarcommand");
     document.persist("sidebar-box", "width");
@@ -1676,17 +1677,17 @@ function BrowserShutdown()
 // nonBrowserWindowStartup(), nonBrowserWindowDelayedStartup(), and
 // nonBrowserWindowShutdown() are used for non-browser windows in
 // macBrowserOverlay
 function nonBrowserWindowStartup()
 {
   // Disable inappropriate commands / submenus
   var disabledItems = ['Browser:SavePage',
                        'Browser:SendLink', 'cmd_pageSetup', 'cmd_print', 'cmd_find', 'cmd_findAgain',
-                       'viewToolbarsMenu', 'cmd_toggleTaskbar', 'viewSidebarMenuMenu', 'Browser:Reload',
+                       'viewToolbarsMenu', 'viewSidebarMenuMenu', 'Browser:Reload',
                        'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
                        'viewHistorySidebar', 'Browser:AddBookmarkAs', 'View:PageInfo', 'Tasks:InspectPage'];
   var element;
 
   for (var id in disabledItems)
   {
     element = document.getElementById(disabledItems[id]);
     if (element)
@@ -2742,33 +2743,43 @@ var PrintPreviewListener = {
     this._chromeState.sidebarOpen = !sidebar.hidden;
     this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
 
     var notificationBox = gBrowser.getNotificationBox();
     this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
     notificationBox.notificationsHidden = true;
 
     document.getElementById("sidebar").setAttribute("src", "about:blank");
-    var statusbar = document.getElementById("status-bar");
-    this._chromeState.statusbarOpen = !statusbar.hidden;
-    statusbar.hidden = true;
+    var addonBar = document.getElementById("addon-bar");
+    this._chromeState.addonBarOpen = !addonBar.collapsed;
+    addonBar.collapsed = true;
 
     this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
     if (gFindBarInitialized)
       gFindBar.close();
+
+    this._chromeState.syncNotificationsOpen = false;
+    var syncNotifications = document.getElementById("sync-notifications");
+    if (syncNotifications) {
+      this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
+      syncNotifications.notificationsHidden = true;
+    }
   },
   _showChrome: function () {
     if (this._chromeState.notificationsOpen)
       gBrowser.getNotificationBox().notificationsHidden = false;
 
-    if (this._chromeState.statusbarOpen)
-      document.getElementById("status-bar").hidden = false;
+    if (this._chromeState.addonBarOpen)
+      document.getElementById("addon-bar").collapsed = false;
 
     if (this._chromeState.findOpen)
       gFindBar.open();
+
+    if (this._chromeState.syncNotificationsOpen)
+      document.getElementById("sync-notifications").notificationsHidden = false;
   }
 }
 
 function getMarkupDocumentViewer()
 {
   return gBrowser.markupDocumentViewer;
 }
 
@@ -2882,18 +2893,17 @@ var browserDragAndDrop = {
   canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true),
 
   dragOver: function (aEvent, statusString)
   {
     if (this.canDropLink(aEvent)) {
       aEvent.preventDefault();
 
       if (statusString) {
-        var statusTextFld = document.getElementById("statusbar-display");
-        statusTextFld.label = gNavigatorBundle.getString(statusString);
+        XULBrowserWindow.setStatusText(gNavigatorBundle.getString(statusString));
       }
     }
   },
 
   drop: function (aEvent, aName) Services.droppedLinkHandler.dropLink(aEvent, aName)
 };
 
 var homeButtonObserver = {
@@ -2904,18 +2914,17 @@ var homeButtonObserver = {
 
   onDragOver: function (aEvent)
     {
       browserDragAndDrop.dragOver(aEvent, "droponhomebutton");
       aEvent.dropEffect = "link";
     },
   onDragLeave: function (aEvent)
     {
-      var statusTextFld = document.getElementById("statusbar-display");
-      statusTextFld.label = "";
+      XULWindowBrowser.setStatusText("");
     }
 }
 
 function openHomeDialog(aURL)
 {
   var promptTitle = gNavigatorBundle.getString("droponhometitle");
   var promptMsg   = gNavigatorBundle.getString("droponhomemsg");
   var pressedVal  = Services.prompt.confirmEx(window, promptTitle, promptMsg,
@@ -2948,31 +2957,29 @@ var bookmarksButtonObserver = {
   onDragOver: function (aEvent)
   {
     browserDragAndDrop.dragOver(aEvent, "droponbookmarksbutton");
     aEvent.dropEffect = "link";
   },
 
   onDragLeave: function (aEvent)
   {
-    var statusTextFld = document.getElementById("statusbar-display");
-    statusTextFld.label = "";
+    XULWindowBrowser.setStatusText("");
   }
 }
 
 var newTabButtonObserver = {
   onDragOver: function (aEvent)
   {
     browserDragAndDrop.dragOver(aEvent, "droponnewtabbutton");
   },
 
   onDragLeave: function (aEvent)
   {
-    var statusTextFld = document.getElementById("statusbar-display");
-    statusTextFld.label = "";
+    XULWindowBrowser.setStatusText("");
   },
 
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
     var postData = {};
     url = getShortcutOrURI(url, postData);
     if (url) {
@@ -2984,47 +2991,44 @@ var newTabButtonObserver = {
 
 var newWindowButtonObserver = {
   onDragOver: function (aEvent)
   {
     browserDragAndDrop.dragOver(aEvent, "droponnewwindowbutton");
   },
   onDragLeave: function (aEvent)
   {
-    var statusTextFld = document.getElementById("statusbar-display");
-    statusTextFld.label = "";
+    XULWindowBrowser.setStatusText("");
   },
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
     var postData = {};
     url = getShortcutOrURI(url, postData);
     if (url) {
       // allow third-party services to fixup this URL
       openNewWindowWith(url, null, postData.value, true);
     }
   }
 }
 
 var DownloadsButtonDNDObserver = {
   onDragOver: function (aEvent)
   {
-    var statusTextFld = document.getElementById("statusbar-display");
-    statusTextFld.label = gNavigatorBundle.getString("dropondownloadsbutton");
+    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)
   {
-    var statusTextFld = document.getElementById("statusbar-display");
-    statusTextFld.label = "";
+    XULWindowBrowser.setStatusText("");
   },
 
   onDrop: function (aEvent)
   {
     let name = { };
     let url = browserDragAndDrop.drop(aEvent, name);
     if (url)
       saveURL(url, name, null, true, true);
@@ -3609,19 +3613,18 @@ var FullScreen = {
   _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
   toggle: function (event) {
     var enterFS = window.fullScreen;
 
     // We get the fullscreen event _before_ the window transitions into or out of FS mode.
     if (event && event.type == "fullscreen")
       enterFS = !enterFS;
 
-    // show/hide all menubars, toolbars, and statusbars (except the full screen toolbar)
+    // show/hide all menubars, toolbars (except the full screen toolbar)
     this.showXULChrome("toolbar", !enterFS);
-    this.showXULChrome("statusbar", !enterFS);
     document.getElementById("View:FullScreen").setAttribute("checked", enterFS);
 
     if (enterFS) {
       // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
       // This will help simulate the "collapse" metaphor while also requiring less code and
       // events than raw listening of mouse coords.
       let fullScrToggler = document.getElementById("fullscr-toggler");
       if (!fullScrToggler) {
@@ -3957,24 +3960,16 @@ var XULBrowserWindow = {
   get stopCommand () {
     delete this.stopCommand;
     return this.stopCommand = document.getElementById("Browser:Stop");
   },
   get reloadCommand () {
     delete this.reloadCommand;
     return this.reloadCommand = document.getElementById("Browser:Reload");
   },
-  get statusTextField () {
-    delete this.statusTextField;
-    return this.statusTextField = document.getElementById("statusbar-display");
-  },
-  get securityButton () {
-    delete this.securityButton;
-    return this.securityButton = document.getElementById("security-button");
-  },
   get isImage () {
     delete this.isImage;
     return this.isImage = document.getElementById("isImage");
   },
   get _uriFixup () {
     delete this._uriFixup;
     return this._uriFixup = Cc["@mozilla.org/docshell/urifixup;1"]
                               .getService(Ci.nsIURIFixup);
@@ -3991,55 +3986,39 @@ var XULBrowserWindow = {
   },
 
   destroy: function () {
     // XXXjag to avoid leaks :-/, see bug 60729
     delete this.throbberElement;
     delete this.statusMeter;
     delete this.stopCommand;
     delete this.reloadCommand;
-    delete this.statusTextField;
-    delete this.securityButton;
     delete this.statusText;
   },
 
   setJSStatus: function (status) {
     this.jsStatus = status;
-    this.updateStatusField();
   },
 
   setJSDefaultStatus: function (status) {
     this.jsDefaultStatus = status;
-    this.updateStatusField();
   },
 
   setDefaultStatus: function (status) {
     this.defaultStatus = status;
-    this.updateStatusField();
   },
 
   setOverLink: function (link) {
     // Encode bidirectional formatting characters.
     // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
     link = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
                         encodeURIComponent);
     gURLBar.setOverLink(link);
   },
 
-  updateStatusField: function () {
-    var text = this.status || this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
-
-    // check the current value so we don't trigger an attribute change
-    // and cause needless (slow!) UI updates
-    if (this.statusText != text) {
-      this.statusTextField.label = text;
-      this.statusText = text;
-    }
-  },
-
   onLinkIconAvailable: function (aIconURL) {
     if (gProxyFavIcon && gBrowser.userTypedValue === null)
       PageProxySetIcon(aIconURL); // update the favicon in the URL bar
   },
 
   onProgressChange: function (aWebProgress, aRequest,
                               aCurSelfProgress, aMaxSelfProgress,
                               aCurTotalProgress, aMaxTotalProgress) {
@@ -4288,17 +4267,16 @@ var XULBrowserWindow = {
   },
 
   asyncUpdateUI: function () {
     FeedHandler.updateFeeds();
   },
 
   onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
     this.status = aMessage;
-    this.updateStatusField();
   },
 
   // Properties used to cache security state used to update the UI
   _state: null,
   _tooltipText: null,
   _hostChanged: false, // onLocationChange will flip this bit
 
   onSecurityChange: function (aWebProgress, aRequest, aState) {
@@ -4353,31 +4331,25 @@ var XULBrowserWindow = {
         level = "low";
         break;
       case wpl.STATE_IS_BROKEN:
         level = "broken";
         break;
     }
 
     if (level) {
-      this.securityButton.setAttribute("level", level);
-      this.securityButton.hidden = false;
       // We don't style the Location Bar based on the the 'level' attribute
       // anymore, but still set it for third-party themes.
       if (gURLBar)
         gURLBar.setAttribute("level", level);
     } else {
-      this.securityButton.hidden = true;
-      this.securityButton.removeAttribute("level");
       if (gURLBar)
         gURLBar.removeAttribute("level");
     }
 
-    this.securityButton.setAttribute("tooltiptext", this._tooltipText);
-
     // 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 {
       locationObj.host = location.host;
       locationObj.hostname = location.hostname;
@@ -4386,17 +4358,17 @@ var XULBrowserWindow = {
       // 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.
     }
     gIdentityHandler.checkIdentity(this._state, locationObj);
   },
 
   // simulate all change notifications after switching tabs
-  onUpdateCurrentBrowser: function (aStateFlags, aStatus, aMessage, aTotalProgress) {
+  onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
     if (FullZoom.updateBackgroundTabs)
       FullZoom.onLocationChange(gBrowser.currentURI, true);
     var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
     var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
     // use a pseudo-object instead of a (potentially nonexistent) channel for getting
     // a correct error message - and make sure that the UI is always either in
     // loading (STATE_START) or done (STATE_STOP) mode
     this.onStateChange(
@@ -4407,31 +4379,31 @@ var XULBrowserWindow = {
     );
     // status message and progress value are undefined if we're done with loading
     if (loadingDone)
       return;
     this.onStatusChange(gBrowser.webProgress, null, 0, aMessage);
     this.onProgressChange(gBrowser.webProgress, 0, 0, aTotalProgress, 1);
   },
 
-  startDocumentLoad: function (aRequest) {
+  startDocumentLoad: function XWB_startDocumentLoad(aRequest) {
     // clear out feed data
     gBrowser.selectedBrowser.feeds = null;
 
     // clear out search-engine data
     gBrowser.selectedBrowser.engines = null;
 
     var uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
     try {
       Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
     } catch (e) {
     }
   },
 
-  endDocumentLoad: function (aRequest, aStatus) {
+  endDocumentLoad: function XWB_endDocumentLoad(aRequest, aStatus) {
     var urlStr = aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec;
 
     var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
     try {
       Services.obs.notifyObservers(content, notification, urlStr);
     } catch (e) {
     }
   }
@@ -4693,55 +4665,57 @@ function onViewToolbarsPopupShowing(aEve
   if (popup != aEvent.currentTarget)
     return;
 
   var i;
 
   // Empty the menu
   for (i = popup.childNodes.length-1; i >= 0; --i) {
     var deadItem = popup.childNodes[i];
-    if (deadItem.hasAttribute("toolbarindex"))
+    if (deadItem.hasAttribute("toolbarId"))
       popup.removeChild(deadItem);
   }
 
   var firstMenuItem = aInsertPoint || popup.firstChild;
 
-  for (i = 0; i < gNavToolbox.childNodes.length; ++i) {
-    var toolbar = gNavToolbox.childNodes[i];
+  let toolbarNodes = [document.getElementById("addon-bar")];
+  for (i = 0; i < gNavToolbox.childNodes.length; ++i)
+    toolbarNodes.push(gNavToolbox.childNodes[i]);
+  toolbarNodes.forEach(function(toolbar) {
     var toolbarName = toolbar.getAttribute("toolbarname");
     if (toolbarName) {
       let menuItem = document.createElement("menuitem");
       let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
                             "autohide" : "collapsed";
-      menuItem.setAttribute("toolbarindex", i);
+      menuItem.setAttribute("id", "toggle_" + toolbar.id);
+      menuItem.setAttribute("toolbarId", toolbar.id);
       menuItem.setAttribute("type", "checkbox");
       menuItem.setAttribute("label", toolbarName);
       menuItem.setAttribute("checked", toolbar.getAttribute(hidingAttribute) != "true");
       if (popup.id != "appmenu_customizeMenu")
         menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
       popup.insertBefore(menuItem, firstMenuItem);
 
       menuItem.addEventListener("command", onViewToolbarCommand, false);
     }
-    toolbar = toolbar.nextSibling;
-  }
+  }, this);
 }
 
 function onViewToolbarCommand(aEvent) {
-  var index = aEvent.originalTarget.getAttribute("toolbarindex");
-  var toolbar = gNavToolbox.childNodes[index];
-  var visible = aEvent.originalTarget.getAttribute("checked") == "true";
-  setToolbarVisibility(toolbar, visible);
-}
-
-function setToolbarVisibility(toolbar, visible) {
+  var toolbarId = aEvent.originalTarget.getAttribute("toolbarId");
+  var toolbar = document.getElementById(toolbarId);
+  var isVisible = aEvent.originalTarget.getAttribute("checked") == "true";
+  setToolbarVisibility(toolbar, isVisible);
+}
+
+function setToolbarVisibility(toolbar, isVisible) {
   var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
                         "autohide" : "collapsed";
 
-  toolbar.setAttribute(hidingAttribute, !visible);
+  toolbar.setAttribute(hidingAttribute, !isVisible);
   document.persist(toolbar.id, hidingAttribute);
 
   PlacesToolbarHelper.init();
   BookmarksMenuButton.updatePosition();
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
@@ -5479,17 +5453,17 @@ function setStyleDisabled(disabled) {
 /* End of the Page Style functions */
 
 var BrowserOffline = {
   /////////////////////////////////////////////////////////////////////////////
   // BrowserOffline Public Methods
   init: function ()
   {
     if (!this._uiElement)
-      this._uiElement = document.getElementById("goOfflineMenuitem");
+      this._uiElement = document.getElementById("workOfflineMenuitemState");
 
     Services.obs.addObserver(this, "network:offline-status-changed", false);
 
     this._updateOfflineUI(Services.io.offline);
   },
 
   uninit: function ()
   {
@@ -7557,20 +7531,16 @@ let gPrivateBrowsingUI = {
       docElement.setAttribute("title",
         docElement.getAttribute("title_privatebrowsing"));
       docElement.setAttribute("titlemodifier",
         docElement.getAttribute("titlemodifier_privatebrowsing"));
       docElement.setAttribute("browsingmode", "private");
       gBrowser.updateTitlebar();
     }
 
-    setTimeout(function () {
-      DownloadMonitorPanel.updateStatus();
-    }, 0);
-
     if (!aOnWindowOpen && this._disableUIOnToggle)
       document.getElementById("Tools:PrivateBrowsing")
               .setAttribute("disabled", "true");
   },
 
   onExitPrivateBrowsing: function PBUI_onExitPrivateBrowsing() {
     if (BrowserSearch.searchBar) {
       let searchBox = BrowserSearch.searchBar.textbox;
@@ -7619,20 +7589,16 @@ let gPrivateBrowsingUI = {
     document.getElementById("appmenu_privateBrowsing")
             .removeAttribute("disabled");
 #endif
     document.getElementById("Tools:PrivateBrowsing")
             .removeAttribute("disabled");
 
     gLastOpenDirectory.reset();
 
-    setTimeout(function () {
-      DownloadMonitorPanel.updateStatus();
-    }, 0);
-
     if (this._disableUIOnToggle)
       document.getElementById("Tools:PrivateBrowsing")
               .setAttribute("disabled", "true");
   },
 
   _setPBMenuTitle: function PBUI__setPBMenuTitle(aMode) {
     let pbMenuItem = document.getElementById("privateBrowsingItem");
     pbMenuItem.setAttribute("label", pbMenuItem.getAttribute(aMode + "label"));
@@ -7975,16 +7941,32 @@ XPCOMUtils.defineLazyGetter(this, "HUDCo
   try {
     return HUDService.consoleUI;
   }
   catch (ex) {
     Components.utils.reportError(ex);
   }
 });
 
+// Prompt user to restart the browser in safe mode 
+function safeModeRestart()
+{
+  // prompt the user to confirm 
+  let promptTitle = gNavigatorBundle.getString("safeModeRestartPromptTitle");
+  let promptMessage = 
+    gNavigatorBundle.getString("safeModeRestartPromptMessage");
+  let rv = Services.prompt.confirm(window, promptTitle, promptMessage);
+  if (rv) {
+    let environment = Components.classes["@mozilla.org/process/environment;1"].
+      getService(Components.interfaces.nsIEnvironment);
+    environment.set("MOZ_SAFE_MODE_RESTART", "1");
+    Application.restart();
+  }
+}
+
 /* duplicateTabIn duplicates tab in a place specified by the parameter |where|.
  *
  * |where| can be:
  *  "tab"         new tab
  *  "tabshifted"  same as "tab" but in background if default is to select new
  *                tabs, and vice versa
  *  "window"      new window
  *
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -30,16 +30,17 @@
 #   Pierre Chanial <chanial@noos.fr>
 #   Dean Tessman <dean_tessman@hotmail.com>
 #   Johnathan Nightingale <johnath@mozilla.com>
 #   Dão Gottwald <dao@mozilla.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Robert Strong <robert.bugzilla@gmail.com>
 #   Rob Campbell <rcampbell@mozilla.com>
 #   Patrick Walton <pcwalton@mozilla.com>
+#   David Dahl <ddahl@mozilla.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
@@ -483,16 +484,21 @@
           </hbox>
           <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;"
+                    id="appmenu_offlineModeRecovery"
+                    type="checkbox"
+                    observes="workOfflineMenuitemState"
+                    oncommand="BrowserOffline.toggleOfflineStatus();"/>
           <menuseparator class="appmenu-menuseparator"/>
           <hbox>
             <menuitem id="appmenu-edit-label"
                       label="&appMenuEdit.label;"
                       disabled="true"/>
             <toolbarbutton id="appmenu-cut"
                            class="appmenu-edit-button"
                            command="cmd_cut"
@@ -543,19 +549,19 @@
                           command="cmd_printPreview"/>
                 <menuitem id="appmenu_printSetup"
                           label="&printSetupCmd.label;"
                           command="cmd_pageSetup"/>
               </menupopup>
             </menu>
           </hbox>
           <menuseparator class="appmenu-menuseparator"/>
-          <menu id="appmenu_developer"
-                label="&developerMenu.label;">
-            <menupopup id="appmenu_developer_popup">
+          <menu id="appmenu_webDeveloper"
+                label="&appMenuWebDeveloper.label;">
+            <menupopup id="appmenu_webDeveloper_popup">
               <menuitem id="appmenu_webConsole"
                         label="&webConsoleCmd.label;"
                         type="checkbox"
                         oncommand="HUDConsoleUI.toggleHUD();"
                         key="key_webConsole"/>
               <menuitem id="appmenu_pageInspect"
                         hidden="true"
                         label="&inspectMenu.label;"
@@ -563,26 +569,30 @@
                         command="Tools:Inspect"
                         key="key_inspect"/>
               <menuseparator/>
               <menuitem id="appmenu_pageSource"
                         label="&viewPageSourceCmd.label;"
                         command="View:PageSource"
                         key="key_viewSource"/>
               <menuseparator/>
-#define ID_PREFIX appmenu_
+#define ID_PREFIX appmenu_developer_
 #include browser-charsetmenu.inc
 #undef ID_PREFIX
               <menuseparator/>
               <menuitem label="&goOfflineCmd.label;"
                         type="checkbox"
+                        observes="workOfflineMenuitemState"
                         oncommand="BrowserOffline.toggleOfflineStatus();"/>
             </menupopup>
           </menu>
           <menuseparator class="appmenu-menuseparator"/>
+#define ID_PREFIX appmenu_
+#include browser-charsetmenu.inc
+#undef ID_PREFIX
           <menuitem id="appmenu_fullScreen"
                     class="menuitem-tooltip"
                     label="&fullScreenCmd.label;"
                     type="checkbox"
                     observes="View:FullScreen"
                     key="key_fullScreen"/>
           <menuitem id="appmenu-quit"
                     class="menuitem-iconic"
@@ -598,34 +608,27 @@
             <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">
-              <menupopup id="appmenu_bookmarksMenupopup"
+              <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');
-                                         this.appendChild(document.getElementById('appmenu_unsortedBookmarks_seperator'));
-                                         this.appendChild(document.getElementById('appmenu_unsortedBookmarks'));"
+                                           new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
                          tooltip="bhTooltip"
                          popupsinherittooltip="true">
-                <menuseparator id="appmenu_unsortedBookmarks_seperator"/>
-                <menuitem id="appmenu_unsortedBookmarks"
-                          label="&appMenuUnsorted.label;"
-                          oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
-                          class="menuitem-iconic"/>
                 <menuitem id="appmenu_showAllBookmarks"
                           label="&showAllBookmarks.label;"
                           command="Browser:ShowAllBookmarks"
                           context=""
                           key="manBookmarkKb"/>
                 <menuseparator/>
                 <menuitem id="appmenu_bookmarkThisPage"
                           class="menuitem-iconic"
@@ -643,16 +646,35 @@
                       label="&subscribeToPageMenupopup.label;"
                       observes="multipleFeedsMenuState">
                   <menupopup id="appmenu_subscribeToPageMenupopup"
                              onpopupshowing="return FeedHandler.buildFeedList(event.target);"
                              oncommand="return FeedHandler.subscribeToFeed(null, event);"
                              onclick="checkForMiddleClick(this, event);"/>
                 </menu>
                 <menuseparator/>
+                <menu id="appmenu_bookmarksToolbar"
+                      placesanonid="toolbar-autohide"
+                      class="menu-iconic bookmark-item"
+                      label="&personalbarCmd.label;"
+                      container="true">
+                  <menupopup id="appmenu_bookmarksToolbarPopup"
+                             placespopup="true"
+                             context="placesContext"
+                             onpopupshowing="if (!this.parentNode._placesView)
+                                               new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
+                </menu>
+                <menuseparator/>
+                <!-- 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;"
@@ -716,29 +738,25 @@
               <menuitem id="appmenu_customize"
                         label="&preferencesCmd.label;"
                         class="split-menuitem-item"
                         flex="1"
                         oncommand="openPreferences();"/>
               <menu class="split-menuitem-menu"
                     label="&preferencesCmd.label;">
                 <menupopup id="appmenu_customizeMenu"
-                           onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('appmenu_toggleStatusbar'));">
+                           onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('appmenu_toggleTabsOnTop').previousSibling);">
                   <menuitem id="appmenu_preferences"
 #ifdef XP_UNIX
                             label="&preferencesCmdUnix.label;"
 #else
                             label="&preferencesCmd.label;"
 #endif
                             oncommand="openPreferences();"/>
                   <menuseparator/>
-                  <menuitem id="appmenu_toggleStatusbar"
-                            type="checkbox"
-                            command="cmd_toggleTaskbar"
-                            observes="toggle_taskbar"/>
                   <menuseparator/>
                   <menuitem id="appmenu_toggleTabsOnTop"
                             label="&viewTabsOnTop.label;"
                             type="checkbox"
                             command="cmd_ToggleTabsOnTop"/>
                   <menuitem id="appmenu_toolbarLayout"
                             label="&appMenuToolbarLayout.label;"
                             command="cmd_CustomizeToolbars"/>
@@ -756,16 +774,25 @@
                   <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});"
                             onclick="checkForMiddleClick(this, event);"/>
+                  <menuitem id="appmenu_troubleshootingInfo"
+                            label="&helpTroubleshootingInfo.label;"
+                            oncommand="openTroubleshootingPage()"
+                            onclick="checkForMiddleClick(this,event);"/>
+                  <menuseparator/>
+                  <menuitem id="appmenu_safeMode"
+                            accesskey="&appMenuSafeMode.accesskey;"
+                            label="&appMenuSafeMode.label;"
+                            oncommand="safeModeRestart();"/>
                   <menuseparator/>
                   <menuitem id="appmenu_about"
                             label="&aboutProduct.label;"
                             oncommand="openAboutDialog();"/>
                 </menupopup>
               </menu>
             </hbox>
 #ifdef MOZ_SERVICES_SYNC
@@ -935,17 +962,17 @@
             <image id="star-button"
                    class="urlbar-icon"
                    onclick="PlacesStarButton.onClick(event);"/>
             <image id="go-button"
                    class="urlbar-icon"
                    tooltiptext="&goEndCap.tooltip;"
                    onclick="gURLBar.handleCommand(event);"/>
           </hbox>
-          <progressmeter id="urlbar-progress" mode="normal"/>
+          <progressmeter id="urlbar-progress" mode="normal" value="0" collapsed="true"/>
           <toolbarbutton id="urlbar-go-button"
                          onclick="gURLBar.handleCommand(event);"
                          tooltiptext="&goEndCap.tooltip;"/>
           <toolbarbutton id="urlbar-reload-button"
                          command="Browser:ReloadOrDuplicate"
                          onclick="checkForMiddleClick(this, event);"
                          tooltiptext="&reloadButton.tooltip;"/>
           <toolbarbutton id="urlbar-stop-button"
@@ -989,70 +1016,66 @@
                      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="BMB_viewBookmarksToolbar"
+                      placesanonid="view-toolbar"
+                      toolbarId="PersonalToolbar"
+                      type="checkbox"
+                      oncommand="onViewToolbarCommand(event)"
+                      label="&viewBookmarksToolbar.label;"/>
+            <menuseparator/>
+            <menuitem id="BMB_bookmarksShowAll"
+                      label="&showAllBookmarks.label;"
+                      command="Browser:ShowAllBookmarks"
+                      key="manBookmarkKb"/>
+            <menuseparator/>
+            <menuitem id="BMB_bookmarkThisPage"
+                      class="menuitem-iconic"
+                      label="&bookmarkThisPageCmd.label;"
+                      command="Browser:AddBookmarkAs"
+                      key="addBookmarkAsKb"/>
             <menuitem id="BMB_subscribeToPageMenuitem"
                       label="&subscribeToPageMenuitem.label;"
                       oncommand="return FeedHandler.subscribeToFeed(null, event);"
                       onclick="checkForMiddleClick(this, event);"
                       observes="singleFeedMenuitemState"/>
             <menu id="BMB_subscribeToPageMenupopup"
                   label="&subscribeToPageMenupopup.label;"
                   observes="multipleFeedsMenuState">
               <menupopup id="BMB_subscribeToPageSubmenuMenupopup"
                          onpopupshowing="return FeedHandler.buildFeedList(event.target);"
                          oncommand="return FeedHandler.subscribeToFeed(null, event);"
                          onclick="checkForMiddleClick(this, event);"/>
             </menu>
-            <menuitem id="BMB_bookmarkAllTabs"
-                      label="&addCurPagesCmd.label;"
-                      command="Browser:BookmarkAllTabs"
-                      key="bookmarkAllTabsKb"/>
-            <menuitem id="BMB_bookmarksShowAll"
-                      label="&showAllBookmarks.label;"
-                      command="Browser:ShowAllBookmarks"
-                      key="manBookmarkKb"/>
             <menuseparator/>
-            <menuitem id="BMB_viewBookmarksToolbar"
-                      type="checkbox"
-                      oncommand="onViewToolbarCommand(event)"
-                      label="&viewBookmarksToolbar.label;"/>
-            <menuitem id="BMB_viewBookmarksSidebar"
-                      label="&viewBookmarksSidebar.label;"
-                      type="checkbox"
-                      oncommand="toggleSidebar('viewBookmarksSidebar');"
-                      key="viewBookmarksSidebarKb">
-              <observes element="viewBookmarksSidebar" attribute="checked"/>
-            </menuitem>
-            <menuseparator/>
-            <menu id="BMB_bookmarksToolbarFolderMenu"
+            <menu id="BMB_bookmarksToolbar"
+                  placesanonid="toolbar-autohide"
                   class="menu-iconic bookmark-item"
                   label="&personalbarCmd.label;"
                   container="true">
-              <menupopup id="BMB_bookmarksToolbarFolderPopup"
+              <menupopup id="BMB_bookmarksToolbarPopup"
                          placespopup="true"
                          context="placesContext"
                          onpopupshowing="if (!this.parentNode._placesView)
                                            new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
             </menu>
-            <menu id="BMB_unsortedBookmarksFolderMenu"
-                  class="menu-iconic bookmark-item"
-                  container="true">
-              <menupopup id="BMB_unsortedBookmarksFolderPopup"
-                         placespopup="true"
-                         context="placesContext"
-                         onpopupshowing="if (!this.parentNode._placesView)
-                                           new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS');"/>
-            </menu>
             <menuseparator/>
+            <!-- Bookmarks menu items -->
+            <menuseparator builder="end"
+                           class="hide-if-empty-places-result"/>
+            <menuitem id="BMB_unsortedBookmarks"
+                      label="&bookmarksMenuButton.unsorted.label;"
+                      oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
+                      class="menuitem-iconic"/>
           </menupopup>
         </toolbarbutton>
       </toolbaritem>
 
       <hbox id="fullscreenflex" flex="1" hidden="true" fullscreencontrol="true"/>
       <hbox id="window-controls" hidden="true" fullscreencontrol="true">
         <toolbarbutton id="minimize-button"
                        tooltiptext="&fullScreenMinimize.tooltip;"
@@ -1277,51 +1300,26 @@
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="return contentAreaClick(event, false);"/>
     </vbox>
   </hbox>
 
   <vbox id="browser-bottombox" layer="true">
-    <statusbar class="chromeclass-status" id="status-bar"
-#ifdef WINCE
-               hidden="true"
-#endif
-               >
-      <statusbarpanel id="statusbar-display" label="" flex="1"/>
-      <statusbarpanel id="download-monitor" class="statusbarpanel-iconic-text"
-                      tooltiptext="&downloadMonitor2.tooltip;" hidden="true"
-                      command="Tools:Downloads"/>
-      <statusbarpanel id="security-button" class="statusbarpanel-iconic"
-                      hidden="true"
-                      onclick="if (event.button == 0 &amp;&amp; event.detail == 1) displaySecurityInfo();"/>
-#ifdef MOZ_SERVICES_SYNC
-      <statusbarpanel id="sync-notifications-button"
-                      class="statusbarpanel-iconic-text"
-                      hidden="true"
-                      popup="sync-notifications-panel">
-      </statusbarpanel>
-      <panel id="sync-notifications-panel" position="before_end">
-          <notificationbox id="sync-notifications-box"/>
-      </panel>
-#endif
-      <statusbarpanel id="page-report-button" type="menu"
-                      class="statusbarpanel-menu-iconic"
-                      hidden="true"
-                      tooltiptext="&pageReportIcon.tooltip;">
-        <menupopup onpopupshowing="gPopupBlockerObserver.fillPopupList(event);">
-          <menuitem observes="blockedPopupAllowSite"/>
-          <menuitem observes="blockedPopupEditSettings"/>
-          <menuitem observes="blockedPopupDontShowMessage"/>
-          <menuseparator observes="blockedPopupsSeparator"/>
-        </menupopup>
-      </statusbarpanel>
-    </statusbar>
+    <toolbar id="addon-bar" toolbarname="&addonBarCmd.label;" collapsed="true"
+             class="toolbar-primary chromeclass-toolbar"
+             context="toolbar-context-menu" toolboxid="navigator-toolbox"
+             mode="icons" iconsize="small" defaulticonsize="small"
+             lockiconsize="true"
+             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">
       <svg:rect x="0" y="0" width="1" height="1" fill="white"/>
       <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"/>
--- a/browser/base/content/pageReportFirstTime.xul
+++ b/browser/base/content/pageReportFirstTime.xul
@@ -51,26 +51,23 @@
         title="&caption.label;"
         onload="setTimeout(function() { window.sizeToContent(); }, 100);"
         style="width: 40em;"
         buttons="accept"
         persist="screenX screenY"
         screenX="24" screenY="24">
               
   <description>
-    &startDescription.label;
+    &startDescriptionText.label;
   </description>
 
   <separator class="thin"/>
 
   <hbox pack="center">
-    <statusbar style="width:20em">
-      <statusbarpanel flex="1" pack="left"><description>&done.label;</description></statusbarpanel>
-      <statusbarpanel class="statusbarpanel-iconic" style="min-height:18px" id="page-report-button"/>
-    </statusbar>
+    <!-- insert example here! (bug 594294) -->
   </hbox>
 
   <separator class="thin"/>
 
   <description>
     &endDescription.label;
   </description>
 
--- a/browser/base/content/syncGenericChange.js
+++ b/browser/base/content/syncGenericChange.js
@@ -70,16 +70,17 @@ let Change = {
     let introText2 = document.getElementById("introText2");
     let warningText = document.getElementById("warningText");
 
     // load some other elements & info from the window
     this._dialog = document.getElementById("change-dialog");
     this._dialogType = window.arguments[0];
     this._status = document.getElementById("status");
     this._statusIcon = document.getElementById("statusIcon");
+    this._statusRow = document.getElementById("statusRow");
     this._firstBox = document.getElementById("textBox1");
     this._secondBox = document.getElementById("textBox2");
 
     this._stringBundle =
       Services.strings.createBundle("chrome://browser/locale/syncGenericChange.properties");
 
     switch (this._dialogType) {
       case "UpdatePassphrase":
@@ -168,16 +169,17 @@ let Change = {
         break;
     }
   },
 
   doGeneratePassphrase: function () {
     let passphrase = gSyncUtils.generatePassphrase();
     let el = document.getElementById("passphraseBox");
     el.value = gSyncUtils.hyphenatePassphrase(passphrase);
+    document.getElementById("passphraseStrengthRow").hidden = true;
     this._dialog.getButton("accept").disabled = false;
   },
 
   doChangePassphrase: function Change_doChangePassphrase() {
     let pp = gSyncUtils.normalizePassphrase(this._passphraseBox.value);
     if (this._updatingPassphrase) {
       Weave.Service.passphrase = pp;
       if (Weave.Service.login()) {
@@ -229,26 +231,45 @@ let Change = {
 
     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)
+      if (this._updatingPassphrase) {
         [valid, errorString] = gSyncUtils.validatePassphrase(this._passphraseBox);
-      else
+      } else {
         [valid, errorString] = gSyncUtils.validatePassphrase(this._passphraseBox, true);
+        if (valid)
+          this.displayPassphraseStrength();
+      }
     }
 
     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
@@ -102,30 +102,49 @@
           <label id="generatePassphraseButton"
                  value="&syncKeyGenerate.label;"
                  class="text-link inline-link"
                  onclick="event.stopPropagation();
                           Change.doGeneratePassphrase();"/>
         </row>
       </rows>
     </grid>
-    <hbox id="passphraseBackupButtons">
-      <button label="&button.syncKeyBackup.email.label;"
-              accesskey="&button.syncKeyBackup.email.accesskey;"
-              oncommand="gSyncUtils.passphraseEmail('passphraseBox');"/>
+
+    <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 label="&button.syncKeyBackup.print.label;"
               accesskey="&button.syncKeyBackup.print.accesskey;"
               oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/>
       <button label="&button.syncKeyBackup.save.label;"
               accesskey="&button.syncKeyBackup.save.accesskey;"
               oncommand="gSyncUtils.passphraseSave('passphraseBox');"/>
     </hbox>
 
     <description>
       <html:p class="data" id="warningText"/>
     </description>
-
-    <hbox align="center">
-      <image id="statusIcon" class="statusIcon"/>
-      <label id="status" class="status" value=" "/>
-    </hbox>
   </vbox>
 </dialog>
--- a/browser/base/content/syncNotification.xml
+++ b/browser/base/content/syncNotification.xml
@@ -86,68 +86,47 @@
           // If the view of the notification hasn't been removed yet, remove it.
           var notifications = this.allNotifications;
           for each (var notification in notifications) {
             if (notification.notification == subject) {
               notification.close();
               break;
             }
           }
-
-          // If the user has just closed the last notification, close the panel.
-          // FIXME: this is not quite right, because it might not have been
-          // the user that caused weave:notification:removed to get called.
-          // We need to differentiate between "notification removed" and "user
-          // closed the notification" and only close the panel if it was
-          // the user who closed the last notification.  Maybe we should make
-          // the notification's close method handle closing the panel,
-          // but should the notification box or its notifications really know
-          // they are located inside the panel?
-          var panel = document.getElementById("sync-notifications-panel");
-          if (panel.state == "open" &&
-            Notifications.notifications.length == 0)
-            panel.hidePopup();
         ]]></body>
       </method>
 
       <method name="_appendNotification">
         <parameter name="notification"/>
         <body><![CDATA[
-          var node = this.appendNotification(notification.title,
-                                             notification.description,
+          var node = this.appendNotification(notification.description,
+                                             notification.title,
                                              notification.iconURL,
                                              notification.priority,
                                              notification.buttons);
-          node.className = notification.constructor.name;
           node.notification = notification;
         ]]></body>
       </method>
 
     </implementation>
   </binding>
 
   <binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification">
     <content>
-      <xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type" align="start">
-        <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image" style="padding: 3px;"/>
-        <xul:vbox flex="1">
-          <xul:hbox anonid="details" align="center" flex="1">
-            <xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/>
-            <xul:spacer flex="1"/>
-          </xul:hbox>
-          <xul:description xbl:inherits="xbl:text=value"/>
+      <xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
+        <xul:hbox anonid="details" align="center" flex="1">
+          <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image"/>
+          <xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/>
 
           <!-- The children are the buttons defined by the notification. -->
           <xul:hbox oncommand="document.getBindingParent(this)._doButtonCommand(event);">
-            <xul:spacer flex="1"/>
             <children/>
           </xul:hbox>
-
-        </xul:vbox>
-        <xul:spacer flex="1"/>
+          <xul:spacer flex="1"/>
+        </xul:hbox>
         <xul:toolbarbutton ondblclick="event.stopPropagation();"
                            class="messageCloseButton tabbable"
                            xbl:inherits="hidden=hideclose"
                            tooltiptext="&closeNotification.tooltip;"
                            oncommand="document.getBindingParent(this).close()"/>
       </xul:hbox>
     </content>
     <implementation>
--- a/browser/base/content/syncQuota.xul
+++ b/browser/base/content/syncQuota.xul
@@ -76,21 +76,21 @@
           onclick="gUsageTreeView.onTreeClick(event);"
           flex="1">
       <treecols>
         <treecol id="enabled"
                  type="checkbox"
                  fixed="true"/>
         <splitter class="tree-splitter"/>
         <treecol id="collection"
-                 label="Type"
+                 label="&quota.typeColumn.label;"
                  flex="1"/>
         <splitter class="tree-splitter"/>
         <treecol id="size"
-                 label="Size"
+                 label="&quota.sizeColumn.label;"
                  flex="1"/>
       </treecols>
       <treechildren flex="1"/>
     </tree>
     <separator/>
     <description id="treeCaption"> </description>
   </vbox>
 
--- a/browser/base/content/syncSetup.js
+++ b/browser/base/content/syncSetup.js
@@ -316,16 +316,17 @@ var gSyncSetup = {
   onPassphraseGenerate: function () {
     let passphrase = gSyncUtils.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();
   },
@@ -339,16 +340,33 @@ var gSyncSetup = {
       str = Weave.Utils.getErrorString("change.passphrase.ppSameAsPassword");
     }
     else {
       [valid, str] = gSyncUtils.validatePassphrase(el1);
     }
 
     let feedback = document.getElementById("passphraseFeedbackRow");
     this._setFeedback(feedback, valid, str);
+    if (!valid)
+      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;
   },
 
   onPageShow: function() {
     switch (this.wizard.pageIndex) {
       case INTRO_PAGE:
         this.wizard.getButton("next").hidden = true;
         this.wizard.getButton("back").hidden = true;
@@ -721,42 +739,48 @@ var gSyncSetup = {
               "UNION ALL " +
               "SELECT visit_date FROM moz_historyvisits_temp " +
               "ORDER BY visit_date ASC LIMIT 1 " +
               ")/1000000 " +
             ")/86400) AS daysOfHistory ");
 
         if (stm.step())
           daysOfHistory = stm.getInt32(0);
+        // Support %S for historical reasons (see bug 600141)
         document.getElementById("historyCount").value =
           PluralForm.get(daysOfHistory,
                          this._stringBundle.GetStringFromName("historyDaysCount.label"))
-                             .replace("%S", daysOfHistory);
+                    .replace("%S", daysOfHistory)
+                    .replace("#1", daysOfHistory);
 
         // bookmarks
         let bookmarks = 0;
         stm = db.createStatement(
           "SELECT count(*) AS bookmarks " +
           "FROM moz_bookmarks b " +
           "LEFT JOIN moz_bookmarks t ON " +
           "b.parent = t.id WHERE b.type = 1 AND t.parent <> :tag");
         stm.params.tag = Weave.Svc.Bookmark.tagsFolder;
         if (stm.executeStep())
           bookmarks = stm.row.bookmarks;
+        // Support %S for historical reasons (see bug 600141)
         document.getElementById("bookmarkCount").value =
           PluralForm.get(bookmarks,
                          this._stringBundle.GetStringFromName("bookmarksCount.label"))
-                             .replace("%S", bookmarks);
+                    .replace("%S", bookmarks)
+                    .replace("#1", bookmarks);
 
         // passwords
         let logins = Weave.Svc.Login.getAllLogins({});
+        // Support %S for historical reasons (see bug 600141)
         document.getElementById("passwordCount").value =
           PluralForm.get(logins.length,
                          this._stringBundle.GetStringFromName("passwordsCount.label"))
-                             .replace("%S", logins.length);
+                    .replace("%S", logins.length)
+                    .replace("#1", logins.length);
         this._case1Setup = true;
         break;
       case 2:
         if (this._case2Setup)
           break;
         let count = 0;
         function appendNode(label) {
           let box = document.getElementById("clientList");
@@ -771,20 +795,22 @@ var gSyncSetup = {
           if (name == Weave.Clients.localName)
             continue;
 
           // Only show the first several client names
           if (++count <= 5)
             appendNode(name);
         }
         if (count > 5) {
+          // Support %S for historical reasons (see bug 600141)
           let label =
             PluralForm.get(count - 5,
                            this._stringBundle.GetStringFromName("additionalClientCount.label"))
-                               .replace("%S", count - 5);
+                      .replace("%S", count - 5)
+                      .replace("#1", count - 5);
           appendNode(label);
         }
         this._case2Setup = true;
         break;
     }
 
     return true;
   },
--- a/browser/base/content/syncSetup.xul
+++ b/browser/base/content/syncSetup.xul
@@ -202,42 +202,61 @@
     </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>
       <textbox id="weavePassphrase"
                onkeyup="gSyncSetup.onPassphraseChange()"
                onchange="gSyncSetup.onPassphraseChange()"
                onfocus="this.select()"/>
-      <hbox id="passphraseFeedbackRow" align="center" hidden="true">
-        <spacer/>
-        <hbox>
-          <image class="statusIcon"/>
-          <label class="status" value=" "/>
+
+      <vbox id="passphraseFeedback" pack="center">
+        <hbox id="passphraseFeedbackRow" hidden="true" align="center">
+          <spacer/>
+          <hbox>
+            <image class="statusIcon"/>
+            <label class="status" value=" "/>
+          </hbox>
         </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>
     </groupbox>
 
     <groupbox align="center">
       <description>&syncKeyBackup.description;</description>
       <hbox>
-        <button label="&button.syncKeyBackup.email.label;"
-                accesskey="&button.syncKeyBackup.email.accesskey;"
-                oncommand="gSyncUtils.passphraseEmail('weavePassphrase');
-                           gSyncSetup.afterBackup();"/>
         <button label="&button.syncKeyBackup.print.label;"
                 accesskey="&button.syncKeyBackup.print.accesskey;"
                 oncommand="gSyncUtils.passphrasePrint('weavePassphrase');
                            gSyncSetup.afterBackup();"/>
         <button label="&button.syncKeyBackup.save.label;"
                 accesskey="&button.syncKeyBackup.save.accesskey;"
                 oncommand="gSyncUtils.passphraseSave('weavePassphrase');
                            gSyncSetup.afterBackup();"/>
--- a/browser/base/content/syncUtils.js
+++ b/browser/base/content/syncUtils.js
@@ -49,16 +49,19 @@ let gSyncUtils = {
   _openLink: function (url) {
     let thisDocEl = document.documentElement,
         openerDocEl = window.opener && window.opener.document.documentElement;
     if (thisDocEl.id == "accountSetup" && window.opener &&
         openerDocEl.id == "BrowserPreferences" && !openerDocEl.instantApply)
       openUILinkIn(url, "window");
     else if (thisDocEl.id == "BrowserPreferences" && !thisDocEl.instantApply)
       openUILinkIn(url, "window");
+    else if (document.documentElement.id == "change-dialog")
+      Weave.Svc.WinMediator.getMostRecentWindow("navigator:browser")
+        .openUILinkIn(url, "tab");
     else
       openUILinkIn(url, "tab");
   },
 
   changeName: function changeName(input) {
     // Make sure to update to a modified name, e.g., empty-string -> default
     Weave.Clients.localName = input.value;
     input.value = Weave.Clients.localName;
@@ -97,16 +100,20 @@ 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);
   },
 
@@ -142,41 +149,16 @@ let gSyncUtils = {
   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;
   },
 
   /**
-   * Trigger the mailto protocol handler to send a passphrase backup email.
-   * 
-   * @param elid : ID of the form element containing the passphrase.
-   */
-  passphraseEmail: function(elid) {
-    let pp = document.getElementById(elid).value;
-    let subject = this.bundle.GetStringFromName("email.syncKey.subject");
-    let label = this.bundle.formatStringFromName("email.syncKey.label", [pp], 1);
-    let body = "&body=" + label + "%0A%0A" +
-               this.bundle.GetStringFromName("email.syncKey.description")
-               + "%0A%0A" +
-               this.bundle.GetStringFromName("email.keepItSecret.label") +
-               this.bundle.GetStringFromName("email.keepItSecret.description")
-               + "%0A%0A" +
-               this.bundle.GetStringFromName("email.keepItSafe.label") +
-               this.bundle.GetStringFromName("email.keepItSafe.description")
-               + "%0A%0A" +
-               this.bundle.GetStringFromName("email.findOutMore.label");
-    let uri = Weave.Utils.makeURI("mailto:?subject=" + subject + body);
-    let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
-                     .getService(Ci.nsIExternalProtocolService);
-    protoSvc.loadURI(uri);
-  },
-
-  /**
    * 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;
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -18,21 +18,25 @@
 
 .tab-close-button[selected="true"] {
   /* Make this button focusable so clicking on it will not focus the tab while
      it's getting closed */
   -moz-user-focus: normal;
 }
 
 .tab-label[pinned] {
-  display: none;
+  width: 0;
+  margin-left: 0 !important;
+  margin-right: 0 !important;
+  padding-left: 0 !important;
+  padding-right: 0 !important;
 }
 
 .tab-stack {
-  vertical-align: middle; /* for pinned tabs */
+  vertical-align: top; /* for pinned tabs */
 }
 
 tabpanels {
   background-color: white;
 }
 
 .tab-drop-indicator {
   position: relative;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -65,18 +65,20 @@
 
     <content>
       <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
       <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
                   flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1">
-            <xul:browser flex="1" type="content-primary" message="true" disablehistory="true"
-                         xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
+            <xul:stack flex="1" anonid="browserStack">
+              <xul:browser type="content-primary" message="true" disablehistory="true"
+                           xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
+            </xul:stack>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
     </content>
     <implementation implements="nsIDOMEventListener">
 
       <property name="tabContextMenu" readonly="true"
@@ -271,17 +273,17 @@
         ]]>
         </body>
       </method>
 
       <method name="getNotificationBox">
         <parameter name="aBrowser"/>
         <body>
           <![CDATA[
-            return (aBrowser || this.mCurrentBrowser).parentNode;
+            return (aBrowser || this.mCurrentBrowser).parentNode.parentNode;
           ]]>
         </body>
       </method>
 
       <method name="_callProgressListeners">
         <parameter name="aBrowser"/>
         <parameter name="aMethod"/>
         <parameter name="aArguments"/>
@@ -336,17 +338,17 @@
         <body>
         <![CDATA[
           return ({
             mTabBrowser: this,
             mTab: aTab,
             mBrowser: aBrowser,
             mBlank: aStartsBlank,
 
-            // cache flags for correct status bar update after tab switching
+            // cache flags for correct status UI update after tab switching
             mStateFlags: 0,
             mStatus: 0,
             mMessage: "",
             mTotalProgress: 0,
 
             // count of open requests (should always be 0 or 1)
             mRequestCount: 0,
 
@@ -427,17 +429,16 @@
                 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
                   this.mBrowser.userTypedClear += 2;
 
                 if (!this.mBlank) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
                     this.mTab.setAttribute("progresspercent", "0");
                     this._startStalledTimer();
-                    this.mTabBrowser.updateIcon(this.mTab);
                     this.mTabBrowser.setTabTitleLoading(this.mTab);
                   }
 
                   if (this.mTab.selected)
                     this.mTabBrowser.mIsBusy = true;
                 }
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
@@ -456,17 +457,16 @@
 
                 if (this.mBlank)
                   this.mBlank = false;
 
                 this.mTab.removeAttribute("busy");
                 this.mTab.removeAttribute("progresspercent");
                 this.mTab.removeAttribute("stalled");
                 this._cancelStalledTimer();
-                this.mTabBrowser.updateIcon(this.mTab);
 
                 var location = aRequest.QueryInterface(nsIChannel).URI;
 
                 // For keyword URIs clear the user typed value since they will be changed into real URIs
                 if (location.scheme == "keyword")
                   this.mBrowser.userTypedValue = null;
 
                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
@@ -595,47 +595,39 @@
 
             if (aURI && this.mFaviconService) {
               if (!(aURI instanceof Ci.nsIURI))
                 aURI = makeURI(aURI);
               this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
                                                             aURI, false);
             }
 
-            this.updateIcon(aTab);
+            if ((browser.mIconURL || "") != aTab.getAttribute("image")) {
+              if (browser.mIconURL)
+                aTab.setAttribute("image", browser.mIconURL);
+              else
+                aTab.removeAttribute("image");
+              this._tabAttrModified(aTab);
+            }
 
             this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
           ]]>
         </body>
       </method>
 
       <method name="getIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser;
             return browser.mIconURL;
           ]]>
         </body>
       </method>
 
-      <method name="updateIcon">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            var browser = this.getBrowserForTab(aTab);
-            if (!aTab.hasAttribute("busy") && browser.mIconURL)
-              aTab.setAttribute("image", browser.mIconURL);
-            else
-              aTab.removeAttribute("image");
-            this._tabAttrModified(aTab);
-          ]]>
-        </body>
-      </method>
-
       <method name="shouldLoadFavIcon">
         <parameter name="aURI"/>
         <body>
           <![CDATA[
             return (aURI &&
                     Services.prefs.getBoolPref("browser.chrome.site_icons") &&
                     Services.prefs.getBoolPref("browser.chrome.favicons") &&
                     ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
@@ -643,41 +635,39 @@
         </body>
       </method>
 
       <method name="useDefaultIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             var browser = this.getBrowserForTab(aTab);
-            var docURIObject = browser.contentDocument.documentURIObject; 
+            var docURIObject = browser.contentDocument.documentURIObject;
+            var icon = null;
             if (browser.contentDocument instanceof ImageDocument) {
               if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
+                let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
                 try {
-                  let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
-                  if (!sz)
-                    return;
-
-                  var req = browser.contentDocument.imageRequest;
-                  if (!req || !req.image ||
-                      req.image.width > sz ||
-                      req.image.height > sz)
-                    return;
-
-                  this.setIcon(aTab, browser.currentURI);
+                  let req = browser.contentDocument.imageRequest;
+                  if (req &&
+                      req.image &&
+                      req.image.width <= sz &&
+                      req.image.height <= sz)
+                    icon = browser.currentURI;
                 } catch (e) { }
               }
             }
             // Use documentURIObject in the check for shouldLoadFavIcon so that we
             // do the right thing with about:-style error pages.  Bug 453442
             else if (this.shouldLoadFavIcon(docURIObject)) {
-              var url = docURIObject.prePath + "/favicon.ico";
+              let url = docURIObject.prePath + "/favicon.ico";
               if (!this.isFailedIcon(url))
-                this.setIcon(aTab, url);
+                icon = url;
             }
+            this.setIcon(aTab, icon);
           ]]>
         </body>
       </method>
 
       <method name="isFailedIcon">
         <parameter name="aURI"/>
         <body>
           <![CDATA[
@@ -883,16 +873,19 @@
             }
           ]]>
         </body>
       </method>
 
       <method name="_tabAttrModified">
         <parameter name="aTab"/>
         <body><![CDATA[
+          if (this._removingTabs.indexOf(aTab) > -1)
+            return;
+
           // This event should be dispatched when any of these attributes change:
           // label, crop, busy, image, selected
           var event = document.createEvent("Events");
           event.initEvent("TabAttrModified", true, false);
           aTab.dispatchEvent(event);
         ]]></body>
       </method>
 
@@ -964,17 +957,16 @@
               return;
 
             this.mTabbedMode = true; // Welcome to multi-tabbed mode.
 
             if (XULBrowserWindow.isBusy) {
               this.mCurrentTab.setAttribute("busy", "true");
               this.mIsBusy = true;
               this.setTabTitleLoading(this.mCurrentTab);
-              this.updateIcon(this.mCurrentTab);
             } else {
               this.setIcon(this.mCurrentTab, this.mCurrentBrowser.mIconURL);
             }
 
             var filter;
             if (this.mTabFilters.length > 0) {
               // Use the filter hooked up in our addProgressListener
               filter = this.mTabFilters[0];
@@ -1187,23 +1179,30 @@
             b.setAttribute("type", "content-targetable");
             b.setAttribute("message", "true");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
             if (this.hasAttribute("autocompletepopup"))
               b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
             b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
 
+            // Create the browserStack container
+            var stack = document.createElementNS(
+                                    "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+                                    "stack");
+            stack.setAttribute("anonid", "browserStack");
+            stack.appendChild(b);
+            stack.setAttribute("flex", "1");
+
             // Add the Message and the Browser to the box
             var notificationbox = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "notificationbox");
             notificationbox.setAttribute("flex", "1");
-            notificationbox.appendChild(b);
-            b.setAttribute("flex", "1");
+            notificationbox.appendChild(stack);
 
             var position = this.tabs.length - 1;
             var uniqueId = "panel" + Date.now() + position;
             notificationbox.id = uniqueId;
             t.linkedPanel = uniqueId;
             t.linkedBrowser = b;
             t._tPos = position;
             if (t.previousSibling.selected)
@@ -1575,22 +1574,22 @@
 
             // update first-tab/last-tab/beforeselected/afterselected attributes
             this.selectedTab._selected = true;
 
             // Removing the panel requires fixing up selectedPanel immediately
             // (see below), which would be hindered by the potentially expensive
             // browser removal. So we remove the browser and the panel in two
             // steps.
-            var panel = browser.parentNode;
+            var panel = browser.parentNode.parentNode;
 
             // This will unload the document. An unload handler could remove
             // dependant tabs, so it's important that the tabbrowser is now in
             // a consistent state (tab removed, tab positions updated, etc.).
-            panel.removeChild(browser);
+            panel.removeChild(browser.parentNode);
 
             // As the browser is removed, the removal of a dependent document can
             // cause the whole window to close. So at this point, it's possible
             // that the binding is destructed.
             if (this.mTabBox) {
               let selectedPanel = this.mTabBox.selectedPanel;
 
               this.mPanelContainer.removeChild(panel);
@@ -1708,17 +1707,16 @@
 
             ourBrowser.webProgress.addProgressListener(filter,
               Components.interfaces.nsIWebProgress.NOTIFY_ALL);
 
             if (isBusy)
               this.setTabTitleLoading(aOurTab);
             else
               this.setTabTitle(aOurTab);
-            this.updateIcon(aOurTab);
 
             // If the tab was already selected (this happpens in the scenario
             // of replaceTabWithWindow), notify onLocationChange, etc.
             if (aOurTab == this.selectedTab)
               this.updateCurrentBrowser(true);
           ]]>
         </body>
       </method>
@@ -2353,17 +2351,17 @@
               this._handleKeyEvent(aEvent);
               break;
           }
         ]]></body>
       </method>
 
       <constructor>
         <![CDATA[
-          this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild;
+          this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild.firstChild;
           this.mCurrentTab = this.tabContainer.firstChild;
           document.addEventListener("keypress", this, false);
 
           var uniqueId = "panel" + Date.now();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab._tPos = 0;
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -585,17 +585,17 @@ GroupItem.prototype = Utils.extend(new I
   // ----------
   // Function: _createUndoButton
   // Makes the affordance for undo a close group action
   _createUndoButton: function() {
     let self = this;
     this.$undoContainer = iQ("<div/>")
       .addClass("undo")
       .attr("type", "button")
-      .text("Undo Close Group")
+      .text(tabviewString("groupItem.undoCloseGroup"))
       .appendTo("body");
     let undoClose = iQ("<span/>")
       .addClass("close")
       .appendTo(this.$undoContainer);
 
     this.$undoContainer.css({
       left: this.bounds.left + this.bounds.width/2 - iQ(self.$undoContainer).width()/2,
       top:  this.bounds.top + this.bounds.height/2 - iQ(self.$undoContainer).height()/2,
--- a/browser/base/content/tabview/search.js
+++ b/browser/base/content/tabview/search.js
@@ -105,85 +105,197 @@ function scorePatternMatch(pattern, matc
       score /= matched.length;
       return score;
     }
   }
   return 0.0;  
 }
 
 // ##########
+// Class: TabUtils
+// 
+// A collection of helper functions for dealing with both
+// <TabItem>s and <xul:tab>s without having to worry which
+// one is which.
+var TabUtils = {
+  // ---------
+  // Function: _nameOfTab
+  // Given a <TabItem> or a <xul:tab> returns the tab's name.
+  nameOf: function TabUtils_nameOfTab(tab) {
+    // We can have two types of tabs: A <TabItem> or a <xul:tab>
+    // 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: 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.favEl.src;
+  },
+  
+  // ---------
+  // Function: focus
+  // Given a <TabItem> or a <xul:tab>, focuses it and it's window.
+  focus: function TabUtils_focus(tab) {
+    // Convert a <TabItem> to a <xul:tab>
+    if (tab.tab != undefined) tab = tab.tab;
+    tab.ownerDocument.defaultView.gBrowser.selectedTab = tab;
+    tab.ownerDocument.defaultView.focus();    
+  }
+};
+
+// ##########
 // Class: TabMatcher
 // 
 // A singleton class that allows you to iterate over
 // matching and not-matching tabs, given a case-insensitive
 // search term.
 function TabMatcher(term) { 
   this.term = term; 
 }
 
-TabMatcher.prototype = {
+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");
+    });
+
+    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; 
+    });
+    
+    return tabs;
+  },
+  
+  // ---------
+  // 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");
+    });
+  },
+  
+  // ---------
+  // 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
+  // least once, while <xul:tab>s will be return for windows in which
+  // Panorama has never been activated.
+  _getTabsForOtherWindows: function TabMatcher__getTabsForOtherWindows(){
+    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+                       .getService(Components.interfaces.nsIWindowMediator);
+    var enumerator = wm.getEnumerator("navigator:browser");    
+    var currentWindow = wm.getMostRecentWindow("navigator:browser");
+    
+    var allTabs = [];    
+    while (enumerator.hasMoreElements()) {
+      var win = enumerator.getNext();
+      // This function gets tabs from other windows: not the one you currently
+      // have focused.
+      if (win != currentWindow) {
+        // If TabView is around iterate over all tabs, else get the currently
+        // shown tabs...
+        
+        tvWindow = win.TabView.getContentWindow();
+        if (tvWindow)
+          allTabs = allTabs.concat( tvWindow.TabItems.getItems() );
+        else
+          // win.gBrowser.tabs isn't a proper array, so we can't use concat
+          for (var i=0; i<win.gBrowser.tabs.length; i++) allTabs.push( win.gBrowser.tabs[i] );
+      } 
+    }
+    return allTabs;    
+  },
+  
+  // ----------
+  // Function: matchedTabsFromOtherWindows
+  // Returns an array of <TabItem>s and <xul:tab>s that match the search term
+  // from all windows but the currently focused window. <TabItem>s will be
+  // returned for windows in which Panorama has been activated at least once,
+  // while <xul:tab>s will be return for windows in which Panorama has never
+  // been activated.
+  // (new TabMatcher("app")).matchedTabsFromOtherWindows();
+  matchedTabsFromOtherWindows: function TabMatcher_matchedTabsFromOtherWindows(){
+    if (this.term.length < 2)
+      return [];
+    
+    var tabs = this._getTabsForOtherWindows();
+    tabs = this._filterAndSortForMatches(tabs);
+    return tabs;
+  },
+  
   // ----------
   // Function: matched
   // Returns an array of <TabItem>s which match the current search term.
   // If the term is less than 2 characters in length, it returns
   // nothing.
-  matched: function matched() {
-    var self = this;
+  matched: function TabMatcher_matched() {
     if (this.term.length < 2)
       return [];
-    
+      
     var tabs = TabItems.getItems();
-    tabs = tabs.filter(function(tab){
-      var name = tab.nameEl.innerHTML;      
-      return name.match(self.term, "i");
-    });
-    
-    tabs.sort(function sorter(x, y){
-      var yScore = scorePatternMatch(self.term, y.nameEl.innerHTML);
-      var xScore = scorePatternMatch(self.term, x.nameEl.innerHTML);
-      return yScore - xScore; 
-    });
-     
+    tabs = this._filterAndSortForMatches(tabs);
     return tabs;    
   },
   
   // ----------
   // Function: unmatched
   // Returns all of <TabItem>s that .matched() doesn't return.
-  unmatched: function unmatched() {
-    var self = this;    
+  unmatched: function TabMatcher_unmatched() {
     var tabs = TabItems.getItems();
-    
     if ( this.term.length < 2 )
       return tabs;
-    
-    return tabs.filter(function(tab) {
-      var name = tab.nameEl.innerHTML;
-      return !name.match(self.term, "i");
-    });
+      
+    return this._filterForUnmatches(tabs);
   },
 
   // ----------
   // Function: doSearch
-  // Performs the search. Lets you provide two functions, one that is called
-  // on all matched tabs, and one that is called on all unmatched tabs.
-  // Both functions take two parameters: A <TabItem> and its integer index
+  // Performs the search. Lets you provide three functions.
+  // The first is on all matched tabs in the window, the second on all unmatched
+  // tabs in the window, and the third on all matched tabs in other windows.
+  // The first two functions take two parameters: A <TabItem> and its integer index
   // indicating the absolute rank of the <TabItem> in terms of match to
-  // the search term. 
-  doSearch: function(matchFunc, unmatchFunc) {
+  // the search term. The last function also takes two paramaters, but can be
+  // passed both <TabItem>s and <xul:tab>s and the index is offset by the
+  // number of matched tabs inside the window.
+  doSearch: function TabMatcher_doSearch(matchFunc, unmatchFunc, otherFunc) {
     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){
+      otherFunc(tab, i+matches.length);      
+    });
     
     unmatched.forEach(function(tab, i) {
       unmatchFunc(tab, i);
-    });
+    });    
   }
 };
 
 // ##########
 // Class: SearchEventHandlerClass
 // 
 // A singleton class that handles all of the
 // event handlers.
@@ -242,19 +354,24 @@ SearchEventHandlerClass.prototype = {
   inSearchKeyHandler: function (event) {
     var term = iQ("#searchbox").val();
     
     if (event.which == event.DOM_VK_ESCAPE) 
       hideSearch(event);
     if (event.which == event.DOM_VK_BACK_SPACE && term.length <= 1) 
       hideSearch(event);
 
-    var matches = (new TabMatcher(term)).matched();
-    if (event.which == event.DOM_VK_RETURN && matches.length > 0) {
-      matches[0].zoomIn();
+    var matcher = new TabMatcher(term);
+    var matches = matcher.matched();
+    var others =  matcher.matchedTabsFromOtherWindows();
+    if (event.which == event.DOM_VK_RETURN && (matches.length > 0 || others.length > 0)) {
+      if (matches.length > 0)
+        matches[0].zoomIn();
+      else
+        TabUtils.focus(others[0]);
       hideSearch(event);    
     }
   },
 
   // ----------
   // Function: switchToBeforeMode
   // Make sure the event handlers are appropriate for
   // the before-search mode. 
@@ -276,41 +393,64 @@ SearchEventHandlerClass.prototype = {
       iQ(document).unbind("keydown", this.currentHandler);
     this.currentHandler = function(event) self.inSearchKeyHandler(event);
     iQ(document).keydown(self.currentHandler);
   }
 };
 
 var TabHandlers = {
   onMatch: function(tab, index){
-   tab.setZ(1010);   
-   index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch");
-   
-   // Remove any existing handlers before adding the new ones.
-   // If we don't do this, then we may add more handlers than
-   // we remove.
-   iQ(tab.canvasEl)
+    tab.addClass("onTop");
+    index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch");
+
+    // Remove any existing handlers before adding the new ones.
+    // If we don't do this, then we may add more handlers than
+    // we remove.
+    iQ(tab.canvasEl)
     .unbind("mousedown", TabHandlers._hideHandler)
     .unbind("mouseup", TabHandlers._showHandler);
-   
-   iQ(tab.canvasEl)
+
+    iQ(tab.canvasEl)
     .mousedown(TabHandlers._hideHandler)
     .mouseup(TabHandlers._showHandler);
   },
   
   onUnmatch: function(tab, index){
-    // TODO: Set back as value per tab. bug 593902
-    tab.setZ(500);
+    iQ(tab.container).removeClass("onTop");
     tab.removeClass("notMainMatch");
 
     iQ(tab.canvasEl)
      .unbind("mousedown", TabHandlers._hideHandler)
      .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/>")
+      .addClass("inlineMatch")
+      .click(function(){
+        TabUtils.focus(tab);
+      });
+    
+    iQ("<img/>")
+      .attr("src", TabUtils.faviconURLOf(tab) )
+      .appendTo(item);
+    
+    iQ("<span/>")
+      .text( TabUtils.nameOf(tab) )
+      .appendTo(item);
+      
+    index != 0 ? item.addClass("notMainMatch") : item.removeClass("notMainMatch");      
+    item.appendTo("#results");
+    iQ("#otherresults").show();    
+  },
+  
   _hideHandler: function(event){
     iQ("#search").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
@@ -353,17 +493,24 @@ function hideSearch(event){
 
   // Return focus to the tab window
   UI.blurAll();
   gTabViewFrame.contentWindow.focus();
 }
 
 function performSearch() {
   var matcher = new TabMatcher(iQ("#searchbox").val());
-  matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch);
+
+  // 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 $searchbox = iQ("#searchbox");
   iQ("#searchbutton").css({ opacity: 1 });
   
   
--- a/browser/base/content/tabview/tabview.css
+++ b/browser/base/content/tabview/tabview.css
@@ -221,8 +221,27 @@ body {
   z-index:10;
 }
 
 #actions #searchbutton{
   position: relative;
   top: 0;
   left: 0;
 }
+
+#otherresults{
+  position: absolute;
+  opacity: 0;
+  overflow: hidden;
+}
+
+.onTop{
+  z-index: 1010 !important;
+}
+
+.inlineMatch{
+  display: inline-block;
+}
+
+.inlineMatch>span{
+  display: inline-block;
+  overflow: hidden;
+}
--- a/browser/base/content/tabview/tabview.html
+++ b/browser/base/content/tabview/tabview.html
@@ -15,13 +15,17 @@
     <div id="actions">
       <input type="button" id="searchbutton"/>
     </div>
     <div id="bg" />
   </div>
   
   <div id="search">
     <input id="searchbox" type="text"/>
+    <div id="otherresults">
+      <span class="label"></span>
+      <span id="results"></span>
+    </div>
   </div>
 
   <script type="text/javascript;version=1.8" src="tabview.js"></script>
 </body>
 </html>
--- a/browser/base/content/tabview/tabview.js
+++ b/browser/base/content/tabview/tabview.js
@@ -27,16 +27,22 @@ XPCOMUtils.defineLazyGetter(this, "gTabV
 
 XPCOMUtils.defineLazyGetter(this, "tabviewBundle", function() {
   return Services.strings.
     createBundle("chrome://browser/locale/tabview.properties");
 });
 
 function tabviewString(name) tabviewBundle.GetStringFromName('tabview.' + name);
 
+XPCOMUtils.defineLazyGetter(this, "gPrefBranch", function() {
+  return Cc["@mozilla.org/preferences-service;1"].
+    getService(Ci.nsIPrefService).
+    getBranch("browser.panorama.");
+});
+
 # NB: Certain files need to evaluate before others
 
 #include iq.js
 #include storage.js
 #include items.js
 #include groupitems.js
 #include tabitems.js
 #include drag.js
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -150,65 +150,31 @@ let UI = {
 
       // ___ add tab action handlers
       this._addTabActionHandlers();
 
       // ___ Storage
 
       GroupItems.init();
 
-      var groupItemsData = Storage.readGroupItemsData(gWindow);
-      var firstTime = !groupItemsData || Utils.isEmptyObject(groupItemsData);
-      var groupItemData = Storage.readGroupItemData(gWindow);
+      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);
       GroupItems.reconstitute(groupItemsData, groupItemData);
       GroupItems.killNewTabGroup(); // temporary?
 
       // ___ tabs
       TabItems.init();
       TabItems.pausePainting();
 
-      if (firstTime) {
-        var padding = 10;
-        var infoWidth = 350;
-        var infoHeight = 232;
-        var pageBounds = Items.getPageBounds();
-        pageBounds.inset(padding, padding);
-
-        // ___ make a fresh groupItem
-        var box = new Rect(pageBounds);
-        box.width =
-          Math.min(box.width * 0.667, pageBounds.width - (infoWidth + padding));
-        box.height = box.height * 0.667;
-        var options = {
-          bounds: box
-        };
-
-        var groupItem = new GroupItem([], options);
-
-        var items = TabItems.getItems();
-        items.forEach(function(item) {
-          if (item.parent)
-            item.parent.remove(item);
-
-          groupItem.add(item);
-        });
-
-        // ___ make info item
-        let video = "http://videos-cdn.mozilla.net/firefox4beta/tabcandy_howto.webm";
-        var html =
-          "<div class='intro'>"
-            + "<video src='" + video + "' width='100%' preload controls>"
-          + "</div>";
-
-        box.left = box.right + padding;
-        box.width = infoWidth;
-        box.height = infoHeight;
-        var infoItem = new InfoItem(box);
-        infoItem.html(html);
-      }
+      // if first time in Panorama or no group data:
+      if (firstTime || !groupItemsData || Utils.isEmptyObject(groupItemsData))
+        this.reset(firstTime);
 
       // ___ resizing
       if (this._pageBounds)
         this._resize(true);
       else
         this._pageBounds = Items.getPageBounds();
 
       iQ(window).resize(function() {
@@ -258,16 +224,61 @@ let UI = {
 
     this._removeTabActionHandlers();
     this._currentTab = null;
     this._pageBounds = null;
     this._reorderTabItemsOnShow = null;
     this._reorderTabsOnHide = null;
     this._frameInitialized = false;
   },
+  
+  // Function: reset
+  // Resets the Panorama view to have just one group with all tabs
+  // and, if firstTime == true, add the welcome video/tab
+  reset: function UI_reset(firstTime) {
+    let padding = 10;
+    let infoWidth = 350;
+    let infoHeight = 232;
+    let pageBounds = Items.getPageBounds();
+    pageBounds.inset(padding, padding);
+
+    // ___ make a fresh groupItem
+    let box = new Rect(pageBounds);
+    box.width = 
+      Math.min(box.width * 0.667, pageBounds.width - (infoWidth + padding));
+    box.height = box.height * 0.667;
+
+    GroupItems.groupItems.forEach(function(group) {
+      group.close();
+    });
+
+    let groupItem = new GroupItem([], {bounds: box});
+    let items = TabItems.getItems();
+    items.forEach(function(item) {
+      if (item.parent)
+        item.parent.remove(item);
+      groupItem.add(item);
+    });
+    
+    if (firstTime) {
+      gPrefBranch.setBoolPref("experienced_first_run", true);
+
+      // ___ make info item
+      let video = 
+        "http://videos-cdn.mozilla.net/firefox4beta/tabcandy_howto.webm";
+      let html =
+        "<div class='intro'>"
+          + "<video src='" + video + "' width='100%' preload controls>"
+        + "</div>";
+      let infoBox = new Rect(box.right + padding, box.top,
+                         infoWidth, infoHeight);
+      let infoItem = new InfoItem(infoBox);
+      infoItem.html(html);
+    }
+  },
 
   // Function: blurAll
   // Blurs any currently focused element
   //
   blurAll: function UI_blurAll() {
     iQ(":focus").each(function(element) {
       element.blur();
     });
@@ -673,31 +684,16 @@ let UI = {
         var nextTab = getClosestTabBy(norm);
         if (nextTab) {
           if (nextTab.inStack() && !nextTab.parent.expanded)
             nextTab = nextTab.parent.getChild(0);
           self.setActiveTab(nextTab);
         }
         event.stopPropagation();
         event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_SPACE) {
-        // alt/control + space to zoom into the active tab.
-#ifdef XP_MACOSX
-        if (event.altKey && !event.metaKey && !event.shiftKey &&
-            !event.ctrlKey) {
-#else
-        if (event.ctrlKey && !event.metaKey && !event.shiftKey &&
-            !event.altKey) {
-#endif
-          var activeTab = self.getActiveTab();
-          if (activeTab)
-            activeTab.zoomIn();
-          event.stopPropagation();
-          event.preventDefault();
-        }
       } else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE ||
                  event.keyCode == KeyEvent.DOM_VK_RETURN ||
                  event.keyCode == KeyEvent.DOM_VK_ENTER) {
         let activeTab = self.getActiveTab();
         let activeGroupItem = GroupItems.getActiveGroupItem();
 
         if (activeGroupItem && activeGroupItem.expanded &&
             event.keyCode == KeyEvent.DOM_VK_ESCAPE)
@@ -1001,17 +997,17 @@ let UI = {
 /*
         name: "refresh",
         code: function() {
           location.href = "tabview.html";
         }
       }, {
         name: "reset",
         code: function() {
-          self._reset();
+          self.reset();
         }
       }, {
 */
         name: "save",
         code: function() {
           self._saveAll();
         }
       }];
@@ -1024,24 +1020,16 @@ let UI = {
           .html(commands[a].name)
           .appendTo($select))[0];
       }
     } catch(e) {
       Utils.log(e);
     }
   },
 
-  // -----------
-  // Function: _reset
-  // Wipes all TabView storage and refreshes, giving you the "first-run" state.
-  _reset: function UI__reset() {
-    Storage.wipe();
-    location.href = "";
-  },
-
   // ----------
   // Function: storageSanity
   // Given storage data for this object, returns true if it looks valid.
   _storageSanity: function UI__storageSanity(data) {
     if (Utils.isEmptyObject(data))
       return true;
 
     if (!Utils.isRect(data.pageBounds)) {
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -147,16 +147,19 @@ endif
                  browser_bug563588.js \
                  browser_bug577121.js \
                  browser_bug579872.js \
                  browser_bug580956.js \
                  browser_bug581242.js \
                  browser_bug581947.js \
                  browser_bug585830.js \
                  browser_bug592338.js \
+                 browser_bug594131.js \
+                 browser_bug595507.js \
+                 browser_bug596687.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_hide_removing.js \
                  browser_inspector_initialization.js \
@@ -208,17 +211,16 @@ endif
                  plugin_both2.html \
                  alltabslistener.html \
                  zoom_test.html \
                  dummy_page.html \
                  browser_tabMatchesInAwesomebar.js \
                  file_bug550565_popup.html \
                  file_bug550565_favicon.ico \
                  browser_overLinkInLocationBar.js \
-                 browser_overLinkInLocationBar.html \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
 		browser_bug462289.js \
 		$(NULL)
 else
 _BROWSER_FILES += \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_addon_bar.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 browser add-on bar test code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dietrich Ayala <dietrich@mozilla.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();
+
+  let addonbar = document.getElementById("addon-bar");
+  ok(addonbar.collapsed, "addon bar is collapsed by default");
+
+  let topMenu, toolbarMenu;
+
+  function onTopMenuShown(event) {
+    ok(1, "top menu popupshown listener called");
+    event.currentTarget.removeEventListener("popupshown", arguments.callee, false);
+    // open the customize or toolbars menu
+    toolbarMenu = document.getElementById("appmenu_customizeMenu") ||
+                      document.getElementById("viewToolbarsMenu").firstElementChild;
+    toolbarMenu.addEventListener("popupshown", onToolbarMenuShown, false);
+    toolbarMenu.addEventListener("popuphidden", onToolbarMenuHidden, false);
+    toolbarMenu.openPopup();
+  }
+
+  function onTopMenuHidden(event) {
+    ok(1, "top menu popuphidden listener called");
+    event.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
+    finish();
+  }
+
+  function onToolbarMenuShown(event) {
+    ok(1, "sub menu popupshown listener called");
+    event.currentTarget.removeEventListener("popupshown", arguments.callee, false);
+
+    // test the menu item's default state
+    let menuitem = document.getElementById("toggle_addon-bar");
+    ok(menuitem, "found the menu item");
+    is(menuitem.getAttribute("checked"), "false", "menuitem is not checked by default");
+
+    // click on the menu item
+    // TODO: there's got to be a way to check+command in one shot
+    menuitem.setAttribute("checked", "true");
+    menuitem.click();
+
+    // now the addon bar should be visible and the menu checked
+    is(addonbar.getAttribute("collapsed"), "false", "addon bar is visible after executing the command");
+    is(menuitem.getAttribute("checked"), "true", "menuitem is checked after executing the command");
+
+    toolbarMenu.hidePopup();
+  }
+
+  function onToolbarMenuHidden(event) {
+    ok(1, "toolbar menu popuphidden listener called");
+    event.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
+    topMenu.hidePopup();
+  }
+
+  // open the appmenu or view menu
+  topMenu = document.getElementById("appmenu-popup") ||
+            document.getElementById("menu_viewPopup");
+  topMenu.addEventListener("popupshown", onTopMenuShown, false);
+  topMenu.addEventListener("popuphidden", onTopMenuHidden, false);
+  topMenu.openPopup();
+}
--- a/browser/base/content/test/browser_bug462673.js
+++ b/browser/base/content/test/browser_bug462673.js
@@ -10,17 +10,17 @@ var runs = [
     var newBrowser = newTab.linkedBrowser;
     tabbrowser.removeTab(tab);
     ok(!win.closed, "Window stays open");
     if (!win.closed) {
       is(tabbrowser.tabContainer.childElementCount, 1, "Window has one tab");
       is(tabbrowser.browsers.length, 1, "Window has one browser");
       is(tabbrowser.selectedTab, newTab, "Remaining tab is selected");
       is(tabbrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
-      is(tabbrowser.mTabBox.selectedPanel, newBrowser.parentNode, "Panel for remaining tab is selected");
+      is(tabbrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode, "Panel for remaining tab is selected");
     }
   }
 ];
 
 function test() {
   waitForExplicitFinish();
   runOneTest();
 }
--- a/browser/base/content/test/browser_bug521216.js
+++ b/browser/base/content/test/browser_bug521216.js
@@ -34,13 +34,14 @@ var progressListener = {
   onLocationChange: function onLocationChange(aBrowser) {
     if (aBrowser == tab.linkedBrowser)
       record(arguments.callee.name);
   },
   onStateChange: function onStateChange(aBrowser) {
     if (aBrowser == tab.linkedBrowser)
       record(arguments.callee.name);
   },
-  onLinkIconAvailable: function onLinkIconAvailable(aBrowser) {
-    if (aBrowser == tab.linkedBrowser)
+  onLinkIconAvailable: function onLinkIconAvailable(aBrowser, aIconURL) {
+    if (aBrowser == tab.linkedBrowser &&
+        aIconURL == "about:logo")
       record(arguments.callee.name);
   }
 };
--- a/browser/base/content/test/browser_bug561636.js
+++ b/browser/base/content/test/browser_bug561636.js
@@ -350,25 +350,60 @@ function test9()
       checkPopupHide();
 
       // Clean-up
       Services.obs.removeObserver(gObserver, "invalidformsubmit");
       gObserver.notifyInvalidSubmit = function () {};
       gBrowser.removeTab(tab, {animate: false});
 
       // Next test
-      executeSoon(finish);
+      executeSoon(test10);
     });
   };
 
   Services.obs.addObserver(gObserver, "invalidformsubmit", false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     isnot(gBrowser.selectedTab, tab,
           "This tab should have been loaded in background");
 
     tab.linkedBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   tab.linkedBrowser.loadURI(uri);
 }
+
+/**
+ * In this test, we check that the author defined error message is shown.
+ */
+function test10()
+{
+  let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>";
+  let tab = gBrowser.addTab();
+
+  gInvalidFormPopup.addEventListener("popupshown", function() {
+    gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
+
+    let doc = gBrowser.contentDocument;
+    is(doc.activeElement, doc.getElementById('i'),
+       "First invalid element should be focused");
+
+    checkPopupShow();
+
+    is(gInvalidFormPopup.firstChild.nodeValue, "foo",
+       "The panel should show the author defined error message");
+
+    // Clean-up and next test.
+    gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
+    executeSoon(finish);
+  }, false);
+
+  tab.linkedBrowser.addEventListener("load", function(aEvent) {
+    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gBrowser.contentDocument.getElementById('s').click();
+  }, true);
+
+  gBrowser.selectedTab = tab;
+  gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug594131.js
@@ -0,0 +1,58 @@
+/* ***** 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 594131 test.
+ *
+ * The Initial Developer of the Original Code is
+ * Sindre Dammann <sindrebugzilla@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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() {
+  let backgroundPref = "browser.tabs.loadBookmarksInBackground";
+  let newTab = gBrowser.addTab("http://example.com");
+  waitForExplicitFinish();
+  newTab.linkedBrowser.addEventListener("load", mainPart, true);
+  
+  Services.prefs.setBoolPref(backgroundPref, true);
+  
+  function mainPart() {
+    gBrowser.pinTab(newTab);
+    gBrowser.selectedTab = newTab;
+    
+    openUILinkIn("http://example.org/", "current");
+    isnot(gBrowser.selectedTab, newTab, "shouldn't load in background");
+    
+    if (Services.prefs.prefHasUserValue(backgroundPref))
+      Services.prefs.clearUserPref(backgroundPref);
+    gBrowser.removeTab(newTab);
+    gBrowser.removeTab(gBrowser.tabs[1]); // example.org tab
+    finish();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug595507.js
@@ -0,0 +1,39 @@
+var gInvalidFormPopup = document.getElementById('invalid-form-popup');
+ok(gInvalidFormPopup,
+   "The browser should have a popup to show when a form is invalid");
+
+/**
+ * Make sure that the form validation error message shows even if the form is in an iframe.
+ */
+function test()
+{
+  waitForExplicitFinish();
+
+  let uri = "data:text/html,<iframe src=\"data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>\"</iframe>";
+  let tab = gBrowser.addTab();
+
+  gInvalidFormPopup.addEventListener("popupshown", function() {
+    gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
+
+    let doc = gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument;
+    is(doc.activeElement, doc.getElementById('i'),
+       "First invalid element should be focused");
+
+    ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open',
+       "The invalid form popup should be shown");
+
+    // Clean-up and next test.
+    gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
+    executeSoon(finish);
+  }, false);
+
+  tab.linkedBrowser.addEventListener("load", function(aEvent) {
+    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument
+      .getElementById('s').click();
+  }, true);
+
+  gBrowser.selectedTab = tab;
+  gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug596687.js
@@ -0,0 +1,20 @@
+function test() {
+  var tab = gBrowser.addTab(null, {skipAnimation: true});
+  gBrowser.selectedTab = tab;
+
+  var gotTabAttrModified = false;
+  var gotTabClose = false;
+
+  tab.addEventListener("TabClose", function () {
+    gotTabClose = true;
+
+    tab.addEventListener("TabAttrModified", function () {
+      gotTabAttrModified = true;
+    }, false);
+  }, false);
+
+  gBrowser.removeTab(tab);
+
+  ok(gotTabClose, "should have got the TabClose event");
+  ok(!gotTabAttrModified, "shouldn't have got the TabAttrModified event after TabClose");
+}
deleted file mode 100644
--- a/browser/base/content/test/browser_overLinkInLocationBar.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-  <body>
-    <p>
-      <a href="http://example.com/" id="link">LINK</a>
-    </p>
-  </body>
-</html>
--- a/browser/base/content/test/browser_overLinkInLocationBar.js
+++ b/browser/base/content/test/browser_overLinkInLocationBar.js
@@ -35,38 +35,31 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * This tests the "over-link" that appears in the location bar when the user
  * mouses over a link.  See bug 587908.
  */
 
-const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/browser_overLinkInLocationBar.html";
-
 var gTestIter;
 
 // TESTS //////////////////////////////////////////////////////////////////////
 
 function smokeTestGenerator() {
-  let tab = openTestPage();
-  yield;
+  if (ensureOverLinkHidden())
+    yield;
 
-  let contentDoc = gBrowser.contentDocument;
-  let link = contentDoc.getElementById("link");
-
-  mouseover(link);
+  setOverLink("http://example.com/");
   yield;
   checkURLBar(true);
 
-  mouseout(link);
+  setOverLink("");
   yield;
   checkURLBar(false);
-
-  gBrowser.removeTab(tab);
 }
 
 function test() {
   waitForExplicitFinish();
   gTestIter = smokeTestGenerator();
   cont();
 }
 
@@ -107,71 +100,41 @@ function checkURLBar(shouldShowOverLink)
        "Origin color in over-link layer should be transparent");
     is(overLink.opacity, 0, "Over-link should be transparent");
     isnot(editLayer.color, "transparent",
           "Edit layer color should not be transparent");
   }
 }
 
 /**
- * Opens the test URL in a new foreground tab.  When the page has finished
- * loading, the test iterator is advanced, so you should yield after calling.
+ * Sets the over-link.  This assumes that aStr will cause the over-link to fade
+ * in or out.  When its transition has finished, the test iterator is
+ * incremented, so you should yield after calling.
  *
- * @return The opened <tab>.
- */
-function openTestPage() {
-  gBrowser.addEventListener("load", function onLoad(event) {
-    if (event.target.URL == TEST_URL) {
-      gBrowser.removeEventListener("load", onLoad, true);
-      cont();
-    }
-  }, true);
-  return gBrowser.loadOneTab(TEST_URL, { inBackground: false });
-}
-
-/**
- * Sends a mouseover event to a given anchor node.  When the over-link fade-in
- * transition has completed, the test iterator is advanced, so you should yield
- * after calling.
- *
- * @param anchorNode
- *        An anchor node.
+ * @param aStr
+ *        The over-link will be set to this string or cleared if this is falsey.
  */
-function mouseover(anchorNode) {
-  mouseAnchorNode(anchorNode, true);
-}
-
-/**
- * Sends a mouseout event to a given anchor node.  When the over-link fade-out
- * transition has completed, the test iterator is advanced, so you should yield
- * after calling.
- *
- * @param anchorNode
- *        An anchor node.
- */
-function mouseout(anchorNode) {
-  mouseAnchorNode(anchorNode, false);
-}
-
-/**
- * Helper for mouseover and mouseout.  Sends a mouse event to a given node.
- * When the over-link fade-in or -out transition has completed, the test
- * iterator is advanced, so you should yield after calling.
- *
- * @param node
- *        An anchor node in a content document.
- * @param over
- *        True for "mouseover" and false for "mouseout".
- */
-function mouseAnchorNode(node, over) {
+function setOverLink(aStr) {
   let overLink = gURLBar._overLinkBox;
   overLink.addEventListener("transitionend", function onTrans(event) {
-    if (event.target == overLink) {
+    if (event.target == overLink && event.propertyName == "opacity") {
       overLink.removeEventListener("transitionend", onTrans, false);
       cont();
     }
   }, false);
-  let offset = over ? 0 : -1;
-  let eventType = over ? "mouseover" : "mouseout";
-  EventUtils.synthesizeMouse(node, offset, offset,
-                             { type: eventType, clickCount: 0 },
-                             node.ownerDocument.defaultView);
+  gURLBar.setOverLink(aStr);
 }
+
+/**
+ * If the over-link is hidden, returns false.  Otherwise, hides the overlink and
+ * returns true.  When the overlink is hidden, the test iterator is incremented,
+ * so if this function returns true, you should yield after calling.
+ *
+ * @return True if you should yield and calling and false if not.
+ */
+function ensureOverLinkHidden() {
+  let overLink = gURLBar._overLinkBox;
+  if (window.getComputedStyle(overLink, null).opacity == 0)
+    return false;
+
+  setOverLink("");
+  return true;
+}
--- a/browser/base/content/test/browser_pageInfo.js
+++ b/browser/base/content/test/browser_pageInfo.js
@@ -1,41 +1,26 @@
 function test() {
   waitForExplicitFinish();
 
-  var pageInfo, atTest = 0;
+  var pageInfo;
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function () {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     pageInfo = BrowserPageInfo();
     Services.obs.addObserver(observer, "page-info-dialog-loaded", false);
   }, true);
   content.location =
     "https://example.com/browser/browser/base/content/test/feed_tab.html";
 
   function observer(win, topic, data) {
     if (topic != "page-info-dialog-loaded")
       return;
 
-    switch (atTest) {
-      case 0:
-        atTest++;
-        handlePageInfo();
-        break;
-      case 1:
-        atTest++;
-        pageInfo = win;
-        testLockClick();
-        break;
-      case 2:
-        atTest++;
-        Services.obs.removeObserver(observer, "page-info-dialog-loaded");
-        testLockDoubleClick();
-        break;
-    }
+    handlePageInfo();
   }
 
   function $(aId) { return pageInfo.document.getElementById(aId) };
 
   function handlePageInfo() {
     var feedTab = $("feedTab");
     var feedListbox = $("feedListbox");
 
@@ -48,41 +33,13 @@ function test() {
 
 
     for (var i = 0; i < feedRowsNum; i++) {
       let feedItem = feedListbox.getItemAtIndex(i);
       ok(feedItem.getAttribute("name") == (i+1), 
          "Name given: " + feedItem.getAttribute("name") + ", should be " + (i+1));
     }
 
-    pageInfo.addEventListener("unload", function() {
-      pageInfo.removeEventListener("unload", arguments.callee, false);
-      var lockIcon = document.getElementById("security-button");
-      EventUtils.synthesizeMouse(lockIcon, 0, 0, {clickCount: 1});
-    }, false);
     pageInfo.close();
-  }
-
-  function testLockClick() {
-    var deck = $("mainDeck");
-    is(deck.selectedPanel.id, "securityPanel", "The security tab should open when the lock icon is clicked");
-    pageInfo.addEventListener("unload", function() {
-      pageInfo.removeEventListener("unload", arguments.callee, false);
-      var lockIcon = document.getElementById("security-button");
-      EventUtils.synthesizeMouse(lockIcon, 0, 0, {clickCount: 1});
-      EventUtils.synthesizeMouse(lockIcon, 0, 0, {clickCount: 2});
-    }, false);
-    pageInfo.close();
-  }
-
-  function testLockDoubleClick() {
-    var pageInfoDialogs = Services.wm.getEnumerator("Browser:page-info");
-    var i = 0;
-    while (pageInfoDialogs.hasMoreElements()) {
-      i++;
-      pageInfo = pageInfoDialogs.getNext();
-      pageInfo.close();
-    }
-    is(i, 1, "When the lock is clicked twice there should be only one page info dialog");
     gBrowser.removeCurrentTab();
     finish();
   }
 }
--- a/browser/base/content/test/browser_tab_dragdrop2.js
+++ b/browser/base/content/test/browser_tab_dragdrop2.js
@@ -6,18 +6,18 @@ function test()
   var level2 = false;
   function test1() {
     // Load the following URI (which runs some child popup tests) in a new window (B),
     // then add a blank tab to B and call replaceTabWithWindow to detach the URI tab
     // into yet a new window (C), then close B.
     // Now run the tests again and then close C.
     // The test results does not matter, all this is just to exercise some code to
     // catch assertions or crashes.
-    var uri = "chrome://mochikit/content/browser/" +
-              "browser/base/content/test/browser_tab_dragdrop2_frame1.xul";
+    var chromeroot = getRootDirectory(gTestPath);
+    var uri = chromeroot + "browser_tab_dragdrop2_frame1.xul";
     let window_B = openDialog(location, "_blank", "chrome,all,dialog=no,left=200,top=200,width=200,height=200", uri);
     window_B.addEventListener("load", function(aEvent) {
       window_B.removeEventListener("load", arguments.callee, false);
       if (level1) return; level1=true;
       executeSoon(function () {
         window_B.gBrowser.addEventListener("load", function(aEvent) {
           window_B.removeEventListener("load", arguments.callee, true);
           if (level2) return; level2=true;
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -50,15 +50,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug591706.js \
                  browser_tabview_bug595191.js \
                  browser_tabview_bug595518.js \
                  browser_tabview_bug595943.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
+                 browser_tabview_multiwindow_search.js \
                  browser_tabview_search.js \
                  browser_tabview_snapping.js \
                  browser_tabview_undo_group.js \
+                 browser_tabview_firstrun_pref.js \
                  $(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/base/content/test/tabview/browser_tabview_bug595191.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595191.js
@@ -95,15 +95,11 @@ function toggleTabViewTest(contentWindow
 
     finish();
   }
   contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false);
   // When search is hidden, it focus()es on the background, so avert the 
   // race condition by delaying ourselves on the timeout queue
   setTimeout( function() {
     // Use keyboard shortcut to toggle back to browser view
-    if(navigator.platform.indexOf("Mac") >= 0) {
-      EventUtils.synthesizeKey("VK_SPACE", {altKey : true}, contentWindow);
-    } else {
-      EventUtils.synthesizeKey("VK_SPACE", {ctrlKey : true}, contentWindow);
-    }
+    EventUtils.synthesizeKey("e", { accelKey: true });
   }, 0);
 }
--- a/browser/base/content/test/tabview/browser_tabview_bug595518.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595518.js
@@ -58,36 +58,17 @@ function onTabViewWindowLoaded() {
     
     // verify that the exit button no longer has focus
     is(contentWindow.iQ("#exit-button:focus").length, 0, 
        "The exit button doesn't have the focus");
 
     // verify that the keyboard combo works (this is the crux of bug 595518)
     // Prepare the key combo
     window.addEventListener("tabviewshown", onTabViewShown, false);
-    let utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
-                      getInterface(Components.interfaces.nsIDOMWindowUtils);
-    let keyCode = 0;
-    let charCode;
-    let eventObject;
-    if (navigator.platform.indexOf("Mac") != -1) {
-      charCode = 160; // character code for option (alt) + space
-      eventObject = { altKey: true };
-    } else {
-      charCode = KeyEvent.DOM_VK_SPACE;
-      eventObject = { ctrlKey: true };
-    }
-    
-    // Fire off the key combo
-    let modifiers = EventUtils._parseModifiers(eventObject);
-    let keyDownDefaultHappened = 
-       utils.sendKeyEvent("keydown", keyCode, charCode, modifiers);
-    utils.sendKeyEvent("keypress", keyCode, charCode, modifiers,
-                           !keyDownDefaultHappened);
-    utils.sendKeyEvent("keyup", keyCode, charCode, modifiers);
+    EventUtils.synthesizeKey("e", { accelKey: true }, contentWindow);
   }
   
   let onTabViewShown = function() {
     window.removeEventListener("tabviewshown", onTabViewShown, false);
     
     // test if the key combo worked
     ok(TabView.isVisible(), "Tab View is visible");
 
@@ -96,19 +77,19 @@ function onTabViewWindowLoaded() {
       window.removeEventListener("tabviewhidden", endGame, false);
 
       ok(!TabView.isVisible(), "Tab View is hidden");
       finish();
     }
     window.addEventListener("tabviewhidden", endGame, false);
     TabView.toggle();
   }
-  
+
   window.addEventListener("tabviewhidden", onTabViewHidden, false);
 
   // locate exit button
   let button = contentWindow.document.getElementById("exit-button");
   ok(button, "Exit button exists");
-  
+
   // click exit button
   button.focus();
   EventUtils.sendMouseEvent({ type: "click" }, button, contentWindow);
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_firstrun_pref.js
@@ -0,0 +1,103 @@
+/* ***** 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 Tab View first run (Bug 593157) 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 ***** */
+
+var prefsBranch = Cc["@mozilla.org/preferences-service;1"].
+                  getService(Ci.nsIPrefService).
+                  getBranch("browser.panorama.");
+
+function test() {
+  waitForExplicitFinish();
+
+  ok(!TabView.isVisible(), "Main window TabView is hidden");
+
+  prefsBranch.setBoolPref("experienced_first_run", false);
+  ok(!experienced(), "not experienced");
+
+  newWindowWithTabView(checkFirstRun, part2);
+}
+
+function experienced() {
+  return prefsBranch.prefHasUserValue("experienced_first_run") &&
+    prefsBranch.getBoolPref("experienced_first_run");
+}
+
+function checkFirstRun(win) {
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  
+  let infoItems = contentWindow.iQ(".info-item");
+  is(infoItems.length, 1, "There should be an info item");
+
+  let groupItems = contentWindow.GroupItems.groupItems;
+  is(groupItems.length, 1, "There should be one group");
+  
+  ok(experienced(), "we're now experienced");
+}
+
+function part2() {
+  newWindowWithTabView(checkNotFirstRun, endGame);
+}
+
+function checkNotFirstRun(win) {
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  
+  let infoItems = contentWindow.iQ(".info-item");
+  is(infoItems.length, 0, "There should be no info items");
+}
+
+function endGame() {
+  ok(!TabView.isVisible(), "Main window TabView is still hidden");
+  finish();
+}
+
+function newWindowWithTabView(callback, completeCallback) {
+  let charsetArg = "charset=" + window.content.document.characterSet;
+  let win = window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no,height=800,width=800",
+                              "about:blank", charsetArg, null, null, true);
+  let onLoad = function() {
+    win.removeEventListener("load", onLoad, false);
+    let onShown = function() {
+      win.removeEventListener("tabviewshown", onShown, false);
+      callback(win);
+      win.close();
+      if (typeof completeCallback == "function")
+        completeCallback();
+    };
+    win.addEventListener("tabviewshown", onShown, false);
+    win.TabView.toggle();
+  }
+  win.addEventListener("load", onLoad, false);
+}
--- a/browser/base/content/test/tabview/browser_tabview_launch.js
+++ b/browser/base/content/test/tabview/browser_tabview_launch.js
@@ -40,46 +40,28 @@ function test() {
   
   let tabViewShownCount = 0;
   let onTabViewHidden = function() {
     ok(!TabView.isVisible(), "Tab View is hidden");
 
     if (tabViewShownCount == 1) {
       document.getElementById("menu_tabview").doCommand();
     } else if (tabViewShownCount == 2) {
-       var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
-                        getInterface(Components.interfaces.nsIDOMWindowUtils);
-      if (utils) {
-        var keyCode = 0;
-        var charCode;
-        var eventObject;
-        if (navigator.platform.indexOf("Mac") != -1) {
-          charCode = 160;
-          eventObject = { altKey: true };
-        } else {
-          charCode = 32;
-          eventObject = { ctrlKey: true };
-        }
-        var modifiers = EventUtils._parseModifiers(eventObject);
-        var keyDownDefaultHappened =
-            utils.sendKeyEvent("keydown", keyCode, charCode, modifiers);
-        utils.sendKeyEvent("keypress", keyCode, charCode, modifiers,
-                             !keyDownDefaultHappened);
-        utils.sendKeyEvent("keyup", keyCode, charCode, modifiers);
-      }
+      EventUtils.synthesizeKey("e", { accelKey: true });
     } else if (tabViewShownCount == 3) {
       window.removeEventListener("tabviewshown", onTabViewShown, false);
       window.removeEventListener("tabviewhidden", onTabViewHidden, false);
       finish();
     }
   }
   let onTabViewShown = function() {
-    ok(TabView.isVisible(), "Tab View is visible");
+    // add the count to the message so we can track things more easily.
+    ok(TabView.isVisible(), "Tab View is visible. Count: " + tabViewShownCount);
     tabViewShownCount++
-    TabView.toggle();
+    executeSoon(function() { TabView.toggle(); });
   }
   window.addEventListener("tabviewshown", onTabViewShown, false);
   window.addEventListener("tabviewhidden", onTabViewHidden, false);
   
   ok(!TabView.isVisible(), "Tab View is hidden");
 
   let button = document.getElementById("tabview-button");
   ok(button, "Tab View button exists");
copy from browser/base/content/test/tabview/browser_tabview_search.js
copy to browser/base/content/test/tabview/browser_tabview_multiwindow_search.js
--- a/browser/base/content/test/tabview/browser_tabview_search.js
+++ b/browser/base/content/test/tabview/browser_tabview_multiwindow_search.js
@@ -15,50 +15,59 @@
  *
  * 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>
+ * Ehsan Akhgari <ehsan@mozilla.com>
+ * Sean Dunn <seanedunn@yahoo.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 newTabs = [];
+let newWindows = [];
 
 function test() {
   waitForExplicitFinish();
+  let windowOne = openDialog(location, "", "chrome,all,dialog=no", "data:text/html,");
+  let windowTwo;
 
-  let tabOne = gBrowser.addTab();
-  let tabTwo = gBrowser.addTab("http://mochi.test:8888/");
+  windowOne.addEventListener("load", function() {
+    windowOne.gBrowser.selectedBrowser.addEventListener("load", function() {
+      windowOne.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
-  let browser = gBrowser.getBrowserForTab(tabTwo);
-  let onLoad = function() {
-    browser.removeEventListener("load", onLoad, true);
-    
-    // show the tab view
-    window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
-    ok(!TabView.isVisible(), "Tab View is hidden");
-    TabView.toggle();
-  }
-  browser.addEventListener("load", onLoad, true);
-  newTabs = [ tabOne, tabTwo ];
+      windowTwo = openDialog(location, "", "chrome,all,dialog=no", "http://mochi.test:8888/");
+      windowTwo.addEventListener("load", function() {
+        windowTwo.gBrowser.selectedBrowser.addEventListener("load", function() {
+          windowTwo.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+          newWindows = [ windowOne, windowTwo ];
+
+          // show the tab view
+          window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
+          ok(!TabView.isVisible(), "Tab View is hidden");
+          TabView.toggle();
+        }, true);
+      }, false);
+    }, true);
+  }, false);
 }
 
 function onTabViewWindowLoaded() {
   window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
   ok(TabView.isVisible(), "Tab View is visible");
 
   let contentWindow = document.getElementById("tab-view").contentWindow;
   let search = contentWindow.document.getElementById("search");
@@ -72,60 +81,104 @@ function onTabViewWindowLoaded() {
       "tabviewsearchenabled", onSearchEnabled, false);
     searchTest(contentWindow);
   }
   contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
   // enter search mode
   EventUtils.sendMouseEvent({ type: "mousedown" }, searchButton, contentWindow);
 }
 
+// conveniently combine local and other window tab results from a query
+function getMatchResults(contentWindow, query) {
+  let matcher = new contentWindow.TabMatcher(query);
+  let localMatchResults = matcher.matched();
+  let otherMatchResults = matcher.matchedTabsFromOtherWindows();
+  return localMatchResults.concat(otherMatchResults);
+}
+
 function searchTest(contentWindow) {
   let searchBox = contentWindow.document.getElementById("searchbox");
-
+  let matcher = null;
+  let matchResults = [];
+  
   // get the titles of tabs.
   let tabNames = [];
+
   let tabItems = contentWindow.TabItems.getItems();
-
-  ok(tabItems.length == 3, "Have three tab items");
-  
+  is(tabItems.length, 1, "Have only one tab in the current window's tab items"); 
   tabItems.forEach(function(tab) {
     tabNames.push(tab.nameEl.innerHTML);
   });
+
+  newWindows.forEach(function(win) {
+      for(var i=0; i<win.gBrowser.tabs.length; ++i) {
+        tabNames.push(win.gBrowser.tabs[i].label);
+      }
+  });
+
   ok(tabNames[0] && tabNames[0].length > 2, 
      "The title of tab item is longer than 2 chars")
 
   // empty string
   searchBox.setAttribute("value", "");
-  ok(new contentWindow.TabMatcher(
-      searchBox.getAttribute("value")).matched().length == 0,
-     "Match nothing if it's an empty string");
+  matchResults = getMatchResults(contentWindow, searchBox.getAttribute("value"));
+  ok(matchResults.length == 0, "Match nothing if it's an empty string");
 
   // one char
   searchBox.setAttribute("value", tabNames[0].charAt(0));
-  ok(new contentWindow.TabMatcher(
-      searchBox.getAttribute("value")).matched().length == 0,
+  matchResults = getMatchResults(contentWindow, searchBox.getAttribute("value"));
+  ok(matchResults.length == 0,
      "Match nothing if the length of search term is less than 2");
 
   // the full title
   searchBox.setAttribute("value", tabNames[2]);
-  ok(new contentWindow.TabMatcher(
-      searchBox.getAttribute("value")).matched().length == 1,
-     "Match something when the whole title exists");
-  
+  matchResults = getMatchResults(contentWindow, searchBox.getAttribute("value"));
+  is(matchResults.length, 1,
+    "Match something when the whole title exists");
+
   // part of titled
   searchBox.setAttribute("value", tabNames[0].substr(1));
   contentWindow.performSearch();
-  ok(new contentWindow.TabMatcher(
-      searchBox.getAttribute("value")).matched().length == 2,
+  matchResults = getMatchResults(contentWindow, searchBox.getAttribute("value"));
+  is(matchResults.length, 1,
      "Match something when a part of title exists");
 
+  cleanup(contentWindow);
+}
+
+function cleanup(contentWindow) {
+  contentWindow.hideSearch(null);
+
   let onTabViewHidden = function() {
-    window.removeEventListener("tabviewhidden", onTabViewHidden, false);
-    ok(!TabView.isVisible(), "Tab View is hidden");
-
-    gBrowser.removeTab(newTabs[0]);
-    gBrowser.removeTab(newTabs[1]);
-
-    finish();
+      window.removeEventListener("tabviewhidden", onTabViewHidden, false);
+      ok(!TabView.isVisible(), "Tab View is hidden");
+      let numToClose = newWindows.length;
+      newWindows.forEach(function(win) {
+        whenWindowObservesOnce(win, "domwindowclosed", function() {
+          --numToClose;
+          if(numToClose==0) {
+            finish();
+          }
+        });
+        win.close();
+      });
   }
   window.addEventListener("tabviewhidden", onTabViewHidden, false);
   EventUtils.synthesizeKey("VK_ENTER", {});
 }
+
+function whenWindowObservesOnce(win, topic, func) {
+    let windowWatcher = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+      .getService(Components.interfaces.nsIWindowWatcher);
+    let origWin = win;
+    let origTopic = topic;
+    let origFunc = func;        
+    function windowObserver(aSubject, aTopic, aData) {
+      let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+      if (origWin && theWin != origWin)
+        return;
+      if(aTopic == origTopic) {
+          windowWatcher.unregisterNotification(windowObserver);
+          origFunc.apply(this, []);
+      }
+    }
+    windowWatcher.registerNotification(windowObserver);
+}
\ No newline at end of file
--- a/browser/base/content/test/tabview/browser_tabview_search.js
+++ b/browser/base/content/test/tabview/browser_tabview_search.js
@@ -15,16 +15,17 @@
  *
  * 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>
+ * Sean Dunn <seanedunn@yahoo.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
@@ -112,16 +113,21 @@ function searchTest(contentWindow) {
   
   // part of titled
   searchBox.setAttribute("value", tabNames[0].substr(1));
   contentWindow.performSearch();
   ok(new contentWindow.TabMatcher(
       searchBox.getAttribute("value")).matched().length == 2,
      "Match something when a part of title exists");
 
+  cleanup(contentWindow);
+}
+
+function cleanup(contentWindow) {       
+  contentWindow.hideSearch(null);     
   let onTabViewHidden = function() {
     window.removeEventListener("tabviewhidden", onTabViewHidden, false);
     ok(!TabView.isVisible(), "Tab View is hidden");
 
     gBrowser.removeTab(newTabs[0]);
     gBrowser.removeTab(newTabs[1]);
 
     finish();
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -118,48 +118,58 @@
         this._urlTooltip = document.getElementById("urlTooltip");
 
         this.inputField.controllers.insertControllerAt(0, this._copyCutController);
         this.inputField.addEventListener("mousedown", this, false);
         this.inputField.addEventListener("mousemove", this, false);
         this.inputField.addEventListener("mouseout", this, false);
         this.inputField.addEventListener("overflow", this, false);
         this.inputField.addEventListener("underflow", this, false);
-        this._overLinkBox.addEventListener("transitionend", this, false);
 
         const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
         var textBox = document.getAnonymousElementByAttribute(this,
                                                 "anonid", "textbox-input-box");
         var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                             "anonid", "input-box-contextmenu");
+        var pasteAndGo;
+        cxmenu.addEventListener("popupshowing", function() {
+          if (!pasteAndGo)
+            return;
+          var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
+          var enabled = controller.isCommandEnabled("cmd_paste");
+          if (enabled)
+            pasteAndGo.removeAttribute("disabled");
+          else
+            pasteAndGo.setAttribute("disabled", "true");
+        }, false);
+
         var insertLocation = cxmenu.firstChild;
         while (insertLocation.nextSibling &&
                insertLocation.getAttribute("cmd") != "cmd_paste")
           insertLocation = insertLocation.nextSibling;
         if (insertLocation) {
-          let element = document.createElement("menuitem");
+          pasteAndGo = document.createElement("menuitem");
           let label = Services.strings.createBundle("chrome://browser/locale/browser.properties").
                                    GetStringFromName("pasteAndGo.label");
-          element.setAttribute("label", label);
-          element.setAttribute("cmd", "cmd_paste");
-          element.setAttribute("oncommand", "goDoCommand('cmd_paste'); gURLBar.handleCommand();");
-          cxmenu.insertBefore(element, insertLocation.nextSibling);
+          pasteAndGo.setAttribute("label", label);
+          pasteAndGo.setAttribute("anonid", "paste-and-go");
+          pasteAndGo.setAttribute("oncommand", "goDoCommand('cmd_paste'); gURLBar.handleCommand();");
+          cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling);
         }
       ]]></constructor>
 
       <destructor><![CDATA[
         this._prefs.removeObserver("", this);
         this._prefs = null;
         this.inputField.controllers.removeController(this._copyCutController);
         this.inputField.removeEventListener("mousedown", this, false);
         this.inputField.removeEventListener("mousemove", this, false);
         this.inputField.removeEventListener("mouseout", this, false);
         this.inputField.removeEventListener("overflow", this, false);
         this.inputField.removeEventListener("underflow", this, false);
-        this._overLinkBox.removeEventListener("transitionend", this, false);
       ]]></destructor>
 
       <field name="_value"></field>
 
       <!--
         onBeforeValueGet is called by the base-binding's .value getter.
         It can return an object with a "value" property, to override the
         return value of the getter.
@@ -519,22 +529,16 @@
               break;
             case "overflow":
               this._contentIsCropped = true;
               break;
             case "underflow":
               this._contentIsCropped = false;
               this._hideURLTooltip();
               break;
-            case "transitionend":
-              if (aEvent.target == this._overLinkBox &&
-                  aEvent.propertyName == "opacity") {
-                this._overLinkTransitioning = false;
-              }
-              break;
           }
         ]]></body>
       </method>
 
       <property name="textValue"
                 onget="return this.value;">
         <setter>
           <![CDATA[
@@ -591,25 +595,38 @@
                                                 "over-link-path-label");
       ]]></field>
 
       <field name="_textboxContainer" readonly="true"><![CDATA[
         document.getAnonymousElementByAttribute(this, "anonid",
                                                 "textbox-container");
       ]]></field>
 
+      <field name="_overLinkDelay" readonly="true"><![CDATA[
+        100
+      ]]></field>
+
+      <field name="_overLinkDelayTimer"><![CDATA[
+        undefined
+      ]]></field>
+
       <method name="setOverLink">
         <parameter name="aURL"/>
         <body><![CDATA[
+          // If the over-link is already scheduled to fade-in, cancel it.
+          if (this._overLinkDelayTimer) {
+            clearTimeout(this._overLinkDelayTimer);
+            delete this._overLinkDelayTimer;
+          }
+
           // Hide the over-link if aURL is falsey or if the URL bar is focused.
           if (!aURL || this.focused) {
-            if (this.hasAttribute("overlinkstate")) {
+            // Over-link is fading in or showing.  Fade out.
+            if (this.hasAttribute("overlinkstate"))
               this.removeAttribute("overlinkstate");
-              this._overLinkTransitioning = true;
-            }
             return;
           }
 
           // Get the width of the bar before we go modifying it.
           var barWidth = this._stack.boxObject.width;
 
           // Determine the pre-path and path of the over-link.  Include the
           // path's leading slash in the pre-path so that if the path is
@@ -646,23 +663,29 @@
             }
             overLinkPath.crop = host ? "start" : "end";
             overLink.style.minWidth = maxWidth + "px";
             overLink.style.maxWidth = maxWidth + "px";
           }
 
           this._originLabel.value = this.value;
 
-          // Finally, show the over-link.  If its animation is currently in
-          // transition, show it immediately rather than animating it again.
-          if (this._overLinkTransitioning)
+          // Finally, show the over-link.
+          if (window.getComputedStyle(overLink, null).opacity != 0) {
+            // It's currently becoming transparent, becoming opaque, or is
+            // opaque.  Show it immediately.
             this.setAttribute("overlinkstate", "showing");
+          }
           else {
-            this.setAttribute("overlinkstate", "fade-in");
-            this._overLinkTransitioning = true;
+            // It's not showing at all.  Start fade-in after some delay.
+            this._overLinkDelayTimer =
+              setTimeout(function overLinkDelayTimer(self) {
+                delete self._overLinkDelayTimer;
+                self.setAttribute("overlinkstate", "fade-in");
+              }, this._overLinkDelay, this);
           }
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="draggesture" phase="capturing"><![CDATA[
         // TODO: This should use dragstart but editor code is still using
@@ -923,9 +946,21 @@
       <constructor><![CDATA[
         this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
         // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
         // 592424 is fixed
         document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
       ]]></constructor>
     </implementation>
   </binding>
+  
+  <binding id="urlbar-progress" extends="chrome://global/content/bindings/progressmeter.xml#progressmeter">
+    <handlers>
+      <handler event="transitionend"><![CDATA[
+        if (this.hasAttribute("slideback") || this.value > 0)
+          this.removeAttribute("slideback");
+        else
+          this.setAttribute("slideback", true);
+      ]]></handler>
+    </handlers>
+  </binding>
+  
 </bindings>
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -45,32 +45,16 @@ var TAB_DROP_TYPE = "application/x-moz-t
 
 var gBidiUI = false;
 
 function getBrowserURL()
 {
   return "chrome://browser/content/browser.xul";
 }
 
-function goToggleToolbar( id, elementID )
-{
-  var toolbar = document.getElementById(id);
-  var element = document.getElementById(elementID);
-  if (toolbar)
-  {
-    var isHidden = toolbar.hidden;
-    toolbar.hidden = !isHidden;
-    document.persist(id, 'hidden');
-    if (element) {
-      element.setAttribute("checked", isHidden ? "true" : "false");
-      document.persist(elementID, 'checked');
-    }
-  }
-}
-
 function getTopWin()
 {
   return Services.wm.getMostRecentWindow("navigator:browser");
 }
 
 function openTopWin( url )
 {
   openUILink(url, {})
@@ -229,19 +213,21 @@ function openUILinkIn(url, where, aAllow
   var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground");
 
   if (where == "current" && w.gBrowser.selectedTab.pinned) {
     try {
       let uriObj = Services.io.newURI(url, null, null);
       if (!uriObj.schemeIs("javascript") &&
           w.gBrowser.currentURI.host != uriObj.host) {
         where = "tab";
+        loadInBackground = false;
       }
     } catch (err) {
       where = "tab";
+      loadInBackground = false;
     }
   }
 
   switch (where) {
   case "current":
     w.loadURI(url, aReferrerURI, aPostData, aAllowThirdPartyFixup);
     break;
   case "tabshifted":
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -526,18 +526,17 @@ nsBrowserContentHandler.prototype = {
       }
     }
     if (cmdLine.handleFlag("preferences", false)) {
       openPreferences();
       cmdLine.preventDefault = true;
     }
     if (cmdLine.handleFlag("silent", false))
       cmdLine.preventDefault = true;
-    if (cmdLine.findFlag("private-toggle", false) >= 0 &&
-        cmdLine.state != cmdLine.STATE_INITIAL_LAUNCH)
+    if (cmdLine.findFlag("private-toggle", false) >= 0)
       cmdLine.preventDefault = true;
 
     var searchParam = cmdLine.handleFlagWithParam("search", false);
     if (searchParam) {
       doSearch(searchParam, cmdLine);
       cmdLine.preventDefault = true;
     }
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -160,16 +160,20 @@ BrowserGlue.prototype = {
         this._dispose();
         break;
       case "prefservice:after-app-defaults":
         this._onAppDefaults();
         break;
       case "final-ui-startup":
         this._onProfileStartup();
         break;
+      case "browser-delayed-startup-finished":
+        this._onFirstWindowLoaded();
+        Services.obs.removeObserver(this, "browser-delayed-startup-finished");
+        break;
       case "sessionstore-windows-restored":
         this._onBrowserStartup();
         break;
       case "browser:purge-session-history":
         // reset the console service's error buffer
         Services.console.logStringMessage(null); // clear the console (in case it's open)
         Services.console.reset();
         break;
@@ -255,16 +259,17 @@ BrowserGlue.prototype = {
   }, 
 
   // initialization (called on application startup) 
   _init: function BG__init() {
     let os = Services.obs;
     os.addObserver(this, "xpcom-shutdown", false);
     os.addObserver(this, "prefservice:after-app-defaults", false);
     os.addObserver(this, "final-ui-startup", false);
+    os.addObserver(this, "browser-delayed-startup-finished", false);
     os.addObserver(this, "sessionstore-windows-restored", false);
     os.addObserver(this, "browser:purge-session-history", false);
     os.addObserver(this, "quit-application-requested", false);
     os.addObserver(this, "quit-application-granted", false);
 #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
     os.addObserver(this, "browser-lastwindow-close-requested", false);
     os.addObserver(this, "browser-lastwindow-close-granted", false);
 #endif
@@ -342,16 +347,32 @@ BrowserGlue.prototype = {
       catch (e) {
         Services.io.offline = false;
       }
     }
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
 
+  // the first browser window has finished initializing
+  _onFirstWindowLoaded: function BG__onFirstWindowLoaded() {
+#ifdef XP_WIN
+#ifndef WINCE
+    // For windows seven, initialize the jump list module.
+    const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
+    if (WINTASKBAR_CONTRACTID in Cc &&
+        Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
+      let temp = {};
+      Cu.import("resource://gre/modules/WindowsJumpLists.jsm", temp);
+      temp.WinTaskbarJumpList.startup();
+    }
+#endif
+#endif
+  },
+
   // profile shutdown handler (contains profile cleanup routines)
   _onProfileShutdown: function BG__onProfileShutdown() {
 #ifdef MOZ_UPDATER
 #ifdef WINCE
     // If there's a pending update, clear cache to free up disk space.
     try {
       let um = Cc["@mozilla.org/updates/update-manager;1"].
                getService(Ci.nsIUpdateManager);
@@ -403,29 +424,16 @@ BrowserGlue.prototype = {
     if (this._isPlacesDatabaseLocked) {
       this._showPlacesLockedNotificationBox();
     }
 
     // If there are plugins installed that are outdated, and the user hasn't
     // been warned about them yet, open the plugins update page.
     if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER))
       this._showPluginUpdatePage();
-
-#ifdef XP_WIN
-#ifndef WINCE
-    // For windows seven, initialize the jump list module.
-    const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
-    if (WINTASKBAR_CONTRACTID in Cc &&
-        Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
-      let temp = {};
-      Cu.import("resource://gre/modules/WindowsJumpLists.jsm", temp);
-      temp.WinTaskbarJumpList.startup();
-    }
-#endif
-#endif
   },
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     var windowcount = 0;
--- a/browser/components/places/content/bookmarksPanel.xul
+++ b/browser/components/places/content/bookmarksPanel.xul
@@ -45,17 +45,17 @@
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 
 <!DOCTYPE page SYSTEM "chrome://browser/locale/places/places.dtd">
 
 <page id="bookmarksPanel"
       xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
       xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
       onload="init();"
-      onunload="SidebarUtils.clearURLFromStatusBar();">
+      onunload="SidebarUtils.setMouseoverURL('');">
 
   <script type="application/javascript" 
           src="chrome://browser/content/bookmarks/sidebarUtils.js"/>
   <script type="application/javascript" 
           src="chrome://browser/content/bookmarks/bookmarksPanel.js"/>
 
   <commandset id="placesCommands"/>
   <commandset id="editMenuCommands"/>
@@ -74,16 +74,16 @@
 
   <tree id="bookmarks-view" class="sidebar-placesTree" type="places"
         flex="1"
         hidecolumnpicker="true"
         context="placesContext"
         onkeypress="SidebarUtils.handleTreeKeyPress(event);"
         onclick="SidebarUtils.handleTreeClick(this, event, true);"
         onmousemove="SidebarUtils.handleTreeMouseMove(event);"
-        onmouseout="SidebarUtils.clearURLFromStatusBar();">
+        onmouseout="SidebarUtils.setMouseoverURL('');">
     <treecols>
       <treecol id="title" flex="1" primary="true" hideheader="true"/>
     </treecols>
     <treechildren id="bookmarks-view-children" view="bookmarks-view"
                   class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
   </tree>
 </page>
--- a/browser/components/places/content/history-panel.xul
+++ b/browser/components/places/content/history-panel.xul
@@ -53,17 +53,17 @@
 ]>
 
 <!-- we need to keep id="history-panel" for upgrade and switching
      between versions of the browser -->
 
 <page id="history-panel" orient="vertical"
       xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
       onload="HistorySidebarInit();"
-      onunload="SidebarUtils.clearURLFromStatusBar();">
+      onunload="SidebarUtils.setMouseoverURL('');">
 
   <script type="application/javascript" 
           src="chrome://browser/content/bookmarks/sidebarUtils.js"/>
   <script type="application/javascript" 
           src="chrome://browser/content/places/history-panel.js"/>
 
   <commandset id="editMenuCommands"/>
   <commandset id="placesCommands"/>
@@ -117,15 +117,15 @@
         class="sidebar-placesTree"
         flex="1"
         type="places"
         context="placesContext"
         hidecolumnpicker="true"
         onkeypress="SidebarUtils.handleTreeKeyPress(event);"
         onclick="SidebarUtils.handleTreeClick(this, event, true);"
         onmousemove="SidebarUtils.handleTreeMouseMove(event);"
-        onmouseout="SidebarUtils.clearURLFromStatusBar();">
+        onmouseout="SidebarUtils.setMouseoverURL('');">
     <treecols>
       <treecol id="title" flex="1" primary="true" hideheader="true"/>
     </treecols>
     <treechildren class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
   </tree>
 </page>
--- a/browser/components/places/content/sidebarUtils.js
+++ b/browser/components/places/content/sidebarUtils.js
@@ -112,26 +112,25 @@ var SidebarUtils = {
       return;
 
     var tree = aEvent.target.parentNode;
     var tbo = tree.treeBoxObject;
     var row = { }, col = { }, obj = { };
     tbo.getCellAt(aEvent.clientX, aEvent.clientY, row, col, obj);
 
     // row.value is -1 when the mouse is hovering an empty area within the tree.
-    // To avoid showing a URL from a previously hovered node,
-    // for a currently hovered non-url node, we must clear the URL from the
-    // status bar in these cases.
+    // To avoid showing a URL from a previously hovered node for a currently
+    // hovered non-url node, we must clear the moused-over URL in these cases.
     if (row.value != -1) {
-      var cell = tree.view.nodeForTreeIndex(row.value);
-      if (PlacesUtils.nodeIsURI(cell))
-        window.top.XULBrowserWindow.setOverLink(cell.uri, null);
+      var node = tree.view.nodeForTreeIndex(row.value);
+      if (PlacesUtils.nodeIsURI(node))
+        this.setMouseoverURL(node.uri);
       else
-        this.clearURLFromStatusBar();
+        this.setMouseoverURL("");
     }
     else
-      this.clearURLFromStatusBar();
+      this.setMouseoverURL("");
   },
 
-  clearURLFromStatusBar: function SU_clearURLFromStatusBar() {
-    window.top.XULBrowserWindow.setOverLink("", null);
+  setMouseoverURL: function SU_setMouseoverURL(aURL) {
+    window.top.XULBrowserWindow.setOverLink(aURL, null);
   }
 };
--- a/browser/components/preferences/advanced-scripts.xul
+++ b/browser/components/preferences/advanced-scripts.xul
@@ -52,20 +52,16 @@
 
   <prefpane id="AdvancedJSDialogPane"
             helpTopic="prefs-advanced-javascript">
 
     <preferences>
      <preference id="dom.event.contextmenu.enabled"  name="dom.event.contextmenu.enabled"  type="bool"/>
      <preference id="dom.disable_window_move_resize" name="dom.disable_window_move_resize" type="bool" inverted="true"/>
      <preference id="dom.disable_window_flip"        name="dom.disable_window_flip"        type="bool" inverted="true"/>
-     <preference id="dom.disable_window_open_feature.status" inverted="true"
-                 name="dom.disable_window_open_feature.status" type="bool"/>
-     <preference id="dom.disable_window_status_change" inverted="true"
-                 name="dom.disable_window_status_change" type="bool"/>
     </preferences>
     
     <script type="application/javascript" src="chrome://browser/content/preferences/advanced-scripts.js"/>
 
     <stringbundle id="preferencesBundle" src="chrome://browser/locale/preferences/preferences.properties"/>
 
     <description value="&allowScripts.label;"/>
 
@@ -73,17 +69,11 @@
               accesskey="&moveResizeWindows.accesskey;"
               preference="dom.disable_window_move_resize"/>
     <checkbox id="raiseLowerWindows" label="&raiseLowerWindows.label;" 
               accesskey="&raiseLowerWindows.accesskey;"
               preference="dom.disable_window_flip"/>
     <checkbox id="disableContextMenus" label="&disableContextMenus.label;" 
               accesskey="&disableContextMenus.accesskey;"
               preference="dom.event.contextmenu.enabled"/>
-    <checkbox id="hideStatusBar" label="&hideStatusBar.label;" 
-              accesskey="&hideStatusBar.accesskey;"
-              preference="dom.disable_window_open_feature.status"/>
-    <checkbox id="changeStatusBar" label="&changeStatusBar.label;" 
-              accesskey="&changeStatusBar.accesskey;"
-              preference="dom.disable_window_status_change"/>
 
   </prefpane>
 </prefwindow>
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -186,18 +186,17 @@ var gAdvancedPane = {
 
   // NETWORK TAB
 
   /*
    * Preferences:
    *
    * browser.cache.disk.capacity
    * - the size of the browser cache in KB
-   * browser.cache.disk.smart_size.enabled
-   * - If disabled, disk.capacity is used
+   * - Only used if browser.cache.disk.smart_size.enabled is disabled
    */
 
   /**
    * Displays a dialog in which proxy settings may be changed.
    */
   showConnections: function ()
   {
     document.documentElement.openSubDialog("chrome://browser/content/preferences/connection.xul",
@@ -238,18 +237,20 @@ var gAdvancedPane = {
   {
     document.getElementById("useCacheBefore").disabled = smartSizeEnabled;
     document.getElementById("cacheSize").disabled = smartSizeEnabled;
     document.getElementById("useCacheAfter").disabled = smartSizeEnabled;
   },
 
   readSmartSizeEnabled: function ()
   {
-    var enabled = document.getElementById("browser.cache.disk.smart_size.enabled").value;
-    this.updateCacheSizeUI(enabled);
+    // The smart_size.enabled preference element is inverted="true", so its
+    // value is the opposite of the actual pref value
+    var disabled = document.getElementById("browser.cache.disk.smart_size.enabled").value;
+    this.updateCacheSizeUI(!disabled);
   },
   
   /**
    * Converts the cache size from units of KB to units of MB and returns that
    * value.
    */
   readCacheSize: function ()
   {
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -83,16 +83,17 @@
 #endif
 
       <!-- Network tab -->
       <preference id="browser.cache.disk.capacity"     name="browser.cache.disk.capacity"     type="int"/>
       <preference id="browser.offline-apps.notify"     name="browser.offline-apps.notify"     type="bool"/>
  
       <preference id="browser.cache.disk.smart_size.enabled"
                   name="browser.cache.disk.smart_size.enabled"
+                  inverted="true"
                   type="bool"/>
  
      <!-- Update tab -->
 #ifdef MOZ_UPDATER
       <preference id="app.update.enabled"              name="app.update.enabled"              type="bool"
                   onchange="gAdvancedPane.updateAppUpdateItems();
                             gAdvancedPane.updateAutoItems();
                             gAdvancedPane.updateModeItems();"/>
@@ -238,26 +239,28 @@
               <label id="actualCacheSize" flex="1"/>
               <button id="clearCacheButton" icon="clear"
                       label="&clearCacheNow.label;" accesskey="&clearCacheNow.accesskey;"
                       oncommand="gAdvancedPane.clearCache();"/>
             </hbox>
             <checkbox preference="browser.cache.disk.smart_size.enabled"
                       id="allowSmartSize" flex="1"
                       onsyncfrompreference="return gAdvancedPane.readSmartSizeEnabled();"
-                      label="&smartSizeCache.label;" accesskey="&smartSizeCache.accesskey;"/>
+                      label="&overrideSmartCacheSize.label;" 
+                      accesskey="&overrideSmartCacheSize.accesskey;"/>
             <hbox align="center" class="indent">
               <label id="useCacheBefore" control="cacheSize"
-                     accesskey="&useCacheBefore.accesskey;" value="&useCacheBefore.label;"/>
+                     accesskey="&limitCacheSizeBefore.accesskey;" 
+                     value="&limitCacheSizeBefore.label;"/>
               <textbox id="cacheSize" type="number" size="4" max="1024"
                        preference="browser.cache.disk.capacity"
                        onsyncfrompreference="return gAdvancedPane.readCacheSize();"
                        onsynctopreference="return gAdvancedPane.writeCacheSize();"
                        aria-labelledby="useCacheBefore cacheSize useCacheAfter"/>
-              <label id="useCacheAfter" flex="1">&useCacheAfter.label;</label>
+              <label id="useCacheAfter" flex="1">&limitCacheSizeAfter.label;</label>
             </hbox>
             <hbox align="center">
               <checkbox id="offlineNotify" flex="1"
                         label="&offlineNotify.label;" accesskey="&offlineNotify.accesskey;"
                         preference="browser.offline-apps.notify"
                         onsyncfrompreference="return gAdvancedPane.readOfflineNotify();"/>
               <button id="offlineNotifyExceptions"
                       label="&offlineNotifyExceptions.label;"
--- a/browser/components/preferences/tests/browser_privacypane_1.js
+++ b/browser/components/preferences/tests/browser_privacypane_1.js
@@ -38,19 +38,19 @@
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
 
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     test_pane_visibility,
     test_dependent_elements,
     test_dependent_cookie_elements,
     test_dependent_clearonclose_elements,
     test_dependent_prefs,
 
--- a/browser/components/preferences/tests/browser_privacypane_2.js
+++ b/browser/components/preferences/tests/browser_privacypane_2.js
@@ -38,19 +38,19 @@
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
 
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     test_historymode_retention("remember", undefined),
     test_historymode_retention("dontremember", "remember"),
     test_historymode_retention("custom", "dontremember"),
     // custom without any micro-prefs changed won't retain
     test_historymode_retention("remember", "dontremember"),
     test_historymode_retention("custom", "remember"),
--- a/browser/components/preferences/tests/browser_privacypane_3.js
+++ b/browser/components/preferences/tests/browser_privacypane_3.js
@@ -37,19 +37,19 @@
 
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     test_custom_retention("rememberHistory", "remember"),
     test_custom_retention("rememberHistory", "custom"),
     test_custom_retention("rememberDownloads", "remember"),
     test_custom_retention("rememberDownloads", "custom"),
     test_custom_retention("rememberForms", "remember"),
     test_custom_retention("rememberForms", "custom"),
--- a/browser/components/preferences/tests/browser_privacypane_4.js
+++ b/browser/components/preferences/tests/browser_privacypane_4.js
@@ -37,19 +37,19 @@
 
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     test_custom_retention("acceptCookies", "remember"),
     test_custom_retention("acceptCookies", "custom"),
     test_custom_retention("acceptThirdParty", "remember"),
     test_custom_retention("acceptThirdParty", "custom"),
     test_custom_retention("keepCookiesUntil", "remember", 1),
     test_custom_retention("keepCookiesUntil", "custom", 2),
--- a/browser/components/preferences/tests/browser_privacypane_5.js
+++ b/browser/components/preferences/tests/browser_privacypane_5.js
@@ -37,19 +37,19 @@
 
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     test_locbar_suggestion_retention(-1, undefined),
     test_locbar_suggestion_retention(1, -1),
     test_locbar_suggestion_retention(2, 1),
     test_locbar_suggestion_retention(0, 2),
     test_locbar_suggestion_retention(0, 0),
 
--- a/browser/components/preferences/tests/browser_privacypane_6.js
+++ b/browser/components/preferences/tests/browser_privacypane_6.js
@@ -37,19 +37,19 @@
 
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     test_privatebrowsing_toggle,
     enter_private_browsing, // once again, test with PB initially enabled
     test_privatebrowsing_toggle,
 
     // don't reset preferences, will pick up where we left off in browser_privacypane_7.js
   ]);
--- a/browser/components/preferences/tests/browser_privacypane_7.js
+++ b/browser/components/preferences/tests/browser_privacypane_7.js
@@ -37,19 +37,19 @@
 
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     test_privatebrowsing_ui,
     enter_private_browsing, // once again, test with PB initially enabled
     test_privatebrowsing_ui,
 
     // reset all preferences to their default values once we're done
     reset_preferences
--- a/browser/components/preferences/tests/browser_privacypane_8.js
+++ b/browser/components/preferences/tests/browser_privacypane_8.js
@@ -36,19 +36,19 @@
 
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
-    rootDir = "file://" + tmpdir.path;
+    rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
+  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
 
   run_test_subset([
     // history mode should be initialized to remember
     test_historymode_retention("remember", undefined),
 
     // history mode should remain remember; toggle acceptCookies checkbox
     test_custom_retention("acceptCookies", "remember"),
 
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -124,16 +124,19 @@ PrivateBrowsingService.prototype = {
   _autoStarted: false,
 
   // List of view source window URIs for restoring later
   _viewSrcURLs: [],
 
   // List of nsIXULWindows we are going to be closing during the transition
   _windowsToClose: [],
 
+  // Whether private browsing has been turned on from the command line
+  _lastChangedByCommandLine: false,
+
   // XPCOM registration
   classID: Components.ID("{c31f4883-839b-45f6-82ad-a6a9bc5ad599}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrivateBrowsingService, 
                                          Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsICommandLineHandler]),
 
@@ -226,16 +229,19 @@ PrivateBrowsingService.prototype = {
     // used
     if (!this._autoStarted && this._saveSession) {
       let ss = Cc["@mozilla.org/browser/sessionstore;1"].
                getService(Ci.nsISessionStore);
       // if we have transitioned out of private browsing mode and the session is
       // to be restored, do it now
       if (!this._inPrivateBrowsing) {
         this._currentStatus = STATE_WAITING_FOR_RESTORE;
+        if (!this._getBrowserWindow()) {
+          ss.init(null);
+        }
         ss.setBrowserState(this._savedBrowserState);
         this._savedBrowserState = null;
 
         this._closePageInfoWindows();
 
         // re-open all view-source windows
         let windowWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
                             getService(Ci.nsIWindowWatcher);
@@ -268,16 +274,19 @@ PrivateBrowsingService.prototype = {
                 "url": "about:privatebrowsing"
               }]
             }],
             "_closedTabs": []
           }]
         };
         // Transition into private browsing mode
         this._currentStatus = STATE_WAITING_FOR_RESTORE;
+        if (!this._getBrowserWindow()) {
+          ss.init(null);
+        }
         ss.setBrowserState(JSON.stringify(privateBrowsingState));
       }
     }
   },
 
   _notifyIfTransitionComplete: function PBS__notifyIfTransitionComplete() {
     switch (this._currentStatus) {
       case STATE_TRANSITION_STARTED:
@@ -434,16 +443,20 @@ PrivateBrowsingService.prototype = {
         }
         break;
       case "command-line-startup":
         this._obs.removeObserver(this, "command-line-startup");
         aSubject.QueryInterface(Ci.nsICommandLine);
         if (aSubject.findFlag("private", false) >= 0) {
           this.privateBrowsingEnabled = true;
           this._autoStarted = true;
+          this._lastChangedByCommandLine = true;
+        }
+        else if (aSubject.findFlag("private-toggle", false) >= 0) {
+          this._lastChangedByCommandLine = true;
         }
         break;
       case "sessionstore-browser-state-restored":
         if (this._currentStatus == STATE_WAITING_FOR_RESTORE) {
           this._currentStatus = STATE_RESTORE_FINISHED;
           this._notifyIfTransitionComplete();
         }
         break;
@@ -453,16 +466,17 @@ PrivateBrowsingService.prototype = {
   // nsICommandLineHandler
 
   handle: function PBS_handle(aCmdLine) {
     if (aCmdLine.handleFlag("private", false))
       ; // It has already been handled
     else if (aCmdLine.handleFlag("private-toggle", false)) {
       this.privateBrowsingEnabled = !this.privateBrowsingEnabled;
       this._autoStarted = false;
+      this._lastChangedByCommandLine = true;
     }
   },
 
   get helpInfo() {
     return "  -private           Enable private browsing mode.\n" +
            "  -private-toggle    Toggle private browsing mode.\n";
   },
 
@@ -529,26 +543,34 @@ PrivateBrowsingService.prototype = {
         this._windowsToClose[i].docShell.contentViewer.resetCloseWindow();
       // We don't log an error when the transition is canceled from beforeunload
       if (ex != Cr.NS_ERROR_ABORT)
         Cu.reportError("Exception thrown while processing the " +
           "private browsing mode change request: " + ex.toString());
     } finally {
       this._windowsToClose = [];
       this._notifyIfTransitionComplete();
+      this._lastChangedByCommandLine = false;
     }
   },
 
   /**
    * Whether private browsing has been started automatically.
    */
   get autoStarted() {
     return this._inPrivateBrowsing && this._autoStarted;
   },
 
+  /**
+   * Whether the latest transition was initiated from the command line.
+   */
+  get lastChangedByCommandLine() {
+    return this._lastChangedByCommandLine;
+  },
+
   removeDataFromDomain: function PBS_removeDataFromDomain(aDomain)
   {
 
     // clear any and all network geolocation provider sessions
     try {
         this._prefs.deleteBranch("geo.wifi.access_token.");
     } catch (e) {}
     
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingServiceWrapper.cpp
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingServiceWrapper.cpp
@@ -102,16 +102,25 @@ nsPrivateBrowsingServiceWrapper::GetAuto
 {
   if (!aAutoStarted)
     return NS_ERROR_NULL_POINTER;
   JSStackGuard guard;
   return mPBService->GetAutoStarted(aAutoStarted);
 }
 
 NS_IMETHODIMP
+nsPrivateBrowsingServiceWrapper::GetLastChangedByCommandLine(PRBool *aReason)
+{
+  if (!aReason)
+    return NS_ERROR_NULL_POINTER;
+  JSStackGuard guard;
+  return mPBService->GetLastChangedByCommandLine(aReason);
+}
+
+NS_IMETHODIMP
 nsPrivateBrowsingServiceWrapper::RemoveDataFromDomain(const nsACString & aDomain)
 {
   JSStackGuard guard;
   return mPBService->RemoveDataFromDomain(aDomain);
 }
 
 NS_IMETHODIMP
 nsPrivateBrowsingServiceWrapper::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -44,17 +44,16 @@ relativesrcdir  = browser/components/pri
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES =  \
 		browser_console_clear.js \
 		browser_privatebrowsing_certexceptionsui.js \
 		browser_privatebrowsing_commandline_toggle.js \
 		browser_privatebrowsing_crh.js \
-		browser_privatebrowsing_downloadmonitor.js \
 		browser_privatebrowsing_fastswitch.js \
 		browser_privatebrowsing_findbar.js \
 		browser_privatebrowsing_forgetthissite.js \
 		browser_privatebrowsing_geoprompt.js \
 		browser_privatebrowsing_geoprompt_page.html \
 		browser_privatebrowsing_import.js \
 		browser_privatebrowsing_newwindow_stopcmd.js \
 		browser_privatebrowsing_opendir.js \
@@ -79,16 +78,19 @@ include $(topsrcdir)/config/rules.mk
 		browser_privatebrowsing_zoomrestore.js \
 		ctxmenu.html \
 		ctxmenu-image.png \
 		popup.html \
 		staller.sjs \
 		title.sjs \
 		$(NULL)
 
+# Disabled until bug 564934 is fixed:
+#		browser_privatebrowsing_downloadmonitor.js \
+
 # Turn off private browsing tests that perma-timeout on Linux.
 ifneq (Linux,$(OS_ARCH))
 _BROWSER_TEST_FILES += \
 		browser_privatebrowsing_beforeunload_enter.js \
 		browser_privatebrowsing_beforeunload_exit.js \
 		browser_privatebrowsing_cookieacceptdialog.js \
 		$(NULL)
 endif
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_commandline_toggle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_commandline_toggle.js
@@ -123,25 +123,29 @@ function test() {
   let tab = gBrowser.selectedTab;
   let browser = gBrowser.getBrowserForTab(tab);
   browser.addEventListener("load", function () {
     browser.removeEventListener("load", arguments.callee, true);
     ok(!pb.privateBrowsingEnabled, "The private browsing mode should not be started");
     is(browser.contentWindow.location, "about:", "The correct page has been loaded");
 
     simulatePrivateCommandLineArgument();
+    is(pb.lastChangedByCommandLine, true,
+       "The status change reason should reflect the PB mode being set from the command line");
     tab = gBrowser.selectedTab;
     browser = gBrowser.getBrowserForTab(tab);
     browser.addEventListener("load", function() {
       browser.removeEventListener("load", arguments.callee, true);
       ok(pb.privateBrowsingEnabled, "The private browsing mode should be started");
       is(browser.contentWindow.location, "about:privatebrowsing",
          "about:privatebrowsing should now be loaded");
 
       simulatePrivateCommandLineArgument();
+      is(pb.lastChangedByCommandLine, true,
+         "The status change reason should reflect the PB mode being set from the command line");
       tab = gBrowser.selectedTab;
       browser = gBrowser.getBrowserForTab(tab);
       browser.addEventListener("load", function() {
         browser.removeEventListener("load", arguments.callee, true);
         ok(!pb.privateBrowsingEnabled, "The private browsing mode should be stopped");
         is(browser.contentWindow.location, "about:",
            "about: should now be loaded");
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
@@ -49,43 +49,43 @@ function test() {
   const TEST_URI = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/popup.html";
 
   waitForExplicitFinish();
 
   function testPopupBlockerMenuItem(expectedDisabled, callback) {
     gBrowser.addEventListener("DOMUpdatePageReport", function() {
       gBrowser.removeEventListener("DOMUpdatePageReport", arguments.callee, false);
       executeSoon(function() {
-        let pageReportButton = document.getElementById("page-report-button");
         let notification = gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked");
 
-        ok(!pageReportButton.hidden, "The page report button should not be hidden");
         ok(notification, "The notification box should be displayed");
 
         function checkMenuItem(callback) {
+          dump("CMI: in\n");
           document.addEventListener("popupshown", function(event) {
+            dump("CMI: popupshown\n");
             document.removeEventListener("popupshown", arguments.callee, false);
 
             if (expectedDisabled)
               is(document.getElementById("blockedPopupAllowSite").getAttribute("disabled"), "true",
                  "The allow popups menu item should be disabled");
 
             event.originalTarget.hidePopup();
+            dump("CMI: calling back\n");
             callback();
+            dump("CMI: called back\n");
           }, false);
+          dump("CMI: out\n");
         }
 
         checkMenuItem(function() {
-          checkMenuItem(function() {
-            gBrowser.removeTab(tab);
-            callback();
-          });
-          notification.querySelector("button").doCommand();
+          gBrowser.removeTab(tab);
+          callback();
         });
-        EventUtils.synthesizeMouse(document.getElementById("page-report-button"), 1, 1, {});
+        notification.querySelector("button").doCommand();
       });
     }, false);
 
     let tab = gBrowser.addTab(TEST_URI);
     gBrowser.selectedTab = tab;
   }
 
   testPopupBlockerMenuItem(false, function() {
copy from browser/components/privatebrowsing/test/unit/test_0-privatebrowsing.js
copy to browser/components/privatebrowsing/test/unit/do_test_0-privatebrowsing.js
--- a/browser/components/privatebrowsing/test/unit/test_0-privatebrowsing.js
+++ b/browser/components/privatebrowsing/test/unit/do_test_0-privatebrowsing.js
@@ -36,17 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 // This tests the private browsing service to make sure it implements its
 // documented interface correctly.
 
 // This test should run before the rest of private browsing service unit tests,
 // hence the naming used for this file.
 
-function run_test_on_service() {
+function do_test() {
   // initialization
   var os = Cc["@mozilla.org/observer-service;1"].
            getService(Ci.nsIObserverService);
 
   // the contract ID should be available
   do_check_true(PRIVATEBROWSING_CONTRACT_ID in Cc);
 
   // the interface should be available
@@ -60,24 +60,28 @@ function run_test_on_service() {
     LOG("exception thrown when trying to get the service: " + ex);
     do_throw("private browsing service could not be initialized");
   }
 
   // private browsing should be turned off initially
   do_check_false(pb.privateBrowsingEnabled);
   // private browsing not auto-started
   do_check_false(pb.autoStarted);
+  // and the status should have never been changed
+  do_check_eq(pb.lastChangedByCommandLine, false);
 
   // it should be possible to toggle its status
   pb.privateBrowsingEnabled = true;
   do_check_true(pb.privateBrowsingEnabled);
   do_check_false(pb.autoStarted);
+  do_check_eq(pb.lastChangedByCommandLine, false);
   pb.privateBrowsingEnabled = false;
   do_check_false(pb.privateBrowsingEnabled);
   do_check_false(pb.autoStarted);
+  do_check_eq(pb.lastChangedByCommandLine, false);
 
   // test the private-browsing notification
   var observer = {
     observe: function(aSubject, aTopic, aData) {
       if (aTopic == kPrivateBrowsingNotification)
         this.data = aData;
     },
     data: null
--- a/browser/components/privatebrowsing/test/unit/do_test_privatebrowsing_commandline.js
+++ b/browser/components/privatebrowsing/test/unit/do_test_privatebrowsing_commandline.js
@@ -113,9 +113,11 @@ function do_test() {
     let handler = Cc[contractID].getService(Ci.nsICommandLineHandler);
     handler.handle(testcl);
   }
 
   // the private mode should be entered automatically
   do_check_true(pb.privateBrowsingEnabled);
   // and should appear as auto-started!
   do_check_true(pb.autoStarted);
+  // and should be coming from the command line!
+  do_check_eq(pb.lastChangedByCommandLine, true);
 }
--- a/browser/components/privatebrowsing/test/unit/test_0-privatebrowsing.js
+++ b/browser/components/privatebrowsing/test/unit/test_0-privatebrowsing.js
@@ -33,214 +33,13 @@
  * 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 ***** */
 
 // This tests the private browsing service to make sure it implements its
 // documented interface correctly.
 
-// This test should run before the rest of private browsing service unit tests,
-// hence the naming used for this file.
-
-function run_test_on_service() {
-  // initialization
-  var os = Cc["@mozilla.org/observer-service;1"].
-           getService(Ci.nsIObserverService);
-
-  // the contract ID should be available
-  do_check_true(PRIVATEBROWSING_CONTRACT_ID in Cc);
-
-  // the interface should be available
-  do_check_true("nsIPrivateBrowsingService" in Ci);
-
-  // it should be possible to initialize the component
-  try {
-    var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
-             getService(Ci.nsIPrivateBrowsingService);
-  } catch (ex) {
-    LOG("exception thrown when trying to get the service: " + ex);
-    do_throw("private browsing service could not be initialized");
-  }
-
-  // private browsing should be turned off initially
-  do_check_false(pb.privateBrowsingEnabled);
-  // private browsing not auto-started
-  do_check_false(pb.autoStarted);
-
-  // it should be possible to toggle its status
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled);
-  do_check_false(pb.autoStarted);
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled);
-  do_check_false(pb.autoStarted);
-
-  // test the private-browsing notification
-  var observer = {
-    observe: function(aSubject, aTopic, aData) {
-      if (aTopic == kPrivateBrowsingNotification)
-        this.data = aData;
-    },
-    data: null
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_eq(observer.data, kEnter);
-  pb.privateBrowsingEnabled = false;
-  do_check_eq(observer.data, kExit);
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-
-  // make sure that setting the private browsing mode from within an observer throws
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      if (aTopic == kPrivateBrowsingNotification) {
-        try {
-          pb.privateBrowsingEnabled = (aData == kEnter);
-          do_throw("Setting privateBrowsingEnabled inside the " + aData +
-            " notification should throw");
-        } catch (ex) {
-          if (!("result" in ex && ex.result == Cr.NS_ERROR_FAILURE))
-            do_throw("Unexpected exception caught: " + ex);
-        }
-      }
-    }
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled); // the exception should not interfere with the mode change
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled); // the exception should not interfere with the mode change
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-
-  // make sure that getting the private browsing mode from within an observer doesn't throw
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      if (aTopic == kPrivateBrowsingNotification) {
-        try {
-          var dummy = pb.privateBrowsingEnabled;
-          if (aData == kEnter)
-            do_check_true(dummy);
-          else if (aData == kExit)
-            do_check_false(dummy);
-        } catch (ex) {
-          do_throw("Unexpected exception caught: " + ex);
-        }
-      }
-    }
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled); // just a sanity check
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled); // just a sanity check
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-
-  // check that the private-browsing-cancel-vote notification is sent before the
-  // private-browsing notification
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      switch (aTopic) {
-      case kPrivateBrowsingCancelVoteNotification:
-      case kPrivateBrowsingNotification:
-        this.notifications.push(aTopic + " " + aData);
-      }
-    },
-    notifications: []
-  };
-  os.addObserver(observer, kPrivateBrowsingCancelVoteNotification, false);
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled); // just a sanity check
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled); // just a sanity check
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-  os.removeObserver(observer, kPrivateBrowsingCancelVoteNotification);
-  var reference_order = [
-      kPrivateBrowsingCancelVoteNotification + " " + kEnter,
-      kPrivateBrowsingNotification + " " + kEnter,
-      kPrivateBrowsingCancelVoteNotification + " " + kExit,
-      kPrivateBrowsingNotification + " " + kExit
-    ];
-  do_check_eq(observer.notifications.join(","), reference_order.join(","));
-
-  // make sure that the private-browsing-cancel-vote notification can be used
-  // to cancel the mode switch
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      switch (aTopic) {
-      case kPrivateBrowsingCancelVoteNotification:
-        do_check_neq(aSubject, null);
-        try {
-          aSubject.QueryInterface(Ci.nsISupportsPRBool);
-        } catch (ex) {
-          do_throw("aSubject in " + kPrivateBrowsingCancelVoteNotification +
-            " should implement nsISupportsPRBool");
-        }
-        do_check_false(aSubject.data);
-        aSubject.data = true; // cancel the mode switch
-
-        // fall through
-      case kPrivateBrowsingNotification:
-        this.notifications.push(aTopic + " " + aData);
-      }
-    },
-    nextPhase: function() {
-      this.notifications.push("enter phase " + (++this._phase));
-    },
-    notifications: [],
-    _phase: 0
-  };
-  os.addObserver(observer, kPrivateBrowsingCancelVoteNotification, false);
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_false(pb.privateBrowsingEnabled); // should have been canceled
-  // temporarily disable the observer
-  os.removeObserver(observer, kPrivateBrowsingCancelVoteNotification);
-  observer.nextPhase();
-  pb.privateBrowsingEnabled = true; // this time, should enter successfully
-  do_check_true(pb.privateBrowsingEnabled); // should have been canceled
-  // re-enable the observer
-  os.addObserver(observer, kPrivateBrowsingCancelVoteNotification, false);
-  pb.privateBrowsingEnabled = false;
-  do_check_true(pb.privateBrowsingEnabled); // should have been canceled
-  os.removeObserver(observer, kPrivateBrowsingCancelVoteNotification);
-  observer.nextPhase();
-  pb.privateBrowsingEnabled = false; // this time, should exit successfully
-  do_check_false(pb.privateBrowsingEnabled);
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-  reference_order = [
-      kPrivateBrowsingCancelVoteNotification + " " + kEnter,
-      "enter phase 1",
-      kPrivateBrowsingNotification + " " + kEnter,
-      kPrivateBrowsingCancelVoteNotification + " " + kExit,
-      "enter phase 2",
-      kPrivateBrowsingNotification + " " + kExit,
-    ];
-  do_check_eq(observer.notifications.join(","), reference_order.join(","));
-
-  // make sure that the private browsing transition complete notification is
-  // raised correctly.
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      this.notifications.push(aTopic + " " + aData);
-    },
-    notifications: []
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  os.addObserver(observer, kPrivateBrowsingTransitionCompleteNotification, false);
-  pb.privateBrowsingEnabled = true;
-  pb.privateBrowsingEnabled = false;
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-  os.removeObserver(observer, kPrivateBrowsingTransitionCompleteNotification);
-  reference_order = [
-    kPrivateBrowsingNotification + " " + kEnter,
-    kPrivateBrowsingTransitionCompleteNotification + " ",
-    kPrivateBrowsingNotification + " " + kExit,
-    kPrivateBrowsingTransitionCompleteNotification + " ",
-  ];
-  do_check_eq(observer.notifications.join(","), reference_order.join(","));
+function run_test() {
+  PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
+  load("do_test_0-privatebrowsing.js");
+  do_test();
 }
-
-// Support running tests on both the service itself and its wrapper
-function run_test() {
-  run_test_on_all_services();
-}
copy from browser/components/privatebrowsing/test/unit/test_0-privatebrowsing.js
copy to browser/components/privatebrowsing/test/unit/test_0-privatebrowsingwrapper.js
--- a/browser/components/privatebrowsing/test/unit/test_0-privatebrowsing.js
+++ b/browser/components/privatebrowsing/test/unit/test_0-privatebrowsingwrapper.js
@@ -33,214 +33,13 @@
  * 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 ***** */
 
 // This tests the private browsing service to make sure it implements its
 // documented interface correctly.
 
-// This test should run before the rest of private browsing service unit tests,
-// hence the naming used for this file.
-
-function run_test_on_service() {
-  // initialization
-  var os = Cc["@mozilla.org/observer-service;1"].
-           getService(Ci.nsIObserverService);
-
-  // the contract ID should be available
-  do_check_true(PRIVATEBROWSING_CONTRACT_ID in Cc);
-
-  // the interface should be available
-  do_check_true("nsIPrivateBrowsingService" in Ci);
-
-  // it should be possible to initialize the component
-  try {
-    var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
-             getService(Ci.nsIPrivateBrowsingService);
-  } catch (ex) {
-    LOG("exception thrown when trying to get the service: " + ex);
-    do_throw("private browsing service could not be initialized");
-  }
-
-  // private browsing should be turned off initially
-  do_check_false(pb.privateBrowsingEnabled);
-  // private browsing not auto-started
-  do_check_false(pb.autoStarted);
-
-  // it should be possible to toggle its status
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled);
-  do_check_false(pb.autoStarted);
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled);
-  do_check_false(pb.autoStarted);
-
-  // test the private-browsing notification
-  var observer = {
-    observe: function(aSubject, aTopic, aData) {
-      if (aTopic == kPrivateBrowsingNotification)
-        this.data = aData;
-    },
-    data: null
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_eq(observer.data, kEnter);
-  pb.privateBrowsingEnabled = false;
-  do_check_eq(observer.data, kExit);
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-
-  // make sure that setting the private browsing mode from within an observer throws
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      if (aTopic == kPrivateBrowsingNotification) {
-        try {
-          pb.privateBrowsingEnabled = (aData == kEnter);
-          do_throw("Setting privateBrowsingEnabled inside the " + aData +
-            " notification should throw");
-        } catch (ex) {
-          if (!("result" in ex && ex.result == Cr.NS_ERROR_FAILURE))
-            do_throw("Unexpected exception caught: " + ex);
-        }
-      }
-    }
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled); // the exception should not interfere with the mode change
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled); // the exception should not interfere with the mode change
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-
-  // make sure that getting the private browsing mode from within an observer doesn't throw
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      if (aTopic == kPrivateBrowsingNotification) {
-        try {
-          var dummy = pb.privateBrowsingEnabled;
-          if (aData == kEnter)
-            do_check_true(dummy);
-          else if (aData == kExit)
-            do_check_false(dummy);
-        } catch (ex) {
-          do_throw("Unexpected exception caught: " + ex);
-        }
-      }
-    }
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled); // just a sanity check
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled); // just a sanity check
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-
-  // check that the private-browsing-cancel-vote notification is sent before the
-  // private-browsing notification
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      switch (aTopic) {
-      case kPrivateBrowsingCancelVoteNotification:
-      case kPrivateBrowsingNotification:
-        this.notifications.push(aTopic + " " + aData);
-      }
-    },
-    notifications: []
-  };
-  os.addObserver(observer, kPrivateBrowsingCancelVoteNotification, false);
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_true(pb.privateBrowsingEnabled); // just a sanity check
-  pb.privateBrowsingEnabled = false;
-  do_check_false(pb.privateBrowsingEnabled); // just a sanity check
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-  os.removeObserver(observer, kPrivateBrowsingCancelVoteNotification);
-  var reference_order = [
-      kPrivateBrowsingCancelVoteNotification + " " + kEnter,
-      kPrivateBrowsingNotification + " " + kEnter,
-      kPrivateBrowsingCancelVoteNotification + " " + kExit,
-      kPrivateBrowsingNotification + " " + kExit
-    ];
-  do_check_eq(observer.notifications.join(","), reference_order.join(","));
-
-  // make sure that the private-browsing-cancel-vote notification can be used
-  // to cancel the mode switch
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      switch (aTopic) {
-      case kPrivateBrowsingCancelVoteNotification:
-        do_check_neq(aSubject, null);
-        try {
-          aSubject.QueryInterface(Ci.nsISupportsPRBool);
-        } catch (ex) {
-          do_throw("aSubject in " + kPrivateBrowsingCancelVoteNotification +
-            " should implement nsISupportsPRBool");
-        }
-        do_check_false(aSubject.data);
-        aSubject.data = true; // cancel the mode switch
-
-        // fall through
-      case kPrivateBrowsingNotification:
-        this.notifications.push(aTopic + " " + aData);
-      }
-    },
-    nextPhase: function() {
-      this.notifications.push("enter phase " + (++this._phase));
-    },
-    notifications: [],
-    _phase: 0
-  };
-  os.addObserver(observer, kPrivateBrowsingCancelVoteNotification, false);
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  pb.privateBrowsingEnabled = true;
-  do_check_false(pb.privateBrowsingEnabled); // should have been canceled
-  // temporarily disable the observer
-  os.removeObserver(observer, kPrivateBrowsingCancelVoteNotification);
-  observer.nextPhase();
-  pb.privateBrowsingEnabled = true; // this time, should enter successfully
-  do_check_true(pb.privateBrowsingEnabled); // should have been canceled
-  // re-enable the observer
-  os.addObserver(observer, kPrivateBrowsingCancelVoteNotification, false);
-  pb.privateBrowsingEnabled = false;
-  do_check_true(pb.privateBrowsingEnabled); // should have been canceled
-  os.removeObserver(observer, kPrivateBrowsingCancelVoteNotification);
-  observer.nextPhase();
-  pb.privateBrowsingEnabled = false; // this time, should exit successfully
-  do_check_false(pb.privateBrowsingEnabled);
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-  reference_order = [
-      kPrivateBrowsingCancelVoteNotification + " " + kEnter,
-      "enter phase 1",
-      kPrivateBrowsingNotification + " " + kEnter,
-      kPrivateBrowsingCancelVoteNotification + " " + kExit,
-      "enter phase 2",
-      kPrivateBrowsingNotification + " " + kExit,
-    ];
-  do_check_eq(observer.notifications.join(","), reference_order.join(","));
-
-  // make sure that the private browsing transition complete notification is
-  // raised correctly.
-  observer = {
-    observe: function(aSubject, aTopic, aData) {
-      this.notifications.push(aTopic + " " + aData);
-    },
-    notifications: []
-  };
-  os.addObserver(observer, kPrivateBrowsingNotification, false);
-  os.addObserver(observer, kPrivateBrowsingTransitionCompleteNotification, false);
-  pb.privateBrowsingEnabled = true;
-  pb.privateBrowsingEnabled = false;
-  os.removeObserver(observer, kPrivateBrowsingNotification);
-  os.removeObserver(observer, kPrivateBrowsingTransitionCompleteNotification);
-  reference_order = [
-    kPrivateBrowsingNotification + " " + kEnter,
-    kPrivateBrowsingTransitionCompleteNotification + " ",
-    kPrivateBrowsingNotification + " " + kExit,
-    kPrivateBrowsingTransitionCompleteNotification + " ",
-  ];
-  do_check_eq(observer.notifications.join(","), reference_order.join(","));
+function run_test() {
+  PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing-wrapper;1";
+  load("do_test_0-privatebrowsing.js");
+  do_test();
 }
-
-// Support running tests on both the service itself and its wrapper
-function run_test() {
-  run_test_on_all_services();
-}
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -573,33 +573,45 @@
           if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"))
             this.setAttribute("clickSelectsAll", true);
 
           // Add items to context menu and attach controller to handle them
           var textBox = document.getAnonymousElementByAttribute(this,
                                                 "anonid", "textbox-input-box");
           var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                             "anonid", "input-box-contextmenu");
+          var pasteAndSearch;
+          cxmenu.addEventListener("popupshowing", function() {
+            if (!pasteAndSearch)
+              return;
+            var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
+            var enabled = controller.isCommandEnabled("cmd_paste");
+            if (enabled)
+              pasteAndSearch.removeAttribute("disabled");
+            else
+              pasteAndSearch.setAttribute("disabled", "true");
+          }, false);
 
           var element, label, akey;
 
           element = document.createElementNS(kXULNS, "menuseparator");
           cxmenu.appendChild(element);
 
           var insertLocation = cxmenu.firstChild;
           while (insertLocation.nextSibling &&
                  insertLocation.getAttribute("cmd") != "cmd_paste")
             insertLocation = insertLocation.nextSibling;
           if (insertLocation) {
             element = document.createElementNS(kXULNS, "menuitem");
             label = this._stringBundle.getString("cmd_pasteAndSearch");
             element.setAttribute("label", label);
-            element.setAttribute("cmd", "cmd_paste");
+            element.setAttribute("anonid", "paste-and-search");
             element.setAttribute("oncommand", "goDoCommand('cmd_paste'); document.getElementById('searchbar').handleSearchCommand();");
             cxmenu.insertBefore(element, insertLocation.nextSibling);
+            pasteAndSearch = element;
           }
 
           element = document.createElementNS(kXULNS, "menuitem");
           label = this._stringBundle.getString("cmd_clearHistory");
           akey = this._stringBundle.getString("cmd_clearHistory_accesskey");
           element.setAttribute("label", label);
           element.setAttribute("accesskey", akey);
           element.setAttribute("cmd", "cmd_clearhistory");
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -96,17 +96,17 @@ SessionStartup.prototype = {
 
   /**
    * Initialize the component
    */
   init: function sss_init() {
     // do not need to initialize anything in auto-started private browsing sessions
     let pbs = Cc["@mozilla.org/privatebrowsing;1"].
               getService(Ci.nsIPrivateBrowsingService);
-    if (pbs.autoStarted)
+    if (pbs.autoStarted || pbs.lastChangedByCommandLine)
       return;
 
     let prefBranch = Cc["@mozilla.org/preferences-service;1"].
                      getService(Ci.nsIPrefService).getBranch("browser.");
 
     // get file references
     var dirService = Cc["@mozilla.org/file/directory_service;1"].
                      getService(Ci.nsIProperties);
@@ -232,17 +232,22 @@ SessionStartup.prototype = {
      * window's arguments.
      */
     var defaultArgs = Cc["@mozilla.org/browser/clh;1"].
                       getService(Ci.nsIBrowserHandler).defaultArgs;
     if (aWindow.arguments && aWindow.arguments[0] &&
         aWindow.arguments[0] == defaultArgs)
       aWindow.arguments[0] = null;
 
-    Services.obs.removeObserver(this, "domwindowopened");
+    try {
+      Services.obs.removeObserver(this, "domwindowopened");
+    } catch (e) {
+      // This might throw if we're removing the observer multiple times,
+      // but this is safe to ignore.
+    }
   },
 
 /* ........ Public API ................*/
 
   /**
    * Get the session state as a string
    */
   get state() {
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -209,16 +209,19 @@ SessionStoreService.prototype = {
   _tabsRestoringCount: 0,
 
   // number of tabs to restore concurrently, pref controlled.
   _maxConcurrentTabRestores: null,
 
   // The state from the previous session (after restoring pinned tabs)
   _lastSessionState: null,
 
+  // Whether we've been initialized
+  _initialized: false,
+
 /* ........ Public Getters .............. */
 
   get canRestoreLastSession() {
     // Always disallow restoring the previous session when in private browsing
     return this._lastSessionState && !this._inPrivateBrowsing;
   },
 
   set canRestoreLastSession(val) {
@@ -228,25 +231,17 @@ SessionStoreService.prototype = {
     this._lastSessionState = null;
   },
 
 /* ........ Global Event Handlers .............. */
 
   /**
    * Initialize the component
    */
-  init: function sss_init(aWindow) {
-    if (!aWindow || this._loadState == STATE_RUNNING) {
-      // make sure that all browser windows which try to initialize
-      // SessionStore are really tracked by it
-      if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
-        this.onLoad(aWindow);
-      return;
-    }
-
+  initService: function() {
     this._prefBranch = Services.prefs.getBranch("browser.");
     this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
 
     OBSERVING.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
     }, this);
 
     var pbs = Cc["@mozilla.org/privatebrowsing;1"].
@@ -348,17 +343,45 @@ SessionStoreService.prototype = {
       catch (ex) { Cu.reportError(ex); } // file was write-locked?
     }
 
     // at this point, we've as good as resumed the session, so we can
     // clear the resume_session_once flag, if it's set
     if (this._loadState != STATE_QUITTING &&
         this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
       this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
-    
+
+    this._initialized = true;
+  },
+
+  /**
+   * Start tracking a window.
+   * Important note: despite its name, this function doesn't initialize
+   * the component!
+   */
+  init: function sss_init(aWindow) {
+    if (!aWindow || this._loadState == STATE_RUNNING) {
+      // make sure that all browser windows which try to initialize
+      // SessionStore are really tracked by it
+      if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
+        this.onLoad(aWindow);
+      // If init is being called with a null window, it's possible that we
+      // just want to tell sessionstore that a session is live (as is the case
+      // with starting Firefox with -private, for example; see bug 568816),
+      // so we should mark the load state as running to make sure that
+      // things like setBrowserState calls will succeed in restoring the session.
+      if (!aWindow && this._loadState == STATE_STOPPED)
+        this._loadState = STATE_RUNNING;
+      return;
+    }
+
+    // Initialize the service if needed.
+    if (!this._initialized)
+      this.initService();
+
     // As this is called at delayedStartup, restoration must be initiated here
     this.onLoad(aWindow);
   },
 
   /**
    * Called on application shutdown, after notifications:
    * quit-application-granted, quit-application
    */
@@ -848,25 +871,17 @@ SessionStoreService.prototype = {
     browser.removeEventListener("change", this, true);
     browser.removeEventListener("input", this, true);
     browser.removeEventListener("DOMAutoComplete", this, true);
 
     delete browser.__SS_data;
 
     // If this tab was in the middle of restoring, we want to restore the next
     // tab. If the tab hasn't been restored, we want to remove it from the array.
-    if (browser.__SS_restoring) {
-      this.restoreNextTab(true);
-    }
-    else if (browser.__SS_needsRestore) {
-      if (aTab.hidden)
-        this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
-      else
-        this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab));
-    }
+    this._resetTabRestoringState(aTab, true, false);
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
   },
 
   /**
    * When a tab closes, collect its properties
@@ -2027,18 +2042,23 @@ SessionStoreService.prototype = {
           nonPopupCount++;
       }
     }
 
     // shallow copy this._closedWindows to preserve current state
     let lastClosedWindowsCopy = this._closedWindows.slice();
 
 #ifndef XP_MACOSX
-    // if no non-popup browser window remains open, return the state of the last closed window(s)
-    if (nonPopupCount == 0 && lastClosedWindowsCopy.length > 0) {
+    // If no non-popup browser window remains open, return the state of the last
+    // closed window(s). We only want to do this when we're actually "ending"
+    // the session.
+    //XXXzpao We should do this for _restoreLastWindow == true, but that has
+    //        its own check for popups. c.f. bug 597619
+    if (nonPopupCount == 0 && lastClosedWindowsCopy.length > 0 &&
+        this._loadState == STATE_QUITTING) {
       // prepend the last non-popup browser window, so that if the user loads more tabs
       // at startup we don't accidentally add them to a popup window
       do {
         total.unshift(lastClosedWindowsCopy.shift())
       } while (total[0].isPopup)
     }
 #endif
 
@@ -2192,16 +2212,34 @@ SessionStoreService.prototype = {
 
       if (winData.tabs[t].pinned)
         tabbrowser.pinTab(tabs[t]);
       else
         tabbrowser.unpinTab(tabs[t]);
       tabs[t].hidden = winData.tabs[t].hidden;
     }
 
+    // If overwriting tabs, we want to remove __SS_restoring from the browser.
+    if (aOverwriteTabs) {
+      for (let i = 0; i < tabbrowser.tabs.length; i++)
+        this._resetTabRestoringState(tabbrowser.tabs[i], false, false);
+    }
+
+    // We want to set up a counter on the window that indicates how many tabs
+    // in this window are unrestored. This will be used in restoreNextTab to
+    // determine if gRestoreTabsProgressListener should be removed from the window.
+    // If we aren't overwriting existing tabs, then we want to add to the existing
+    // count in case there are still tabs restoring.
+    if (!aWindow.__SS_tabsToRestore)
+      aWindow.__SS_tabsToRestore = 0;
+    if (aOverwriteTabs)
+      aWindow.__SS_tabsToRestore = newTabCount;
+    else
+      aWindow.__SS_tabsToRestore += newTabCount;
+
     // We want to correlate the window with data from the last session, so
     // assign another id if we have one. Otherwise clear so we don't do
     // anything with it.
     delete aWindow.__SS_lastSessionWindowID;
     if (winData.__lastSessionWindowID)
       aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
 
     // when overwriting tabs, remove all superflous ones
@@ -2277,64 +2315,22 @@ SessionStoreService.prototype = {
           var restoreHistoryFunc = function(self) {
             self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount + 1);
           }
           aWindow.setTimeout(restoreHistoryFunc, 100, this);
           return;
         }
       }
     }
-    
-    // mark the tabs as loading
-    for (t = 0; t < aTabs.length; t++) {
-      let tab = aTabs[t];
-      let browser = tabbrowser.getBrowserForTab(tab);
-      let tabData = aTabData[t];
-
-      if (tabData.pinned)
-        tabbrowser.pinTab(tab);
-      else
-        tabbrowser.unpinTab(tab);
-      tab.hidden = tabData.hidden;
-
-      tabData._tabStillLoading = true;
-
-      // keep the data around to prevent dataloss in case
-      // a tab gets closed before it's been properly restored
-      browser.__SS_data = tabData;
-      browser.__SS_needsRestore = true;
-
-      if (!tabData.entries || tabData.entries.length == 0) {
-        // make sure to blank out this tab's content
-        // (just purging the tab's history won't be enough)
-        browser.contentDocument.location = "about:blank";
-        continue;
-      }
-      
-      browser.stop(); // in case about:blank isn't done yet
-      
-      tab.setAttribute("busy", "true");
-      tabbrowser.updateIcon(tab);
-      
-      // wall-paper fix for bug 439675: make sure that the URL to be loaded
-      // is always visible in the address bar
-      let activeIndex = (tabData.index || tabData.entries.length) - 1;
-      let activePageData = tabData.entries[activeIndex] || null;
-      browser.userTypedValue = activePageData ? activePageData.url || null : null;
-
-      // If the page has a title, set it.
-      if (activePageData && activePageData.title)
-        tab.label = activePageData.title;
-    }
-    
+
     if (aTabs.length > 0) {
       // Load hidden tabs last, by pushing them to the end of the list
       let unhiddenTabs = aTabs.length;
       for (let t = 0; t < unhiddenTabs; ) {
-        if (aTabs[t].hidden) {
+        if (aTabData[t].hidden) {
           aTabs = aTabs.concat(aTabs.splice(t, 1));
           aTabData = aTabData.concat(aTabData.splice(t, 1));
           if (aSelectTab > t)
             --aSelectTab;
           --unhiddenTabs;
           continue;
         }
         ++t;
@@ -2362,16 +2358,65 @@ SessionStoreService.prototype = {
       // make sure to restore the selected tab first (if any)
       if (aSelectTab-- && aTabs[aSelectTab]) {
         aTabs.unshift(aTabs.splice(aSelectTab, 1)[0]);
         aTabData.unshift(aTabData.splice(aSelectTab, 1)[0]);
         tabbrowser.selectedTab = aTabs[0];
       }
     }
 
+    // Prepare the tabs so that they can be properly restored. We'll pin/unpin
+    // and show/hide tabs as necessary. We'll also set the labels, user typed
+    // value, and attach a copy of the tab's data in case we close it before
+    // it's been restored.
+    for (t = 0; t < aTabs.length; t++) {
+      let tab = aTabs[t];
+      let browser = tabbrowser.getBrowserForTab(tab);
+      let tabData = aTabData[t];
+
+      if (tabData.pinned)
+        tabbrowser.pinTab(tab);
+      else
+        tabbrowser.unpinTab(tab);
+      tab.hidden = tabData.hidden;
+
+      tabData._tabStillLoading = true;
+
+      // keep the data around to prevent dataloss in case
+      // a tab gets closed before it's been properly restored
+      browser.__SS_data = tabData;
+      browser.__SS_needsRestore = true;
+
+      if (!tabData.entries || tabData.entries.length == 0) {
+        // make sure to blank out this tab's content
+        // (just purging the tab's history won't be enough)
+        browser.contentDocument.location = "about:blank";
+        continue;
+      }
+
+      browser.stop(); // in case about:blank isn't done yet
+
+      // wall-paper fix for bug 439675: make sure that the URL to be loaded
+      // is always visible in the address bar
+      let activeIndex = (tabData.index || tabData.entries.length) - 1;
+      let activePageData = tabData.entries[activeIndex] || null;
+      browser.userTypedValue = activePageData ? activePageData.url || null : null;
+
+      // If the page has a title, set it.
+      if (activePageData) {
+        if (activePageData.title) {
+          tab.label = activePageData.title;
+          tab.crop = "end";
+        } else if (activePageData.url != "about:blank") {
+          tab.label = activePageData.url;
+          tab.crop = "center";
+        }
+      }
+    }
+
     if (!this._isWindowLoaded(aWindow)) {
       // from now on, the data will come from the actual window
       delete this._statesToRestore[aWindow.__SS_restoreID];
       delete aWindow.__SS_restoreID;
       delete this._windows[aWindow.__SSi]._restoring;
     }
     
     // helper hashes for ensuring unique frame IDs and unique document
@@ -2408,17 +2453,20 @@ SessionStoreService.prototype = {
 
     var browser = aWindow.gBrowser.getBrowserForTab(tab);
     var history = browser.webNavigation.sessionHistory;
     
     if (history.count > 0) {
       history.PurgeHistory(history.count);
     }
     history.QueryInterface(Ci.nsISHistoryInternal);
-    
+
+    browser.__SS_shistoryListener = new SessionStoreSHistoryListener(this, tab);
+    history.addSHistoryListener(browser.__SS_shistoryListener);
+
     if (!tabData.entries) {
       tabData.entries = [];
     }
     if (tabData.extData) {
       tab.__SS_extdata = {};
       for (let key in tabData.extData)
         tab.__SS_extdata[key] = tabData.extData[key];
     }
@@ -2468,27 +2516,31 @@ SessionStoreService.prototype = {
         this._tabsToRestore.hidden.push(tab);
       else
         this._tabsToRestore.visible.push(tab);
       this.restoreNextTab();
     }
   },
 
   restoreTab: function(aTab) {
+    let window = aTab.ownerDocument.defaultView;
     let browser = aTab.linkedBrowser;
     let tabData = browser.__SS_data;
 
     // There are cases within where we haven't actually started a load and so we
     // should call restoreNextTab. We don't want to do it immediately though
     // since we might not set userTypedValue in a timely fashion.
     let shouldRestoreNextTab = false;
 
     // Increase our internal count.
     this._tabsRestoringCount++;
 
+    // Decrement the number of tabs this window needs to restore
+    window.__SS_tabsToRestore--;
+
     delete browser.__SS_needsRestore;
 
     let activeIndex = (tabData.index || tabData.entries.length) - 1;
     if (activeIndex >= tabData.entries.length)
       activeIndex = tabData.entries.length - 1;
 
     // Attach data that will be restored on "load" event, after tab is restored.
     if (activeIndex > -1) {
@@ -2559,19 +2611,21 @@ SessionStoreService.prototype = {
 
     if (nextTabArray) {
       let tab = nextTabArray.shift();
       this.restoreTab(tab);
     }
     else {
       // Remove the progress listener from windows. It will get re-added as needed.
       this._forEachBrowserWindow(function(aWindow) {
-        // This won't fail since removeTabsProgressListener just filters. It
-        // doesn't attempt to splice.
-        aWindow.gBrowser.removeTabsProgressListener(gRestoreTabsProgressListener)
+        if (!aWindow.__SS_tabsToRestore) {
+          // This won't fail since removeTabsProgressListener just filters. It
+          // doesn't attempt to splice.
+          aWindow.gBrowser.removeTabsProgressListener(gRestoreTabsProgressListener);
+        }
       });
     }
   },
 
   /**
    * expands serialized history data into a session-history-entry instance
    * @param aEntry
    *        Object containing serialized history data for a URL
@@ -3481,21 +3535,88 @@ SessionStoreService.prototype = {
 #endif
     this._closedWindows.splice(spliceTo);
   },
 
   /**
    * Reset state to prepare for a new session state to be restored.
    */
   _resetRestoringState: function sss__initRestoringState() {
-    //
-    this._tasToRestore = { visible: [], hidden: [] };
+    this._tabsToRestore = { visible: [], hidden: [] };
     this._tabsRestoringCount = 0;
   },
 
+  /**
+   * Reset the restoring state for a particular tab. This will be called when
+   * removing a tab, when a tab needs to be reset (it's being overwritten), or
+   * when reload occurs. This is multipurpose and is meant to provide a single
+   * path for very similar functionality.
+   *
+   * @param aTab
+   *        The tab that will be "reset"
+   * @param aRestoreNextTab
+   *        If the tab is currently restoring, should we allow a new restore to
+   *        begin
+   * @param aRestoreThisTab
+   *        In the process of "resetting" this tab, should we also restore it.
+   */
+  _resetTabRestoringState:
+    function sss__resetTabRestoringState(aTab, aRestoreNextTab, aRestoreThisTab) {
+    let browser = aTab.linkedBrowser;
+
+    if (browser.__SS_restoring) {
+      // If the session history listener hasn't been detached, make sure we
+      // remove it and delete the reference.
+      if (browser.__SS_shistoryListener) {
+        browser.webNavigation.sessionHistory.
+                              removeSHistoryListener(browser.__SS_shistoryListener);
+        delete browser.__SS_shistoryListener;
+      }
+
+      delete browser.__SS_restoring;
+      if (aRestoreNextTab) {
+        // this._tabsRestoringCount is decremented in restoreNextTab.
+        this.restoreNextTab(true);
+      }
+      else {
+        // Even if we aren't restoring the next tab, we still need to decrement
+        // the restoring count. Normally it gets done within restoreNextTab.
+        this._tabsRestoringCount--;
+      }
+    }
+    else if (browser.__SS_needsRestore) {
+      // First we'll remove the tab from the hidden/visible array of tabs left
+      // to restore.
+      let splicedTabs;
+      if (aTab.hidden) {
+        splicedTabs =
+          this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab), 1);
+      }
+      else {
+        splicedTabs =
+          this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab), 1);
+      }
+      if (aRestoreThisTab && splicedTabs.length) {
+        // If we want to restore the tab, then we'll do that. This tab still
+        // needs restore, so we're going to restore this one, not the next one
+        // as was done above.
+        this.restoreTab(aTab);
+      }
+      else {
+        // If we don't want to restore the tab, we want to explicitly remove
+        // __SS_needsRestore and decrement __SS_tabsToRestore. Normally this is
+        // done in restoreTab.
+        let window = aTab.ownerDocument.defaultView;
+        window.__SS_tabsToRestore--;
+        delete browser.__SS_needsRestore;
+      }
+
+    }
+  },
+
 /* ........ Storage API .............. */
 
   /**
    * write file to disk
    * @param aFile
    *        nsIFile
    * @param aData
    *        String data
@@ -3617,29 +3738,61 @@ let XPathHelper = {
 };
 
 // This is used to help meter the number of restoring tabs. This is the control
 // point for telling the next tab to restore. It gets attached to each gBrowser
 // via gBrowser.addTabsProgressListener
 let gRestoreTabsProgressListener = {
   ss: null,
   onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-    // Ignore state changes on browsers that we've already restored
-    if (!aBrowser.__SS_restoring)
-      return;
-
-    if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+    // Ignore state changes on browsers that we've already restored and state
+    // changes that aren't applicable.
+    if (aBrowser.__SS_restoring &&
+        aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
-      delete aBrowser.__SS_restoring;
-      this.ss.restoreNextTab(true);
+      // We need to reset the tab before starting the next restore.
+      // _resetTabRestoringState will make sure we remove the session history
+      // listener and will call restoreNextTab.
+      let window = aBrowser.ownerDocument.defaultView;
+      let tab = window.gBrowser._getTabForContentWindow(aBrowser.contentWindow);
+      this.ss._resetTabRestoringState(tab, true, false);
     }
   }
 }
 
+// A SessionStoreSHistoryListener will be attached to each browser before it is
+// restored. We need to catch reloads that occur before the tab is restored
+// because otherwise, docShell will reload an old URI (usually about:blank).
+function SessionStoreSHistoryListener(ss, aTab) {
+  this.tab = aTab;
+  this.ss = ss;
+}
+SessionStoreSHistoryListener.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISHistoryListener,
+                                         Ci.nsISupportsWeakReference]),
+  browser: null,
+  ss: null,
+  OnHistoryNewEntry: function(aNewURI) { },
+  OnHistoryGoBack: function(aBackURI) { return true; },
+  OnHistoryGoForward: function(aForwardURI) { return true; },
+  OnHistoryGotoIndex: function(aIndex, aGotoURI) { return true; },
+  OnHistoryPurge: function(aNumEntries) { return true; },
+  OnHistoryReload: function(aReloadURI, aReloadFlags) {
+    // On reload, we want to make sure that session history loads the right
+    // URI. In order to do that, we will call _resetTabRestoringState.
+    // __SS_needsRestore will still be on this tab's browser, so we will end up
+    // calling restoreTab and this tab will be loaded.
+    this.ss._resetTabRestoringState(this.tab, false, true);
+    // Returning false will stop the load that docshell is attempting.
+    return false;
+  }
+}
+
+
 // see nsPrivateBrowsingService.js
 String.prototype.hasRootDomain = function hasRootDomain(aDomain)
 {
   let index = this.indexOf(aDomain);
   if (index == -1)
     return false;
 
   if (this == aDomain)
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -115,10 +115,16 @@ include $(topsrcdir)/config/rules.mk
 	browser_524745.js \
 	browser_528776.js \
 	browser_579879.js \
 	browser_580512.js \
 	browser_586147.js \
 	browser_586068-cascaded_restore.js \
 	$(NULL)
 
+ifneq ($(OS_ARCH),Darwin)
+_BROWSER_TEST_FILES += \
+	browser_597071.js \
+	$(NULL)
+endif
+
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
+++ b/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
@@ -43,39 +43,47 @@ let stateBackup = ss.getBrowserState();
 
 
 function test() {
   /** Test for Bug 586068 - Cascade page loads when restoring **/
   waitForExplicitFinish();
   runNextTest();
 }
 
-let tests = [test_cascade, test_select];
+let tests = [test_cascade, test_select, test_multiWindowState,
+             test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
+             test_setBrowserStateInterrupted, test_reload];
 function runNextTest() {
+  // Reset the pref
+  try {
+    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+  } catch (e) {}
+
+  // set an empty state & run the next test, or finish
   if (tests.length) {
-    ss.setWindowState(window,
-                      JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }], }] }),
-                      true);
+    ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }], }] }));
+    info("running next test");
     executeSoon(tests.shift());
   }
   else {
     ss.setBrowserState(stateBackup);
     executeSoon(finish);
   }
 }
 
 
 function test_cascade() {
   // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
   Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
         test_cascade_progressCallback();
     }
   }
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
@@ -97,125 +105,551 @@ function test_cascade() {
     [3, 1, 2],
     [2, 1, 3],
     [1, 1, 4],
     [0, 1, 5]
   ];
 
   function test_cascade_progressCallback() {
     loadCount++;
-    // We'll get the first <length of windows[0].tabs> load events here, even
-    // though they are ignored by sessionstore. Those are due to explicit
-    // "stop" events before any restoring action takes place. We can safely
-    // ignore these events.
-    if (loadCount <= state.windows[0].tabs.length)
-      return;
-
     let counts = countTabs();
-    let expected = expectedCounts[loadCount - state.windows[0].tabs.length - 1];
+    let expected = expectedCounts[loadCount - 1];
 
     is(counts[0], expected[0], "test_cascade: load " + loadCount + " - # tabs that need to be restored");
     is(counts[1], expected[1], "test_cascade: load " + loadCount + " - # tabs that are restoring");
     is(counts[2], expected[2], "test_cascade: load " + loadCount + " - # tabs that has been restored");
 
-    if (loadCount == state.windows[0].tabs.length * 2) {
-      window.gBrowser.removeTabsProgressListener(progressListener);
-      // Reset the pref
-      try {
-        Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
-      } catch (e) {}
-      runNextTest();
-    }
+    if (loadCount < state.windows[0].tabs.length)
+      return;
+
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    runNextTest();
   }
 
   // This progress listener will get attached before the listener in session store.
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_select() {
   // Set the pref to 0 so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
   Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
         test_select_progressCallback(aBrowser);
     }
   }
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } }
-  ], selectedIndex: 1 }] };
+  ], selected: 1 }] };
 
   let loadCount = 0;
   // expectedCounts looks a little wierd for the test case, but it works. See
   // comment in test_cascade for an explanation
   let expectedCounts = [
     [5, 1, 0],
     [4, 1, 1],
     [3, 1, 2],
     [2, 1, 3],
     [1, 1, 4],
     [0, 1, 5]
   ];
   let tabOrder = [0, 5, 1, 4, 3, 2];
 
   function test_select_progressCallback(aBrowser) {
     loadCount++;
-    // We'll get the first <length of windows[0].tabs> load events here, even
-    // though they are ignored by sessionstore. Those are due to explicit
-    // "stop" events before any restoring action takes place. We can safely
-    // ignore these events.
-    if (loadCount <= state.windows[0].tabs.length)
-      return;
 
-    let loadIndex = loadCount - state.windows[0].tabs.length - 1;
     let counts = countTabs();
-    let expected = expectedCounts[loadIndex];
+    let expected = expectedCounts[loadCount - 1];
 
     is(counts[0], expected[0], "test_select: load " + loadCount + " - # tabs that need to be restored");
     is(counts[1], expected[1], "test_select: load " + loadCount + " - # tabs that are restoring");
     is(counts[2], expected[2], "test_select: load " + loadCount + " - # tabs that has been restored");
 
-    if (loadCount == state.windows[0].tabs.length * 2) {
-      window.gBrowser.removeTabsProgressListener(progressListener);
-      // Reset the pref
-      try {
-        Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
-      } catch (e) {}
-      runNextTest();
-    }
-    else {
+    if (loadCount < state.windows[0].tabs.length) {
       // double check that this tab was the right one
-      let expectedData = state.windows[0].tabs[tabOrder[loadIndex]].extData.uniq;
+      let expectedData = state.windows[0].tabs[tabOrder[loadCount - 1]].extData.uniq;
       let tab;
       for (let i = 0; i < window.gBrowser.tabs.length; i++) {
         if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
           tab = window.gBrowser.tabs[i];
       }
       is(ss.getTabValue(tab, "uniq"), expectedData, "test_select: load " + loadCount + " - correct tab was restored");
 
       // select the next tab
-      window.gBrowser.selectTabAtIndex(tabOrder[loadIndex + 1]);
+      window.gBrowser.selectTabAtIndex(tabOrder[loadCount]);
+      return;
+    }
+
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    runNextTest();
+  }
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_multiWindowState() {
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_multiWindowState_progressCallback(aBrowser);
+    }
+  }
+
+  // The first window will be put into the already open window and the second
+  // window will be opened with _openWindowWithState, which is the source of the problem.
+  let state = { windows: [
+    {
+      tabs: [
+        { entries: [{ url: "http://example.org#0" }], extData: { "uniq": r() } }
+      ],
+      selected: 1
+    },
+    {
+      tabs: [
+        { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } }
+      ],
+      selected: 4
+    }
+  ] };
+  let numTabs = state.windows[0].tabs.length + state.windows[1].tabs.length;
+
+  let loadCount = 0;
+  function test_multiWindowState_progressCallback(aBrowser) {
+    loadCount++;
+
+    if (loadCount < numTabs)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs, "test_multiWindowState: all tabs were restored");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_multiWindowState: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    runNextTest();
+  }
+
+  // We also want to catch the 2nd window, so we need to observe domwindowopened
+  function windowObserver(aSubject, aTopic, aData) {
+    let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+    if (aTopic == "domwindowopened") {
+      theWin.addEventListener("load", function() {
+        theWin.removeEventListener("load", arguments.callee, false);
+
+        Services.ww.unregisterNotification(windowObserver);
+        theWin.gBrowser.addTabsProgressListener(progressListener);
+      }, false);
+    }
+  }
+  Services.ww.registerNotification(windowObserver);
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_setWindowStateNoOverwrite() {
+  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_setWindowStateNoOverwrite_progressCallback(aBrowser);
+    }
+  }
+
+  // We'll use 2 states so that we can make sure calling setWindowState doesn't
+  // wipe out currently restoring data.
+  let state1 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.com#1" }] },
+    { entries: [{ url: "http://example.com#2" }] },
+    { entries: [{ url: "http://example.com#3" }] },
+    { entries: [{ url: "http://example.com#4" }] },
+    { entries: [{ url: "http://example.com#5" }] },
+  ] }] };
+  let state2 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org#1" }] },
+    { entries: [{ url: "http://example.org#2" }] },
+    { entries: [{ url: "http://example.org#3" }] },
+    { entries: [{ url: "http://example.org#4" }] },
+    { entries: [{ url: "http://example.org#5" }] }
+  ] }] };
+
+  let numTabs = state1.windows[0].tabs.length + state2.windows[0].tabs.length;
+
+  let loadCount = 0;
+  function test_setWindowStateNoOverwrite_progressCallback(aBrowser) {
+    loadCount++;
+
+    // When loadCount == 2, we'll also restore state2 into the window
+    if (loadCount == 2)
+      ss.setWindowState(window, JSON.stringify(state2), false);
+
+    if (loadCount < numTabs)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
+    is(window.__SS_tabsToRestore, 0,
+       "test_setWindowStateNoOverwrite: window doesn't think there are more tabs to restore");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_setWindowStateNoOverwrite: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+
+    runNextTest();
+  }
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setWindowStateOverwrite() {
+  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_setWindowStateOverwrite_progressCallback(aBrowser);
+    }
+  }
+
+  // We'll use 2 states so that we can make sure calling setWindowState doesn't
+  // wipe out currently restoring data.
+  let state1 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.com#1" }] },
+    { entries: [{ url: "http://example.com#2" }] },
+    { entries: [{ url: "http://example.com#3" }] },
+    { entries: [{ url: "http://example.com#4" }] },
+    { entries: [{ url: "http://example.com#5" }] },
+  ] }] };
+  let state2 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org#1" }] },
+    { entries: [{ url: "http://example.org#2" }] },
+    { entries: [{ url: "http://example.org#3" }] },
+    { entries: [{ url: "http://example.org#4" }] },
+    { entries: [{ url: "http://example.org#5" }] }
+  ] }] };
+
+  let numTabs = 2 + state2.windows[0].tabs.length;
+
+  let loadCount = 0;
+  function test_setWindowStateOverwrite_progressCallback(aBrowser) {
+    loadCount++;
+
+    // When loadCount == 2, we'll also restore state2 into the window
+    if (loadCount == 2)
+      ss.setWindowState(window, JSON.stringify(state2), true);
+
+    if (loadCount < numTabs)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs, "test_setWindowStateOverwrite: all tabs were restored");
+    is(window.__SS_tabsToRestore, 0,
+       "test_setWindowStateOverwrite: window doesn't think there are more tabs to restore");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_setWindowStateOverwrite: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+
+    runNextTest();
+  }
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setBrowserStateInterrupted() {
+  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_setBrowserStateInterrupted_progressCallback(aBrowser);
+    }
+  }
+
+  // The first state will be loaded using setBrowserState, followed by the 2nd
+  // state also being loaded using setBrowserState, interrupting the first restore.
+  let state1 = { windows: [
+    {
+      tabs: [
+        { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }
+      ],
+      selected: 1
+    },
+    {
+      tabs: [
+        { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+      ],
+      selected: 3
+    }
+  ] };
+  let state2 = { windows: [
+    {
+      tabs: [
+        { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#6" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#7" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#8" }], extData: { "uniq": r() } }
+      ],
+      selected: 3
+    },
+    {
+      tabs: [
+        { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#7" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#8" }], extData: { "uniq": r() } },
+      ],
+      selected: 1
+    }
+  ] };
+
+  // interruptedAfter will be set after the selected tab from each window have loaded.
+  let interruptedAfter = 0;
+  let loadedWindow1 = false;
+  let loadedWindow2 = false;
+  let numTabs = state2.windows[0].tabs.length + state2.windows[1].tabs.length;
+
+  let loadCount = 0;
+  function test_setBrowserStateInterrupted_progressCallback(aBrowser) {
+    loadCount++;
+
+    if (aBrowser.currentURI.spec == state1.windows[0].tabs[2].entries[0].url)
+      loadedWindow1 = true;
+    if (aBrowser.currentURI.spec == state1.windows[1].tabs[0].entries[0].url)
+      loadedWindow2 = true;
+
+    if (!interruptedAfter && loadedWindow1 && loadedWindow2) {
+      interruptedAfter = loadCount;
+      ss.setBrowserState(JSON.stringify(state2));
+      return;
+    }
+
+    if (loadCount < numTabs + interruptedAfter)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs + interruptedAfter,
+       "test_setBrowserStateInterrupted: all tabs were restored");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_setBrowserStateInterrupted: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    Services.ww.unregisterNotification(windowObserver);
+    runNextTest();
+  }
+
+  // We also want to catch the extra windows (there should be 2), so we need to observe domwindowopened
+  function windowObserver(aSubject, aTopic, aData) {
+    let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+    if (aTopic == "domwindowopened") {
+      theWin.addEventListener("load", function() {
+        theWin.removeEventListener("load", arguments.callee, false);
+
+        Services.ww.unregisterNotification(windowObserver);
+        theWin.gBrowser.addTabsProgressListener(progressListener);
+      }, false);
+    }
+  }
+  Services.ww.registerNotification(windowObserver);
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setBrowserState(JSON.stringify(state1));
+}
+
+
+function test_reload() {
+  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // any given time. This guarantees that a finishing load won't start another.
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_reload_progressCallback(aBrowser);
+    }
+  }
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org/#1" }], extData: { "uniq": r() } },
+    { entries: [{ url: "http://example.org/#2" }], extData: { "uniq": r() } },
+    { entries: [{ url: "http://example.org/#3" }], extData: { "uniq": r() } },
+    { entries: [{ url: "http://example.org/#4" }], extData: { "uniq": r() } },
+    { entries: [{ url: "http://example.org/#5" }], extData: { "uniq": r() } },
+    { entries: [{ url: "http://example.org/#6" }], extData: { "uniq": r() } }
+  ], selected: 1 }] };
+
+  let loadCount = 0;
+  function test_reload_progressCallback(aBrowser) {
+    loadCount++;
+
+    is(aBrowser.currentURI.spec, state.windows[0].tabs[loadCount - 1].entries[0].url,
+       "test_reload: load " + loadCount + " - browser loaded correct url");
+
+    if (loadCount <= state.windows[0].tabs.length) {
+      // double check that this tab was the right one
+      let expectedData = state.windows[0].tabs[loadCount - 1].extData.uniq;
+      let tab;
+      for (let i = 0; i < window.gBrowser.tabs.length; i++) {
+        if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
+          tab = window.gBrowser.tabs[i];
+      }
+      is(ss.getTabValue(tab, "uniq"), expectedData,
+         "test_reload: load " + loadCount + " - correct tab was restored");
+
+      if (loadCount == state.windows[0].tabs.length) {
+        window.gBrowser.removeTabsProgressListener(progressListener);
+        test_reload2(state);
+      }
+      else {
+        // reload the next tab
+        window.gBrowser.reloadTab(window.gBrowser.tabs[loadCount]);
+      }
+    }
+
+  }
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setBrowserState(JSON.stringify(state));
+}
+
+
+// This test shouldn't be added to tests. It will be called directly from
+// test_reload. This guarantees that we're already in a tested restored state
+// and we know what the state should be.
+function test_reload2(aState) {
+  info("starting test_reload2");
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_reload2_progressCallback(aBrowser);
+    }
+  }
+
+  // Simulate a left mouse button click with no modifiers, which is what
+  // Command-R, or clicking reload does.
+  let fakeEvent = {
+    button: 0,
+    metaKey: false,
+    altKey: false,
+    ctrlKey: false,
+    shiftKey: false,
+  }
+
+  let loadCount = 0;
+  function test_reload2_progressCallback(aBrowser) {
+    loadCount++;
+
+    if (loadCount <= aState.windows[0].tabs.length) {
+      // double check that this tab was the right one
+      let expectedData = aState.windows[0].tabs[loadCount - 1].extData.uniq;
+      let tab;
+      for (let i = 0; i < window.gBrowser.tabs.length; i++) {
+        if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
+          tab = window.gBrowser.tabs[i];
+      }
+      is(ss.getTabValue(tab, "uniq"), expectedData,
+         "test_reload2: load " + loadCount + " - correct tab was reloaded");
+
+      if (loadCount == aState.windows[0].tabs.length) {
+        window.gBrowser.removeTabsProgressListener(progressListener);
+        runNextTest();
+      }
+      else {
+        // reload the next tab
+        window.gBrowser.selectTabAtIndex(loadCount);
+        BrowserReloadOrDuplicate(fakeEvent);
+      }
     }
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
-  ss.setBrowserState(JSON.stringify(state));
+  BrowserReloadOrDuplicate(fakeEvent);
 }
 
 
 function countTabs() {
   let needsRestore = 0,
       isRestoring = 0,
       wasRestored = 0;
 
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_597071.js
@@ -0,0 +1,102 @@
+/* ***** 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 th