Merge mozilla-central to tracemonkey.
authorRobert Sayre <sayrer@gmail.com>
Tue, 30 Mar 2010 21:13:01 -0700
changeset 40414 3a9063bea021f22b0b29c5503764ce50787f76be
parent 40412 46c47aedb0d4218c74783b3c949fdcffe490de9b (current diff)
parent 40032 06ff8717ce97f9530d2a3abb29baf94ce671d9a6 (diff)
child 40415 8df53b3a4bd9f0d338f61a8da331a169bb9df3e0
push id12610
push userrsayre@mozilla.com
push dateMon, 05 Apr 2010 17:26:41 +0000
treeherdermozilla-central@1942c0b4e101 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.3a4pre
Merge mozilla-central to tracemonkey.
accessible/tests/mochitest/test_name_nsApplicationAcc.html
browser/components/microsummaries/Makefile.in
browser/components/microsummaries/public/Makefile.in
browser/components/microsummaries/public/nsIMicrosummaryService.idl
browser/components/microsummaries/src/Makefile.in
browser/components/microsummaries/src/nsMicrosummaryService.js
browser/components/places/public/nsIPlacesImportExportService.idl
browser/components/places/src/nsPlacesImportExportService.cpp
browser/components/places/src/nsPlacesImportExportService.h
browser/themes/winstripe/browser/tabbrowser/tabbrowser-tabs-bkgnd.png
browser/themes/winstripe/browser/tabbrowser/tabstrip-bottom.png
caps/src/nsScriptSecurityManager.cpp
configure.in
content/base/public/nsContentUtils.h
content/canvas/src/nsCanvasRenderingContext2D.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsJSEnvironment.cpp
dom/interfaces/html/nsIDOMNSHTMLFormControlList.idl
dom/src/threads/nsDOMThreadService.cpp
gfx/layers/BasicLayers.cpp
gfx/layers/BasicLayers.h
gfx/thebes/public/gfxWindowsFonts.h
gfx/thebes/src/gfxWindowsFonts.cpp
js/ctypes/CTypes.cpp
js/ctypes/Function.cpp
js/ctypes/Function.h
js/ctypes/Library.cpp
js/src/Makefile.in
js/src/configure.in
js/src/jscntxt.cpp
js/src/jsfun.cpp
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsparse.cpp
js/src/jsregexp.cpp
js/src/jsregexp.h
js/src/jsscope.cpp
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/shell/js.cpp
js/src/xpconnect/src/XPCChromeObjectWrapper.cpp
js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
js/src/xpconnect/src/XPCNativeWrapper.cpp
js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp
js/src/xpconnect/src/XPCWrapper.cpp
js/src/xpconnect/src/XPCWrapper.h
js/src/xpconnect/src/qsgen.py
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcconvert.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcquickstubs.cpp
js/src/xpconnect/src/xpcwrappednativescope.cpp
js/src/xpconnect/tests/mochitest/Makefile.in
layout/reftests/backgrounds/lime-32x32.png
layout/reftests/box-properties/column-gap-percent-1-ref.html
layout/reftests/box-properties/column-gap-percent-1.html
layout/reftests/box-properties/column-gap-percent-2-ref.html
layout/reftests/box-properties/column-gap-percent-2.html
layout/reftests/bugs/401361-1-ref.xul
layout/reftests/bugs/401361-1.xul
layout/reftests/bugs/438987-2d.html
layout/reftests/svg/smil/api-sanity-1-ref.svg
layout/reftests/svg/smil/smil-transitions-interaction-1.svg
layout/reftests/svg/smil/smil-transitions-interaction-2.svg
layout/reftests/svg/smil/smil-transitions-interaction-3.svg
layout/reftests/svg/smil/smil-transitions-interaction-4.svg
layout/reftests/table-bordercollapse/frame_lhs_rules_grous.html
media/libogg/src/bitwise.c
media/libogg/src/framing.c
modules/freetype2/README.CVS
modules/freetype2/src/base/ftnames.c
modules/plugin/base/src/nsNPAPIPlugin.cpp
parser/html/nsHtml5SpeculativeLoader.cpp
parser/html/nsHtml5SpeculativeLoader.h
toolkit/components/passwordmgr/test/unit/head_storage_legacy_1.js
toolkit/crashreporter/google-breakpad/src/processor/stack_frame_info.h
toolkit/themes/pinstripe/global/plugins.css
toolkit/themes/pinstripe/global/toolbar/toolbar-background.gif
xpfe/Makefile.in
xpfe/components/find/public/nsISearchContext.idl
--- a/.hgtags
+++ b/.hgtags
@@ -30,8 +30,10 @@ 8a601ed6bc4c7b3d1e35aa9e81f257512d984bd5
 d7d64f68423b68a671f623f123e90057ebc49dac UPDATE_PACKAGING_R7
 fb32f6e1859c07846a01b4478a7b1678019e0b45 UPDATE_PACKAGING_R7
 f817a4378f32b1ad0a7c4b5a9949586dba816da5 FENNEC_M11
 5c1e7c779b6edc8ff912001990edc579f80597f4 FENNEC_B1
 fe9cc55b8db7f56f7e68a246acba363743854979 UPDATE_PACKAGING_R8
 6fd4bb500d425c406c1b52f66e5b195b20ae5e0a chromium-import-r15462
 6fd4bb500d425c406c1b52f66e5b195b20ae5e0a chromium-import-latest
 376b78fc72230aaf2ca4e279a8f4ef1efd4a1d9f GECKO_1_9_2_BASE
+138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R9
+138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R10
--- a/Makefile.in
+++ b/Makefile.in
@@ -56,20 +56,25 @@ default::
 TIERS += base
 
 #
 # tier "base" - basic setup
 #
 tier_base_dirs = \
 	config \
 	build \
-	memory \
 	probes \
 	$(NULL)
 
+ifndef LIBXUL_SDK
+tier_base_dirs += \
+	memory \
+	$(NULL)
+endif
+
 ifdef COMPILE_ENVIRONMENT
 include $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk
 endif
 
 TIERS += testharness
 
 # test harnesses
 ifdef ENABLE_TESTS
--- a/accessible/public/Makefile.in
+++ b/accessible/public/Makefile.in
@@ -50,16 +50,17 @@ MODULE    = accessibility
 XPIDL_MODULE= accessibility
 GRE_MODULE	= 1
 
 XPIDLSRCS = \
       nsIAccessibleTypes.idl \
       nsIAccessibilityService.idl \
       nsIAccessibleRetrieval.idl \
       nsIAccessible.idl \
+      nsIAccessibleApplication.idl \
       nsIAccessibleRelation.idl \
       nsIAccessibleRole.idl \
       nsIAccessibleStates.idl \
       nsIAccessibleDocument.idl \
       nsIAccessibleProvider.idl \
       nsIAccessibleSelectable.idl \
       nsIAccessNode.idl \
       nsIAccessibleEvent.idl \
--- a/accessible/public/nsIAccessNode.idl
+++ b/accessible/public/nsIAccessNode.idl
@@ -51,17 +51,17 @@ interface nsIDOMCSSPrimitiveValue;
  * The nsIAccessNode implementations are instantiated lazily.
  * The nsIAccessNode tree for a given dom window
  * has a one to one relationship to the DOM tree.
  * If the DOM node for this access node is "accessible",
  * then a QueryInterface to nsIAccessible will succeed.
  *
  * @status UNDER_REVIEW
  */
-[scriptable, uuid(71a3b4e7-e83d-45cf-a20e-9ce292bcf19f)]
+[scriptable, uuid(bd458843-1895-42c6-b7f9-f0ca88eeab6b)]
 interface nsIAccessNode : nsISupports
 {
   /**
    * The DOM node this nsIAccessNode is associated with.
    */
   readonly attribute nsIDOMNode DOMNode;
 
   /**
@@ -99,21 +99,25 @@ interface nsIAccessNode : nsISupports
   readonly attribute nsIAccessNode previousSiblingNode;
 
   /**
    * The next nsIAccessNode sibling
    */
   readonly attribute nsIAccessNode nextSiblingNode;
 
   /**
-   * The nsIAccessibleDocument that this nsIAccessNode
-   * resides in.
+   * The document accessible that this access node resides in.
    */
-  readonly attribute nsIAccessibleDocument accessibleDocument;
-  
+  readonly attribute nsIAccessibleDocument document;
+
+  /**
+   * The root document accessible that this access node resides in.
+   */
+  readonly attribute nsIAccessibleDocument rootDocument;
+
   /**
    * The innerHTML for the DOM node
    * This is a text string of all the markup inside the DOM
    * node, not including the start and end tag for the node.
    */
   readonly attribute DOMString innerHTML;
 
   /**
new file mode 100644
--- /dev/null
+++ b/accessible/public/nsIAccessibleApplication.idl
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Alexander Surkov <surkov.alexander@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+/**
+ * This interface is implemented by top level accessible object in hierarchy and
+ * provides information about application.
+ */
+[scriptable, uuid(79251626-387c-4531-89f3-680d31d6cf05)]
+interface nsIAccessibleApplication : nsISupports
+{
+  /**
+   * Returns the application name.
+   */
+  readonly attribute DOMString appName;
+
+  /**
+   * Returns the application version.
+   */
+  readonly attribute DOMString appVersion;
+
+  /**
+   * Returns the platform name.
+   */
+  readonly attribute DOMString platformName;
+
+  /**
+   * Returns the platform version.
+   */
+  readonly attribute DOMString platformVersion;
+};
--- a/accessible/public/nsIAccessibleRetrieval.idl
+++ b/accessible/public/nsIAccessibleRetrieval.idl
@@ -51,20 +51,25 @@ interface nsIDOMDOMStringList;
  * An interface for in-process accessibility clients
  * wishing to get an nsIAccessible or nsIAccessNode for
  * a given DOM node.
  * More documentation at:
  *   http://www.mozilla.org/projects/ui/accessibility
  *
  * @status UNDER_REVIEW
  */
-[scriptable, uuid(5f5a6f98-835d-4771-8bfe-a0c7cdc85fec)]
+[scriptable, uuid(3df14f00-7253-4b9c-97c2-b4632090da23)]
 interface nsIAccessibleRetrieval : nsISupports
 {
   /**
+   * Return application accessible.
+   */
+  nsIAccessible getApplicationAccessible();
+
+  /**
    * Return an nsIAccessible for a DOM node in pres shell 0.
    * Create a new accessible of the appropriate type if necessary,
    * or use one from the accessibility cache if it already exists.
    * @param aNode The DOM node to get an accessible for.
    * @return The nsIAccessible for the given DOM node.
    */
   nsIAccessible getAccessibleFor(in nsIDOMNode aNode);
 
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -1090,20 +1090,20 @@ nsAccessibleWrap *GetAccessibleWrap(AtkO
 
     // Check if AccessibleWrap was deconstructed
     if (tmpAccWrap == nsnull) {
         return nsnull;
     }
 
     NS_ENSURE_TRUE(tmpAccWrap->GetAtkObject() == aAtkObj, nsnull);
 
-    nsRefPtr<nsApplicationAccessibleWrap> appAccWrap =
+    nsApplicationAccessible *applicationAcc =
         nsAccessNode::GetApplicationAccessible();
     nsAccessibleWrap* tmpAppAccWrap =
-        static_cast<nsAccessibleWrap*>(appAccWrap.get());
+        static_cast<nsAccessibleWrap*>(applicationAcc);
 
     if (tmpAppAccWrap != tmpAccWrap && !tmpAccWrap->IsValidObject())
         return nsnull;
 
     return tmpAccWrap;
 }
 
 nsresult
--- a/accessible/src/atk/nsApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.cpp
@@ -441,21 +441,21 @@ mai_util_get_root(void)
         // We've shutdown, try to use gail instead
         // (to avoid assert in spi_atk_tidy_windows())
         if (gail_get_root)
             return gail_get_root();
 
         return nsnull;
     }
 
-    nsRefPtr<nsApplicationAccessibleWrap> root =
+    nsApplicationAccessible *applicationAcc =
         nsAccessNode::GetApplicationAccessible();
 
-    if (root)
-        return root->GetAtkObject();
+    if (applicationAcc)
+        return applicationAcc->GetAtkObject();
 
     return nsnull;
 }
 
 G_CONST_RETURN gchar *
 mai_util_get_toolkit_name(void)
 {
     return MAI_NAME;
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -70,17 +70,16 @@ CPPSRCS = \
   nsCaretAccessible.cpp \
   nsTextAccessible.cpp \
   nsTextEquivUtils.cpp \
   nsTextAttrs.cpp \
   $(NULL)
 
 EXPORTS = \
   nsRootAccessible.h \
-  nsAccEvent.h \
   nsAccessNode.h \
   $(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -674,41 +674,41 @@ nsStateMapEntry nsARIAMap::gWAIStateMap[
   // eARIASelectable
   nsStateMapEntry(&nsAccessibilityAtoms::aria_selected, kBoolType,
                   nsIAccessibleStates::STATE_SELECTABLE,
                   nsIAccessibleStates::STATE_SELECTED, 0,
                   0, 0, PR_TRUE)
 };
 
 /**
- * Universal states:
+ * Universal (Global) states:
  * The following state rules are applied to any accessible element,
  * whether there is an ARIA role or not:
  */
 eStateMapEntryID nsARIAMap::gWAIUnivStateMap[] = {
-  eARIARequired,
-  eARIAInvalid,
-  eARIAHasPopup,
   eARIABusy,
   eARIADisabled,
-  eARIAExpanded,
+  eARIAExpanded,  // Currently under spec review but precedent exists
+  eARIAHasPopup,  // Note this is technically a "property"
+  eARIAInvalid,
+  eARIARequired,  // XXX not global, Bug 553117
   eARIANone
 };
 
 
 /**
  * ARIA attribute map for attribute characteristics
  * 
  * @note ARIA attributes that don't have any flags are not included here
  */
 nsAttributeCharacteristics nsARIAMap::gWAIUnivAttrMap[] = {
   {&nsAccessibilityAtoms::aria_activedescendant,  ATTR_BYPASSOBJ                 },
   {&nsAccessibilityAtoms::aria_atomic,                             ATTR_VALTOKEN },
   {&nsAccessibilityAtoms::aria_busy,                               ATTR_VALTOKEN },
-  {&nsAccessibilityAtoms::aria_checked,           ATTR_BYPASSOBJ | ATTR_VALTOKEN }, /* exposes checkable  obj attr */
+  {&nsAccessibilityAtoms::aria_checked,           ATTR_BYPASSOBJ | ATTR_VALTOKEN }, /* exposes checkable obj attr */
   {&nsAccessibilityAtoms::aria_controls,          ATTR_BYPASSOBJ                 },
   {&nsAccessibilityAtoms::aria_describedby,       ATTR_BYPASSOBJ                 },
   {&nsAccessibilityAtoms::aria_disabled,          ATTR_BYPASSOBJ | ATTR_VALTOKEN },
   {&nsAccessibilityAtoms::aria_dropeffect,                         ATTR_VALTOKEN },
   {&nsAccessibilityAtoms::aria_expanded,          ATTR_BYPASSOBJ | ATTR_VALTOKEN },
   {&nsAccessibilityAtoms::aria_flowto,            ATTR_BYPASSOBJ                 },  
   {&nsAccessibilityAtoms::aria_grabbed,                            ATTR_VALTOKEN },
   {&nsAccessibilityAtoms::aria_haspopup,          ATTR_BYPASSOBJ | ATTR_VALTOKEN },
@@ -735,164 +735,186 @@ nsAttributeCharacteristics nsARIAMap::gW
 };
 
 PRUint32 nsARIAMap::gWAIUnivAttrMapLength = NS_ARRAY_LENGTH(nsARIAMap::gWAIUnivAttrMap);
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsStateMapEntry
 
-nsStateMapEntry:: nsStateMapEntry(nsIAtom **aAttrName, eStateValueType aType,
-                                  PRUint32 aPermanentState,
-                                  PRUint32 aTrueState, PRUint32 aTrueExtraState,
-                                  PRUint32 aFalseState, PRUint32 aFalseExtraState,
-                                  PRBool aDefinedIfAbsent) :
-  attributeName(aAttrName), isToken(PR_TRUE), permanentState(aPermanentState)
-{
-  value1 = "false";
-  state1 = aFalseState;
-  extraState1 = aFalseExtraState;
+nsStateMapEntry::nsStateMapEntry() :
+  mAttributeName(nsnull),
+  mIsToken(PR_FALSE),
+  mPermanentState(0),
+  mValue1(nsnull),
+  mState1(0),
+  mExtraState1(0),
+  mValue2(nsnull),
+  mState2(0),
+  mExtraState2(0),
+  mValue3(nsnull),
+  mState3(0),
+  mExtraState3(0),
+  mDefaultState(0),
+  mDefaultExtraState(0),
+  mDefinedIfAbsent(PR_FALSE)
+{}
 
+nsStateMapEntry::nsStateMapEntry(nsIAtom **aAttrName, eStateValueType aType,
+                                 PRUint32 aPermanentState,
+                                 PRUint32 aTrueState, PRUint32 aTrueExtraState,
+                                 PRUint32 aFalseState, PRUint32 aFalseExtraState,
+                                 PRBool aDefinedIfAbsent) :
+  mAttributeName(aAttrName),
+  mIsToken(PR_TRUE),
+  mPermanentState(aPermanentState),
+  mValue1("false"),
+  mState1(aFalseState),
+  mExtraState1(aFalseExtraState),
+  mValue2(nsnull),
+  mState2(0),
+  mExtraState2(0),
+  mValue3(nsnull),
+  mState3(0),
+  mExtraState3(0),
+  mDefaultState(aTrueState),
+  mDefaultExtraState(aTrueExtraState),
+  mDefinedIfAbsent(aDefinedIfAbsent)
+{
   if (aType == kMixedType) {
-    value2 = "mixed";
-    state2 = nsIAccessibleStates::STATE_MIXED;
-    extraState2 = 0;
+    mValue2 = "mixed";
+    mState2 = nsIAccessibleStates::STATE_MIXED;
   }
-
-  defaultState = aTrueState;
-  defaultExtraState = aTrueExtraState;
-
-  definedIfAbsent = aDefinedIfAbsent;
 }
 
 nsStateMapEntry::nsStateMapEntry(nsIAtom **aAttrName,
                                  const char *aValue1,
                                  PRUint32 aState1, PRUint32 aExtraState1,
                                  const char *aValue2,
                                  PRUint32 aState2, PRUint32 aExtraState2,
                                  const char *aValue3,
                                  PRUint32 aState3, PRUint32 aExtraState3) :
-  attributeName(aAttrName), isToken(PR_FALSE), permanentState(0),
-  value1(aValue1), state1(aState1), extraState1(aExtraState1),
-  value2(aValue2), state2(aState2), extraState2(aExtraState2),
-  value3(aValue3), state3(aState3), extraState3(aExtraState3),
-  defaultState(0), defaultExtraState(0), definedIfAbsent(PR_FALSE)
+  mAttributeName(aAttrName), mIsToken(PR_FALSE), mPermanentState(0),
+  mValue1(aValue1), mState1(aState1), mExtraState1(aExtraState1),
+  mValue2(aValue2), mState2(aState2), mExtraState2(aExtraState2),
+  mValue3(aValue3), mState3(aState3), mExtraState3(aExtraState3),
+  mDefaultState(0), mDefaultExtraState(0), mDefinedIfAbsent(PR_FALSE)
 {
 }
 
 nsStateMapEntry::nsStateMapEntry(nsIAtom **aAttrName,
                                  EDefaultStateRule aDefaultStateRule,
                                  const char *aValue1,
                                  PRUint32 aState1, PRUint32 aExtraState1,
                                  const char *aValue2,
                                  PRUint32 aState2, PRUint32 aExtraState2,
                                  const char *aValue3,
                                  PRUint32 aState3, PRUint32 aExtraState3) :
-  attributeName(aAttrName), isToken(PR_TRUE), permanentState(0),
-  value1(aValue1), state1(aState1), extraState1(aExtraState1),
-  value2(aValue2), state2(aState2), extraState2(aExtraState2),
-  value3(aValue3), state3(aState3), extraState3(aExtraState3),
-  defaultState(0), defaultExtraState(0), definedIfAbsent(PR_TRUE)
+  mAttributeName(aAttrName), mIsToken(PR_TRUE), mPermanentState(0),
+  mValue1(aValue1), mState1(aState1), mExtraState1(aExtraState1),
+  mValue2(aValue2), mState2(aState2), mExtraState2(aExtraState2),
+  mValue3(aValue3), mState3(aState3), mExtraState3(aExtraState3),
+  mDefaultState(0), mDefaultExtraState(0), mDefinedIfAbsent(PR_TRUE)
 {
   if (aDefaultStateRule == eUseFirstState) {
-    defaultState = aState1;
-    defaultExtraState = aExtraState1;
+    mDefaultState = aState1;
+    mDefaultExtraState = aExtraState1;
   }
 }
 
 PRBool
 nsStateMapEntry::MapToStates(nsIContent *aContent,
                              PRUint32 *aState, PRUint32 *aExtraState,
                              eStateMapEntryID aStateMapEntryID)
 {
   // Return true if we should continue.
   if (aStateMapEntryID == eARIANone)
     return PR_FALSE;
 
   const nsStateMapEntry& entry = nsARIAMap::gWAIStateMap[aStateMapEntryID];
 
-  if (entry.isToken) {
+  if (entry.mIsToken) {
     // If attribute is considered as defined when it's absent then let's act
     // attribute value is "false" supposedly.
-    PRBool hasAttr = aContent->HasAttr(kNameSpaceID_None, *entry.attributeName);
-    if (entry.definedIfAbsent && !hasAttr) {
-      if (entry.permanentState)
-        *aState |= entry.permanentState;
-      if (entry.state1)
-        *aState |= entry.state1;
-      if (aExtraState && entry.extraState1)
-        *aExtraState |= entry.extraState1;
+    PRBool hasAttr = aContent->HasAttr(kNameSpaceID_None, *entry.mAttributeName);
+    if (entry.mDefinedIfAbsent && !hasAttr) {
+      if (entry.mPermanentState)
+        *aState |= entry.mPermanentState;
+      if (entry.mState1)
+        *aState |= entry.mState1;
+      if (aExtraState && entry.mExtraState1)
+        *aExtraState |= entry.mExtraState1;
 
       return PR_TRUE;
     }
 
     // We only have attribute state mappings for NMTOKEN (and boolean) based
     // ARIA attributes. According to spec, a value of "undefined" is to be
     // treated equivalent to "", or the absence of the attribute. We bail out
     // for this case here.
     // Note: If this method happens to be called with a non-token based
     // attribute, for example: aria-label="" or aria-label="undefined", we will
     // bail out and not explore a state mapping, which is safe.
     if (!hasAttr ||
-        aContent->AttrValueIs(kNameSpaceID_None, *entry.attributeName,
+        aContent->AttrValueIs(kNameSpaceID_None, *entry.mAttributeName,
                               nsAccessibilityAtoms::_empty, eCaseMatters) ||
-        aContent->AttrValueIs(kNameSpaceID_None, *entry.attributeName,
+        aContent->AttrValueIs(kNameSpaceID_None, *entry.mAttributeName,
                               nsAccessibilityAtoms::_undefined, eCaseMatters)) {
 
-      if (entry.permanentState)
-        *aState &= ~entry.permanentState;
+      if (entry.mPermanentState)
+        *aState &= ~entry.mPermanentState;
       return PR_TRUE;
     }
 
-    if (entry.permanentState)
-      *aState |= entry.permanentState;
+    if (entry.mPermanentState)
+      *aState |= entry.mPermanentState;
   }
 
   nsAutoString attrValue;
-  if (!aContent->GetAttr(kNameSpaceID_None, *entry.attributeName, attrValue))
+  if (!aContent->GetAttr(kNameSpaceID_None, *entry.mAttributeName, attrValue))
     return PR_TRUE;
 
   // Apply states for matched value. If no values was matched then apply default
   // states.
   PRBool applyDefaultStates = PR_TRUE;
-  if (entry.value1) {
-    if (attrValue.EqualsASCII(entry.value1)) {
+  if (entry.mValue1) {
+    if (attrValue.EqualsASCII(entry.mValue1)) {
       applyDefaultStates = PR_FALSE;
 
-      if (entry.state1)
-        *aState |= entry.state1;
+      if (entry.mState1)
+        *aState |= entry.mState1;
 
-      if (aExtraState && entry.extraState1)
-        *aExtraState |= entry.extraState1;
+      if (aExtraState && entry.mExtraState1)
+        *aExtraState |= entry.mExtraState1;
 
-    } else if (entry.value2) {
-      if (attrValue.EqualsASCII(entry.value2)) {
+    } else if (entry.mValue2) {
+      if (attrValue.EqualsASCII(entry.mValue2)) {
         applyDefaultStates = PR_FALSE;
 
-        if (entry.state2)
-          *aState |= entry.state2;
+        if (entry.mState2)
+          *aState |= entry.mState2;
 
-        if (aExtraState && entry.extraState2)
-          *aExtraState |= entry.extraState2;
+        if (aExtraState && entry.mExtraState2)
+          *aExtraState |= entry.mExtraState2;
 
-      } else if (entry.value3) {
-        if (attrValue.EqualsASCII(entry.value3)) {
+      } else if (entry.mValue3) {
+        if (attrValue.EqualsASCII(entry.mValue3)) {
           applyDefaultStates = PR_FALSE;
 
-          if (entry.state3)
-            *aState |= entry.state3;
+          if (entry.mState3)
+            *aState |= entry.mState3;
 
-          if (aExtraState && entry.extraState3)
-            *aExtraState |= entry.extraState3;
+          if (aExtraState && entry.mExtraState3)
+            *aExtraState |= entry.mExtraState3;
         }
       }
     }
   }
 
   if (applyDefaultStates) {
-    if (entry.defaultState)
-      *aState |= entry.defaultState;
-    if (entry.defaultExtraState && aExtraState)
-      *aExtraState |= entry.defaultExtraState;
+    if (entry.mDefaultState)
+      *aState |= entry.mDefaultState;
+    if (entry.mDefaultExtraState && aExtraState)
+      *aExtraState |= entry.mDefaultExtraState;
   }
 
   return PR_TRUE;
 }
--- a/accessible/src/base/nsARIAMap.h
+++ b/accessible/src/base/nsARIAMap.h
@@ -189,17 +189,17 @@ enum eStateMapEntryID
 };
 
 class nsStateMapEntry
 {
 public:
   /**
    * Used to create stub.
    */
-  nsStateMapEntry() {}
+  nsStateMapEntry();
 
   /**
    * Used for ARIA attributes having boolean or mixed values.
    */
   nsStateMapEntry(nsIAtom **aAttrName, eStateValueType aType,
                   PRUint32 aPermanentState,
                   PRUint32 aTrueState, PRUint32 aTrueExtraState,
                   PRUint32 aFalseState = 0, PRUint32 aFalseExtraState = 0,
@@ -235,43 +235,43 @@ public:
    * @return                   true if state map entry ID is valid
    */
   static PRBool MapToStates(nsIContent *aContent,
                             PRUint32 *aState, PRUint32 *aExtraState,
                             eStateMapEntryID aStateMapEntryID);
 
 private:
   // ARIA attribute name
-  nsIAtom** attributeName;
+  nsIAtom** mAttributeName;
 
   // Indicates if attribute is token (can be undefined)
-  PRBool isToken;
+  PRBool mIsToken;
 
   // State applied always if attribute is defined
-  PRUint32 permanentState;
+  PRUint32 mPermanentState;
 
   // States applied if attribute value is matched to the stored value
-  const char* value1;
-  PRUint32 state1;
-  PRUint32 extraState1;
+  const char* mValue1;
+  PRUint32 mState1;
+  PRUint32 mExtraState1;
 
-  const char* value2;
-  PRUint32 state2;
-  PRUint32 extraState2;
+  const char* mValue2;
+  PRUint32 mState2;
+  PRUint32 mExtraState2;
 
-  const char* value3;
-  PRUint32 state3;
-  PRUint32 extraState3;
+  const char* mValue3;
+  PRUint32 mState3;
+  PRUint32 mExtraState3;
 
   // States applied if no stored values above are matched
-  PRUint32 defaultState;
-  PRUint32 defaultExtraState;
+  PRUint32 mDefaultState;
+  PRUint32 mDefaultExtraState;
 
   // Permanent and false states are applied if attribute is absent
-  PRBool definedIfAbsent;
+  PRBool mDefinedIfAbsent;
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Role map entry
 
 /**
  * For each ARIA role, this maps the nsIAccessible information.
--- a/accessible/src/base/nsAccEvent.cpp
+++ b/accessible/src/base/nsAccEvent.cpp
@@ -32,22 +32,20 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAccEvent.h"
-#include "nsAccessibilityAtoms.h"
+
 #include "nsApplicationAccessibleWrap.h"
-#include "nsCoreUtils.h"
-#include "nsIAccessibilityService.h"
-#include "nsIAccessNode.h"
-#include "nsIDocument.h"
+#include "nsDocAccessible.h"
+
 #include "nsIDOMDocument.h"
 #include "nsIEventStateManager.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIServiceManager.h"
 #ifdef MOZ_XUL
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsXULTreeAccessible.h"
 #endif
@@ -58,17 +56,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccEvent. nsISupports
 
-NS_IMPL_CYCLE_COLLECTION_2(nsAccEvent, mAccessible, mDocAccessible)
+NS_IMPL_CYCLE_COLLECTION_1(nsAccEvent, mAccessible)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccEvent)
   NS_INTERFACE_MAP_ENTRY(nsIAccessibleEvent)
   NS_INTERFACE_MAP_ENTRY(nsAccEvent)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccEvent)
@@ -133,51 +131,62 @@ nsAccEvent::GetAccessible(nsIAccessible 
 }
 
 NS_IMETHODIMP
 nsAccEvent::GetDOMNode(nsIDOMNode **aDOMNode)
 {
   NS_ENSURE_ARG_POINTER(aDOMNode);
   *aDOMNode = nsnull;
 
-  if (!mNode) {
-    nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(mAccessible));
-    NS_ENSURE_TRUE(accessNode, NS_ERROR_FAILURE);
-
-    nsCOMPtr<nsIDOMNode> DOMNode;
-    accessNode->GetDOMNode(getter_AddRefs(DOMNode));
-
-    mNode = do_QueryInterface(DOMNode);
-  }
+  if (!mNode)
+    mNode = GetNode();
 
   if (mNode)
     CallQueryInterface(mNode, aDOMNode);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccEvent::GetAccessibleDocument(nsIAccessibleDocument **aDocAccessible)
 {
   NS_ENSURE_ARG_POINTER(aDocAccessible);
-  *aDocAccessible = nsnull;
+
+  NS_IF_ADDREF(*aDocAccessible = GetDocAccessible());
+  return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsAccEvent: public methods
 
-  if (!mDocAccessible) {
-    if (!mAccessible) {
-      nsCOMPtr<nsIAccessible> accessible;
-      GetAccessible(getter_AddRefs(accessible));
-    }
+nsINode*
+nsAccEvent::GetNode()
+{
+  if (!mNode) {
+    nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(mAccessible));
+    if (!accessNode)
+      return nsnull;
 
-    nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(mAccessible));
-    NS_ENSURE_TRUE(accessNode, NS_ERROR_FAILURE);
-    accessNode->GetAccessibleDocument(getter_AddRefs(mDocAccessible));
+    nsCOMPtr<nsIDOMNode> DOMNode;
+    accessNode->GetDOMNode(getter_AddRefs(DOMNode));
+
+    mNode = do_QueryInterface(DOMNode);
   }
 
-  NS_IF_ADDREF(*aDocAccessible = mDocAccessible);
-  return NS_OK;
+  return mNode;
+}
+
+nsDocAccessible*
+nsAccEvent::GetDocAccessible()
+{
+  nsINode *node = GetNode();
+  if (node)
+    return nsAccessNode::GetDocAccessibleFor(node->GetOwnerDoc());
+
+  return nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccEvent: protected methods
 
 already_AddRefed<nsIAccessible>
 nsAccEvent::GetAccessibleByNode()
 {
@@ -228,20 +237,20 @@ nsAccEvent::CaptureIsFromUserInput(EIsFr
   nsCOMPtr<nsIDOMNode> targetNode;
   GetDOMNode(getter_AddRefs(targetNode));
 
 #ifdef DEBUG
   if (!targetNode) {
     // XXX: remove this hack during reorganization of 506907. Meanwhile we
     // want to get rid an assertion for application accessible events which
     // don't have DOM node (see bug 506206).
-    nsRefPtr<nsApplicationAccessibleWrap> applicationAcc =
+    nsApplicationAccessible *applicationAcc =
       nsAccessNode::GetApplicationAccessible();
 
-    if (mAccessible != static_cast<nsIAccessible*>(applicationAcc.get()))
+    if (mAccessible != static_cast<nsIAccessible*>(applicationAcc))
       NS_ASSERTION(targetNode, "There should always be a DOM node for an event");
   }
 #endif
 
   if (aIsFromUserInput != eAutoDetect) {
     mIsFromUserInput = aIsFromUserInput == eFromUserInput ? PR_TRUE : PR_FALSE;
     return;
   }
--- a/accessible/src/base/nsAccEvent.h
+++ b/accessible/src/base/nsAccEvent.h
@@ -36,28 +36,27 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsAccEvent_H_
 #define _nsAccEvent_H_
 
-#include "nsAutoPtr.h"
+#include "nsIAccessibleEvent.h"
+
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
-#include "nsIAccessibleEvent.h"
-#include "nsIAccessible.h"
-#include "nsIAccessibleDocument.h"
-#include "nsIDOMNode.h"
 #include "nsString.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsAccUtils.h"
 
-class nsIPresShell;
+#include "nsINode.h"
+#include "nsIDOMNode.h"
+
+class nsDocAccessible;
 
 // Constants used to point whether the event is from user input.
 enum EIsFromUserInput
 {
   // eNoUserInput: event is not from user input
   eNoUserInput = 0,
   // eFromUserInput: event is from user input
   eFromUserInput = 1,
@@ -78,23 +77,30 @@ class nsAccEvent: public nsIAccessibleEv
 public:
 
   // Rule for accessible events.
   // The rule will be applied when flushing pending events.
   enum EEventRule {
      // eAllowDupes : More than one event of the same type is allowed.
      //    This event will always be emitted.
      eAllowDupes,
+
      // eCoalesceFromSameSubtree : For events of the same type from the same
      //    subtree or the same node, only the umbrella event on the ancestor
      //    will be emitted.
      eCoalesceFromSameSubtree,
+
+    // eCoalesceFromSameDocument : For events of the same type from the same
+    //    document, only the newest event will be emitted.
+    eCoalesceFromSameDocument,
+
      // eRemoveDupes : For repeat events, only the newest event in queue
      //    will be emitted.
      eRemoveDupes,
+
      // eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
      eDoNotEmit
   };
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCEVENT_IMPL_CID)
 
   // Initialize with an nsIAccessible
   nsAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible,
@@ -115,16 +121,19 @@ public:
 
   // nsAccEvent
   PRUint32 GetEventType() const { return mEventType; }
   EEventRule GetEventRule() const { return mEventRule; }
   PRBool IsAsync() const { return mIsAsync; }
   PRBool IsFromUserInput() const { return mIsFromUserInput; }
   nsIAccessible* GetAccessible() const { return mAccessible; }
 
+  nsINode* GetNode();
+  nsDocAccessible* GetDocAccessible();
+
 protected:
   /**
    * Get an accessible from event target node.
    */
   already_AddRefed<nsIAccessible> GetAccessibleByNode();
 
   /**
    * Determine whether the event is from user input by event state manager if
@@ -134,17 +143,16 @@ protected:
 
   PRBool mIsFromUserInput;
 
   PRUint32 mEventType;
   EEventRule mEventRule;
   PRPackedBool mIsAsync;
   nsCOMPtr<nsIAccessible> mAccessible;
   nsCOMPtr<nsINode> mNode;
-  nsCOMPtr<nsIAccessibleDocument> mDocAccessible;
 
   friend class nsAccEventQueue;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAccEvent, NS_ACCEVENT_IMPL_CID)
 
 
 #define NS_ACCREORDEREVENT_IMPL_CID                     \
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -37,17 +37,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsCoreUtils.h"
 #include "nsAccUtils.h"
 
 #include "nsIAccessibleStates.h"
 #include "nsIAccessibleTypes.h"
 
-#include "nsAccEvent.h"
 #include "nsHyperTextAccessible.h"
 #include "nsHTMLTableAccessible.h"
 #include "nsDocAccessible.h"
 #include "nsAccessibilityAtoms.h"
 #include "nsAccTreeWalker.h"
 #include "nsAccessible.h"
 #include "nsARIAMap.h"
 #include "nsXULTreeGridAccessible.h"
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -31,17 +31,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsAccessNode.h"
+#include "nsDocAccessible.h"
 #include "nsIAccessible.h"
 #include "nsAccessibilityAtoms.h"
 #include "nsHashtable.h"
 #include "nsAccessibilityService.h"
 #include "nsApplicationAccessibleWrap.h"
 #include "nsIAccessibleDocument.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
@@ -73,19 +73,20 @@
  */
 
 nsIStringBundle *nsAccessNode::gStringBundle = 0;
 nsIStringBundle *nsAccessNode::gKeyStringBundle = 0;
 nsIDOMNode *nsAccessNode::gLastFocusedNode = 0;
 
 PRBool nsAccessNode::gIsCacheDisabled = PR_FALSE;
 PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
-nsAccessNodeHashtable nsAccessNode::gGlobalDocAccessibleCache;
+nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible>
+  nsAccessNode::gGlobalDocAccessibleCache;
 
-nsApplicationAccessibleWrap *nsAccessNode::gApplicationAccessible = nsnull;
+nsApplicationAccessible *nsAccessNode::gApplicationAccessible = nsnull;
 
 /*
  * Class nsAccessNode
  */
  
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible. nsISupports
 
@@ -215,17 +216,17 @@ NS_IMETHODIMP nsAccessNode::GetOwnerWind
 {
   *aWindow = nsnull;
   nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
   if (!docAccessible)
     return NS_ERROR_FAILURE; // This node or doc accessible is shut down
   return docAccessible->GetWindowHandle(aWindow);
 }
 
-already_AddRefed<nsApplicationAccessibleWrap>
+nsApplicationAccessible*
 nsAccessNode::GetApplicationAccessible()
 {
   NS_ASSERTION(!nsAccessibilityService::gIsShutdown,
                "Accessibility wasn't initialized!");
 
   if (!gApplicationAccessible) {
     nsApplicationAccessibleWrap::PreCreate();
 
@@ -233,23 +234,22 @@ nsAccessNode::GetApplicationAccessible()
     if (!gApplicationAccessible)
       return nsnull;
 
     // Addref on create. Will Release in ShutdownXPAccessibility()
     NS_ADDREF(gApplicationAccessible);
 
     nsresult rv = gApplicationAccessible->Init();
     if (NS_FAILED(rv)) {
+      gApplicationAccessible->Shutdown();
       NS_RELEASE(gApplicationAccessible);
-      gApplicationAccessible = nsnull;
       return nsnull;
     }
   }
 
-  NS_ADDREF(gApplicationAccessible);   // Addref because we're a getter
   return gApplicationAccessible;
 }
 
 void nsAccessNode::InitXPAccessibility()
 {
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   if (stringBundleService) {
@@ -292,23 +292,25 @@ void nsAccessNode::ShutdownXPAccessibili
   // Called by nsAccessibilityService::Shutdown()
   // which happens when xpcom is shutting down
   // at exit of program
 
   NS_IF_RELEASE(gStringBundle);
   NS_IF_RELEASE(gKeyStringBundle);
   NS_IF_RELEASE(gLastFocusedNode);
 
-  nsApplicationAccessibleWrap::Unload();
   ClearCache(gGlobalDocAccessibleCache);
 
   // Release gApplicationAccessible after everything else is shutdown
   // so we don't accidently create it again while tearing down root accessibles
-  NS_IF_RELEASE(gApplicationAccessible);
-  gApplicationAccessible = nsnull;  
+  nsApplicationAccessibleWrap::Unload();
+  if (gApplicationAccessible) {
+    gApplicationAccessible->Shutdown();
+    NS_RELEASE(gApplicationAccessible);
+  }
 
   NotifyA11yInitOrShutdown(PR_FALSE);
 }
 
 PRBool
 nsAccessNode::IsDefunct()
 {
   if (!mDOMNode)
@@ -341,22 +343,16 @@ nsPresContext* nsAccessNode::GetPresCont
 {
   nsCOMPtr<nsIPresShell> presShell(GetPresShell());
   if (!presShell) {
     return nsnull;
   }
   return presShell->GetPresContext();
 }
 
-// nsAccessNode protected
-already_AddRefed<nsIAccessibleDocument> nsAccessNode::GetDocAccessible()
-{
-  return GetDocAccessibleFor(mWeakShell); // Addref'd
-}
-
 already_AddRefed<nsRootAccessible> nsAccessNode::GetRootAccessible()
 {
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
     nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
   NS_ASSERTION(docShellTreeItem, "No docshell tree item for mDOMNode");
   if (!docShellTreeItem) {
     return nsnull;
   }
@@ -427,19 +423,31 @@ nsAccessNode::GetNumChildren(PRInt32 *aN
   }
 
   *aNumChildren = content->GetChildCount();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsAccessNode::GetAccessibleDocument(nsIAccessibleDocument **aDocAccessible)
+nsAccessNode::GetDocument(nsIAccessibleDocument **aDocument)
 {
-  *aDocAccessible = GetDocAccessibleFor(mWeakShell).get();
+  NS_ENSURE_ARG_POINTER(aDocument);
+
+  NS_IF_ADDREF(*aDocument = GetDocAccessibleFor(mWeakShell));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAccessNode::GetRootDocument(nsIAccessibleDocument **aRootDocument)
+{
+  NS_ENSURE_ARG_POINTER(aRootDocument);
+
+  nsRefPtr<nsRootAccessible> rootDocument = GetRootAccessible();
+  NS_IF_ADDREF(*aRootDocument = rootDocument.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessNode::GetInnerHTML(nsAString& aInnerHTML)
 {
   aInnerHTML.Truncate();
 
@@ -638,66 +646,66 @@ nsAccessNode::GetComputedStyleCSSValue(c
   NS_ENSURE_TRUE(cssValue, NS_ERROR_FAILURE);
 
   return CallQueryInterface(cssValue, aCSSValue);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode public static
 
-already_AddRefed<nsIAccessibleDocument>
+nsDocAccessible*
 nsAccessNode::GetDocAccessibleFor(nsIDocument *aDocument)
 {
-  if (!aDocument) {
-    return nsnull;
-  }
+  return aDocument ?
+    gGlobalDocAccessibleCache.GetWeak(static_cast<void*>(aDocument)) : nsnull;
+}
 
-  nsCOMPtr<nsIAccessibleDocument> docAccessible(do_QueryInterface(
-    gGlobalDocAccessibleCache.GetWeak(static_cast<void*>(aDocument))));
-  return docAccessible.forget();
-}
- 
-already_AddRefed<nsIAccessibleDocument>
+nsDocAccessible*
 nsAccessNode::GetDocAccessibleFor(nsIWeakReference *aWeakShell)
 {
   nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(aWeakShell));
   if (!presShell) {
     return nsnull;
   }
 
-  return nsAccessNode::GetDocAccessibleFor(presShell->GetDocument());
+  return GetDocAccessibleFor(presShell->GetDocument());
 }
 
 already_AddRefed<nsIAccessibleDocument>
 nsAccessNode::GetDocAccessibleFor(nsIDocShellTreeItem *aContainer,
                                   PRBool aCanCreate)
 {
   if (!aCanCreate) {
     nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
     NS_ASSERTION(docShell, "This method currently only supports docshells");
     nsCOMPtr<nsIPresShell> presShell;
     docShell->GetPresShell(getter_AddRefs(presShell));
-    return presShell ? GetDocAccessibleFor(presShell->GetDocument()) : nsnull;
+    if (!presShell)
+      return nsnull;
+
+    nsDocAccessible *docAcc = GetDocAccessibleFor(presShell->GetDocument());
+    NS_IF_ADDREF(docAcc);
+    return docAcc;
   }
 
   nsCOMPtr<nsIDOMNode> node = nsCoreUtils::GetDOMNodeForContainer(aContainer);
   if (!node) {
     return nsnull;
   }
 
   nsCOMPtr<nsIAccessible> accessible;
   GetAccService()->GetAccessibleFor(node, getter_AddRefs(accessible));
   nsIAccessibleDocument *docAccessible = nsnull;
   if (accessible) {
     CallQueryInterface(accessible, &docAccessible);
   }
   return docAccessible;
 }
  
-already_AddRefed<nsIAccessibleDocument>
+nsDocAccessible*
 nsAccessNode::GetDocAccessibleFor(nsIDOMNode *aNode)
 {
   nsCOMPtr<nsIPresShell> eventShell = nsCoreUtils::GetPresShellFor(aNode);
   if (eventShell) {
     return GetDocAccessibleFor(eventShell->GetDocument());
   }
 
   nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode));
@@ -773,8 +781,17 @@ nsAccessNode::GetLanguage(nsAString& aLa
     nsIDocument *doc = content->GetOwnerDoc();
     if (doc) {
       doc->GetHeaderData(nsAccessibilityAtoms::headerContentLanguage, aLanguage);
     }
   }
  
   return NS_OK;
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// nsAccessNode protected
+
+nsDocAccessible*
+nsAccessNode::GetDocAccessible() const
+{
+  return GetDocAccessibleFor(mWeakShell);
+}
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -58,17 +58,17 @@
 #include "nsAccessibilityService.h"
 
 class nsIPresShell;
 class nsPresContext;
 class nsIAccessibleDocument;
 class nsIFrame;
 class nsIDOMNodeList;
 class nsRootAccessible;
-class nsApplicationAccessibleWrap;
+class nsApplicationAccessible;
 class nsIDocShellTreeItem;
 
 #define ACCESSIBLE_BUNDLE_URL "chrome://global-platform/locale/accessible.properties"
 #define PLATFORM_KEYS_BUNDLE_URL "chrome://global-platform/locale/platformKeys.properties"
 
 // What we want is: NS_INTERFACE_MAP_ENTRY(self) for static IID accessors,
 // but some of our classes have an ambiguous base class of nsISupports which
 // prevents this from working (the default macro converts it to nsISupports,
@@ -113,28 +113,30 @@ class nsAccessNode: public nsIAccessNode
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAccessNode, nsIAccessNode)
 
     NS_DECL_NSIACCESSNODE
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCESSNODE_IMPL_CID)
 
     static void InitXPAccessibility();
     static void ShutdownXPAccessibility();
 
-    /**
-     * Return an application accessible.
-     */
-    static already_AddRefed<nsApplicationAccessibleWrap> GetApplicationAccessible();
+  /**
+   * Return an application accessible.
+   */
+  static nsApplicationAccessible* GetApplicationAccessible();
 
-    // Static cache methods for global document cache
-    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIDocument *aDocument);
-    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIWeakReference *aWeakShell);
-    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIDocShellTreeItem *aContainer, PRBool aCanCreate = PR_FALSE);
-    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIDOMNode *aNode);
+  /**
+   * Return the document accessible for this accesnode.
+   */
+  nsDocAccessible* GetDocAccessible() const;
 
-    already_AddRefed<nsRootAccessible> GetRootAccessible();
+  /**
+   * Return the root document accessible for this accessnode.
+   */
+  already_AddRefed<nsRootAccessible> GetRootAccessible();
 
     static nsIDOMNode *gLastFocusedNode;
 
     already_AddRefed<nsIDOMNode> GetCurrentFocus();
 
     /**
      * Returns true when the accessible is defunct.
      */
@@ -168,21 +170,35 @@ class nsAccessNode: public nsIAccessNode
 
 #ifdef DEBUG
   /**
    * Return true if the access node is cached.
    */
   PRBool IsInCache();
 #endif
 
+  /**
+   * Return cached document accessible.
+   */
+  static nsDocAccessible* GetDocAccessibleFor(nsIDocument *aDocument);
+  static nsDocAccessible* GetDocAccessibleFor(nsIWeakReference *aWeakShell);
+  static nsDocAccessible* GetDocAccessibleFor(nsIDOMNode *aNode);
+
+  /**
+   * Return document accessible.
+   */
+  static already_AddRefed<nsIAccessibleDocument>
+    GetDocAccessibleFor(nsIDocShellTreeItem *aContainer,
+                        PRBool aCanCreate = PR_FALSE);
+
 protected:
     nsresult MakeAccessNode(nsIDOMNode *aNode, nsIAccessNode **aAccessNode);
 
     nsPresContext* GetPresContext();
-    already_AddRefed<nsIAccessibleDocument> GetDocAccessible();
+
     void LastRelease();
 
     nsCOMPtr<nsIDOMNode> mDOMNode;
     nsCOMPtr<nsIWeakReference> mWeakShell;
 
 #ifdef DEBUG_A11Y
     PRBool mIsInitialized;
 #endif
@@ -194,19 +210,20 @@ protected:
 
     // Static data, we do our own refcounting for our static data
     static nsIStringBundle *gStringBundle;
     static nsIStringBundle *gKeyStringBundle;
 
     static PRBool gIsCacheDisabled;
     static PRBool gIsFormFillEnabled;
 
-    static nsAccessNodeHashtable gGlobalDocAccessibleCache;
+  static nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible>
+    gGlobalDocAccessibleCache;
 
 private:
-  static nsApplicationAccessibleWrap *gApplicationAccessible;
+  static nsApplicationAccessible *gApplicationAccessible;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAccessNode,
                               NS_ACCESSNODE_IMPL_CID)
 
 #endif
 
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -956,16 +956,25 @@ nsAccessibilityService::GetCachedAccessN
     nsAccUtils::QueryObject<nsDocAccessible>(accessibleDoc);
   return docAccessible->GetCachedAccessNode(static_cast<void*>(aNode));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibleRetrieval
 
 NS_IMETHODIMP
+nsAccessibilityService::GetApplicationAccessible(nsIAccessible **aAccessibleApplication)
+{
+  NS_ENSURE_ARG_POINTER(aAccessibleApplication);
+
+  NS_IF_ADDREF(*aAccessibleApplication = nsAccessNode::GetApplicationAccessible());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsAccessibilityService::GetStringRole(PRUint32 aRole, nsAString& aString)
 {
   if ( aRole >= NS_ARRAY_LENGTH(kRoleNames)) {
     aString.AssignLiteral("unknown");
     return NS_OK;
   }
 
   CopyUTF8toUTF16(kRoleNames[aRole], aString);
@@ -1620,17 +1629,17 @@ nsAccessibilityService::GetAccessible(ns
 
   // If no accessible, see if we need to create a generic accessible because
   // of some property that makes this object interesting
   // We don't do this for <body>, <html>, <window>, <dialog> etc. which 
   // correspond to the doc accessible and will be created in any case
   if (!newAcc && content->Tag() != nsAccessibilityAtoms::body && content->GetParent() && 
       ((weakFrame.GetFrame() && weakFrame.GetFrame()->IsFocusable()) ||
        (isHTML && nsCoreUtils::HasClickListener(content)) ||
-       HasUniversalAriaProperty(content, aWeakShell) || roleMapEntry ||
+       HasUniversalAriaProperty(content) || roleMapEntry ||
        HasRelatedContent(content) || nsCoreUtils::IsXLink(content))) {
     // This content is focusable or has an interesting dynamic content accessibility property.
     // If it's interesting we need it in the accessibility hierarchy so that events or
     // other accessibles can point to it, or so that it can hold a state, etc.
     if (isHTML) {
       // Interesting HTML container which may have selectable text and/or embedded objects
       newAcc = new nsHyperTextAccessibleWrap(aNode, aWeakShell);
     }
@@ -1641,36 +1650,36 @@ nsAccessibilityService::GetAccessible(ns
   }
 
   if (InitAccessible(newAcc, roleMapEntry))
     return newAcc.forget();
   return nsnull;
 }
 
 PRBool
-nsAccessibilityService::HasUniversalAriaProperty(nsIContent *aContent,
-                                                 nsIWeakReference *aWeakShell)
+nsAccessibilityService::HasUniversalAriaProperty(nsIContent *aContent)
 {
-  // ARIA attributes that take token values (NMTOKEN, bool) are special cased.
+  // ARIA attributes that take token values (NMTOKEN, bool) are special cased
+  // because of special value "undefined" (see HasDefinedARIAToken).
   return nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_atomic) ||
          nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_busy) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_controls) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_describedby) ||
+         aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_disabled) ||
          nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_dropeffect) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_flowto) ||
          nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_grabbed) ||
          nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_haspopup) ||
+         // purposely ignore aria-hidden; since we use gecko for detecting this anyways
          nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_invalid) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby) ||
          nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_live) ||
          nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_owns) ||
-         nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_relevant) ||
-         nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_required) ||
-         nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_sort);
+         nsAccUtils::HasDefinedARIAToken(aContent, nsAccessibilityAtoms::aria_relevant);
 }
 
 // nsIAccessibleRetrieval
 NS_IMETHODIMP
 nsAccessibilityService::GetRelevantContentNodeFor(nsIDOMNode *aNode,
                                                   nsIDOMNode **aRelevantNode)
 {
   // The method returns node that is relevant for attached accessible check.
@@ -1985,39 +1994,39 @@ NS_IMETHODIMP nsAccessibilityService::Ad
 {
 #ifdef MOZ_ACCESSIBILITY_ATK
   nsNativeRootAccessibleWrap* rootAccWrap =
     new nsNativeRootAccessibleWrap((AtkObject*)aAtkAccessible);
 
   *aRootAccessible = static_cast<nsIAccessible*>(rootAccWrap);
   NS_ADDREF(*aRootAccessible);
 
-  nsRefPtr<nsApplicationAccessibleWrap> appRoot =
+  nsApplicationAccessible *applicationAcc =
     nsAccessNode::GetApplicationAccessible();
-  NS_ENSURE_STATE(appRoot);
+  NS_ENSURE_STATE(applicationAcc);
 
-  appRoot->AddRootAccessible(*aRootAccessible);
+  applicationAcc->AddRootAccessible(*aRootAccessible);
 
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
 NS_IMETHODIMP nsAccessibilityService::RemoveNativeRootAccessible(nsIAccessible * aRootAccessible)
 {
 #ifdef MOZ_ACCESSIBILITY_ATK
   void* atkAccessible;
   aRootAccessible->GetNativeInterface(&atkAccessible);
 
-  nsRefPtr<nsApplicationAccessibleWrap> appRoot =
+  nsApplicationAccessible *applicationAcc =
     nsAccessNode::GetApplicationAccessible();
-  NS_ENSURE_STATE(appRoot);
+  NS_ENSURE_STATE(applicationAcc);
 
-  appRoot->RemoveRootAccessible(aRootAccessible);
+  applicationAcc->RemoveRootAccessible(aRootAccessible);
 
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
 // Called from layout when the frame tree owned by a node changes significantly
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -186,20 +186,19 @@ private:
   
   static nsAccessibilityService *gAccessibilityService;
 
   /**
    * Does this content node have a universal ARIA property set on it?
    * A universal ARIA property is one that can be defined on any element even if there is no role.
    *
    * @param aContent The content node to test
-   * @param aWeakShell  A weak reference to the pres shell
    * @return PR_TRUE if there is a universal ARIA property set on the node
    */
-  PRBool HasUniversalAriaProperty(nsIContent *aContent, nsIWeakReference *aWeakShell);
+  PRBool HasUniversalAriaProperty(nsIContent *aContent);
 
   /**
    *  Process the internal doc load event.
    *
    *  @param  aWebProgress  [in] the nsIWebProgress object for the load event
    *  @param  aEventType    [in] the type of load event, one of:
    *                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_START,
    *                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE,
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -33,23 +33,22 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAccessible.h"
+
+#include "nsIXBLAccessible.h"
+
+#include "nsAccTreeWalker.h"
 #include "nsAccessibleRelation.h"
-#include "nsHyperTextAccessibleWrap.h"
-
-#include "nsIAccessibleDocument.h"
-#include "nsIAccessibleHyperText.h"
-#include "nsIXBLAccessible.h"
-#include "nsAccTreeWalker.h"
+#include "nsDocAccessible.h"
 
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentXBL.h"
 #include "nsIDOMDocumentTraversal.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIDOMNodeFilter.h"
@@ -877,25 +876,20 @@ nsAccessible::GetChildAtPoint(PRInt32 aX
   }
 
   // Search an accessible at the given point starting from accessible document
   // because containing block (see CSS2) for out of flow element (for example,
   // absolutely positioned element) may be different from its DOM parent and
   // therefore accessible for containing block may be different from accessible
   // for DOM parent but GetFrameForPoint() should be called for containing block
   // to get an out of flow element.
-  nsCOMPtr<nsIAccessibleDocument> accDocument;
-  rv = GetAccessibleDocument(getter_AddRefs(accDocument));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsDocAccessible *accDocument = GetDocAccessible();
   NS_ENSURE_TRUE(accDocument, NS_ERROR_FAILURE);
 
-  nsRefPtr<nsAccessNode> docAccessNode =
-    nsAccUtils::QueryAccessNode(accDocument);
-
-  nsIFrame *frame = docAccessNode->GetFrame();
+  nsIFrame *frame = accDocument->GetFrame();
   NS_ENSURE_STATE(frame);
 
   nsPresContext *presContext = frame->PresContext();
 
   nsIntRect screenRect = frame->GetScreenRectExternal();
   nsPoint offset(presContext->DevPixelsToAppUnits(aX - screenRect.x),
                  presContext->DevPixelsToAppUnits(aY - screenRect.y));
 
@@ -2895,17 +2889,17 @@ nsAccessible*
 nsAccessible::GetParent()
 {
   if (IsDefunct())
     return nsnull;
 
   if (mParent)
     return mParent;
 
-  nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
+  nsDocAccessible *docAccessible = GetDocAccessible();
   NS_ASSERTION(docAccessible, "No document accessible for valid accessible!");
 
   if (!docAccessible)
     return nsnull;
 
   nsCOMPtr<nsIAccessible> parent;
   docAccessible->GetAccessibleInParentChain(mDOMNode, PR_TRUE,
                                             getter_AddRefs(parent));
--- a/accessible/src/base/nsApplicationAccessible.cpp
+++ b/accessible/src/base/nsApplicationAccessible.cpp
@@ -50,17 +50,18 @@
 nsApplicationAccessible::nsApplicationAccessible() :
   nsAccessibleWrap(nsnull, nsnull)
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
-NS_IMPL_ISUPPORTS_INHERITED0(nsApplicationAccessible, nsAccessible)
+NS_IMPL_ISUPPORTS_INHERITED1(nsApplicationAccessible, nsAccessible,
+                             nsIAccessibleApplication)
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessible
 
 NS_IMETHODIMP
 nsApplicationAccessible::GetName(nsAString& aName)
 {
   aName.Truncate();
@@ -120,27 +121,93 @@ nsApplicationAccessible::GetParent(nsIAc
 {
   NS_ENSURE_ARG_POINTER(aAccessible);
   *aAccessible = nsnull;
 
   return IsDefunct() ? NS_ERROR_FAILURE : NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsIAccessibleApplication
+
+NS_IMETHODIMP
+nsApplicationAccessible::GetAppName(nsAString& aName)
+{
+  aName.Truncate();
+
+  if (!mAppInfo)
+    return NS_ERROR_FAILURE;
+
+  nsCAutoString cname;
+  nsresult rv = mAppInfo->GetName(cname);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AppendUTF8toUTF16(cname, aName);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsApplicationAccessible::GetAppVersion(nsAString& aVersion)
+{
+  aVersion.Truncate();
+
+  if (!mAppInfo)
+    return NS_ERROR_FAILURE;
+
+  nsCAutoString cversion;
+  nsresult rv = mAppInfo->GetVersion(cversion);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AppendUTF8toUTF16(cversion, aVersion);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsApplicationAccessible::GetPlatformName(nsAString& aName)
+{
+  aName.AssignLiteral("Gecko");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsApplicationAccessible::GetPlatformVersion(nsAString& aVersion)
+{
+  aVersion.Truncate();
+
+  if (!mAppInfo)
+    return NS_ERROR_FAILURE;
+
+  nsCAutoString cversion;
+  nsresult rv = mAppInfo->GetPlatformVersion(cversion);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AppendUTF8toUTF16(cversion, aVersion);
+  return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode public methods
 
 PRBool
 nsApplicationAccessible::IsDefunct()
 {
   return nsAccessibilityService::gIsShutdown;
 }
 
 nsresult
 nsApplicationAccessible::Init()
 {
+  mAppInfo = do_GetService("@mozilla.org/xre/app-info;1");
+  return NS_OK;
+}
+
+nsresult
+nsApplicationAccessible::Shutdown()
+{
+  mAppInfo = nsnull;
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible public methods
 
 nsresult
 nsApplicationAccessible::GetRoleInternal(PRUint32 *aRole)
--- a/accessible/src/base/nsApplicationAccessible.h
+++ b/accessible/src/base/nsApplicationAccessible.h
@@ -39,47 +39,55 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NS_APPLICATION_ACCESSIBLE_H__
 #define __NS_APPLICATION_ACCESSIBLE_H__
 
 #include "nsAccessibleWrap.h"
+#include "nsIAccessibleApplication.h"
+
 #include "nsIMutableArray.h"
+#include "nsIXULAppInfo.h"
 
 /**
  * nsApplicationAccessible is for the whole application of Mozilla.
  * Only one instance of nsApplicationAccessible exists for one Mozilla instance.
  * And this one should be created when Mozilla Startup (if accessibility
  * feature has been enabled) and destroyed when Mozilla Shutdown.
  *
  * All the accessibility objects for toplevel windows are direct children of
  * the nsApplicationAccessible instance.
  */
 
-class nsApplicationAccessible: public nsAccessibleWrap
+class nsApplicationAccessible: public nsAccessibleWrap,
+                               public nsIAccessibleApplication
 {
 public:
   nsApplicationAccessible();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetName(nsAString& aName);
   NS_IMETHOD GetDescription(nsAString& aValue);
   NS_IMETHOD GetRole(PRUint32 *aRole);
   NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
 
   NS_IMETHOD GetParent(nsIAccessible **aAccessible);
 
+  // nsIAccessibleApplication
+  NS_DECL_NSIACCESSIBLEAPPLICATION
+
   // nsAccessNode
   virtual PRBool IsDefunct();
   virtual nsresult Init();
+  virtual nsresult Shutdown();
 
   // nsAccessible
   virtual nsresult GetRoleInternal(PRUint32 *aRole);
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
   virtual nsAccessible* GetParent();
 
   virtual void InvalidateChildren();
 
@@ -88,12 +96,15 @@ public:
   virtual nsresult RemoveRootAccessible(nsIAccessible *aRootAccWrap);
 
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
                                            nsresult *aError = nsnull);
+
+private:
+  nsCOMPtr<nsIXULAppInfo> mAppInfo;
 };
 
 #endif
 
--- a/accessible/src/base/nsCaretAccessible.cpp
+++ b/accessible/src/base/nsCaretAccessible.cpp
@@ -45,17 +45,16 @@
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIFrame.h"
 #include "nsIPresShell.h"
 #include "nsRootAccessible.h"
 #include "nsISelectionPrivate.h"
 #include "nsISelection2.h"
 #include "nsServiceManagerUtils.h"
-#include "nsIViewManager.h"
 
 class nsIWidget;
 
 NS_IMPL_ISUPPORTS1(nsCaretAccessible, nsISelectionListener)
   
 nsCaretAccessible::nsCaretAccessible( nsRootAccessible *aRootAccessible):
 mLastCaretOffset(-1), mRootAccessible(aRootAccessible)
 {
@@ -322,43 +321,37 @@ nsCaretAccessible::GetCaretRect(nsIWidge
   nsCOMPtr<nsIPresShell> presShell =
     nsCoreUtils::GetPresShellFor(lastNodeWithCaret);
   NS_ENSURE_TRUE(presShell, caretRect);
 
   nsRefPtr<nsCaret> caret;
   presShell->GetCaret(getter_AddRefs(caret));
   NS_ENSURE_TRUE(caret, caretRect);
 
-  PRBool isCollapsed;
-  nsIView *view;
   nsCOMPtr<nsISelection> caretSelection(do_QueryReferent(mLastUsedSelection));
   NS_ENSURE_TRUE(caretSelection, caretRect);
   
-  nsRect rect;
-  caret->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates, caretSelection,
-                             &rect, &isCollapsed, &view);
-  if (!view || rect.IsEmpty()) {
-    return nsIntRect(); // Return empty rect
-  }
-
   PRBool isVisible;
   caret->GetCaretVisible(&isVisible);
   if (!isVisible) {
     return nsIntRect();  // Return empty rect
   }
-  nsPoint offsetFromWidget;
-  *aOutWidget = view->GetNearestWidget(&offsetFromWidget);
-  NS_ENSURE_TRUE(*aOutWidget, nsIntRect());
+
+  nsRect rect;
+  nsIFrame* frame = caret->GetGeometry(caretSelection, &rect);
+  if (!frame || rect.IsEmpty()) {
+    return nsIntRect(); // Return empty rect
+  }
 
-  nsPresContext *presContext = presShell->GetPresContext();
-  NS_ENSURE_TRUE(presContext, nsIntRect());
+  nsPoint offset;
+  *aOutWidget = frame->GetWindowOffset(offset);
+  NS_ENSURE_TRUE(*aOutWidget, nsIntRect());
+  rect.MoveBy(offset);
 
-  rect += offsetFromWidget;
-  caretRect = rect.ToOutsidePixels(presContext->AppUnitsPerDevPixel());
-
+  caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
   caretRect.MoveBy((*aOutWidget)->WidgetToScreenOffset());
 
   // Correct for character size, so that caret always matches the size of the character
   // This is important for font size transitions, and is necessary because the Gecko caret uses the
   // previous character's size as the user moves forward in the text by character.
   PRInt32 charX, charY, charWidth, charHeight;
   if (NS_SUCCEEDED(mLastTextAccessible->GetCharacterExtents(mLastCaretOffset, &charX, &charY,
                                                             &charWidth, &charHeight,
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -33,17 +33,16 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsRootAccessible.h"
 #include "nsAccessibilityAtoms.h"
-#include "nsAccEvent.h"
 #include "nsAccessibilityService.h"
 #include "nsIMutableArray.h"
 #include "nsICommandManager.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocument.h"
 #include "nsIDOMAttr.h"
 #include "nsIDOMCharacterData.h"
@@ -1331,16 +1330,21 @@ void nsDocAccessible::ContentStatesChang
   if (0 == (aStateMask & NS_EVENT_STATE_CHECKED)) {
     return;
   }
 
   nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
   nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
 }
 
+void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
+                                            PRInt32 aStateMask)
+{
+}
+
 void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
                                               nsIContent* aContent,
                                               CharacterDataChangeInfo* aInfo)
 {
   FireTextChangeEventForText(aContent, aInfo, PR_FALSE);
 }
 
 void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
--- a/accessible/src/base/nsEventShell.cpp
+++ b/accessible/src/base/nsEventShell.cpp
@@ -324,16 +324,28 @@ nsAccEventQueue::CoalesceEvents()
         // We should do this in all cases even when tailEvent is hide event and
         // it's caused by DOM node removal because the rule can applied for
         // sibling event targets caused by style changes.
         ApplyToSiblings(0, tail, tailEvent->mEventType,
                         tailEvent->mNode, nsAccEvent::eAllowDupes);
       }
     } break; // case eCoalesceFromSameSubtree
 
+    case nsAccEvent::eCoalesceFromSameDocument:
+    {
+      for (PRInt32 index = 0; index < tail; index ++) {
+        nsAccEvent* thisEvent = mEvents[index];
+        if (thisEvent->mEventType == tailEvent->mEventType &&
+            thisEvent->mEventRule == tailEvent->mEventRule &&
+            thisEvent->GetDocAccessible() == tailEvent->GetDocAccessible()) {
+          thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
+        }
+      }
+    } break; // case eCoalesceFromSameDocument
+
     case nsAccEvent::eRemoveDupes:
     {
       // Check for repeat events.
       for (PRInt32 index = 0; index < tail; index ++) {
         nsAccEvent* accEvent = mEvents[index];
         if (accEvent->mEventType == tailEvent->mEventType &&
             accEvent->mEventRule == tailEvent->mEventRule &&
             accEvent->mNode == tailEvent->mNode) {
--- a/accessible/src/base/nsEventShell.h
+++ b/accessible/src/base/nsEventShell.h
@@ -34,18 +34,22 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsEventShell_H_
 #define _nsEventShell_H_
 
+#include "nsAccEvent.h"
+
+#include "nsAutoPtr.h"
 #include "nsCoreUtils.h"
-#include "nsAccEvent.h"
+
+class nsIPersistentProperties;
 
 /**
  * Used for everything about events.
  */
 class nsEventShell
 {
 public:
 
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -32,17 +32,16 @@
  * 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 ***** */
 
 // NOTE: alphabetically ordered
 #include "nsAccessibilityService.h"
-#include "nsAccEvent.h"
 #include "nsApplicationAccessibleWrap.h"
 
 #include "nsHTMLSelectAccessible.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDOMDocument.h"
@@ -513,36 +512,20 @@ nsRootAccessible::FireAccessibleFocusEve
   }
 
   NS_IF_RELEASE(gLastFocusedNode);
   gLastFocusedNode = finalFocusNode;
   NS_IF_ADDREF(gLastFocusedNode);
 
   gLastFocusedFrameType = (focusFrame && focusFrame->GetStyleVisibility()->IsVisible()) ? focusFrame->GetType() : 0;
 
-  nsCOMPtr<nsIAccessibleDocument> docAccessible = do_QueryInterface(finalFocusAccessible);
-  if (docAccessible) {
-    // Doc is gaining focus, but actual focus may be on an element within document
-    nsCOMPtr<nsIDOMNode> realFocusedNode = GetCurrentFocus();
-    if ((realFocusedNode != aNode || realFocusedNode == mDOMNode) &&
-        !(nsAccUtils::ExtendedState(finalFocusAccessible) &
-                    nsIAccessibleStates::EXT_STATE_EDITABLE)) {
-      // Suppress document focus, because real DOM focus will be fired next,
-      // except in the case of editable documents because we can't rely on a
-      // followup focus event for an element in an editable document.
-      // Make sure we never fire focus for the nsRootAccessible (mDOMNode)
-
-      // XXX todo dig deeper on editor focus inconsistency in bug 526313
-
-      return PR_FALSE;
-    }
-  }
-
+  // Coalesce focus events from the same document, because DOM focus event might
+  // be fired for the document node and then for the focused DOM element.
   FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
-                             finalFocusNode, nsAccEvent::eRemoveDupes,
+                             finalFocusNode, nsAccEvent::eCoalesceFromSameDocument,
                              aIsAsynch, aIsFromUserInput);
 
   return PR_TRUE;
 }
 
 void
 nsRootAccessible::FireCurrentFocusEvent()
 {
@@ -562,17 +545,31 @@ nsRootAccessible::FireCurrentFocusEvent(
                                            getter_AddRefs(event))) &&
         NS_SUCCEEDED(event->InitEvent(NS_LITERAL_STRING("focus"), PR_TRUE, PR_TRUE))) {
       // Get the target node we really want for the event.
       nsIAccessibilityService* accService = GetAccService();
       if (accService) {
         nsCOMPtr<nsIDOMNode> targetNode;
         accService->GetRelevantContentNodeFor(focusedNode,
                                             getter_AddRefs(targetNode));
+
         if (targetNode) {
+          // If the focused element is document element or HTML body element
+          // then simulate the focus event for the document.
+          nsCOMPtr<nsIContent> targetContent(do_QueryInterface(targetNode));
+          if (targetContent) {
+            nsCOMPtr<nsIDOMNode> document =
+              do_QueryInterface(targetContent->GetOwnerDoc());
+            if (targetContent == nsCoreUtils::GetRoleContent(document)) {
+              HandleEventWithTarget(event, document);
+              return;
+            }
+          }
+
+          // Otherwise simulate the focus event for currently focused node.
           HandleEventWithTarget(event, targetNode);
         }
       }
     }
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -938,36 +935,36 @@ void nsRootAccessible::GetTargetNode(nsI
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode
 
 nsresult
 nsRootAccessible::Init()
 {
-  nsRefPtr<nsApplicationAccessibleWrap> root = GetApplicationAccessible();
-  NS_ENSURE_STATE(root);
+  nsApplicationAccessible *applicationAcc = GetApplicationAccessible();
+  NS_ENSURE_STATE(applicationAcc);
 
-  root->AddRootAccessible(this);
+  applicationAcc->AddRootAccessible(this);
 
   return nsDocAccessibleWrap::Init();
 }
 
 nsresult
 nsRootAccessible::Shutdown()
 {
   // Called manually or by nsAccessNode::LastRelease()
   if (!mWeakShell) {
     return NS_OK;  // Already shutdown
   }
 
-  nsRefPtr<nsApplicationAccessibleWrap> root = GetApplicationAccessible();
-  NS_ENSURE_STATE(root);
+  nsApplicationAccessible *applicationAcc = GetApplicationAccessible();
+  NS_ENSURE_STATE(applicationAcc);
 
-  root->RemoveRootAccessible(this);
+  applicationAcc->RemoveRootAccessible(this);
 
   mCurrentARIAMenubar = nsnull;
 
   return nsDocAccessibleWrap::Shutdown();
 }
 
 // nsRootAccessible protected member
 already_AddRefed<nsIDocShellTreeItem>
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -33,17 +33,19 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHTMLTableAccessible.h"
-#include "nsAccessibilityAtoms.h"
+
+#include "nsDocAccessible.h"
+
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentRange.h"
 #include "nsIDOMRange.h"
 #include "nsISelection2.h"
 #include "nsISelectionPrivate.h"
 #include "nsINameSpaceManager.h"
 #include "nsIAccessibilityService.h"
@@ -1369,17 +1371,17 @@ nsHTMLTableAccessible::IsProbablyForLayo
 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { *aIsProbablyForLayout = isLayout; return NS_OK; }
 #endif
 
   *aIsProbablyForLayout = PR_FALSE;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIAccessible> docAccessible = do_QueryInterface(nsCOMPtr<nsIAccessibleDocument>(GetDocAccessible()));
+  nsDocAccessible *docAccessible = GetDocAccessible();
   if (docAccessible) {
     PRUint32 state, extState;
     docAccessible->GetState(&state, &extState);
     if (extState & nsIAccessibleStates::EXT_STATE_EDITABLE) {  // Need to see all elements while document is being edited
       RETURN_LAYOUT_ANSWER(PR_FALSE, "In editable document");
     }
   }
 
--- a/accessible/src/html/nsHTMLTextAccessible.cpp
+++ b/accessible/src/html/nsHTMLTextAccessible.cpp
@@ -33,18 +33,19 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHTMLTextAccessible.h"
-#include "nsIAccessibleDocument.h"
-#include "nsIAccessibleEvent.h"
+
+#include "nsDocAccessible.h"
+
 #include "nsIFrame.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsISelection.h"
 #include "nsISelectionController.h"
 #include "nsComponentManagerUtils.h"
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -81,18 +82,17 @@ nsHTMLTextAccessible::GetRoleInternal(PR
 }
 
 nsresult
 nsHTMLTextAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
 {
   nsresult rv = nsTextAccessible::GetStateInternal(aState, aExtraState);
   NS_ENSURE_A11Y_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIAccessible> docAccessible = 
-    do_QueryInterface(nsCOMPtr<nsIAccessibleDocument>(GetDocAccessible()));
+  nsDocAccessible *docAccessible = GetDocAccessible();
   if (docAccessible) {
      PRUint32 state, extState;
      docAccessible->GetState(&state, &extState);
      if (0 == (extState & nsIAccessibleStates::EXT_STATE_EDITABLE)) {
        *aState |= nsIAccessibleStates::STATE_READONLY; // Links not focusable in editor
      }
   }
 
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -39,17 +39,16 @@
 
 #ifndef _nsHyperTextAccessible_H_
 #define _nsHyperTextAccessible_H_
 
 #include "nsAccessibleWrap.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleHyperText.h"
 #include "nsIAccessibleEditableText.h"
-#include "nsAccEvent.h"
 #include "nsTextAttrs.h"
 
 #include "nsFrameSelection.h"
 #include "nsISelectionController.h"
 
 enum EGetTextType { eGetBefore=-1, eGetAt=0, eGetAfter=1 };
 
 // This character marks where in the text returned via nsIAccessibleText(),
--- a/accessible/src/mac/nsAccessibleWrap.mm
+++ b/accessible/src/mac/nsAccessibleWrap.mm
@@ -31,19 +31,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsAccessibleWrap.h"
-#include "nsIAccessibleDocument.h"
-#include "nsIAccessibleText.h"
+#include "nsDocAccessible.h"
 #include "nsObjCExceptions.h"
 
 #import "nsRoleMap.h"
 
 #import "mozAccessibleWrapper.h"
 #import "mozAccessible.h"
 #import "mozActionElements.h"
 #import "mozTextAccessible.h"
@@ -87,18 +85,20 @@ nsAccessibleWrap::GetNativeInterface (vo
   return NS_ERROR_FAILURE;
 }
 
 // get the native NSWindow we reside in.
 void
 nsAccessibleWrap::GetNativeWindow (void **aOutNativeWindow)
 {
   *aOutNativeWindow = nsnull;
-  nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
-  docAccessible->GetWindowHandle (aOutNativeWindow);
+
+  nsDocAccessible *docAcc = GetDocAccessible();
+  if (docAcc)
+    docAcc->GetWindowHandle (aOutNativeWindow);
 }
 
 // overridden in subclasses to create the right kind of object. by default we create a generic
 // 'mozAccessible' node.
 objc_class*
 nsAccessibleWrap::GetNativeType () 
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
--- a/accessible/src/msaa/nsAccessNodeWrap.cpp
+++ b/accessible/src/msaa/nsAccessNodeWrap.cpp
@@ -140,19 +140,21 @@ nsAccessNodeWrap::QueryService(REFGUID g
   if (guidService != IID_ISimpleDOMNode &&
       guidService != IID_SimpleDOMDeprecated &&
       guidService != IID_IAccessible &&  guidService != IID_IAccessible2 &&
       guidService != IID_IAccessibleApplication)
     return E_INVALIDARG;
 
   // Can get to IAccessibleApplication from any node via QS
   if (iid == IID_IAccessibleApplication) {
-    nsRefPtr<nsApplicationAccessibleWrap> app =
-      GetApplicationAccessible();
-    nsresult rv = app->QueryNativeInterface(iid, ppv);
+    nsApplicationAccessible *applicationAcc = GetApplicationAccessible();
+    if (!applicationAcc)
+      return E_NOINTERFACE;
+
+    nsresult rv = applicationAcc->QueryNativeInterface(iid, ppv);
     return NS_SUCCEEDED(rv) ? S_OK : E_NOINTERFACE;
   }
 
   /**
    * To get an ISimpleDOMNode, ISimpleDOMDocument, ISimpleDOMText
    * or any IAccessible2 interface on should use IServiceProvider like this:
    * -----------------------------------------------------------------------
    * ISimpleDOMDocument *pAccDoc = NULL;
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -1777,18 +1777,17 @@ nsAccessibleWrap::GetHWNDFor(nsIAccessib
       // combo box, we need to use an ensure that we never fire an event with
       // an HWND for a hidden window.
       hWnd = (HWND)frame->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
     }
   }
 
   if (!hWnd) {
     void* handle = nsnull;
-    nsCOMPtr<nsIAccessibleDocument> accessibleDoc;
-    accessNode->GetAccessibleDocument(getter_AddRefs(accessibleDoc));
+    nsDocAccessible *accessibleDoc = accessNode->GetDocAccessible();
     if (!accessibleDoc)
       return 0;
 
     accessibleDoc->GetWindowHandle(&handle);
     hWnd = (HWND)handle;
   }
 
   return hWnd;
--- a/accessible/src/msaa/nsApplicationAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsApplicationAccessibleWrap.cpp
@@ -39,133 +39,128 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsApplicationAccessibleWrap.h"
 
 #include "AccessibleApplication_i.c"
 
 #include "nsServiceManagerUtils.h"
 
-nsIXULAppInfo* nsApplicationAccessibleWrap::sAppInfo = nsnull;
-
+////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 NS_IMPL_ISUPPORTS_INHERITED0(nsApplicationAccessibleWrap,
                              nsApplicationAccessible)
 
+////////////////////////////////////////////////////////////////////////////////
 // IUnknown
 
 STDMETHODIMP
 nsApplicationAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
 {
   *ppv = NULL;
 
   if (IID_IAccessibleApplication == iid) {
     *ppv = static_cast<IAccessibleApplication*>(this);
     (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
     return S_OK;
   }
 
   return nsAccessibleWrap::QueryInterface(iid, ppv);
 }
 
+////////////////////////////////////////////////////////////////////////////////
 // IAccessibleApplication
 
 STDMETHODIMP
 nsApplicationAccessibleWrap::get_appName(BSTR *aName)
 {
 __try {
   *aName = NULL;
 
-  if (!sAppInfo)
-    return E_FAIL;
-
-  nsCAutoString cname;
-  nsresult rv = sAppInfo->GetName(cname);
+  nsAutoString name;
+  nsresult rv = GetAppName(name);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
-  if (cname.IsEmpty())
+  if (name.IsEmpty())
     return S_FALSE;
 
-  NS_ConvertUTF8toUTF16 name(cname);
   *aName = ::SysAllocStringLen(name.get(), name.Length());
   return *aName ? S_OK : E_OUTOFMEMORY;
 
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
 nsApplicationAccessibleWrap::get_appVersion(BSTR *aVersion)
 {
 __try {
   *aVersion = NULL;
 
-  if (!sAppInfo)
-    return E_FAIL;
-
-  nsCAutoString cversion;
-  nsresult rv = sAppInfo->GetVersion(cversion);
+  nsAutoString version;
+  nsresult rv = GetAppVersion(version);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
-  if (cversion.IsEmpty())
+  if (version.IsEmpty())
     return S_FALSE;
 
-  NS_ConvertUTF8toUTF16 version(cversion);
   *aVersion = ::SysAllocStringLen(version.get(), version.Length());
   return *aVersion ? S_OK : E_OUTOFMEMORY;
 
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
 nsApplicationAccessibleWrap::get_toolkitName(BSTR *aName)
 {
 __try {
-  *aName = ::SysAllocString(L"Gecko");
+  nsAutoString name;
+  nsresult rv = GetPlatformName(name);
+  if (NS_FAILED(rv))
+    return GetHRESULT(rv);
+
+  if (name.IsEmpty())
+    return S_FALSE;
+
+  *aName = ::SysAllocStringLen(name.get(), name.Length());
   return *aName ? S_OK : E_OUTOFMEMORY;
 
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
 nsApplicationAccessibleWrap::get_toolkitVersion(BSTR *aVersion)
 {
 __try {
   *aVersion = NULL;
 
-  if (!sAppInfo)
-    return E_FAIL;
-
-  nsCAutoString cversion;
-  nsresult rv = sAppInfo->GetPlatformVersion(cversion);
+  nsAutoString version;
+  nsresult rv = GetPlatformVersion(version);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
-  if (cversion.IsEmpty())
+  if (version.IsEmpty())
     return S_FALSE;
 
-  NS_ConvertUTF8toUTF16 version(cversion);
   *aVersion = ::SysAllocStringLen(version.get(), version.Length());
   return *aVersion ? S_OK : E_OUTOFMEMORY;
 
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
-// nsApplicationAccessibleWrap
+////////////////////////////////////////////////////////////////////////////////
+// nsApplicationAccessibleWrap public static
 
 void
 nsApplicationAccessibleWrap::PreCreate()
 {
-  nsresult rv = CallGetService("@mozilla.org/xre/app-info;1", &sAppInfo);
-  NS_ASSERTION(NS_SUCCEEDED(rv), "No XUL application info service");
 }
 
 void
 nsApplicationAccessibleWrap::Unload()
 {
-  NS_IF_RELEASE(sAppInfo);
 }
 
--- a/accessible/src/msaa/nsApplicationAccessibleWrap.h
+++ b/accessible/src/msaa/nsApplicationAccessibleWrap.h
@@ -40,18 +40,16 @@
 
 #ifndef __NS_APPLICATION_ACCESSIBLE_WRAP_H__
 #define __NS_APPLICATION_ACCESSIBLE_WRAP_H__
 
 #include "nsApplicationAccessible.h"
 
 #include "AccessibleApplication.h"
 
-#include "nsIXULAppInfo.h"
-
 class nsApplicationAccessibleWrap: public nsApplicationAccessible,
                                    public IAccessibleApplication
 {
 public:
   // nsISupporst
   NS_DECL_ISUPPORTS_INHERITED
 
   // IUnknown
@@ -68,15 +66,12 @@ public:
       /* [retval][out] */ BSTR *name);
 
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_toolkitVersion(
           /* [retval][out] */ BSTR *version);
 
 public:
   static void PreCreate();
   static void Unload();
-
-private:
-  static nsIXULAppInfo* sAppInfo;
 };
 
 #endif
 
--- a/accessible/src/msaa/nsDocAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsDocAccessibleWrap.cpp
@@ -278,30 +278,27 @@ STDMETHODIMP nsDocAccessibleWrap::get_ac
 
 struct nsSearchAccessibleInCacheArg
 {
   nsRefPtr<nsAccessNode> mAccessNode;
   void *mUniqueID;
 };
 
 static PLDHashOperator
-SearchAccessibleInCache(const void* aKey, nsAccessNode* aAccessNode,
+SearchAccessibleInCache(const void* aKey, nsDocAccessible* aDocAccessible,
                         void* aUserArg)
 {
-  nsCOMPtr<nsIAccessibleDocument> accessibleDoc(do_QueryInterface(aAccessNode));
-  NS_ASSERTION(accessibleDoc,
+  NS_ASSERTION(aDocAccessible,
                "No doc accessible for the object in doc accessible cache!");
 
-  nsRefPtr<nsDocAccessible> docAccessible =
-    nsAccUtils::QueryObject<nsDocAccessible>(accessibleDoc);
-  if (docAccessible) {
+  if (aDocAccessible) {
     nsSearchAccessibleInCacheArg* arg =
       static_cast<nsSearchAccessibleInCacheArg*>(aUserArg);
     nsAccessNode* accessNode =
-      docAccessible->GetCachedAccessNode(arg->mUniqueID);
+      aDocAccessible->GetCachedAccessNode(arg->mUniqueID);
     if (accessNode) {
       arg->mAccessNode = accessNode;
       return PL_DHASH_STOP;
     }
   }
 
   return PL_DHASH_NEXT;
 }
--- a/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
+++ b/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
@@ -32,17 +32,16 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHTMLWin32ObjectAccessible.h"
-#include "nsAccessibleWrap.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLWin32ObjectOwnerAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLWin32ObjectOwnerAccessible::
   nsHTMLWin32ObjectOwnerAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell,
                                    void* aHwnd) :
--- a/accessible/src/msaa/nsTextAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsTextAccessibleWrap.cpp
@@ -34,17 +34,19 @@
  * 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 ***** */
 
 // NOTE: alphabetically ordered
 #include "nsTextAccessibleWrap.h"
 #include "ISimpleDOMText_i.c"
-#include "nsIAccessibleDocument.h"
+
+#include "nsDocAccessible.h"
+
 #include "nsIFontMetrics.h"
 #include "nsIFrame.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIRenderingContext.h"
 #include "nsIComponentManager.h"
 
 // --------------------------------------------------------
@@ -115,21 +117,21 @@ STDMETHODIMP nsTextAccessibleWrap::get_c
 __try {
   *aX = *aY = *aWidth = *aHeight = 0;
   nscoord x, y, width, height, docX, docY, docWidth, docHeight;
   HRESULT rv = get_unclippedSubstringBounds(aStartIndex, aEndIndex, &x, &y, &width, &height);
   if (FAILED(rv)) {
     return rv;
   }
 
-  nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
-  nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(docAccessible));
-  NS_ASSERTION(accessible, "There must always be a doc accessible, but there isn't");
+  nsDocAccessible *docAccessible = GetDocAccessible();
+  NS_ASSERTION(docAccessible,
+               "There must always be a doc accessible, but there isn't. Crash!");
 
-  accessible->GetBounds(&docX, &docY, &docWidth, &docHeight);
+  docAccessible->GetBounds(&docX, &docY, &docWidth, &docHeight);
 
   nsIntRect unclippedRect(x, y, width, height);
   nsIntRect docRect(docX, docY, docWidth, docHeight);
   nsIntRect clippedRect;
 
   clippedRect.IntersectRect(unclippedRect, docRect);
 
   *aX = clippedRect.x;
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -310,17 +310,18 @@ nsXFormsEditableAccessible::GetAssociate
 
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsSelectableAccessible,
                              nsXFormsEditableAccessible,
                              nsIAccessibleSelectable)
 
 nsXFormsSelectableAccessible::
   nsXFormsSelectableAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell) :
-  nsXFormsEditableAccessible(aNode, aShell)
+  nsXFormsEditableAccessible(aNode, aShell),
+  mIsSelect1Element(nsnull)
 {
   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
   if (!content)
     return;
 
   mIsSelect1Element =
     content->NodeInfo()->Equals(nsAccessibilityAtoms::select1);
 }
--- a/accessible/src/xul/nsXULTextAccessible.cpp
+++ b/accessible/src/xul/nsXULTextAccessible.cpp
@@ -35,17 +35,16 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 // NOTE: alphabetically ordered
 #include "nsAccessibilityAtoms.h"
 #include "nsCoreUtils.h"
-#include "nsAccUtils.h"
 #include "nsBaseWidgetAccessible.h"
 #include "nsIDOMXULDescriptionElement.h"
 #include "nsINameSpaceManager.h"
 #include "nsString.h"
 #include "nsXULTextAccessible.h"
 #include "nsNetUtil.h"
 
 /**
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -1209,23 +1209,24 @@ nsXULTreeItemAccessible::Shutdown()
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeItemAccessible: nsAccessible implementation
 
 nsresult
 nsXULTreeItemAccessible::GetRoleInternal(PRUint32 *aRole)
 {
-  nsCOMPtr<nsITreeColumn> column =
-    nsCoreUtils::GetFirstSensibleColumn(mTree);
+  nsCOMPtr<nsITreeColumns> columns;
+  mTree->GetColumns(getter_AddRefs(columns));
+  NS_ENSURE_STATE(columns);
 
-  PRBool isPrimary = PR_FALSE;
-  column->GetPrimary(&isPrimary);
+  nsCOMPtr<nsITreeColumn> primaryColumn;
+  columns->GetPrimaryColumn(getter_AddRefs(primaryColumn));
 
-  *aRole = isPrimary ?
+  *aRole = primaryColumn ?
     nsIAccessibleRole::ROLE_OUTLINEITEM :
     nsIAccessibleRole::ROLE_LISTITEM;
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeItemAccessible: nsXULTreeItemAccessibleBase implementation
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -79,24 +79,24 @@ include $(topsrcdir)/config/rules.mk
 		test_bug420863.html \
 	$(warning   test_childAtPoint.html temporarily disabled) \
 	$(warning	test_childAtPoint.xul temporarily disabled) \
 		test_descr.html \
 		test_editabletext_1.html \
 		test_editabletext_2.html \
 		test_elm_listbox.xul \
 	$(warning   test_elm_media.html temporarily disabled) \
+		test_elm_nsApplicationAcc.html \
 		test_elm_plugin.html \
 		test_invalidate_accessnode.html \
 		test_name.html \
 		test_name.xul \
 		test_name_button.html \
 		test_name_link.html \
 		test_name_markup.html \
-		test_name_nsApplicationAcc.html \
 		test_name_nsRootAcc.xul \
 	$(warning test_nsIAccessible_comboboxes.xul temporarily disabled) \
  		test_nsIAccessible_selects.html \
 		test_nsIAccessible_focus.html \
 		test_nsIAccessibleDocument.html \
 		test_nsIAccessibleHyperLink.html \
 		test_nsIAccessibleHyperLink.xul \
 		test_nsIAccessibleHyperText.html \
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -17,16 +17,17 @@ const nsIAccessibleRelation = Components
 
 const nsIAccessNode = Components.interfaces.nsIAccessNode;
 const nsIAccessible = Components.interfaces.nsIAccessible;
 
 const nsIAccessibleCoordinateType =
       Components.interfaces.nsIAccessibleCoordinateType;
 
 const nsIAccessibleDocument = Components.interfaces.nsIAccessibleDocument;
+const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication;
 
 const nsIAccessibleText = Components.interfaces.nsIAccessibleText;
 const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableText;
 
 const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink;
 const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText;
 
 const nsIAccessibleImage = Components.interfaces.nsIAccessibleImage;
@@ -256,54 +257,26 @@ function isAccessible(aAccOrElmOrID, aIn
 }
 
 /**
  * Return root accessible for the given identifier.
  */
 function getRootAccessible(aAccOrElmOrID)
 {
   var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document);
-  while (acc) {
-    var parent = acc.parent;
-    if (parent && !parent.parent)
-      return acc;
-
-    acc = parent;
-  }
-
-  return null;
+  return acc ? acc.rootDocument.QueryInterface(nsIAccessible) : null;
 }
 
 /**
  * Return application accessible.
  */
 function getApplicationAccessible()
 {
-  var acc = getAccessible(document), parent = null;
-  while (acc) {
-
-    try {
-      parent = acc.parent;
-    } catch (e) {
-      ok(false, "Can't get a parent for " + prettyName(acc));
-      return null;
-    }
-
-    if (!parent) {
-      if (acc.role == ROLE_APP_ROOT)
-        return acc;
-
-      ok(false, "No application accessible!");
-      return null;
-    }
-
-    acc = parent;
-  }
-
-  return null;
+  return gAccRetrieval.getApplicationAccessible().
+    QueryInterface(nsIAccessibleApplication);
 }
 
 /**
  * Run through accessible tree of the given identifier so that we ensure
  * accessible tree is created.
  */
 function ensureAccessibleTree(aAccOrElmOrID)
 {
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -594,28 +594,48 @@ function sequence()
  */
 function synthClick(aNodeOrID, aChecker, aEventType)
 {
   this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
 
   this.invoke = function synthClick_invoke()
   {
     // Scroll the node into view, otherwise synth click may fail.
-    this.DOMNode.scrollIntoView(true);
+    if (this.DOMNode instanceof nsIDOMNSHTMLElement)
+      this.DOMNode.scrollIntoView(true);
 
     synthesizeMouse(this.DOMNode, 1, 1, {});
   }
 
   this.getID = function synthClick_getID()
   {
     return prettyName(aNodeOrID) + " click"; 
   }
 }
 
 /**
+ * Mouse move invoker.
+ */
+function synthMouseMove(aNodeOrID, aChecker, aEventType)
+{
+  this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
+
+  this.invoke = function synthMouseMove_invoke()
+  {
+    synthesizeMouse(this.DOMNode, 1, 1, { type: "mousemove" });
+    synthesizeMouse(this.DOMNode, 2, 2, { type: "mousemove" });
+  }
+
+  this.getID = function synthMouseMove_getID()
+  {
+    return prettyName(aNodeOrID) + " mouse move"; 
+  }
+}
+
+/**
  * General key press invoker.
  */
 function synthKey(aNodeOrID, aKey, aArgs, aChecker, aEventType)
 {
   this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
 
   this.invoke = function synthKey_invoke()
   {
@@ -715,16 +735,35 @@ function synthFocus(aNodeOrID, aChecker,
 
   this.getID = function synthFocus_getID() 
   { 
     return prettyName(aNodeOrID) + " focus";
   }
 }
 
 /**
+ * Focus invoker. Focus the HTML body of content document of iframe.
+ */
+function synthFocusOnFrame(aNodeOrID, aChecker, aEventType)
+{
+  this.__proto__ = new synthAction(getNode(aNodeOrID).contentDocument,
+                                   aChecker, aEventType);
+  
+  this.invoke = function synthFocus_invoke()
+  {
+    this.DOMNode.body.focus();
+  }
+  
+  this.getID = function synthFocus_getID() 
+  { 
+    return prettyName(aNodeOrID) + " frame document focus";
+  }
+}
+
+/**
  * Select all invoker.
  */
 function synthSelectAll(aNodeOrID, aChecker, aEventType)
 {
   this.__proto__ = new synthAction(aNodeOrID, aChecker, aEventType);
 
   this.invoke = function synthSelectAll_invoke()
   {
--- a/accessible/tests/mochitest/events/test_focus.html
+++ b/accessible/tests/mochitest/events/test_focus.html
@@ -30,68 +30,83 @@
       }
 
       this.getID = function openCloseDialog_getID()
       {
         return "Open close dialog on " + prettyName(aID);
       }
     }
 
-    function focusDocument(aFrameID)
+    function focusElmWhileSubdocIsFocused(aID)
     {
-      this.DOMNode = getNode(aFrameID).contentDocument;
+      this.DOMNode = getNode(aID);
 
-      this.invoke = function focusDocument_invoke()
+      this.invoke = function focusElmWhileSubdocIsFocused_invoke()
       {
-        this.DOMNode.body.focus();
+        this.DOMNode.focus();
       }
 
-      this.getID = function focusDocument_getID()
+      this.eventSeq = [
+        new invokerChecker(EVENT_FOCUS, this.DOMNode)
+      ];
+
+      this.unexpectedEventSeq = [
+        new invokerChecker(EVENT_FOCUS, this.DOMNode.ownerDocument)
+      ];
+
+      this.getID = function focusElmWhileSubdocIsFocused_getID()
       {
-        return "Focus document of " + prettyName(aFrameID);
+        return "Focus element while subdocument is focused " + prettyName(aID);
       }
     }
 
     /**
      * Do tests.
      */
 
     // gA11yEventDumpID = "eventdump"; // debug stuff
 
     var gQueue = null;
 
     function doTests()
     {
-      gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
+      gQueue = new eventQueue(EVENT_FOCUS);
 
       gQueue.push(new synthFocus("editablearea"));
       gQueue.push(new synthFocus("textbox"));
 
       gQueue.push(new synthFocus("button"));
       gQueue.push(new openCloseDialog("button"));
 
       var frameNode = getNode("editabledoc");
-      gQueue.push(new focusDocument(frameNode));
+      gQueue.push(new synthFocusOnFrame(frameNode));
       gQueue.push(new openCloseDialog(frameNode.contentDocument));
 
+      gQueue.push(new focusElmWhileSubdocIsFocused("button"));
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=551679"
      title="focus is not fired for focused document when switching between windows">
     Mozilla Bug 551679
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=352220"
+     title=" Inconsistent focus events when returning to a document frame">
+    Mozilla Bug 352220
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="editablearea" contentEditable="true">editable area</div>
   <input id="textbox">
   <button id="button">button</button>
--- a/accessible/tests/mochitest/events/test_focus.xul
+++ b/accessible/tests/mochitest/events/test_focus.xul
@@ -5,69 +5,97 @@
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="Accessible focus event testing">
 
   <script type="application/javascript" 
           src="chrome://mochikit/content/MochiKit/packed.js" />
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
 
   <script type="application/javascript"
           src="chrome://mochikit/content/a11y/accessible/common.js" />
   <script type="application/javascript"
           src="chrome://mochikit/content/a11y/accessible/events.js" />
 
   <script type="application/javascript">
     /**
-     * Focus invoker.
+     * Click menu item invoker.
      */
-    function synthFocus(aNodeOrID)
+    function clickMenuItem(aNodeOrID, aFocusNodeOrID)
     {
-      this.DOMNode = getNode(aNodeOrID);
+      this.DOMNode = getNode(aFocusNodeOrID);
 
-      this.invoke = function synthFocus_invoke()
+      this.invoke = function clickMenuItem_invoke()
       {
-        this.DOMNode.focus();
+        synthesizeMouse(getNode(aNodeOrID), 1, 1, {});
       }
 
-      this.getID = function synthFocus_getID() { return aNodeOrID + " focus"; }
+      this.getID = function clickMenuItem_getID()
+      {
+        return prettyName(aNodeOrID) + " click menu item";
+      }
     }
-
+    
     /**
      * Do tests.
      */
+
+    // gA11yEventDumpID = "eventdump"; // debug stuff
+
     var gQueue = null;
 
     function doTests()
     {
       // Test focus events.
       gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
 
       gQueue.push(new synthFocus("textbox"));
       gQueue.push(new synthFocus("scale"));
+      gQueue.push(new synthFocusOnFrame("editabledoc"));
+      gQueue.push(new synthClick("menu"));
+      gQueue.push(new synthMouseMove("menuitem"));
+      gQueue.push(new clickMenuItem("menuitem",
+                                    getNode("editabledoc").contentDocument));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 
   <hbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=492518"
          title="xul:slider accessible of xul:scale is accessible illegally">
         Mozilla Bug 492518
       </a>
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
+         title=" fire focus event on document accessible whenever the root or body element is focused">
+        Mozilla Bug 552368
+      </a>
       <p id="display"></p>
       <div id="content" style="display: none"></div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox flex="1">
       <textbox id="textbox" value="hello"/>
       <scale id="scale" min="0" max="9" value="5"/>
+      <menubar>
+        <menu id="menu" label="menu">
+          <menupopup>
+            <menuitem id="menuitem" label="menuitem"/>
+          </menupopup>
+        </menu>
+      </menubar>
+      <iframe id="editabledoc" src="focus.html"/>
+
+      <vbox id="eventdump"/>
     </vbox>
   </hbox>
 </window>
--- a/accessible/tests/mochitest/events/test_scroll.xul
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -31,16 +31,18 @@
   <script type="application/javascript">
   <![CDATA[
 
     ////////////////////////////////////////////////////////////////////////////
     // Hack to make xul:tabbrowser work
 
     const Ci = Components.interfaces;
 
+    Components.utils.import("resource://gre/modules/Services.jsm");
+
     var XULBrowserWindow = {
       isBusy: false,
       setOverLink: function (link, b) {
       }
     };
 
     ////////////////////////////////////////////////////////////////////////////
     // Tests
@@ -96,13 +98,22 @@
         <menu label="menu">
           <menupopup>
             <menuitem label="close window hook" id="menu_closeWindow"/>
             <menuitem label="close hook" id="menu_close"/>
           </menupopup>
         </menu>
       </menubar>
 
-      <tabbrowser type="content-primary" flex="1" id="tabBrowser"/>
+      <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
+            tabbrowser="tabBrowser"
+            flex="1"
+            setfocus="false">
+        <tab class="tabbrowser-tab" selected="true"/>
+      </tabs>
+      <tabbrowser id="tabBrowser"
+                  type="content-primary"
+                  tabcontainer="tabbrowser-tabs"
+                  flex="1"/>
     </vbox>
   </hbox>
 
 </window>
--- a/accessible/tests/mochitest/name_nsRootAcc_wnd.xul
+++ b/accessible/tests/mochitest/name_nsRootAcc_wnd.xul
@@ -18,16 +18,17 @@
       gOpenerWnd.SimpleTest.ok(aCond, aMsg);
     }
 
     function is(aExpected, aActual, aMsg) {
       gOpenerWnd.SimpleTest.is(aExpected, aActual, aMsg);
     }
 
     // Hack to make xul:tabbrowser work.
+    Components.utils.import("resource://gre/modules/Services.jsm");
     var XULBrowserWindow = {
       isBusy: false,
       setOverLink: function (link, b) {
       }
     };
     var gFindBar = {
       hidden: true
     };
@@ -104,11 +105,20 @@
     <menu label="menu">
       <menupopup>
         <menuitem label="close window hook" id="menu_closeWindow"/>
         <menuitem label="close hook" id="menu_close"/>
       </menupopup>
     </menu>
   </menubar>
 
-  <tabbrowser type="content-primary" flex="1" id="content"/>
+  <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
+        tabbrowser="content"
+        flex="1"
+        setfocus="false">
+    <tab class="tabbrowser-tab" selected="true"/>
+  </tabs>
+  <tabbrowser id="content"
+              type="content-primary"
+              tabcontainer="tabbrowser-tabs"
+              flex="1"/>
 
 </window>
rename from accessible/tests/mochitest/test_name_nsApplicationAcc.html
rename to accessible/tests/mochitest/test_elm_nsApplicationAcc.html
--- a/accessible/tests/mochitest/test_name_nsApplicationAcc.html
+++ b/accessible/tests/mochitest/test_elm_nsApplicationAcc.html
@@ -18,32 +18,43 @@
     function doTest()
     {
         var accessible = getApplicationAccessible();
         if (!accessible) {
           SimpleTest.finish();
           return;
         }
 
+        // nsIAccessible::name
         var bundleServ = Components.classes["@mozilla.org/intl/stringbundle;1"]
                          .getService(Components.interfaces.nsIStringBundleService);
         var bundle = bundleServ.createBundle("chrome://branding/locale/brand.properties");
 
         var applicationName = "";
 
         try {
             applicationName = bundle.GetStringFromName("brandShortName");
         }  catch(e) {
         }
 
         if (applicationName == "")
             applicationName = "Gecko based application";
 
         is (accessible.name, applicationName, "wrong application accessible name");
 
+        // nsIAccessibleApplication
+        var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
+          getService(Components.interfaces.nsIXULAppInfo);
+
+        is(accessible.appName, appInfo.name, "Wrong application name");
+        is(accessible.appVersion, appInfo.version, "Wrong application version");
+        is(accessible.platformName, "Gecko", "Wrong platform name");
+        is(accessible.platformVersion, appInfo.platformVersion,
+           "Wrong platform version");
+
         SimpleTest.finish();
     }
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
   </head>
   <body>
   <a target="_blank"
--- a/accessible/tests/mochitest/test_relations.html
+++ b/accessible/tests/mochitest/test_relations.html
@@ -97,17 +97,17 @@
 
       // 'embeds' relation for root accessible
       var docAcc = null;
       var parentOfDocAcc = null;
       var parentDocAcc = getAccessible(document);
       do {
         docAcc = parentDocAcc;
         parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.accessibleDocument,
+        parentDocAcc = getAccessible(parentOfDocAcc.document,
                                      [nsIAccessible]);
       } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
 
       testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
 
       // finish test
       SimpleTest.finish();
     }
--- a/accessible/tests/mochitest/test_relations.xul
+++ b/accessible/tests/mochitest/test_relations.xul
@@ -88,17 +88,17 @@
 
       // 'embeds' relation for root accessible
       var docAcc = null;
       var parentOfDocAcc = null;
       var parentDocAcc = getAccessible(document);
       do {
         docAcc = parentDocAcc;
         parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.accessibleDocument,
+        parentDocAcc = getAccessible(parentOfDocAcc.document,
                                      [nsIAccessible]);
       } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
 
       testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
 
       // 'labelled by'/'label for' relation for xul:goupbox and xul:label of
       // xul:caption
       var groupboxAcc = getAccessible("groupbox");
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -41,16 +41,17 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/tree
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
+		test_aria_globals.html \
 		test_button.xul \
 		test_colorpicker.xul \
 		test_combobox.xul \
 		test_filectrl.html \
 		test_formctrl.html \
 		test_formctrl.xul \
 		test_gencontent.html \
 		test_groupbox.xul \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_globals.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test Global ARIA States and Accessible Creation</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/role.js"></script>
+
+  <script type="application/javascript">
+    function doTest()
+    {
+      var accTree = {
+        role: ROLE_GROUPING,
+        children: [
+          {  role: ROLE_TEXT_CONTAINER }, // pawn
+          {  role: ROLE_TEXT_CONTAINER }, // aria-atomic
+          {  role: ROLE_TEXT_CONTAINER }, // aria-busy
+          {  role: ROLE_TEXT_CONTAINER }, // aria-controls
+          {  role: ROLE_TEXT_CONTAINER }, // aria-describedby
+          {  role: ROLE_TEXT_CONTAINER }, // aria-disabled
+          {  role: ROLE_TEXT_CONTAINER }, // aria-dropeffect
+          {  role: ROLE_TEXT_CONTAINER }, // aria-flowto
+          {  role: ROLE_TEXT_CONTAINER }, // aria-grabbed
+          {  role: ROLE_TEXT_CONTAINER }, // aria-haspopup
+          {  role: ROLE_TEXT_CONTAINER }, // aria-invalid
+          {  role: ROLE_TEXT_CONTAINER }, // aria-label
+          {  role: ROLE_TEXT_CONTAINER }, // aria-labelledby
+          {  role: ROLE_TEXT_CONTAINER }, // aria-live
+          {  role: ROLE_TEXT_CONTAINER }, // aria-owns
+          {  role: ROLE_TEXT_CONTAINER }  // aria-relevant
+        ]
+      };
+
+      testAccessibleTree("global_aria_states_and_props", accTree);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Update universal ARIA attribute support to latest spec"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=551978">
+    Mozilla Bug 551978
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <!-- Test that global aria states and properties are enough to cause the
+  creation of accessible objects -->
+  <div id="global_aria_states_and_props" role="group">
+    <span id="pawn"></span>
+    <span id="atomic" aria-atomic="true"></span>
+    <span id="busy" aria-busy="false"></span>
+    <span id="controls" aria-controls="pawn"></span>
+    <span id="describedby" aria-describedby="pawn"></span>
+    <span id="disabled" aria-disabled="true"></span>
+    <span id="dropeffect" aria-dropeffect="move"></span>
+    <span id="flowto" aria-flowto="pawn"></span>
+    <span id="grabbed" aria-grabbed="false"></span>
+    <span id="haspopup" aria-haspopup="false"></span>
+<!-- we use gecko to detect hidden-ness so we currently ignore aria-hidden
+     we might want to special case for aria-hidden="false", but we would need
+     a good case for it. aria-hidden is only in spec as an author aid to DOM
+     based AT -->
+    <span id="invalid" aria-invalid="false"></span>
+    <span id="label" aria-label="hi"></span>
+    <span id="labelledby" aria-labelledby="label"></span>
+    <span id="live" aria-live="polite"></span>
+    <span id="owns" aria-owns="pawn"></span>
+    <span id="relevant" aria-relevant="additions"></span>
+  </div>
+
+</body>
+</html>
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -21,35 +21,28 @@
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     const Ci = Components.interfaces;
 
     // Hack to make xul:tabbrowser work.
+    Components.utils.import("resource://gre/modules/Services.jsm");
     var XULBrowserWindow = {
       isBusy: false,
       setOverLink: function (link, b) {
       }
     };
     var gFindBar = {
       hidden: true
     };
 
     function doTest()
     {
-      if (LINUX) {
-        // XXX: bug 540529
-
-        todo(false, "Failure on Linux.");
-        SimpleTest.finish();
-        return;
-      }
-
       var tabBrowser = document.getElementById("tabbrowser");
 
       var progressListener =
       {
         onStateChange: function onStateChange(aWebProgress,
                                               aRequest,
                                               aStateFlags,
                                               aStatus)
@@ -62,71 +55,69 @@
       tabBrowser.addProgressListener(progressListener,
                                      Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
 
       tabBrowser.loadTabs(["about:", "about:mozilla"], false, true);
     }
 
     function testAccTree()
     {
-      var accTree = {
-        role: ROLE_PANE,
+      var tabsAccTree = {
+        role: ROLE_PAGETABLIST,
         children: [
           {
-            role: ROLE_TOOLTIP
-          },
-          {
             role: ROLE_MENUPOPUP
           },
           {
-            role: ROLE_PAGETABLIST,
+            role: ROLE_PAGETAB,
             children: [
               {
-                role: ROLE_PAGETAB,
-                children: [
-                  {
-                    role: ROLE_PUSHBUTTON
-                  }
-                ]
-              },
-              {
-                role: ROLE_PAGETAB,
-                children: [
-                  {
-                    role: ROLE_PUSHBUTTON
-                  }
-                ]
-              },
-              {
-                role: ROLE_PUSHBUTTON
-              },
-              {
                 role: ROLE_PUSHBUTTON
               }
             ]
           },
           {
+            role: ROLE_PAGETAB,
+            children: [
+              {
+                role: ROLE_PUSHBUTTON
+              }
+            ]
+          },
+          {
+            role: ROLE_PUSHBUTTON
+          },
+          {
+            role: ROLE_PUSHBUTTON
+          }
+        ]
+      };
+      var tabboxAccTree = {
+        role: ROLE_PANE,
+        children: [
+          {
             role: ROLE_PROPERTYPAGE
           },
           {
             role: ROLE_PROPERTYPAGE
           }
         ]
       };
-      testAccessibleTree(getNode("tabbrowser").mTabBox, accTree);
+      testAccessibleTree(getNode("tabbrowser").tabContainer, tabsAccTree);
+      testAccessibleTree(getNode("tabbrowser").mTabBox, tabboxAccTree);
 
       SimpleTest.finish()
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
-  <hbox flex="1" style="overflow: auto;">
+  <vbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389"
          title=" WARNING: Bad accessible tree!: [tabbrowser tab] ">
         Mozilla Bug 540389
       </a><br/>
       <p id="display"></p>
       <div id="content" style="display: none">
@@ -140,13 +131,21 @@
       <menu label="menu">
         <menupopup>
           <menuitem label="close window hook" id="menu_closeWindow"/>
           <menuitem label="close hook" id="menu_close"/>
         </menupopup>
       </menu>
     </menubar>
 
-    <tabbrowser type="content-primary" flex="1" id="tabbrowser"/>
-  </hbox>
+    <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
+          tabbrowser="tabbrowser"
+          setfocus="false">
+      <tab class="tabbrowser-tab" selected="true"/>
+    </tabs>
+    <tabbrowser id="tabbrowser"
+                type="content-primary"
+                tabcontainer="tabbrowser-tabs"
+                flex="1"/>
+  </vbox>
 
 </window>
 
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -80,17 +80,17 @@ if [ "$WINCE" ]; then
     build/wince/shunt/Makefile
     build/wince/shunt/include/windows.h
     build/wince/shunt/include/ymath.h
     build/wince/shunt/include/stdlib.h
     build/wince/shunt/include/sys/Makefile
   "
 fi
 
-if [ "$MOZ_MEMORY" ]; then
+if [ "$MOZ_MEMORY" -a "$LIBXUL_SDK" = "" ]; then
   add_makefiles "
     memory/jemalloc/Makefile
   "
 fi
 
 #
 # Application-specific makefiles
 #
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -307,16 +307,18 @@ libs:: $(srcdir)/blocklist.xml
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 
 APP_NAME = $(MOZ_APP_DISPLAYNAME)
 
 ifdef MOZ_DEBUG
 APP_NAME := $(APP_NAME)Debug
 endif
 
+LOWER_APP_NAME = $(shell echo $(APP_NAME) | tr '[A-Z]' '[a-z]')
+
 AB_CD = $(MOZ_UI_LOCALE)
 
 AB := $(firstword $(subst -, ,$(AB_CD)))
 
 clean clobber repackage::
 	rm -rf $(DIST)/$(APP_NAME).app
 
 ifdef LIBXUL_SDK
@@ -325,18 +327,18 @@ else
 APPFILES = MacOS
 endif
 
 libs repackage:: $(PROGRAM) application.ini
 	mkdir -p $(DIST)/$(APP_NAME).app/Contents/MacOS
 	rsync -a --exclude CVS --exclude "*.in" $(srcdir)/macbuild/Contents $(DIST)/$(APP_NAME).app --exclude English.lproj
 	mkdir -p $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj
 	rsync -a --exclude CVS --exclude "*.in" $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj
-	sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/$(APP_NAME).app/Contents/Info.plist
-	sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj/InfoPlist.strings
+	sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" -e "s/%LOWER_APP_NAME%/$(LOWER_APP_NAME)/" $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/$(APP_NAME).app/Contents/Info.plist
+	sed -e "s/%APP_NAME%/$(APP_NAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj/InfoPlist.strings
 	rsync -a $(DIST)/bin/ $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)
 	$(RM) $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/mangle $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/shlibsign
 ifdef LIBXUL_SDK
 	cp $(LIBXUL_DIST)/bin/$(XR_STUB_NAME) $(DIST)/$(APP_NAME).app/Contents/MacOS/firefox-bin
 else
 	rm -f $(DIST)/$(APP_NAME).app/Contents/MacOS/$(PROGRAM)
 	rsync -aL $(PROGRAM) $(DIST)/$(APP_NAME).app/Contents/MacOS
 endif
--- a/browser/app/macbuild/Contents/Info.plist.in
+++ b/browser/app/macbuild/Contents/Info.plist.in
@@ -73,17 +73,17 @@
 	</array>
 	<key>CFBundleExecutable</key>
 	<string>firefox-bin</string>
 	<key>CFBundleGetInfoString</key>
 	<string>%APP_NAME% %APP_VERSION%</string>
 	<key>CFBundleIconFile</key>
 	<string>firefox</string>
 	<key>CFBundleIdentifier</key>
-	<string>org.mozilla.firefox</string>
+	<string>org.mozilla.%LOWER_APP_NAME%</string>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<key>CFBundleName</key>
 	<string>%APP_NAME%</string>
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
 	<string>%APP_VERSION%</string>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -235,24 +235,26 @@ pref("browser.urlbar.maxRichResults", 12
 pref("browser.urlbar.delay", 50);
 
 // The special characters below can be typed into the urlbar to either restrict
 // the search to visited history, bookmarked, tagged pages; or force a match on
 // just the title text or url.
 pref("browser.urlbar.restrict.history", "^");
 pref("browser.urlbar.restrict.bookmark", "*");
 pref("browser.urlbar.restrict.tag", "+");
+pref("browser.urlbar.restrict.openpage", "%");
 pref("browser.urlbar.restrict.typed", "~");
 pref("browser.urlbar.match.title", "#");
 pref("browser.urlbar.match.url", "@");
 
 // The default behavior for the urlbar can be configured to use any combination
 // of the restrict or match filters with each additional filter restricting
 // more (intersection). Add the following values to set the behavior as the
-// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed
+// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed,
+//          64: javascript, 128: tabs
 // E.g., 0 = show all results (no filtering), 1 = only visited pages in history,
 // 2 = only bookmarks, 3 = visited bookmarks, 1+16 = history matching in the url
 pref("browser.urlbar.default.behavior", 0);
 
 // Number of milliseconds to wait for the http headers (and thus
 // the Content-Disposition filename) before giving up and falling back to 
 // picking a filename without that info in hand so that the user sees some
 // feedback from their action.
@@ -901,17 +903,21 @@ pref("browser.sessionstore.interval", 60
 
 // Whether to use a panel that looks like an OS X sheet for customization
 #ifdef XP_MACOSX
 pref("toolbar.customization.usesheet", true);
 #else
 pref("toolbar.customization.usesheet", false);
 #endif
 
+#ifdef XP_MACOSX
+pref("dom.ipc.plugins.enabled", false);
+#else
 pref("dom.ipc.plugins.enabled", true);
+#endif
 
 #ifdef XP_WIN
 #ifndef WINCE
 pref("browser.taskbar.previews.enable", true);
 pref("browser.taskbar.previews.max", 20);
 pref("browser.taskbar.previews.cachetime", 20);
 pref("browser.taskbar.lists.enabled", true);
 pref("browser.taskbar.lists.frequent.enabled", true);
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -20,17 +20,17 @@
   background-repeat: no-repeat;
   background-color: #FFFFFF;
   padding-top: 203px;
   color: #000000;
 }
 
 #brandName {
   font-weight: bold; font-size: larger;
-} 
+}
 
 #userAgent {
   direction: ltr;
   margin-top: 10px;
   -moz-margin-end: 13px;
   margin-bottom: 0;
   -moz-margin-start: 13px;
   background-color: #FFFFFF;
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -269,17 +269,31 @@ var FullZoom = {
    * @param aIsTabSwitch
    *        Whether this location change has happened because of a tab switch.
    * @param aBrowser
    *        (optional) browser object displaying the document
    */
   onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
     if (!aURI || (aIsTabSwitch && !this.siteSpecific))
       return;
-    this._applyPrefToSetting(this._cps.getPref(aURI, this.name), aBrowser);
+
+    // Avoid the cps roundtrip and apply the default/global pref.
+    if (aURI.spec == "about:blank") {
+      this._applyPrefToSetting(undefined, aBrowser);
+      return;
+    }
+
+    var self = this;
+    this._cps.getPref(aURI, this.name, function(aResult) {
+      // Check that we're still where we expect to be in case this took a while.
+      let isSaneURI = (aBrowser && aBrowser.currentURI) ?
+        aURI.equals(aBrowser.currentURI) : false;
+      if (!aBrowser || isSaneURI)
+        self._applyPrefToSetting(aResult, aBrowser);
+    });
   },
 
   // update state of zoom type menu item
 
   updateMenu: function FullZoom_updateMenu() {
     var menuItem = document.getElementById("toggle_zoom");
 
     menuItem.setAttribute("checked", !ZoomManager.useFullZoom);
@@ -326,17 +340,17 @@ var FullZoom = {
    * We don't check first to see if the new value is the same as the current
    * one.
    **/
   _applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser) {
     if ((!this.siteSpecific && !this._inPrivateBrowsing) ||
         gInPrintPreviewMode)
       return;
 
-    var browser = aBrowser || gBrowser.selectedBrowser;
+    var browser = aBrowser || (gBrowser && gBrowser.selectedBrowser);
     try {
       if (browser.contentDocument instanceof Ci.nsIImageDocument ||
           this._inPrivateBrowsing)
         ZoomManager.setZoomForBrowser(browser, 1);
       else if (typeof aValue != "undefined")
         ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
       else if (typeof this.globalValue != "undefined")
         ZoomManager.setZoomForBrowser(browser, this.globalValue);
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -627,17 +627,17 @@ var HistoryMenu = {
       m.setAttribute("label", undoItems[i].title);
       if (undoItems[i].image) {
         let iconURL = undoItems[i].image;
         // 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");
+      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.
       let tabData = undoItems[i].state;
       let activeIndex = (tabData.index || tabData.entries.length) - 1;
       if (activeIndex >= 0 && tabData.entries[activeIndex])
@@ -709,17 +709,17 @@ var HistoryMenu = {
       let selectedTab = undoItem.tabs[undoItem.selected - 1];
       if (selectedTab.attributes.image) {
         let iconURL = selectedTab.attributes.image;
         // 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");
+      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.
       // 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);
 
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -107,18 +107,18 @@
     <command id="Browser:Stop"    oncommand="BrowserStop();" disabled="true"/>
     <command id="Browser:Reload"  oncommand="if (event.shiftKey) BrowserReloadSkipCache(); else BrowserReload()" disabled="true"/>
     <command id="Browser:ReloadOrDuplicate" oncommand="BrowserReloadOrDuplicate(event)" disabled="true">
       <observes element="Browser:Reload" attribute="disabled"/>
     </command>
     <command id="Browser:ReloadSkipCache" oncommand="BrowserReloadSkipCache()" disabled="true">
       <observes element="Browser:Reload" attribute="disabled"/>
     </command>
-    <command id="Browser:NextTab" oncommand="gBrowser.mTabContainer.advanceSelectedTab(1, true);"/>
-    <command id="Browser:PrevTab" oncommand="gBrowser.mTabContainer.advanceSelectedTab(-1, true);"/>
+    <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="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();"/>    
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -197,17 +197,17 @@ var ctrlTab = {
   get canvasWidth () Math.min(tabPreviews.width,
                               Math.ceil(screen.availWidth * .85 / this.tabPreviewCount)),
   get canvasHeight () Math.round(this.canvasWidth * tabPreviews.aspectRatio),
 
   get tabList () {
     if (this._tabList)
       return this._tabList;
 
-    var list = Array.slice(gBrowser.mTabs);
+    var list = Array.slice(gBrowser.tabs);
 
     if (this._closing)
       this.detachTab(this._closing, list);
 
     for (let i = 0; i < gBrowser.tabContainer.selectedIndex; i++)
       list.push(list.shift());
 
     if (this.recentlyUsedLimit != 0) {
@@ -440,19 +440,19 @@ var ctrlTab = {
     switch (event.keyCode) {
       case event.DOM_VK_TAB:
         if (event.ctrlKey && !event.altKey && !event.metaKey) {
           if (isOpen) {
             this.advanceFocus(!event.shiftKey);
           } else if (!event.shiftKey) {
             event.preventDefault();
             event.stopPropagation();
-            if (gBrowser.mTabs.length > 2) {
+            if (gBrowser.tabs.length > 2) {
               this.open();
-            } else if (gBrowser.mTabs.length == 2) {
+            } else if (gBrowser.tabs.length == 2) {
               gBrowser.selectedTab = gBrowser.selectedTab.nextSibling ||
                                      gBrowser.selectedTab.previousSibling;
             }
           }
         }
         break;
       default:
         if (isOpen && event.ctrlKey) {
@@ -572,17 +572,17 @@ var allTabs = {
   get previews () this.container.getElementsByClassName("allTabs-preview"),
   get isOpen () this.panel.state == "open" || this.panel.state == "showing",
 
   init: function allTabs_init() {
     if (this._initiated)
       return;
     this._initiated = true;
 
-    Array.forEach(gBrowser.mTabs, function (tab) {
+    Array.forEach(gBrowser.tabs, function (tab) {
       this._addPreview(tab);
     }, this);
 
     gBrowser.tabContainer.addEventListener("TabOpen", this, false);
     gBrowser.tabContainer.addEventListener("TabAttrModified", this, false);
     gBrowser.tabContainer.addEventListener("TabMove", this, false);
     gBrowser.tabContainer.addEventListener("TabClose", this, false);
   },
@@ -667,50 +667,58 @@ var allTabs = {
   },
 
   open: function allTabs_open() {
     this.init();
 
     if (this.isOpen)
       return;
 
+    this._maxPanelHeight = Math.max(gBrowser.clientHeight, screen.availHeight / 2);
+    this._maxPanelWidth = Math.max(gBrowser.clientWidth, screen.availWidth / 2);
+
     this.filter();
 
     tabPreviewPanelHelper.opening(this);
 
-    this.panel.popupBoxObject.setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
-    var estimateHeight = (this._maxHeight + parseInt(this.container.maxHeight) + 50) / 2;
-    this.panel.openPopupAtScreen(screen.availLeft + (screen.availWidth - this._maxWidth) / 2,
-                                 screen.availTop + (screen.availHeight - estimateHeight) / 2,
-                                 false);
+    this.panel.popupBoxObject.setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_NO_CONSUME);
+    this.panel.openPopup(gBrowser, "overlap", 0, 0, false, true);
   },
 
   close: function allTabs_close() {
     this.panel.hidePopup();
   },
 
   setupGUI: function allTabs_setupGUI() {
     this.filterField.focus();
     this.filterField.placeholder = this.filterField.tooltipText;
 
     this.panel.addEventListener("keypress", this, false);
     this.panel.addEventListener("keypress", this, true);
     this._browserCommandSet.addEventListener("command", this, false);
+
+    // When the panel is open, a second click on the all tabs button should
+    // close the panel but not re-open it.
+    document.getElementById("Browser:ShowAllTabs").setAttribute("disabled", "true");
   },
 
   suspendGUI: function allTabs_suspendGUI() {
     this.filterField.placeholder = "";
     this.filterField.value = "";
     this._currentFilter = null;
 
     this._updateTabCloseButton();
 
     this.panel.removeEventListener("keypress", this, false);
     this.panel.removeEventListener("keypress", this, true);
     this._browserCommandSet.removeEventListener("command", this, false);
+
+    setTimeout(function () {
+      document.getElementById("Browser:ShowAllTabs").removeAttribute("disabled");
+    }, 300);
   },
 
   handleEvent: function allTabs_handleEvent(event) {
     if (/^Tab/.test(event.type)) {
       var tab = event.target;
       if (event.type != "TabOpen")
         var preview = this._getPreview(tab);
     }
@@ -749,18 +757,16 @@ var allTabs = {
           this.close();
         }
         break;
     }
   },
 
   _visible: 0,
   _currentFilter: null,
-  get _maxWidth () screen.availWidth * .9,
-  get _maxHeight () screen.availHeight * .75,
   get _stack () {
     delete this._stack;
     return this._stack = document.getElementById("allTabs-stack");
   },
   get _browserCommandSet () {
     delete this._browserCommandSet;
     return this._browserCommandSet = document.getElementById("mainCommandSet");
   },
@@ -781,31 +787,32 @@ var allTabs = {
         return previews[i];
     }
     return null;
   },
 
   _reflow: function allTabs_reflow() {
     this._updateTabCloseButton();
 
+    const CONTAINER_MAX_WIDTH = this._maxPanelWidth * .95;
+    const CONTAINER_MAX_HEIGHT = this._maxPanelHeight - 35;
     // the size of the whole preview relative to the thumbnail
     const REL_PREVIEW_THUMBNAIL = 1.2;
+    const REL_PREVIEW_HEIGHT_WIDTH = tabPreviews.height / tabPreviews.width;
+    const PREVIEW_MAX_WIDTH = tabPreviews.width * REL_PREVIEW_THUMBNAIL;
 
-    var maxHeight = this._maxHeight;
-    var maxWidth = this._maxWidth;
-    var rel = tabPreviews.height / tabPreviews.width;
     var rows, previewHeight, previewWidth, outerHeight;
-    var previewMaxWidth = tabPreviews.width * REL_PREVIEW_THUMBNAIL;
-    this._columns = Math.floor(maxWidth / previewMaxWidth);
+    this._columns = Math.floor(CONTAINER_MAX_WIDTH / PREVIEW_MAX_WIDTH);
     do {
       rows = Math.ceil(this._visible / this._columns);
-      previewWidth = Math.min(previewMaxWidth, Math.round(maxWidth / this._columns));
-      previewHeight = Math.round(previewWidth * rel);
+      previewWidth = Math.min(PREVIEW_MAX_WIDTH,
+                              Math.round(CONTAINER_MAX_WIDTH / this._columns));
+      previewHeight = Math.round(previewWidth * REL_PREVIEW_HEIGHT_WIDTH);
       outerHeight = previewHeight + this._previewLabelHeight;
-    } while (rows * outerHeight > maxHeight && ++this._columns);
+    } while (rows * outerHeight > CONTAINER_MAX_HEIGHT && ++this._columns);
 
     var outerWidth = previewWidth;
     {
       let innerWidth = Math.ceil(previewWidth / REL_PREVIEW_THUMBNAIL);
       let innerHeight = Math.ceil(previewHeight / REL_PREVIEW_THUMBNAIL);
       var canvasStyle = "max-width:" + innerWidth + "px;" +
                         "min-width:" + innerWidth + "px;" +
                         "max-height:" + innerHeight + "px;" +
@@ -829,20 +836,20 @@ var allTabs = {
       }
       preview.setAttribute("minwidth", outerWidth);
       preview.setAttribute("height", outerHeight);
       preview.setAttribute("canvasstyle", canvasStyle);
       preview.removeAttribute("closebuttonhover");
       row.appendChild(preview);
     }, this);
 
-    this._stack.width = maxWidth;
+    this._stack.width = this._maxPanelWidth;
     this.container.width = Math.ceil(outerWidth * Math.min(this._columns, this._visible));
-    this.container.left = Math.round((maxWidth - this.container.width) / 2);
-    this.container.maxWidth = maxWidth - this.container.left;
+    this.container.left = Math.round((this._maxPanelWidth - this.container.width) / 2);
+    this.container.maxWidth = this._maxPanelWidth - this.container.left;
     this.container.maxHeight = rows * outerHeight;
   },
 
   _addPreview: function allTabs_addPreview(aTab) {
     var preview = document.createElement("button");
     preview.className = "allTabs-preview";
     preview._tab = aTab;
     this.container.lastChild.appendChild(preview);
@@ -876,29 +883,29 @@ var allTabs = {
         return;
 
       this.tabCloseButton._targetPreview.removeAttribute("closebuttonhover");
     }
 
     if (event &&
         event.target.parentNode.parentNode == this.container &&
         (event.target._tab.previousSibling || event.target._tab.nextSibling)) {
-      let preview = event.target.getBoundingClientRect();
+      let canvas = event.target.firstChild.getBoundingClientRect();
       let container = this.container.getBoundingClientRect();
       let tabCloseButton = this.tabCloseButton.getBoundingClientRect();
       let alignLeft = getComputedStyle(this.panel, "").direction == "rtl";
 #ifdef XP_MACOSX
       alignLeft = !alignLeft;
 #endif
-      this.tabCloseButton.left = preview.left -
+      this.tabCloseButton.left = canvas.left -
                                  container.left +
                                  parseInt(this.container.left) +
                                  (alignLeft ? 0 :
-                                  preview.width - tabCloseButton.width);
-      this.tabCloseButton.top = preview.top - container.top;
+                                  canvas.width - tabCloseButton.width);
+      this.tabCloseButton.top = canvas.top - container.top;
       this.tabCloseButton._targetPreview = event.target;
       this.tabCloseButton.style.visibility = "visible";
       event.target.setAttribute("closebuttonhover", "true");
     } else {
       this.tabCloseButton.style.visibility = "hidden";
       this.tabCloseButton.left = this.tabCloseButton.top = 0;
       this.tabCloseButton._targetPreview = null;
     }
--- a/browser/base/content/browser-tabPreviews.xml
+++ b/browser/base/content/browser-tabPreviews.xml
@@ -93,15 +93,15 @@
         let tab = event.dataTransfer.mozGetDataAt("application/x-moz-node", 0);
         if (tab && tab.parentNode == gBrowser.tabContainer)
           event.preventDefault();
       ]]></handler>
 
       <handler event="drop"><![CDATA[
         let tab = event.dataTransfer.mozGetDataAt("application/x-moz-node", 0);
         if (tab && tab.parentNode == gBrowser.tabContainer) {
-          let newIndex = Array.indexOf(gBrowser.tabContainer.childNodes, this._tab);
+          let newIndex = Array.indexOf(gBrowser.tabs, this._tab);
           gBrowser.moveTabTo(tab, newIndex);
         }
       ]]></handler>
     </handlers>
   </binding>
 </bindings>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -4,16 +4,24 @@
 searchbar {
   -moz-binding: url("chrome://browser/content/search/search.xml#searchbar");
 }
 
 tabbrowser {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
 }
 
+.tabbrowser-tabs {
+  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
+}
+
+.tabbrowser-tab {
+  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
+}
+
 toolbar[printpreview="true"] {
   -moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
 }
 
 toolbarpaletteitem[place="palette"] > toolbaritem > hbox[type="places"] {
   display: none;
 }
 
@@ -38,16 +46,36 @@ toolbar[mode="icons"] > #reload-button[d
   background-position: bottom left;
 }
 
 /* ::::: location bar ::::: */
 #urlbar {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
+/* Some child nodes want to be ordered based on the locale's direction, while
+   everything else should be ltr. */
+#urlbar:-moz-locale-dir(rtl) > .autocomplete-textbox-container > .textbox-input-box {
+  direction: rtl;
+}
+
+#urlbar html|*.autocomplete-textbox {
+  direction: ltr;
+}
+
+/* For results that are actions, their description text is shown instead of
+   the URL - this needs to follow the locale's direction, unlike URLs. */
+richlistitem[type="action"]:-moz-locale-dir(rtl) > .ac-url-box {
+  direction: rtl;
+}
+
+#urlbar:not([actiontype]) > #urlbar-display {
+  display: none;
+}
+
 #wrapper-urlbar-container > #urlbar-container > #urlbar {
   -moz-user-input: disabled;
   cursor: -moz-grab;
 }
 
 #PopupAutoComplete {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#browser-autocomplete-result-popup");
 }
@@ -101,24 +129,16 @@ toolbar[mode="icons"] > #reload-button[d
 .unified-nav-current {
   font-weight: bold;
 }
 
 toolbarbutton.bookmark-item {
   max-width: 13em;
 }
 
-%ifdef MOZ_WIDGET_GTK2
-/* Bookmarks override the "images-in-menus" metric in xul.css */
-.bookmark-item > .menu-iconic-left,
-.searchbar-engine-menuitem > .menu-iconic-left {
-  visibility: inherit;
-}
-%endif
-
 #editBMPanel_tagsSelector {
   /* override default listbox width from xul.css */
   width: auto;
 }
 
 menuitem.spell-suggestion {
   font-weight: bold;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -258,25 +258,25 @@ function SetClickAndHoldHandlers() {
                         .firstChild.cloneNode(true);
     var backButton = document.getElementById("back-button");
     backButton.setAttribute("type", "menu");
     backButton.appendChild(popup);
     _addClickAndHoldListenersOnElement(backButton);
     var forwardButton = document.getElementById("forward-button");
     popup = popup.cloneNode(true);
     forwardButton.setAttribute("type", "menu");
-    forwardButton.appendChild(popup);    
+    forwardButton.appendChild(popup);
     _addClickAndHoldListenersOnElement(forwardButton);
     unifiedButton._clickHandlersAttached = true;
   }
 }
 #endif
 
-function BookmarkThisTab() {
-  PlacesCommandHook.bookmarkPage(gBrowser.mContextTab.linkedBrowser,
+function BookmarkThisTab(aTab) {
+  PlacesCommandHook.bookmarkPage(aTab.linkedBrowser,
                                  PlacesUtils.bookmarksMenuFolderId, true);
 }
 
 const gSessionHistoryObserver = {
   observe: function(subject, topic, data)
   {
     if (topic != "browser:purge-session-history")
       return;
@@ -290,31 +290,31 @@ const gSessionHistoryObserver = {
       // Clear undo history of the URL bar
       gURLBar.editor.transactionManager.clear()
     }
   }
 };
 
 /**
  * Given a starting docshell and a URI to look up, find the docshell the URI
- * is loaded in. 
+ * is loaded in.
  * @param   aDocument
- *          A document to find instead of using just a URI - this is more specific. 
+ *          A document to find instead of using just a URI - this is more specific.
  * @param   aDocShell
  *          The doc shell to start at
  * @param   aSoughtURI
  *          The URI that we're looking for
- * @returns The doc shell that the sought URI is loaded in. Can be in 
+ * @returns The doc shell that the sought URI is loaded in. Can be in
  *          subframes.
  */
 function findChildShell(aDocument, aDocShell, aSoughtURI) {
   aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation);
   aDocShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
   var doc = aDocShell.getInterface(Components.interfaces.nsIDOMDocument);
-  if ((aDocument && doc == aDocument) || 
+  if ((aDocument && doc == aDocument) ||
       (aSoughtURI && aSoughtURI.spec == aDocShell.currentURI.spec))
     return aDocShell;
 
   var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
   for (var i = 0; i < node.childCount; ++i) {
     var docShell = node.getChildAt(i);
     docShell = findChildShell(aDocument, docShell, aSoughtURI);
     if (docShell)
@@ -869,17 +869,17 @@ let gGestureSupport = {
 
   /**
    * Convert the swipe gesture into a browser action based on the direction
    *
    * @param aEvent
    *        The swipe event to handle
    */
   onSwipe: function GS_onSwipe(aEvent) {
-    // Figure out which one (and only one) direction was triggered 
+    // Figure out which one (and only one) direction was triggered
     ["UP", "RIGHT", "DOWN", "LEFT"].forEach(function (dir) {
       if (aEvent.direction == aEvent["DIRECTION_" + dir])
         return this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
     }, this);
   },
 
   /**
    * Get a gesture preference or use a default if it doesn't exist
@@ -891,17 +891,17 @@ let gGestureSupport = {
    */
   _getPref: function GS__getPref(aPref, aDef) {
     // Preferences branch under which all gestures preferences are stored
     const branch = "browser.gesture.";
 
     try {
       // Determine what type of data to load based on default value's type
       let type = typeof aDef;
-      let getFunc = "get" + (type == "boolean" ? "Bool" : 
+      let getFunc = "get" + (type == "boolean" ? "Bool" :
                              type == "number" ? "Int" : "Char") + "Pref";
       return gPrefService[getFunc](branch + aPref);
     }
     catch (e) {
       return aDef;
     }
   },
 };
@@ -921,17 +921,17 @@ function BrowserStartup() {
   if ("arguments" in window && window.arguments[0])
     uriToLoad = window.arguments[0];
 
   var isLoadingBlank = uriToLoad == "about:blank";
   var mustLoadSidebar = false;
 
   prepareForStartup();
 
-  if (uriToLoad && !isLoadingBlank) { 
+  if (uriToLoad && !isLoadingBlank) {
     if (uriToLoad instanceof Ci.nsISupportsArray) {
       let count = uriToLoad.Count();
       let specs = [];
       for (let i = 0; i < count; i++) {
         let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
         specs.push(urisstring.data);
       }
 
@@ -1192,17 +1192,17 @@ function delayedStartup(isLoadingBlank, 
 
   if (mustLoadSidebar) {
     let sidebar = document.getElementById("sidebar");
     let sidebarBox = document.getElementById("sidebar-box");
     sidebar.setAttribute("src", sidebarBox.getAttribute("src"));
   }
 
   UpdateUrlbarSearchSplitterState();
-  
+
   PlacesStarButton.init();
 
   // called when we go into full screen, even if it is
   // initiated by a web page script
   window.addEventListener("fullscreen", onFullScreen, true);
 
   if (isLoadingBlank && gURLBar && isElementVisible(gURLBar))
     gURLBar.focus();
@@ -1211,19 +1211,17 @@ function delayedStartup(isLoadingBlank, 
 
   gNavToolbox.customizeDone = BrowserToolboxCustomizeDone;
   gNavToolbox.customizeChange = BrowserToolboxCustomizeChange;
 
   // Set up Sanitize Item
   initializeSanitizer();
 
   // Enable/Disable auto-hide tabbar
-  gAutoHideTabbarPrefListener.toggleAutoHideTabbar();
-  gPrefService.addObserver(gAutoHideTabbarPrefListener.domain,
-                           gAutoHideTabbarPrefListener, false);
+  gBrowser.tabContainer.updateVisibility();
 
   gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
 
   var homeButton = document.getElementById("home-button");
   gHomeButton.updateTooltip(homeButton);
   gHomeButton.updatePersonalToolbarStyle(homeButton);
 
 #ifdef HAVE_SHELL_SERVICE
@@ -1321,17 +1319,17 @@ function delayedStartup(isLoadingBlank, 
     try {
       Cc["@mozilla.org/microsummary/service;1"].getService(Ci.nsIMicrosummaryService);
     } catch (ex) {
       Components.utils.reportError("Failed to init microsummary service:\n" + ex);
     }
   }, 4000);
 
   // Delayed initialization of the livemarks update timer.
-  // Livemark updates don't need to start until after bookmark UI 
+  // Livemark updates don't need to start until after bookmark UI
   // such as the toolbar has initialized. Starting 5 seconds after
   // delayedStartup in order to stagger this after the microsummary
   // service (see above) and before the download manager starts (see below).
   setTimeout(function() PlacesUtils.livemarks.start(), 5000);
 
   // Initialize the download manager some time after the app starts so that
   // auto-resume downloads begin (such as after crashing or quitting with
   // active downloads) and speeds up the first-load of the download manager UI.
@@ -1400,18 +1398,16 @@ function BrowserShutdown()
     gBrowser.removeProgressListener(window.XULBrowserWindow);
     gBrowser.removeTabsProgressListener(window.TabsProgressListener);
   } catch (ex) {
   }
 
   PlacesStarButton.uninit();
 
   try {
-    gPrefService.removeObserver(gAutoHideTabbarPrefListener.domain,
-                                gAutoHideTabbarPrefListener);
     gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
   } catch (ex) {
     Components.utils.reportError(ex);
   }
 
   BrowserOffline.uninit();
   OfflineApps.uninit();
   DownloadMonitorPanel.uninit();
@@ -1495,52 +1491,32 @@ function nonBrowserWindowStartup()
 
   setTimeout(nonBrowserWindowDelayedStartup, 0);
 }
 
 function nonBrowserWindowDelayedStartup()
 {
   // initialise the offline listener
   BrowserOffline.init();
-  
+
   // Set up Sanitize Item
   initializeSanitizer();
 
   // initialize the private browsing UI
   gPrivateBrowsingUI.init();
 }
 
 function nonBrowserWindowShutdown()
 {
   BrowserOffline.uninit();
 
   gPrivateBrowsingUI.uninit();
 }
 #endif
 
-var gAutoHideTabbarPrefListener = {
-  domain: "browser.tabs.autoHide",
-  observe: function (aSubject, aTopic, aPrefName) {
-    if (aTopic == "nsPref:changed" && aPrefName == this.domain)
-      this.toggleAutoHideTabbar();
-  },
-  toggleAutoHideTabbar: function () {
-    if (gBrowser.tabContainer.childNodes.length == 1 &&
-        window.toolbar.visible) {
-      var aVisible = false;
-      try {
-        aVisible = !gPrefService.getBoolPref(this.domain);
-      }
-      catch (e) {
-      }
-      gBrowser.setStripVisibilityTo(aVisible);
-    }
-  }
-}
-
 function initializeSanitizer()
 {
   const kDidSanitizeDomain = "privacy.sanitize.didShutdownSanitize";
   if (gPrefService.prefHasUserValue(kDidSanitizeDomain)) {
     gPrefService.clearUserPref(kDidSanitizeDomain);
     // We need to persist this preference change, since we want to
     // check it at next app start even if the browser exits abruptly
     gPrefService.savePrefFile(null);
@@ -1758,17 +1734,17 @@ function loadOneOrMoreURIs(aURIString)
     window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
     return;
   }
 #endif
   // This function throws for certain malformed URIs, so use exception handling
   // so that we don't disrupt startup
   try {
     gBrowser.loadTabs(aURIString.split("|"), false, true);
-  } 
+  }
   catch (e) {
   }
 }
 
 function focusAndSelectUrlBar() {
   if (gURLBar && !gURLBar.readOnly) {
     if (window.fullScreen)
       FullScreen.mouseoverToggle(true);
@@ -1995,23 +1971,23 @@ function getShortcutOrURI(aURL, aPostDat
     // the original URL.
     aPostDataRef.value = null;
 
     return aURL;
   }
 
   return shortcutURL;
 }
- 
+
 function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
   var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].
                    createInstance(Ci.nsIStringInputStream);
   aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
   dataStream.data = aStringData;
- 
+
   var mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"].
                    createInstance(Ci.nsIMIMEInputStream);
   mimeStream.addHeader("Content-Type", aType);
   mimeStream.addContentLength = true;
   mimeStream.setData(dataStream);
   return mimeStream.QueryInterface(Ci.nsIInputStream);
 }
 
@@ -2353,45 +2329,45 @@ function BrowserOnCommand(event) {
     var ot = event.originalTarget;
     var errorDoc = ot.ownerDocument;
 
     // If the event came from an ssl error page, it is probably either the "Add
     // Exception…" or "Get me out of here!" button
     if (/^about:certerror/.test(errorDoc.documentURI)) {
       if (ot == errorDoc.getElementById('exceptionDialogButton')) {
         var params = { exceptionAdded : false, handlePrivateBrowsing : true };
-        
+
         try {
           switch (gPrefService.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
               params.prefetchCert = true;
             case 1 : // Pre-populate
               params.location = errorDoc.location.href;
           }
         } catch (e) {
           Components.utils.reportError("Couldn't get ssl_override pref: " + e);
         }
-        
+
         window.openDialog('chrome://pippki/content/exceptionDialog.xul',
                           '','chrome,centerscreen,modal', params);
-        
+
         // If the user added the exception cert, attempt to reload the page
         if (params.exceptionAdded)
           errorDoc.location.reload();
       }
       else if (ot == errorDoc.getElementById('getMeOutOfHereButton')) {
         getMeOutOfHere();
       }
     }
     else if (/^about:blocked/.test(errorDoc.documentURI)) {
       // The event came from a button on a malware/phishing block page
       // First check whether it's malware or phishing, so that we can
       // use the right strings/links
       var isMalware = /e=malwareBlocked/.test(errorDoc.documentURI);
-      
+
       if (ot == errorDoc.getElementById('getMeOutButton')) {
         getMeOutOfHere();
       }
       else if (ot == errorDoc.getElementById('reportButton')) {
         // This is the "Why is this site blocked" button.  For malware,
         // we can fetch a site-specific report, for phishing, we redirect
         // to the generic page describing phishing protection.
 
@@ -2442,17 +2418,17 @@ function BrowserOnCommand(event) {
           buttons[1] = {
             label: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.label"),
             accessKey: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.accessKey"),
             callback: function() {
               openUILinkIn(safebrowsing.getReportURL('Error'), 'tab');
             }
           };
         }
-        
+
         let notificationBox = gBrowser.getNotificationBox();
         let value = "blocked-badware-page";
 
         let previousNotification = notificationBox.getNotificationWithValue(value);
         if (previousNotification)
           notificationBox.removeNotification(previousNotification);
 
         notificationBox.appendNotification(
@@ -2590,33 +2566,29 @@ var PrintPreviewListener = {
   },
   _hideChrome: function () {
     this._chromeState = {};
 
     var sidebar = document.getElementById("sidebar-box");
     this._chromeState.sidebarOpen = !sidebar.hidden;
     this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
 
-    gBrowser.mStrip.setAttribute("moz-collapsed", "true");
-
     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;
 
     this._chromeState.findOpen = !gFindBar.hidden;
     gFindBar.close();
   },
   _showChrome: function () {
-    gBrowser.mStrip.removeAttribute("moz-collapsed");
-
     if (this._chromeState.notificationsOpen)
       gBrowser.getNotificationBox().notificationsHidden = false;
 
     if (this._chromeState.statusbarOpen)
       document.getElementById("status-bar").hidden = false;
 
     if (this._chromeState.findOpen)
       gFindBar.open();
@@ -2660,17 +2632,17 @@ function FillInHTMLTooltip(tipElement)
     if (tipElement.nodeType == Node.ELEMENT_NODE) {
       titleText = tipElement.getAttribute("title");
       if ((tipElement instanceof HTMLAnchorElement && tipElement.href) ||
           (tipElement instanceof HTMLAreaElement && tipElement.href) ||
           (tipElement instanceof HTMLLinkElement && tipElement.href) ||
           (tipElement instanceof SVGAElement && tipElement.hasAttributeNS(XLinkNS, "href"))) {
         XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
       }
-      if (lookingForSVGTitle && 
+      if (lookingForSVGTitle &&
           !(tipElement instanceof SVGElement &&
             tipElement.parentNode instanceof SVGElement &&
             !(tipElement.parentNode instanceof SVGForeignObjectElement))) {
         lookingForSVGTitle = false;
       }
       if (lookingForSVGTitle) {
         let length = tipElement.childNodes.length;
         for (let i = 0; i < length; i++) {
@@ -2689,17 +2661,17 @@ function FillInHTMLTooltip(tipElement)
       direction = defView.getComputedStyle(tipElement, "")
         .getPropertyValue("direction");
     }
     tipElement = tipElement.parentNode;
   }
 
   var tipNode = document.getElementById("aHTMLTooltip");
   tipNode.style.direction = direction;
-  
+
   [titleText, XLinkTitleText, SVGTitleText].forEach(function (t) {
     if (t && /\S/.test(t)) {
 
       // Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be
       // replaced with spaces, and LFs should be removed entirely.
       // XXX Bug 322270: We don't preserve the result of entities like &#13;,
       // which should result in a line break in the tooltip, because we can't
       // distinguish that from a literal character in the source by this point.
@@ -3007,17 +2979,17 @@ const DOMLinkHandler = {
                                          != Ci.nsIContentPolicy.ACCEPT)
               break;
 
             var browserIndex = gBrowser.getBrowserIndexForDocument(targetDoc);
             // no browser? no favicon.
             if (browserIndex == -1)
               break;
 
-            var tab = gBrowser.mTabContainer.childNodes[browserIndex];
+            let tab = gBrowser.tabs[browserIndex];
             gBrowser.setIcon(tab, link.href);
             iconAdded = true;
           }
           break;
         case "search":
           if (!searchAdded) {
             var type = link.type && link.type.toLowerCase();
             type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
@@ -3077,23 +3049,23 @@ const BrowserSearch = {
     else {
       browser.engines = engines;
       if (browser == gBrowser.selectedBrowser)
         this.updateSearchButton();
     }
   },
 
   /**
-   * Update the browser UI to show whether or not additional engines are 
-   * available when a page is loaded or the user switches tabs to a page that 
+   * Update the browser UI to show whether or not additional engines are
+   * available when a page is loaded or the user switches tabs to a page that
    * has search engines.
    */
   updateSearchButton: function() {
     var searchBar = this.searchBar;
-    
+
     // The search bar binding might not be applied even though the element is
     // in the document (e.g. when the navigation toolbar is hidden), so check
     // for .searchButton specifically.
     if (!searchBar || !searchBar.searchButton)
       return;
 
     var engines = gBrowser.selectedBrowser.engines;
     if (engines && engines.length > 0)
@@ -3151,33 +3123,33 @@ const BrowserSearch = {
    *        The search terms to use for the search.
    *
    * @param useNewTab
    *        Boolean indicating whether or not the search should load in a new
    *        tab.
    */
   loadSearch: function BrowserSearch_search(searchText, useNewTab) {
     var engine;
-  
+
     // If the search bar is visible, use the current engine, otherwise, fall
     // back to the default engine.
     if (isElementVisible(this.searchBar))
       engine = Services.search.currentEngine;
     else
       engine = Services.search.defaultEngine;
-  
+
     var submission = engine.getSubmission(searchText, null); // HTML response
 
     // getSubmission can return null if the engine doesn't have a URL
     // with a text/html response type.  This is unlikely (since
     // SearchService._addEngineToStore() should fail for such an engine),
     // but let's be on the safe side.
     if (!submission)
       return;
-  
+
     if (useNewTab) {
       gBrowser.loadOneTab(submission.uri.spec, {
                           postData: submission.postData,
                           relatedToCurrent: true});
     } else
       loadURI(submission.uri.spec, null, submission.postData, false);
   },
 
@@ -3253,25 +3225,25 @@ function FillHistoryMenu(aParent) {
         let iconURL = Cc["@mozilla.org/browser/favicon-service;1"]
                          .getService(Ci.nsIFaviconService)
                          .getFaviconForPage(entry.URI).spec;
         item.style.listStyleImage = "url(" + iconURL + ")";
       } catch (ex) {}
     }
 
     if (j < index) {
-      item.className = "unified-nav-back menuitem-iconic";
+      item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
       item.setAttribute("tooltiptext", tooltipBack);
     } else if (j == index) {
       item.setAttribute("type", "radio");
       item.setAttribute("checked", "true");
       item.className = "unified-nav-current";
       item.setAttribute("tooltiptext", tooltipCurrent);
     } else {
-      item.className = "unified-nav-forward menuitem-iconic";
+      item.className = "unified-nav-forward menuitem-iconic menuitem-with-favicon";
       item.setAttribute("tooltiptext", tooltipForward);
     }
 
     aParent.appendChild(item);
   }
   return true;
 }
 
@@ -3447,17 +3419,17 @@ function BrowserToolboxCustomizeChange()
  * edit-related items in the context menu, and edit-related toolbar buttons
  * is visible, then update the edit commands' enabled state accordingly.  We use
  * this flag to skip updating the edit commands on focus or selection changes
  * when no UI is visible to improve performance (including pageload performance,
  * since focus changes when you load a new page).
  *
  * If UI is visible, we use goUpdateGlobalEditMenuItems to set the commands'
  * enabled state so the UI will reflect it appropriately.
- * 
+ *
  * If the UI isn't visible, we enable all edit commands so keyboard shortcuts
  * still work and just lazily disable them as needed when the user presses a
  * shortcut.
  *
  * This doesn't work on Mac, since Mac menus flash when users press their
  * keyboard shortcuts, so edit UI is essentially always visible on the Mac,
  * and we need to always update the edit commands.  Thus on Mac this function
  * is a no op.
@@ -3671,18 +3643,17 @@ var FullScreen =
       FullScreen._isAnimating = false;
       FullScreen._shouldAnimate = true;
       return;
     }
 
     var animateFrameAmount = 2;
     function animateUpFrame() {
       animateFrameAmount *= 2;
-      if (animateFrameAmount >=
-          (gNavToolbox.boxObject.height + gBrowser.mStrip.boxObject.height)) {
+      if (animateFrameAmount >= gNavToolbox.boxObject.height) {
         // We've animated enough
         clearInterval(FullScreen._animationInterval);
         gNavToolbox.style.marginTop = "0px";
         FullScreen._isAnimating = false;
         FullScreen._shouldAnimate = false; // Just to make sure
         FullScreen.mouseoverToggle(false);
         return;
       }
@@ -3721,17 +3692,16 @@ var FullScreen =
       gBrowser.mPanelContainer.addEventListener("mousemove",
                                                 this._collapseCallback, false);
     }
     else {
       gBrowser.mPanelContainer.removeEventListener("mousemove",
                                                    this._collapseCallback, false);
     }
 
-    gBrowser.mStrip.setAttribute("moz-collapsed", !aShow);
     var allFSToolbars = document.getElementsByTagNameNS(this._XULNS, "toolbar");
     for (var i = 0; i < allFSToolbars.length; i++) {
       if (allFSToolbars[i].getAttribute("fullscreentoolbar") == "true")
         allFSToolbars[i].setAttribute("moz-collapsed", !aShow);
     }
     document.getElementById("fullscr-toggler").setAttribute("moz-collapsed", aShow);
     this._isChromeCollapsed = !aShow;
     if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2)
@@ -3809,17 +3779,17 @@ var FullScreen =
 };
 
 /**
  * Returns true if |aMimeType| is text-based, false otherwise.
  *
  * @param aMimeType
  *        The MIME type to check.
  *
- * If adding types to this function, please also check the similar 
+ * If adding types to this function, please also check the similar
  * function in findbar.xml
  */
 function mimeTypeIsTextBased(aMimeType)
 {
   return /^text\/|\+xml$/.test(aMimeType) ||
          aMimeType == "application/x-javascript" ||
          aMimeType == "application/javascript" ||
          aMimeType == "application/xml" ||
@@ -3830,17 +3800,16 @@ var XULBrowserWindow = {
   // Stored Status, Link and Loading values
   status: "",
   defaultStatus: "",
   jsStatus: "",
   jsDefaultStatus: "",
   overLink: "",
   startTime: 0,
   statusText: "",
-  lastURI: null,
   isBusy: false,
 
   _progressCollapseTimer: 0,
 
   QueryInterface: function (aIID) {
     if (aIID.equals(Ci.nsIWebProgressListener) ||
         aIID.equals(Ci.nsIWebProgressListener2) ||
         aIID.equals(Ci.nsISupportsWeakReference) ||
@@ -3894,17 +3863,16 @@ var XULBrowserWindow = {
     // 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;
-    delete this.lastURI;
   },
 
   setJSStatus: function (status) {
     this.jsStatus = status;
     this.updateStatusField();
   },
 
   setJSDefaultStatus: function (status) {
@@ -3930,17 +3898,17 @@ var XULBrowserWindow = {
 
     // 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 (aBrowser, aIconURL) {
     if (gProxyFavIcon && gBrowser.userTypedValue === null)
       PageProxySetIcon(aIconURL); // update the favicon in the URL bar
   },
 
   onProgressChange: function (aWebProgress, aRequest,
                               aCurSelfProgress, aMaxSelfProgress,
                               aCurTotalProgress, aMaxTotalProgress) {
@@ -4109,17 +4077,16 @@ var XULBrowserWindow = {
         newSpec = newSpec.substr(0, newSpec.indexOf("#"));
       if (newSpec != oldSpec) {
         // Remove all the notifications, except for those which want to
         // persist across the first location change.
         let nBox = gBrowser.getNotificationBox(selectedBrowser);
         nBox.removeTransientNotifications();
       }
     }
-    selectedBrowser.lastURI = aLocationURI;
 
     // Disable menu entries for images, enable otherwise
     if (content.document && mimeTypeIsTextBased(content.document.contentType))
       this.isImage.removeAttribute('disabled');
     else
       this.isImage.setAttribute('disabled', 'true');
 
     this.setOverLink("", null);
@@ -4169,17 +4136,17 @@ var XULBrowserWindow = {
 
     // See bug 358202, when tabs are switched during a drag operation,
     // timers don't fire on windows (bug 203573)
     if (aRequest)
       setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
     else
       this.asyncUpdateUI();
   },
-  
+
   asyncUpdateUI: function () {
     FeedHandler.updateFeeds();
     BrowserSearch.updateSearchButton();
   },
 
   onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
     this.status = aMessage;
     this.updateStatusField();
@@ -4196,17 +4163,17 @@ var XULBrowserWindow = {
     if (this._state == aState &&
         this._tooltipText == gBrowser.securityUI.tooltipText &&
         !this._hostChanged) {
 #ifdef DEBUG
       try {
         var contentHost = gBrowser.contentWindow.location.host;
         if (this._host !== undefined && this._host != contentHost) {
             Components.utils.reportError(
-              "ASSERTION: browser.js host is inconsistent. Content window has " + 
+              "ASSERTION: browser.js host is inconsistent. Content window has " +
               "<" + contentHost + "> but cached host is <" + this._host + ">.\n"
             );
         }
       } catch (ex) {}
 #endif
       return;
     }
     this._state = aState;
@@ -4257,17 +4224,17 @@ var XULBrowserWindow = {
       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 
+    // 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;
       locationObj.port = location.port;
@@ -4301,17 +4268,17 @@ var XULBrowserWindow = {
     this.onProgressChange(gBrowser.webProgress, 0, 0, aTotalProgress, 1);
   },
 
   startDocumentLoad: function (aRequest) {
     // clear out feed data
     gBrowser.selectedBrowser.feeds = null;
 
     // clear out search-engine data
-    gBrowser.selectedBrowser.engines = null;    
+    gBrowser.selectedBrowser.engines = null;
 
     var uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
 
     if (gURLBar &&
         gURLBar.value == "" &&
         getWebNavigation().currentURI.spec == "about:blank")
       URLBarSetURI(uri);
 
@@ -4427,17 +4394,17 @@ var TabsProgressListener = {
 #endif
   },
 
   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI) {
     // Filter out any sub-frame loads
     if (aBrowser.contentWindow == aWebProgress.DOMWindow)
       FullZoom.onLocationChange(aLocationURI, false, aBrowser);
   },
-  
+
   onStatusChange: function (aBrowser, aWebProgress, aRequest, aStatus, aMessage) {
   },
 
   onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
     if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
       let brandBundle = document.getElementById("bundle_brand");
       let brandShortName = brandBundle.getString("brandShortName");
       let refreshButtonText =
@@ -4635,17 +4602,17 @@ function displaySecurityInfo()
  * @param forceOpen boolean indicating whether the sidebar should be
  *                  opened regardless of its current state (optional).
  * @note
  * We expect to find a xul:broadcaster element with the specified ID.
  * The following attributes on that element may be used and/or modified:
  *  - id           (required) the string to match commandID. The convention
  *                 is to use this naming scheme: 'view<sidebar-name>Sidebar'.
  *  - sidebarurl   (required) specifies the URL to load in this sidebar.
- *  - sidebartitle or label (in that order) specify the title to 
+ *  - sidebartitle or label (in that order) specify the title to
  *                 display on the sidebar.
  *  - checked      indicates whether the sidebar is currently displayed.
  *                 Note that toggleSidebar updates this attribute when
  *                 it changes the sidebar's visibility.
  *  - group        this attribute must be set to "sidebar".
  */
 function toggleSidebar(commandID, forceOpen) {
 
@@ -4930,17 +4897,17 @@ function asyncOpenWebPanel(event)
          if (wrapper.href.substr(0, 5) === "data:")
            return true;
 
          try {
            urlSecurityCheck(wrapper.href, wrapper.ownerDocument.nodePrincipal);
          }
          catch(ex) {
            return false;
-         } 
+         }
 
          var postData = { };
          var url = getShortcutOrURI(wrapper.href, postData);
          if (!url)
            return true;
          loadURI(url, null, postData.value, false);
          event.preventDefault();
          return false;
@@ -5003,24 +4970,24 @@ function handleLinkClick(event, href, li
       if (event.ctrlKey) {
 #endif
         openNewTabWith(href, doc, null, event, false);
         event.stopPropagation();
         return true;
       }
 
       if (event.shiftKey && event.altKey) {
-        var feedService = 
+        var feedService =
             Cc["@mozilla.org/browser/feeds/result-service;1"].
             getService(Ci.nsIFeedResultService);
         feedService.forcePreviewPage = true;
         loadURI(href, null, null, false);
         return false;
       }
-                                                       
+
       if (event.shiftKey) {
         openNewWindowWith(href, doc, null, false);
         event.stopPropagation();
         return true;
       }
 
       if (event.altKey) {
         saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
@@ -5371,17 +5338,17 @@ var BrowserOffline = {
   {
     var ioService = Services.io;
 
     // Stop automatic management of the offline status
     try {
       ioService.manageOfflineStatus = false;
     } catch (ex) {
     }
-  
+
     if (!ioService.offline && !this._canGoOffline()) {
       this._updateOfflineUI(false);
       return;
     }
 
     ioService.offline = !ioService.offline;
 
     // Save the current state for later use as the initial state
@@ -5825,17 +5792,17 @@ function AddKeywordForSearchField() {
                        charset);
 
   var formURI = makeURI(node.form.getAttribute("action"),
                         charset,
                         docURI);
 
   var spec = formURI.spec;
 
-  var isURLEncoded = 
+  var isURLEncoded =
                (node.form.method.toUpperCase() == "POST"
                 && (node.form.enctype == "application/x-www-form-urlencoded" ||
                     node.form.enctype == ""));
 
   var title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
                                                   [node.ownerDocument.title]);
   var description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument);
 
@@ -5858,17 +5825,17 @@ function AddKeywordForSearchField() {
     if (el == node) {
       formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) :
                                      // Don't escape "%s", just append
                                      escapeNameValuePair(el.name, "", false) + "%s");
       continue;
     }
 
     type = el.type.toLowerCase();
-    
+
     if ((type == "text" || type == "hidden" || type == "textarea") ||
         ((type == "checkbox" || type == "radio") && el.checked)) {
       formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded));
     } else if (el instanceof HTMLSelectElement && el.selectedIndex >= 0) {
       for (var j=0; j < el.options.length; j++) {
         if (el.options[j].selected)
           formData.push(escapeNameValuePair(el.name, el.options[j].value,
                                             isURLEncoded));
@@ -5907,17 +5874,17 @@ function getPluginInfo(pluginElement)
     }
 
     // only attempt if a pluginsPage is defined.
     if (pluginsPage) {
       var doc = pluginElement.ownerDocument;
       var docShell = findChildShell(doc, gBrowser.docShell, null);
       try {
         pluginsPage = makeURI(pluginsPage, doc.characterSet, docShell.currentURI).spec;
-      } catch (ex) { 
+      } catch (ex) {
         pluginsPage = "";
       }
     }
 
     tagMimetype = pluginElement.QueryInterface(Components.interfaces.nsIObjectLoadingContent)
                                .actualType;
 
     if (tagMimetype == "") {
@@ -5939,62 +5906,100 @@ var gMissingPluginInstaller = {
   get crashReportHelpURL() {
     delete this.crashReportHelpURL;
     let url = formatURL("app.support.baseURL", true);
     url += "plugin-crashed";
     this.crashReportHelpURL = url;
     return this.crashReportHelpURL;
   },
 
+  addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) {
+    // XXX just doing (callback)(arg) was giving a same-origin error. bug?
+    let self = this;
+    let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
+    linkNode.addEventListener("click",
+                              function(evt) {
+                                if (!evt.isTrusted)
+                                  return;
+                                evt.preventDefault();
+                                if (callbackArgs.length == 0)
+                                  callbackArgs = [ evt ];
+                                (self[callbackName]).apply(self, callbackArgs);
+                              },
+                              true);
+
+    linkNode.addEventListener("keydown",
+                              function(evt) {
+                                if (!evt.isTrusted)
+                                  return;
+                                if (evt.keyCode == evt.DOM_VK_RETURN) {
+                                  evt.preventDefault();
+                                  if (callbackArgs.length == 0)
+                                    callbackArgs = [ evt ];
+                                  evt.preventDefault();
+                                  (self[callbackName]).apply(self, callbackArgs);
+                                }
+                              },
+                              true);
+  },
+
+  // Callback for user clicking on a missing (unsupported) plugin.
   installSinglePlugin: function (aEvent) {
-    if (!aEvent.isTrusted)
-        return;
     var missingPluginsArray = {};
 
     var pluginInfo = getPluginInfo(aEvent.target);
     missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
 
-    if (missingPluginsArray) {
-      openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
-                 "PFSWindow", "chrome,centerscreen,resizable=yes",
-                 {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
-    }
-
-    aEvent.stopPropagation();
-  },
-
+    openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
+               "PFSWindow", "chrome,centerscreen,resizable=yes",
+               {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
+  },
+
+  // Callback for user clicking on a disabled plugin
   managePlugins: function (aEvent) {
-    if (!aEvent.isTrusted)
-        return;
     BrowserOpenAddonsMgr("plugins");
-    aEvent.stopPropagation();
-  },
-
+  },
+
+  // Callback for user clicking "submit a report" link
+  submitReport : function(pluginDumpID, browserDumpID) {
+    // The crash reporter wants a DOM element it can append an IFRAME to,
+    // which it uses to submit a form. Let's just give it gBrowser.
+    this.CrashSubmit.submit(pluginDumpID, gBrowser, null, null);
+    if (browserDumpID)
+      this.CrashSubmit.submit(browserDumpID, gBrowser, null, null);
+  },
+
+  // Callback for user clicking a "reload page" link
+  reloadPage: function (browser) {
+    browser.reload();
+  },
+
+  // Callback for user clicking the help icon
+  openHelpPage: function () {
+    openHelpLink("plugin-crashed", false);
+  },
+
+
+
+  // event listener for missing/blocklisted/outdated plugins.
   newMissingPlugin: function (aEvent) {
     // Since we are expecting also untrusted events, make sure
     // that the target is a plugin
     if (!(aEvent.target instanceof Ci.nsIObjectLoadingContent))
       return;
 
     // For broken non-object plugin tags, register a click handler so
     // that the user can click the plugin replacement to get the new
     // plugin. Object tags can, and often do, deal with that themselves,
     // so don't stomp on the page developers toes.
 
     if (aEvent.type != "PluginBlocklisted" &&
         aEvent.type != "PluginOutdated" &&
         !(aEvent.target instanceof HTMLObjectElement)) {
-      aEvent.target.addEventListener("click",
-                                     gMissingPluginInstaller.installSinglePlugin,
-                                     true);
-      aEvent.target.addEventListener("keydown",
-                                     function(evt) { if (evt.keyCode == evt.DOM_VK_RETURN)
-                                                       gMissingPluginInstaller.installSinglePlugin(evt) },
-                                     true);
-                                                    
+          gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "installSinglePlugin");
     }
 
     let hideBarPrefName = aEvent.type == "PluginOutdated" ?
                     "plugins.hide_infobar_for_outdated_plugin" :
                     "plugins.hide_infobar_for_missing_plugin";
     if (gPrefService.getBoolPref(hideBarPrefName))
       return;
 
@@ -6093,66 +6098,72 @@ var gMissingPluginInstaller = {
       let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
       let messageString = gNavigatorBundle.getString("missingpluginsMessage.title");
       let buttons = [{
         label: gNavigatorBundle.getString("missingpluginsMessage.button.label"),
         accessKey: gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"),
         popup: null,
         callback: showPluginsMissing
       }];
-    
+
       notificationBox.appendNotification(messageString, "missing-plugins",
                                          iconURL, priority, buttons);
     }
   },
 
   newDisabledPlugin: function (aEvent) {
     // Since we are expecting also untrusted events, make sure
     // that the target is a plugin
     if (!(aEvent.target instanceof Ci.nsIObjectLoadingContent))
       return;
 
-    aEvent.target.addEventListener("click",
-                                   gMissingPluginInstaller.managePlugins,
-                                   true);
-    aEvent.target.addEventListener("keydown",
-                                   function(evt) { if (evt.keyCode == evt.DOM_VK_RETURN)
-                                                     gMissingPluginInstaller.managePlugins(evt) },
-                                   true);
+    gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "managePlugins");
   },
 
   // Crashed-plugin observer. Notified once per plugin crash, before events
   // are dispatched to individual plugin instances.
   pluginCrashed : function(subject, topic, data) {
     let propertyBag = subject;
     if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
         !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
      return;
 
 #ifdef MOZ_CRASHREPORTER
-    let minidumpID = subject.getPropertyAsAString("minidumpID");
-    let submitted = gCrashReporter.submitReports && minidumpID.length;
-    // The crash reporter wants a DOM element it can append an IFRAME to,
-    // which it uses to submit a form. Let's just give it gBrowser.
-    if (submitted)
-      submitted = gMissingPluginInstaller.CrashSubmit.submit(minidumpID, gBrowser, null, null);
-    propertyBag.setPropertyAsBool("submittedCrashReport", submitted);
+    let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
+    let browserDumpID= propertyBag.getPropertyAsAString("browserDumpID");
+    let shouldSubmit = gCrashReporter.submitReports;
+    let doPrompt     = true; // XXX followup to get via gCrashReporter
+
+    // Submit automatically when appropriate.
+    if (pluginDumpID && shouldSubmit && !doPrompt) {
+      this.submitReport(pluginDumpID, browserDumpID);
+      // Submission is async, so we can't easily show failure UI.
+      propertyBag.setPropertyAsBool("submittedCrashReport", true);
+    }
 #endif
   },
 
+  // Crashed-plugin event listener. Called for every instance of a
+  // plugin in content.
   pluginInstanceCrashed: function (aEvent) {
+    let self = gMissingPluginInstaller;
+
     // Evil content could fire a fake event at us, ignore them.
     if (!aEvent.isTrusted)
       return;
 
     if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent))
       return;
 
     let submittedReport = aEvent.getData("submittedCrashReport");
+    let doPrompt        = true; // XXX followup for .getData("doPrompt");
+    let submitReports   = true; // XXX followup for .getData("submitReports");
     let pluginName      = aEvent.getData("pluginName");
+    let pluginDumpID    = aEvent.getData("pluginDumpID");
+    let browserDumpID   = aEvent.getData("browserDumpID");
 
     // We're expecting this to be a plugin.
     let plugin = aEvent.target;
     if (!(plugin instanceof Ci.nsIObjectLoadingContent))
       return;
 
     // Force a style flush, so that we ensure our binding is attached.
     plugin.clientTop;
@@ -6166,99 +6177,188 @@ var gMissingPluginInstaller = {
     let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
 
     // The binding has role="link" here, since missing/disabled/blocked
     // plugin UI has a onclick handler on the whole thing. This isn't needed
     // for the plugin-crashed UI, because we use actual HTML links in the text.
     overlay.removeAttribute("role");
 
 #ifdef MOZ_CRASHREPORTER
+    // Determine which message to show regarding crash reports.
     let helpClass, showClass;
-
-    // If we didn't submit a report but don't have submission disabled,
-    // we probably just didn't collect a crash report; don't put up any 
-    // special crashing text.
-    if (submittedReport) {
-      helpClass = "submitLink";
+    if (submittedReport) { // submitReports && !doPrompt, handled in observer
       showClass = "msg msgSubmitted";
     }
-    else if (!gCrashReporter.submitReports) {
-      helpClass = "notSubmitLink";
+    else if (!submitReports && !doPrompt) {
       showClass = "msg msgNotSubmitted";
     }
-
-    if (helpClass) {
-      let helpLink = doc.getAnonymousElementByAttribute(plugin, "class", helpClass);
-      helpLink.href = gMissingPluginInstaller.crashReportHelpURL;
-      let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass);
-      textToShow.style.display = "block";
+    else { // doPrompt
+      showClass = "msg msgPleaseSubmit";
+      // XXX can we make the link target actually be blank?
+      let pleaseLink = doc.getAnonymousElementByAttribute(
+                            plugin, "class", "pleaseSubmitLink");
+      self.addLinkClickCallback(pleaseLink, "submitReport",
+                                pluginDumpID, browserDumpID);
+    }
+
+    // If we don't have a minidumpID, we can't (or didn't) submit anything.
+    // This can happen if the plugin is killed from the task manager.
+    if (!pluginDumpID) {
+        showClass = "msg msgNoCrashReport";
+    }
+
+    let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass);
+    textToShow.style.display = "block";
+
+    let bottomLinks = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgBottomLinks");
+    bottomLinks.style.display = "block";
+    let helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon");
+    self.addLinkClickCallback(helpIcon, "openHelpPage");
+
+    // If we're showing the link to manually trigger report submission, we'll
+    // want to be able to update all the instances of the UI for this crash to
+    // show an updated message when a report is submitted.
+    if (doPrompt) {
+      let observer = {
+        QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                               Ci.nsISupportsWeakReference]),
+        observe : function(subject, topic, data) {
+          let propertyBag = subject;
+          if (!(propertyBag instanceof Ci.nsIPropertyBag2))
+            return;
+          // Ignore notifications for other crashes.
+          if (propertyBag.get("minidumpID") != pluginDumpID)
+            return;
+          self.updateSubmissionStatus(plugin, propertyBag, data);
+        },
+
+        handleEvent : function(event) {
+            // Not expected to be called, just here for the closure.
+        }
+      }
+
+      // Use a weak reference, so we don't have to remove it...
+      Services.obs.addObserver(observer, "crash-report-status", true);
+      // ...alas, now we need something to hold a strong reference to prevent
+      // it from being GC. But I don't want to manually manage the reference's
+      // lifetime (which should be no greater than the page).
+      // Clever solution? Use a closue with an event listener on the document.
+      // When the doc goes away, so do the listener references and the closure.
+      doc.addEventListener("mozCleverClosureHack", observer, false);
     }
 #endif
 
     let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
     crashText.textContent = messageString;
 
+    let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
+
     let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
-    link.addEventListener("click", function(e) { if (e.isTrusted) browser.reload(); }, true);
-
-    let browser = gBrowser.getBrowserForDocument(plugin.ownerDocument
-                                                       .defaultView.top.document);
+    self.addLinkClickCallback(link, "reloadPage", browser);
+
     let notificationBox = gBrowser.getNotificationBox(browser);
 
     // Is the <object>'s size too small to hold what we want to show?
     let pluginRect = plugin.getBoundingClientRect();
     // XXX bug 446693. The text-shadow on the submitted-report text at
     //     the bottom causes scrollHeight to be larger than it should be.
     let isObjectTooSmall = (overlay.scrollWidth > pluginRect.width) ||
                            (overlay.scrollHeight - 5 > pluginRect.height);
     if (isObjectTooSmall) {
         // Hide the overlay's contents. Use visibility style, so that it
         // doesn't collapse down to 0x0.
         overlay.style.visibility = "hidden";
         // If another plugin on the page was large enough to show our UI, we
         // don't want to show a notification bar.
         if (!doc.mozNoPluginCrashedNotification)
-          showNotificationBar();
+          showNotificationBar(pluginDumpID, browserDumpID);
     } else {
         // If a previous plugin on the page was too small and resulted in
         // adding a notification bar, then remove it because this plugin
         // instance it big enough to serve as in-content notification.
         hideNotificationBar();
         doc.mozNoPluginCrashedNotification = true;
     }
 
     function hideNotificationBar() {
       let notification = notificationBox.getNotificationWithValue("plugin-crashed");
       if (notification)
         notificationBox.removeNotification(notification, true);
     }
 
-    function showNotificationBar() {
+    function showNotificationBar(pluginDumpID, browserDumpID) {
       // If there's already an existing notification bar, don't do anything.
       let notification = notificationBox.getNotificationWithValue("plugin-crashed");
       if (notification)
         return;
 
       // Configure the notification bar
       let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
       let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
-      let label = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
-      let accessKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
+      let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
+      let reloadKey   = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
+      let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
+      let submitKey   = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
 
       let buttons = [{
-        label: label,
-        accessKey: accessKey,
+        label: reloadLabel,
+        accessKey: reloadKey,
         popup: null,
         callback: function() { browser.reload(); },
       }];
+#ifdef MOZ_CRASHREPORTER
+      let submitButton = {
+        label: submitLabel,
+        accessKey: submitKey,
+        popup: null,
+          callback: function() { gMissingPluginInstaller.submitReport(pluginDumpID, browserDumpID); },
+      };
+      if (minidumpID)
+        buttons.push(submitButton);
+#endif
 
       let notification = notificationBox.appendNotification(messageString, "plugin-crashed",
                                                             iconURL, priority, buttons);
-    }
-
+
+      // Add the "learn more" link.
+      let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+      let link = notification.ownerDocument.createElementNS(XULNS, "label");
+      link.className = "text-link";
+      link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
+      link.href = gMissingPluginInstaller.crashReportHelpURL;
+      let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
+      description.appendChild(link);
+    }
+
+  },
+
+  updateSubmissionStatus : function (plugin, propBag, status) {
+    let doc = plugin.ownerDocument;
+
+    // One of these two may already be visible, reset them to be hidden.
+    let pleaseText     = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgPleaseSubmit");
+    let submittingText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgSubmitting");
+    pleaseText.style.display = "";
+    submittingText.style.display = "";
+
+    let msgClass;
+    switch (status) {
+      case "submitting":
+        msgClass = "msg msgSubmitting";
+        break;
+      case "success":
+        msgClass = "msg msgSubmitted";
+        break;
+      case "failed":
+        msgClass = "msg msgSubmitFailed";
+        break;
+    }
+
+    let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", msgClass);
+    textToShow.style.display = "block";
   },
 
   refreshBrowser: function (aEvent) {
     // browser elements are anonymous so we can't just use target.
     var browser = aEvent.originalTarget;
     var notificationBox = gBrowser.getNotificationBox(browser);
     var notification = notificationBox.getNotificationWithValue("missing-plugins");
 
@@ -6278,45 +6378,45 @@ function convertFromUnicode(charset, str
   try {
     var unicodeConverter = Components
        .classes["@mozilla.org/intl/scriptableunicodeconverter"]
        .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
     unicodeConverter.charset = charset;
     str = unicodeConverter.ConvertFromUnicode(str);
     return str + unicodeConverter.Finish();
   } catch(ex) {
-    return null; 
+    return null;
   }
 }
 
 /**
  * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
- * and shows UI when they are discovered. 
+ * and shows UI when they are discovered.
  */
 var FeedHandler = {
   /**
    * The click handler for the Feed icon in the location bar. Opens the
    * subscription page if user is not given a choice of feeds.
-   * (Otherwise the list of available feeds will be presented to the 
+   * (Otherwise the list of available feeds will be presented to the
    * user in a popup menu.)
    */
   onFeedButtonClick: function(event) {
     event.stopPropagation();
 
     if (event.target.hasAttribute("feed") &&
         event.eventPhase == Event.AT_TARGET &&
         (event.button == 0 || event.button == 1)) {
         this.subscribeToFeed(null, event);
     }
   },
-  
+
   /**
-   * Called when the user clicks on the Feed icon in the location bar. 
+   * Called when the user clicks on the Feed icon in the location bar.
    * Builds a menu of unique feeds associated with the page, and if there
-   * is only one, shows the feed inline in the browser window. 
+   * is only one, shows the feed inline in the browser window.
    * @param   menuPopup
    *          The feed list menupopup to be populated.
    * @returns true if the menu should be shown, false if there was only
    *          one feed and the feed should be shown inline in the browser
    *          window (do not show the menupopup).
    */
   buildFeedList: function(menuPopup) {
     var feeds = gBrowser.selectedBrowser.feeds;
@@ -6336,43 +6436,43 @@ var FeedHandler = {
 
     if (feeds.length == 1) {
       var feedButton = document.getElementById("feed-button");
       if (feedButton)
         feedButton.setAttribute("feed", feeds[0].href);
       return false;
     }
 
-    // Build the menu showing the available feed choices for viewing. 
+    // Build the menu showing the available feed choices for viewing.
     for (var i = 0; i < feeds.length; ++i) {
       var feedInfo = feeds[i];
       var menuItem = document.createElement("menuitem");
       var baseTitle = feedInfo.title || feedInfo.href;
       var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
       menuItem.setAttribute("class", "feed-menuitem");
       menuItem.setAttribute("label", labelStr);
       menuItem.setAttribute("feed", feedInfo.href);
       menuItem.setAttribute("tooltiptext", feedInfo.href);
       menuItem.setAttribute("crop", "center");
       menuPopup.appendChild(menuItem);
     }
     return true;
   },
-  
+
   /**
    * Subscribe to a given feed.  Called when
    *   1. Page has a single feed and user clicks feed icon in location bar
    *   2. Page has a single feed and user selects Subscribe menu item
    *   3. Page has multiple feeds and user selects from feed icon popup
    *   4. Page has multiple feeds and user selects from Subscribe submenu
    * @param   href
    *          The feed to subscribe to. May be null, in which case the
    *          event target's feed attribute is examined.
    * @param   event
-   *          The event this method is handling. Used to decide where 
+   *          The event this method is handling. Used to decide where
    *          to open the preview UI. (Optional, unless href is null)
    */
   subscribeToFeed: function(href, event) {
     // Just load the feed in the content area to either subscribe or show the
     // preview UI
     if (!href)
       href = event.target.getAttribute("feed");
     urlSecurityCheck(href, gBrowser.contentPrincipal,
@@ -6386,25 +6486,25 @@ var FeedHandler = {
   },
 
   loadFeed: function(href, event) {
     var feeds = gBrowser.selectedBrowser.feeds;
     try {
       openUILink(href, event, false, true, false, null);
     }
     finally {
-      // We might default to a livebookmarks modal dialog, 
+      // We might default to a livebookmarks modal dialog,
       // so reset that if the user happens to click it again
       gBrowser.selectedBrowser.feeds = feeds;
     }
   },
 
   /**
    * Update the browser UI to show whether or not feeds are available when
-   * a page is loaded or the user switches tabs to a page that has feeds. 
+   * a page is loaded or the user switches tabs to a page that has feeds.
    */
   updateFeeds: function() {
     var feedButton = document.getElementById("feed-button");
     if (!this._feedMenuitem)
       this._feedMenuitem = document.getElementById("subscribeToPageMenuitem");
     if (!this._feedMenupopup)
       this._feedMenupopup = document.getElementById("subscribeToPageMenupopup");
 
@@ -6415,33 +6515,33 @@ var FeedHandler = {
         feedButton.removeAttribute("feed");
       }
       this._feedMenuitem.setAttribute("disabled", "true");
       this._feedMenupopup.setAttribute("hidden", "true");
       this._feedMenuitem.removeAttribute("hidden");
     } else {
       if (feedButton)
         feedButton.collapsed = false;
-      
+
       if (feeds.length > 1) {
         this._feedMenuitem.setAttribute("hidden", "true");
         this._feedMenupopup.removeAttribute("hidden");
         if (feedButton)
           feedButton.removeAttribute("feed");
       } else {
         if (feedButton)
           feedButton.setAttribute("feed", feeds[0].href);
 
         this._feedMenuitem.setAttribute("feed", feeds[0].href);
         this._feedMenuitem.removeAttribute("disabled");
         this._feedMenuitem.removeAttribute("hidden");
         this._feedMenupopup.setAttribute("hidden", "true");
       }
     }
-  }, 
+  },
 
   addFeed: function(link, targetDoc) {
     // find which tab this is for, and set the attribute on the browser
     var browserForLink = gBrowser.getBrowserForDocument(targetDoc);
     if (!browserForLink) {
       // ignore feeds loaded in subframes (see bug 305472)
       return;
     }
@@ -6463,34 +6563,34 @@ var FeedHandler = {
  * Re-open a closed tab.
  * @param aIndex
  *        The index of the tab (via nsSessionStore.getClosedTabData)
  * @returns a reference to the reopened tab.
  */
 function undoCloseTab(aIndex) {
   // wallpaper patch to prevent an unnecessary blank tab (bug 343895)
   var blankTabToRemove = null;
-  if (gBrowser.tabContainer.childNodes.length == 1 &&
+  if (gBrowser.tabs.length == 1 &&
       !gPrefService.getBoolPref("browser.tabs.autoHide") &&
       gBrowser.sessionHistory.count < 2 &&
       gBrowser.currentURI.spec == "about:blank" &&
       !gBrowser.contentDocument.body.hasChildNodes() &&
       !gBrowser.selectedTab.hasAttribute("busy"))
     blankTabToRemove = gBrowser.selectedTab;
 
   var tab = null;
   var ss = Cc["@mozilla.org/browser/sessionstore;1"].
            getService(Ci.nsISessionStore);
   if (ss.getClosedTabCount(window) > (aIndex || 0)) {
     tab = ss.undoCloseTab(window, aIndex || 0);
-    
+
     if (blankTabToRemove)
       gBrowser.removeTab(blankTabToRemove);
   }
-  
+
   return tab;
 }
 
 /**
  * Re-open a closed window.
  * @param aIndex
  *        The index of the window (via nsSessionStore.getClosedWindowData)
  * @returns a reference to the reopened window.
@@ -6526,17 +6626,17 @@ var gBookmarkAllTabsHandler = {
   init: function () {
     this._command = document.getElementById("Browser:BookmarkAllTabs");
     gBrowser.tabContainer.addEventListener("TabOpen", this, true);
     gBrowser.tabContainer.addEventListener("TabClose", this, true);
     this._updateCommandState();
   },
 
   _updateCommandState: function BATH__updateCommandState(aTabClose) {
-    var numTabs = gBrowser.tabContainer.childNodes.length;
+    var numTabs = gBrowser.tabs.length;
 
     // The TabClose event is fired before the tab is removed from the DOM
     if (aTabClose)
       numTabs--;
 
     if (numTabs > 1)
       this._command.removeAttribute("disabled");
     else
@@ -6649,55 +6749,55 @@ var gIdentityHandler = {
   /**
    * Handler for mouseclicks on the "More Information" button in the
    * "identity-popup" panel.
    */
   handleMoreInfoClick : function(event) {
     displaySecurityInfo();
     event.stopPropagation();
   },
-  
+
   /**
    * Helper to parse out the important parts of _lastStatus (of the SSL cert in
    * particular) for use in constructing identity UI strings
   */
   getIdentityData : function() {
     var result = {};
     var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
     var cert = status.serverCert;
-    
+
     // Human readable name of Subject
     result.subjectOrg = cert.organization;
-    
+
     // SubjectName fields, broken up for individual access
     if (cert.subjectName) {
       result.subjectNameFields = {};
       cert.subjectName.split(",").forEach(function(v) {
         var field = v.split("=");
         this[field[0]] = field[1];
       }, result.subjectNameFields);
-      
+
       // Call out city, state, and country specifically
       result.city = result.subjectNameFields.L;
       result.state = result.subjectNameFields.ST;
       result.country = result.subjectNameFields.C;
     }
-    
+
     // Human readable name of Certificate Authority
     result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
     result.cert = cert;
-    
+
     return result;
   },
-  
+
   /**
    * Determine the identity of the page being displayed by examining its SSL cert
    * (if available) and, if necessary, update the UI to reflect this.  Intended to
    * be called by onSecurityChange
-   * 
+   *
    * @param PRUint32 state
    * @param JS Object location that mirrors an nsLocation (i.e. has .host and
    *                           .hostname and .port)
    */
   checkIdentity : function(state, location) {
     var currentStatus = gBrowser.securityUI
                                 .QueryInterface(Components.interfaces.nsISSLStatusProvider)
                                 .SSLStatus;
@@ -6709,96 +6809,96 @@ var gIdentityHandler = {
       this.setMode(this.IDENTITY_MODE_IDENTIFIED);
     else if (state & nsIWebProgressListener.STATE_SECURE_HIGH)
       this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
     else if (state & nsIWebProgressListener.STATE_IS_BROKEN)
       this.setMode(this.IDENTITY_MODE_MIXED_CONTENT);
     else
       this.setMode(this.IDENTITY_MODE_UNKNOWN);
   },
-  
+
   /**
    * Return the eTLD+1 version of the current hostname
    */
   getEffectiveHost : function() {
     // Cache the eTLDService if this is our first time through
     if (!this._eTLDService)
       this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
                          .getService(Ci.nsIEffectiveTLDService);
     try {
       return this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
     } catch (e) {
       // If something goes wrong (e.g. hostname is an IP address) just fail back
       // to the full domain.
       return this._lastLocation.hostname;
     }
   },
-  
+
   /**
    * Update the UI to reflect the specified mode, which should be one of the
    * IDENTITY_MODE_* constants.
    */
   setMode : function(newMode) {
     if (!this._identityBox) {
       // No identity box means the identity box is not visible, in which
       // case there's nothing to do.
       return;
     }
 
     this._identityBox.className = newMode;
     this.setIdentityMessages(newMode);
-    
+
     // Update the popup too, if it's open
     if (this._identityPopup.state == "open")
       this.setPopupMessages(newMode);
   },
-  
+
   /**
    * Set up the messages for the primary identity UI based on the specified mode,
    * and the details of the SSL cert, where applicable
    *
    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
   setIdentityMessages : function(newMode) {
     if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
-      var iData = this.getIdentityData();     
-      
+      var iData = this.getIdentityData();
+
       // It would be sort of nice to use the CN= field in the cert, since that's
       // typically what we want here, but thanks to x509 certs being extensible,
       // it's not the only place you have to check, there can be more than one domain,
       // et cetera, ad nauseum.  We know the cert is valid for location.host, so
       // let's just use that. Check the pref to determine how much of the verified
       // hostname to show
       var icon_label = "";
       var icon_country_label = "";
       var icon_labels_dir = "ltr";
       switch (gPrefService.getIntPref("browser.identity.ssl_domain_display")) {
         case 2 : // Show full domain
           icon_label = this._lastLocation.hostname;
           break;
         case 1 : // Show eTLD.
           icon_label = this.getEffectiveHost();
       }
-      
+
       // We need a port number for all lookups.  If one hasn't been specified, use
       // the https default
       var lookupHost = this._lastLocation.host;
       if (lookupHost.indexOf(':') < 0)
         lookupHost += ":443";
 
       // Verifier is either the CA Org, for a normal cert, or a special string
       // for certs that are trusted because of a security exception.
       var tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
                                                         [iData.caOrg]);
-      
+
       // Check whether this site is a security exception. XPConnect does the right
       // thing here in terms of converting _lastLocation.port from string to int, but
       // the overrideService doesn't like undefined ports, so make sure we have
       // something in the default case (bug 432241).
-      if (this._overrideService.hasMatchingOverride(this._lastLocation.hostname, 
+      if (this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
                                                     (this._lastLocation.port || 443),
                                                     iData.cert, {}, {}))
         tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
     }
     else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
       // If it's identified, then we can populate the dialog with credentials
       iData = this.getIdentityData();
       tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
@@ -6816,115 +6916,115 @@ var gIdentityHandler = {
                         "rtl" : "ltr";
     }
     else {
       tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");
       icon_label = "";
       icon_country_label = "";
       icon_labels_dir = "ltr";
     }
-    
+
     // Push the appropriate strings out to the UI
     this._identityBox.tooltipText = tooltip;
     this._identityIconLabel.value = icon_label;
     this._identityIconCountryLabel.value = icon_country_label;
     // Set cropping and direction
     this._identityIconLabel.crop = icon_country_label ? "end" : "center";
     this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
     // Hide completely if the organization label is empty
     this._identityIconLabel.parentNode.hidden = icon_label ? false : true;
   },
-  
+
   /**
    * Set up the title and content messages for the identity message popup,
    * based on the specified mode, and the details of the SSL cert, where
    * applicable
    *
    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
   setPopupMessages : function(newMode) {
-      
+
     this._identityPopup.className = newMode;
     this._identityPopupContentBox.className = newMode;
-    
+
     // Set the static strings up front
     this._identityPopupEncLabel.textContent = this._encryptionLabel[newMode];
-    
+
     // Initialize the optional strings to empty values
     var supplemental = "";
     var verifier = "";
-    
+
     if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
       var iData = this.getIdentityData();
       var host = this.getEffectiveHost();
       var owner = gNavigatorBundle.getString("identity.ownerUnknown2");
       verifier = this._identityBox.tooltipText;
       supplemental = "";
     }
     else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
       // If it's identified, then we can populate the dialog with credentials
       iData = this.getIdentityData();
       host = this.getEffectiveHost();
-      owner = iData.subjectOrg; 
+      owner = iData.subjectOrg;
       verifier = this._identityBox.tooltipText;
 
       // Build an appropriate supplemental block out of whatever location data we have
       if (iData.city)
-        supplemental += iData.city + "\n";        
+        supplemental += iData.city + "\n";
       if (iData.state && iData.country)
         supplemental += gNavigatorBundle.getFormattedString("identity.identified.state_and_country",
                                                             [iData.state, iData.country]);
       else if (iData.state) // State only
         supplemental += iData.state;
       else if (iData.country) // Country only
         supplemental += iData.country;
     }
     else {
       // These strings will be hidden in CSS anyhow
       host = "";
       owner = "";
     }
-    
+
     // Push the appropriate strings out to the UI
     this._identityPopupContentHost.textContent = host;
     this._identityPopupContentOwner.textContent = owner;
     this._identityPopupContentSupp.textContent = supplemental;
     this._identityPopupContentVerif.textContent = verifier;
   },
 
   hideIdentityPopup : function() {
     this._identityPopup.hidePopup();
   },
 
   /**
-   * Click handler for the identity-box element in primary chrome.  
+   * Click handler for the identity-box element in primary chrome.
    */
   handleIdentityButtonEvent : function(event) {
-  
+
     event.stopPropagation();
- 
+
     if ((event.type == "click" && event.button != 0) ||
         (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
          event.keyCode != KeyEvent.DOM_VK_RETURN))
       return; // Left click, space or enter only
 
     // Revert the contents of the location bar, see bug 406779
     gURLBar.handleRevert();
 
     // Make sure that the display:none style we set in xul is removed now that
     // the popup is actually needed
     this._identityPopup.hidden = false;
-    
+
     // Tell the popup to consume dismiss clicks, to avoid bug 395314
     this._identityPopup.popupBoxObject
         .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
-    
+
     // Update the popup strings
     this.setPopupMessages(this._identityBox.className);
-    
+
     // Make sure the identity popup hangs toward the middle of the location bar
     // in RTL builds
     var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'after_end' : 'after_start';
 
     // Add the "open" attribute to the identity box for styling
     this._identityBox.setAttribute("open", "true");
     var self = this;
     this._identityPopup.addEventListener("popuphidden", function (e) {
@@ -6993,17 +7093,17 @@ let DownloadMonitorPanel = {
 
     // Hide the panel and reset the "last time" if there's no downloads
     if (numActive == 0) {
       this._panel.hidden = true;
       this._lastTime = Infinity;
 
       return;
     }
-  
+
     // Find the download with the longest remaining time
     let numPaused = 0;
     let maxTime = -Infinity;
     let dls = gDownloadMgr.activeDownloads;
     while (dls.hasMoreElements()) {
       let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
       if (dl.state == gDownloadMgr.DOWNLOAD_DOWNLOADING) {
         // Figure out if this download takes longer
@@ -7194,17 +7294,17 @@ let gPrivateBrowsingUI = {
       this._searchBarValue = BrowserSearch.searchBar.textbox.value;
 
     if (gFindBar)
       this._findBarValue = gFindBar.getElement("findbar-textbox").value;
 
     this._setPBMenuTitle("stop");
 
     document.getElementById("menu_import").setAttribute("disabled", "true");
-    
+
     // Disable the Clear Recent History... menu item when in PB mode
     // temporary fix until bug 463607 is fixed
     document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
 
     if (this._privateBrowsingService.autoStarted) {
       // Disable the menu item in auto-start mode
       document.getElementById("privateBrowsingItem")
               .setAttribute("disabled", "true");
@@ -7481,8 +7581,47 @@ var LightWeightThemeWebInstaller = {
     return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
   },
 
   _getThemeFromNode: function (node) {
     return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
                                     node.baseURI);
   }
 }
+
+function switchToTabHavingURI(aURI) {
+  function switchIfURIInWindow(aWindow) {
+    if (!("gBrowser" in aWindow))
+      return false;
+    let browsers = aWindow.gBrowser.browsers;
+    for (let i = 0; i < browsers.length; i++) {
+      let browser = browsers[i];
+      if (browser.currentURI.equals(aURI)) {
+        gURLBar.handleRevert();
+        aWindow.focus();
+        aWindow.gBrowser.tabContainer.selectedIndex = i;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // This can be passed either nsIURI or a string.
+  if (!(aURI instanceof Ci.nsIURI))
+    aURI = makeURI(aURI);
+
+  // Prioritise this window.
+  if (switchIfURIInWindow(window))
+    return true;
+
+  let winEnum = Services.wm.getEnumerator("navigator:browser");
+  while (winEnum.hasMoreElements()) {
+    let browserWin = winEnum.getNext();
+    // Skip closed (but not yet destroyed) windows,
+    // and the current window (which was checked earlier).
+    if (browserWin.closed || browserWin == window)
+      continue;
+    if (switchIfURIInWindow(browserWin))
+      return true;
+  }
+  // No opened tab has that url.
+  return false;
+}
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -271,19 +271,16 @@
         <button class="ctrlTab-preview" flex="1"/>
       </hbox>
       <hbox pack="center">
         <button id="ctrlTab-showAll" class="ctrlTab-preview" noicon="true"/>
       </hbox>
     </panel>
 
     <panel id="allTabs-panel" hidden="true" norestorefocus="true" ignorekeys="true"
-# XXX: KUI style disabled, because the transparent background slows down
-#      interacting with the panel, esp. the search field.
-#           class="KUI-panel"
            onmouseover="allTabs._updateTabCloseButton(event);">
       <hbox id="allTabs-meta" align="center">
         <spacer flex="1"/>
         <textbox id="allTabs-filter"
                  tooltiptext="&allTabs.filter.emptyText;"
                  type="search"
                  oncommand="allTabs.filter();"/>
         <spacer flex="1"/>
@@ -306,16 +303,18 @@
 
     <panel id="customizeToolbarSheetPopup"
            noautohide="true"
            onpopupshown="this.moveTo(this.boxObject.screenX + (window.innerWidth - this.boxObject.width) / 2, this.boxObject.screenY);">
       <iframe id="customizeToolbarSheetIFrame"
               style="&dialog.style;"
               hidden="true"/>
     </panel>
+
+    <tooltip id="tabbrowser-tab-tooltip" onpopupshowing="gBrowser.createTooltip(event);"/>
   </popupset>
 
   <toolbox id="navigator-toolbox" class="toolbox-top" mode="icons"
 #ifdef WINCE
            defaulticonsize="small" iconsize="small"
 #endif
            defaultmode="icons">
     <!-- Menu -->
@@ -400,16 +399,17 @@
                    title="&locationItem.title;" class="chromeclass-location" removable="true">
         <textbox id="urlbar" flex="1"
                  bookmarkhistoryplaceholder="&urlbar.bookmarkhistory.emptyText;"
                  bookmarkplaceholder="&urlbar.bookmark.emptyText;"
                  historyplaceholder="&urlbar.history.emptyText;"
                  noneplaceholder="&urlbar.none.emptyText;"
                  type="autocomplete"
                  autocompletesearch="history"
+                 autocompletesearchparam="enable-actions"
                  autocompletepopup="PopupAutoCompleteRichResult"
                  completeselectedindex="true"
                  tabscrolling="true"
                  showcommentcolumn="true"
                  showimagecolumn="true"
                  enablehistory="true"
                  maxrows="6"
                  newlines="stripsurroundingwhitespace"
@@ -438,16 +438,17 @@
                        onerror="this.removeAttribute('src');"/>
               </stack>
               <hbox id="identity-icon-labels">
                 <label id="identity-icon-label" class="plain" flex="1"/>
                 <label id="identity-icon-country-label" class="plain"/>
               </hbox>
             </hbox>
           </box>
+          <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
           <hbox id="urlbar-icons">
             <button type="menu"
                     style="-moz-user-focus: none"
                     class="plain urlbar-icon"
                     id="feed-button"
                     collapsed="true"
                     tooltiptext="&feedButton.tooltip;"
                     onclick="return FeedHandler.onFeedButtonClick(event);">
@@ -511,16 +512,29 @@
                context="placesContext"
                onclick="BookmarksEventHandler.onClick(event);"
                oncommand="BookmarksEventHandler.onCommand(event);"
                onpopupshowing="BookmarksEventHandler.onPopupShowing(event);"
                tooltip="bhTooltip" popupsinherittooltip="true"/>
       </toolbaritem>
     </toolbar>
 
+    <toolbar id="TabsToolbar"
+             fullscreentoolbar="true"
+             ordinal="100"
+             collapsed="true">
+      <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
+            tabbrowser="content"
+            flex="1"
+            setfocus="false"
+            tooltip="tabbrowser-tab-tooltip">
+        <tab class="tabbrowser-tab" selected="true"/>
+      </tabs>
+    </toolbar>
+
     <toolbarpalette id="BrowserToolbarPalette">
 
       <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&printButton.label;" command="cmd_print"
                      tooltiptext="&printButton.tooltip;"/>
 
       <toolbaritem id="navigator-throbber" title="&throbberItem.title;" align="center" pack="center"
                    mousethrough="always">
@@ -598,16 +612,17 @@
       <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true"
                 style="min-width: 14em; width: 18em; max-width: 36em;"/>
     </vbox>
 
     <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
     <vbox id="appcontent" flex="1">
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
+                  tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   ondrop="contentAreaDNDObserver.onDrop(event)"
                   onclick="return contentAreaClick(event, false);"/>
     </vbox>
   </hbox>
 
   <vbox id="browser-bottombox">
--- a/browser/base/content/fullscreen-video.xhtml
+++ b/browser/base/content/fullscreen-video.xhtml
@@ -32,17 +32,17 @@
 # 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 *****
 -->
-<html xmlns="http://www.w3.org/1999/xhtml">
+<html xmlns="http://www.w3.org/1999/xhtml" accelerated="11">
 <head>
   <style type="text/css"><![CDATA[
 
 html,
 body,
 video {
   height: 100%;
 }
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -1,14 +1,10 @@
-.tabbrowser-tabs {
-  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
-}
-
-.tabbrowser-tab {
-  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
+.tabbrowser-tabbox {
+  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabbox");
 }
 
 .tabbrowser-arrowscrollbox {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-arrowscrollbox");
 }
 
 .tabs-alltabs-popup {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup");
@@ -18,26 +14,23 @@
 .tabs-closebutton {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-close-tab-button");
 }
 
 .tab-close-button {
   display: none;
 }
 
-.tabbrowser-tabs:not([closebuttons="noclose"]):not([closebuttons="closeatend"]) > .tabbrowser-tab[selected="true"]:not(:only-child) > .tab-close-button {
+.tabbrowser-tabs:not([closebuttons="noclose"]):not([closebuttons="closeatend"]) > .tabbrowser-tab[selected="true"] > .tab-close-button,
+.tabbrowser-tabs[closebuttons="alltabs"] > .tabbrowser-tab > .tab-close-button {
   display: -moz-box;
 }
 
-.tabbrowser-tabs[closebuttons="alltabs"] > .tabbrowser-tab:not(:only-child) > .tab-close-button {
-  display: -moz-box;
-}
-
-.tabs-container:not([overflow="true"]) > .tabs-newtab-button,
-.tabs-container[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+.tabbrowser-tabs:not([overflow="true"]) > .tabs-newtab-button,
+.tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
   visibility: collapse;
 }
 
 .tabs-newtab-button > .toolbarbutton-text {
   display: none;
 }
 
 tabpanels {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -60,129 +60,60 @@
 
   <binding id="tabbrowser">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content>
       <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
-      <xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
+      <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
+                  flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
-        <xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
-                  anonid="strip"
-                  ondragstart="this.parentNode.parentNode._onDragStart(event);"
-                  ondragover="this.parentNode.parentNode._onDragOver(event);"
-                  ondrop="this.parentNode.parentNode._onDrop(event);"
-                  ondragend="this.parentNode.parentNode._onDragEnd(event);"
-                  ondragleave="this.parentNode.parentNode._onDragLeave(event);">
-          <xul:hbox align="start">
-            <xul:image class="tab-drop-indicator" anonid="tab-drop-indicator" collapsed="true"/>
-          </xul:hbox>
-          <xul:tooltip onpopupshowing="return this.parentNode.parentNode.parentNode.createTooltip(event);"/>
-          <xul:menupopup anonid="tabContextMenu" onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
-            <xul:menuitem id="context_newTab" label="&newTab.label;" accesskey="&newTab.accesskey;"
-                          command="cmd_newNavigatorTab"/>
-            <xul:menuseparator/>
-            <xul:menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
-                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
-                                     tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
-            <xul:menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
-                          tbattr="tabbrowser-multiple"
-                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
-                                     tabbrowser.reloadAllTabs(tabbrowser.mContextTab);"/>
-            <xul:menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
-                          tbattr="tabbrowser-multiple"
-                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
-                                     tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
-            <xul:menuseparator/>
-            <xul:menuitem id="context_openTabInWindow" label="&openTabInNewWindow.label;"
-                          accesskey="&openTabInNewWindow.accesskey;"
-                          tbattr="tabbrowser-multiple"
-                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
-                                     tabbrowser.replaceTabWithWindow(tabbrowser.mContextTab);"/>
-            <xul:menuseparator/>
-            <xul:menuitem id="context_bookmarkTab"
-                          label="&bookmarkThisTab.label;"
-                          accesskey="&bookmarkThisTab.accesskey;"
-                          oncommand="BookmarkThisTab();"/>
-            <xul:menuitem id="context_bookmarkAllTabs"
-                          label="&bookmarkAllTabs.label;"
-                          accesskey="&bookmarkAllTabs.accesskey;"
-                          command="Browser:BookmarkAllTabs"/>
-            <xul:menuitem id="context_undoCloseTab"
-                          label="&undoCloseTab.label;"
-                          accesskey="&undoCloseTab.accesskey;"
-                          command="History:UndoCloseTab"
-                          anonid="undoCloseTabMenuItem"/>
-            <xul:menuseparator/>
-            <xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
-                          tbattr="tabbrowser-multiple"
-                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
-                                     tabbrowser.removeTab(tabbrowser.mContextTab);"/>
-          </xul:menupopup>
-
-          <xul:tabs class="tabbrowser-tabs" flex="1"
-                    anonid="tabcontainer"
-                    setfocus="false"
-                    onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
-                    ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);">
-            <xul:tab selected="true" validate="never"
-                     onerror="this.removeAttribute('image');"
-                     maxwidth="250" width="0" minwidth="100" flex="100"
-                     class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
-          </xul:tabs>
-        </xul:hbox>
         <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:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
     </content>
     <implementation implements="nsIDOMEventListener">
-      <field name="mPrefs" readonly="true">
-        Components.classes['@mozilla.org/preferences-service;1']
-                  .getService(Components.interfaces.nsIPrefService)
-                  .getBranch(null);
+
+      <property name="tabContextMenu" readonly="true"
+                onget="return this.tabContainer.contextMenu;"/>
+
+      <field name="tabContainer" readonly="true">
+        document.getElementById(this.getAttribute("tabcontainer"));
+      </field>
+      <field name="tabs" readonly="true">
+        this.tabContainer.childNodes;
       </field>
       <field name="mURIFixup" readonly="true">
         Components.classes["@mozilla.org/docshell/urifixup;1"]
                   .getService(Components.interfaces.nsIURIFixup);
       </field>
       <field name="mFaviconService" readonly="true">
         Components.classes["@mozilla.org/browser/favicon-service;1"]
                   .getService(Components.interfaces.nsIFaviconService);
       </field>
+      <field name="mBrowserHistory" readonly="true">
+        Components.classes["@mozilla.org/browser/nav-history-service;1"]
+                  .getService(Components.interfaces.nsIBrowserHistory);
+      </field>
       <field name="mTabBox" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
       </field>
-      <field name="_tabDropIndicator">
-        document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator");
-      </field>
-      <field name="mStrip" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "strip");
-      </field>
-      <field name="mTabContainer" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
-      </field>
       <field name="mPanelContainer" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
       </field>
-      <field name="mTabs" readonly="true">
-        this.mTabContainer.childNodes
-      </field>
       <field name="mStringBundle">
         document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
       </field>
-      <field name="mUndoCloseTabMenuItem">
-        document.getAnonymousElementByAttribute(this, "anonid", "undoCloseTabMenuItem");
-      </field>
       <field name="mCurrentTab">
         null
       </field>
       <field name="_lastRelatedTab">
         null
       </field>
       <field name="mCurrentBrowser">
         null
@@ -200,36 +131,30 @@
         new Array()
       </field>
       <field name="mTabbedMode">
         false
       </field>
       <field name="mIsBusy">
         false
       </field>
-      <field name="mContextTab">
-        null
-      </field>
       <field name="arrowKeysShouldWrap" readonly="true">
 #ifdef XP_MACOSX
         true
 #else
         false
 #endif
       </field>
       <field name="mAddProgressListenerWasCalled">
         false
       </field>
       <field name="_browsers">
         null
       </field>
 
-      <field name="_blockDblClick">
-        false
-      </field>
       <field name="_autoScrollPopup">
         null
       </field>
 
       <field name="_previewMode">
         false
       </field>
 
@@ -282,17 +207,17 @@
       </method>
 
       <method name="_getTabForContentWindow">
         <parameter name="aWindow"/>
         <body>
         <![CDATA[
           for (let i = 0; i < this.browsers.length; i++) {
             if (this.browsers[i].contentWindow == aWindow)
-              return this.mTabs[i];
+              return this.tabs[i];
           }
           return null;
         ]]>
         </body>
       </method>
 
       <method name="getNotificationBox">
         <parameter name="aBrowser"/>
@@ -515,49 +440,60 @@
             },
 
             onLocationChange : function(aWebProgress, aRequest, aLocation)
             {
               // The document loaded correctly, clear the value if we should
               if (this.mBrowser.userTypedClear > 0)
                 this.mBrowser.userTypedValue = null;
 
+              // Don't clear the favicon if this onLocationChange was triggered
+              // by a pushState or a replaceState.  See bug 550565.
               if (aWebProgress.DOMWindow == this.mBrowser.contentWindow &&
-                  aWebProgress.isLoadingDocument)
-                this.mTabBrowser.getBrowserForTab(this.mTab).mIconURL = null;
+                  aWebProgress.isLoadingDocument &&
+                  !(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) {
+                this.mBrowser.mIconURL = null;
+              }
 
               // changing location, clear out the missing plugins list
               this.mBrowser.missingPlugins = null;
 
-              if (this.mBlank)
-                return;
-
-              if (this.mTabBrowser.mCurrentTab == this.mTab) {
-                for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
-                  let p = this.mTabBrowser.mProgressListeners[i];
+              var browserHistory = this.mTabBrowser.mBrowserHistory;
+              if ("lastURI" in this.mBrowser && this.mBrowser.lastURI)
+                browserHistory.unregisterOpenPage(this.mBrowser.lastURI);
+              browserHistory.registerOpenPage(aLocation);
+
+              if (!this.mBlank) {
+                if (this.mTabBrowser.mCurrentTab == this.mTab) {
+                  for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
+                    let p = this.mTabBrowser.mProgressListeners[i];
+                    if (p)
+                      try {
+                        p.onLocationChange(aWebProgress, aRequest, aLocation);
+                      } catch (e) {
+                        // don't inhibit other listeners
+                        Components.utils.reportError(e);
+                      }
+                  }
+                }
+
+                for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
+                  let p = this.mTabBrowser.mTabsProgressListeners[i];
                   if (p)
                     try {
-                      p.onLocationChange(aWebProgress, aRequest, aLocation);
+                      p.onLocationChange(this.mBrowser, aWebProgress, aRequest, aLocation);
                     } catch (e) {
                       // don't inhibit other listeners
                       Components.utils.reportError(e);
                     }
                 }
               }
 
-              for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
-                let p = this.mTabBrowser.mTabsProgressListeners[i];
-                if (p)
-                  try {
-                    p.onLocationChange(this.mBrowser, aWebProgress, aRequest, aLocation);
-                  } catch (e) {
-                    // don't inhibit other listeners
-                    Components.utils.reportError(e);
-                  }
-              }
+              this.mBrowser.lastURI = aLocation;
+
             },
 
             onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
             {
               if (this.mBlank)
                 return;
 
               if (this.mTabBrowser.mCurrentTab == this.mTab) {
@@ -742,33 +678,34 @@
           ]]>
         </body>
       </method>
 
       <method name="shouldLoadFavIcon">
         <parameter name="aURI"/>
         <body>
           <![CDATA[
-            return (aURI && this.mPrefs.getBoolPref("browser.chrome.site_icons") &&
-                    this.mPrefs.getBoolPref("browser.chrome.favicons") &&
+            return (aURI &&
+                    Services.prefs.getBoolPref("browser.chrome.site_icons") &&
+                    Services.prefs.getBoolPref("browser.chrome.favicons") &&
                     ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
           ]]>
         </body>
       </method>
 
       <method name="useDefaultIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             var browser = this.getBrowserForTab(aTab);
             var docURIObject = browser.contentDocument.documentURIObject; 
             if (browser.contentDocument instanceof ImageDocument) {
-              if (this.mPrefs.getBoolPref("browser.chrome.site_icons")) {
+              if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
                 try {
-                  var sz = this.mPrefs.getIntPref("browser.chrome.image_icons.max_size");
+                  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;
@@ -848,43 +785,21 @@
       <method name="updateTitlebar">
         <body>
           <![CDATA[
             this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
           ]]>
         </body>
       </method>
 
-      <method name="updatePopupMenu">
-        <parameter name="aPopupMenu"/>
-        <body>
-          <![CDATA[
-            this.mContextTab = document.popupNode.localName == "tab" ?
-                               document.popupNode : this.selectedTab;
-            var disabled = this.mTabs.length == 1;
-            var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
-            for (var i = 0; i < menuItems.length; i++)
-              menuItems[i].disabled = disabled;
-
-            // Session store
-            // XXXzeniko should't we just disable this item as we disable
-            // the tabbrowser-multiple items above - for consistency?
-            this.mUndoCloseTabMenuItem.hidden =
-              Cc["@mozilla.org/browser/sessionstore;1"].
-              getService(Ci.nsISessionStore).
-              getClosedTabCount(window) == 0;
-          ]]>
-        </body>
-      </method>
-
       <method name="updateCurrentBrowser">
         <parameter name="aForceUpdate"/>
         <body>
           <![CDATA[
-            var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex);
+            var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
             if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
               return;
 
             var oldTab = this.mCurrentTab;
 
             // Preview mode should not reset the owner
             if (!this._previewMode && oldTab != this.selectedTab)
               oldTab.owner = null;
@@ -920,17 +835,17 @@
               if (p)
                 try {
                   p.onLocationChange(webProgress, null, loc);
                   if (securityUI)
                     p.onSecurityChange(webProgress, null, securityUI.state);
 
                   // make sure that all status indicators are properly updated
                   if ("onUpdateCurrentBrowser" in p) {
-                    var listener = this.mTabListeners[this.mTabContainer.selectedIndex] || null;
+                    let listener = this.mTabListeners[this.tabContainer.selectedIndex] || null;
                     if (listener && listener.mStateFlags)
                       p.onUpdateCurrentBrowser(listener.mStateFlags, listener.mStatus,
                                                listener.mMessage, listener.mTotalProgress);
                   }
                 } catch (e) {
                   // don't inhibit other listeners or following code
                   Components.utils.reportError(e);
                 }
@@ -1003,32 +918,16 @@
           // 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>
 
-      <method name="onTabClick">
-        <parameter name="event"/>
-        <body>
-          <![CDATA[
-            if (event.button != 1 || event.target.localName != 'tab')
-              return;
-
-            if (this.mTabs.length > 1 ||
-                !this.mPrefs.getBoolPref("browser.tabs.closeWindowWithLastTab"))
-              this.removeTab(event.target);
-
-            event.stopPropagation();
-          ]]>
-        </body>
-      </method>
-
       <method name="setTabTitleLoading">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             aTab.label = this.mStringBundle.getString("tabs.loading");
             aTab.setAttribute("crop", "end");
             this._tabAttrModified(aTab);
           ]]>
@@ -1061,56 +960,32 @@
                   const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
                                                  .getService(Components.interfaces.nsITextToSubURI);
                   title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
                 } catch(ex) { /* Do nothing. */ }
 
                 crop = "center";
 
               } else // Still no title?  Fall back to our untitled string.
-                title = this.mStringBundle.getString("tabs.untitled");
+                title = this.mStringBundle.getString("tabs.emptyTabTitle");
             }
 
             aTab.label = title;
             aTab.setAttribute("crop", crop);
             this._tabAttrModified(aTab);
           ]]>
         </body>
       </method>
 
-      <method name="setStripVisibilityTo">
-        <parameter name="aShow"/>
-        <body>
-        <![CDATA[
-          this.mStrip.collapsed = !aShow;
-          if (aShow) {
-            // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
-            document.getElementById("menu_closeWindow").hidden = false;
-            document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.closeTab"));
-            if (!this.mTabbedMode)
-              this.enterTabbedMode();
-          }
-          else {
-            // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
-            document.getElementById("menu_closeWindow").hidden = true;
-            document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.close"));
-          }
-        ]]>
-        </body>
-      </method>
-
-      <method name="getStripVisibility">
-        <body>
-          return !this.mStrip.collapsed;
-        </body>
-      </method>
-
       <method name="enterTabbedMode">
         <body>
           <![CDATA[
+            if (this.mTabbedMode)
+              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 {
@@ -1164,17 +1039,17 @@
               aCharset              = params.charset;
               aPostData             = params.postData;
               aLoadInBackground     = params.inBackground;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aRelatedToCurrent     = params.relatedToCurrent;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
-                         this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
+                         Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   charset: aCharset,
                                   postData: aPostData,
                                   ownerTab: owner,
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   relatedToCurrent: aRelatedToCurrent});
@@ -1214,17 +1089,17 @@
             } catch (e) {
               // Ignore failure in case a URI is wrong, so we can continue
               // opening the next ones.
             }
           }
           else
             firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner});
 
-          var tabNum = this.mTabContainer.selectedIndex;
+          var tabNum = this.tabContainer.selectedIndex;
           for (let i = 1; i < aURIs.length; ++i) {
             let tab = this.addTab(aURIs[i]);
             if (aReplace)
               this.moveTabTo(tab, ++tabNum);
           }
 
           if (!aLoadInBackground) {
             if (firstTabAdded) {
@@ -1256,56 +1131,55 @@
               aPostData             = params.postData;
               aOwner                = params.ownerTab;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aRelatedToCurrent     = params.relatedToCurrent;
             }
 
             this._browsers = null; // invalidate cache
 
-            if (!this.mTabbedMode)
-              this.enterTabbedMode();
+            this.enterTabbedMode();
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(
               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                              "tab");
 
             var blank = !aURI || (aURI == "about:blank");
 
             if (blank)
-              t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
+              t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
             else
               t.setAttribute("label", aURI);
 
             t.setAttribute("crop", "end");
-            t.maxWidth = this.mTabContainer.mTabMaxWidth;
-            t.minWidth = this.mTabContainer.mTabMinWidth;
+            t.maxWidth = this.tabContainer.mTabMaxWidth;
+            t.minWidth = this.tabContainer.mTabMinWidth;
             t.width = 0;
             t.setAttribute("flex", "100");
             t.setAttribute("validate", "never");
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
-            this.mTabContainer.appendChild(t);
+            this.tabContainer.appendChild(t);
 
             if (this.tabContainer.mTabstrip._isRTLScrollbox) {
               /* In RTL UI, the tab is visually added to the left side of the
                * tabstrip. This means the tabstip has to be scrolled back in
                * order to make sure the same set of tabs is visible before and
                * after the new tab is added.  See bug 508816. */
 
-              this.tabContainer.mTabstrip.scrollByPixels(this.mTabs[0].clientWidth);
+              this.tabContainer.mTabstrip.scrollByPixels(this.tabs[0].clientWidth);
             }
 
-            // invalidate cache, because mTabContainer is about to change
-            this._browsers = null; 
+            // invalidate cache, because tabContainer is about to change
+            this._browsers = null;
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
             var b = document.createElementNS(
               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                              "browser");
@@ -1321,22 +1195,20 @@
             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");
             this.mPanelContainer.appendChild(notificationbox);
 
-            if (this.mStrip.collapsed &&
-                this.mTabs.length - this._removingTabs.length > 1)
-              this.setStripVisibilityTo(true);
+            this.tabContainer.updateVisibility();
 
             // wire up a progress listener for the new browser object.
-            var position = this.mTabContainer.childNodes.length-1;
+            var position = this.tabs.length - 1;
             var tabListener = this.mTabProgressListener(t, b, blank);
             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                      .createInstance(Components.interfaces.nsIWebProgress);
             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             this.mTabListeners[position] = tabListener;
             this.mTabFilters[position] = filter;
 
@@ -1393,43 +1265,42 @@
             }
 
             // Check if we're opening a tab related to the current tab and
             // move it to after the current tab.
             // aReferrerURI is null or undefined if the tab is opened from
             // an external application or bookmark, i.e. somewhere other
             // than the current tab.
             if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
-                this.mPrefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
+                Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
               let newTabPos = (this._lastRelatedTab ||
                                this.selectedTab)._tPos + 1;
               this.moveTabTo(t, newTabPos);
               this._lastRelatedTab = t;
             }
 
             return t;
           ]]>
         </body>
       </method>
 
       <method name="warnAboutClosingTabs">
       <parameter name="aAll"/>
       <body>
         <![CDATA[
-          var numTabs = this.mTabContainer.childNodes.length;
+          var numTabs = this.tabs.length;
           var reallyClose = true;
           if (numTabs <= 1)
             return reallyClose;
 
           const pref = "browser.tabs.warnOnClose";
-          var shouldPrompt = this.mPrefs.getBoolPref(pref);
+          var shouldPrompt = Services.prefs.getBoolPref(pref);
 
           if (shouldPrompt) {
-            var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
-                                          .getService(Components.interfaces.nsIPromptService);
+            let promptService = Services.prompt;
 
             //default to true: if it were false, we wouldn't get this far
             var warnOnClose = { value:true };
             var bundle = this.mStringBundle;
             var tabsToClose = numTabs;  //number of tabs to be removed
             if (!aAll)
               --tabsToClose;
 
@@ -1448,33 +1319,33 @@
                                                         + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
                                                         bundle.getString(closeKey),
                                                         null, null,
                                                         bundle.getString('tabs.closeWarningPromptMe'),
                                                         warnOnClose);
             reallyClose = (buttonPressed == 0);
             // don't set the pref unless they press OK and it's false
             if (reallyClose && !warnOnClose.value)
-              this.mPrefs.setBoolPref(pref, false);
+              Services.prefs.setBoolPref(pref, false);
           }
           return reallyClose;
         ]]>
       </body>
       </method>
 
       <method name="removeAllTabsBut">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             if (this.warnAboutClosingTabs(false)) {
               this.selectedTab = aTab;
 
-              for (let i = this.mTabs.length - 1; i >= 0; --i) {
-                if (this.mTabs[i] != aTab)
-                  this.removeTab(this.mTabs[i]);
+              for (let i = this.tabs.length - 1; i >= 0; --i) {
+                if (this.tabs[i] != aTab)
+                  this.removeTab(this.tabs[i]);
               }
             }
           ]]>
         </body>
       </method>
 
       <method name="removeCurrentTab">
         <body>
@@ -1513,68 +1384,66 @@
           <![CDATA[
             if (this._removingTabs.indexOf(aTab) > -1 || this._windowIsClosing)
               return null;
 
             var browser = this.getBrowserForTab(aTab);
 
             if (!aTabWillBeMoved) {
               let ds = browser.docShell;
-              if (ds.contentViewer && !ds.contentViewer.permitUnload())
+              if (ds && ds.contentViewer && !ds.contentViewer.permitUnload())
                 return null;
             }
 
             var closeWindow = false;
-            var l = this.mTabs.length - this._removingTabs.length;
             var newTab = false;
-            if (l == 1) {
+            if (this.tabs.length - this._removingTabs.length == 1) {
               closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
                             !window.toolbar.visible ||
-                              this.mPrefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
+                              this.tabContainer._closeWindowWithLastTab;
 
               // Closing the tab and replacing it with a blank one is notably slower
               // than closing the window right away. If the caller opts in, take
               // the fast path.
               if (closeWindow &&
                   aCloseWindowFastpath &&
                   this._removingTabs.length == 0 &&
                   (this._windowIsClosing = window.closeWindow(true)))
                 return null;
 
               newTab = true;
-              l++;
-            }
-            if (l == 2) {
-              let autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
-              let tabStripHide = !window.toolbar.visible;
-              if (autohide || tabStripHide)
-                this.setStripVisibilityTo(false);
             }
 
             this._removingTabs.push(aTab);
 
+            this.tabContainer.updateVisibility();
+
             // We're committed to closing the tab now.
             // Dispatch a notification.
             // We dispatch it before any teardown so that event listeners can
             // inspect the tab that's about to close.
             var evt = document.createEvent("UIEvent");
             evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0);
             aTab.dispatchEvent(evt);
 
             // Remove the tab's filter and progress listener.
             const filter = this.mTabFilters[aTab._tPos];
             browser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
             this.mTabListeners[aTab._tPos].destroy();
 
+            let closedBrowser = this.getBrowserForTab(aTab);
+            if (closedBrowser.currentURI)
+              this.mBrowserHistory.unregisterOpenPage(closedBrowser.currentURI);
+
             // We are no longer the primary content area.
             browser.setAttribute("type", "content-targetable");
 
             // Remove this tab as the owner of any other tabs, since it's going away.
-            Array.forEach(this.mTabs, function (tab) {
+            Array.forEach(this.tabs, function (tab) {
               if ("owner" in tab && tab.owner == aTab)
                 // |tab| is a child of the tab we're removing, make it an orphan
                 tab.owner = null;
             });
 
             return [aTab, closeWindow, newTab];
           ]]>
         </body>
@@ -1636,18 +1505,18 @@
             // Invalidate browsers cache, as the tab is removed from the
             // tab container.
             this._browsers = null;
 
             // Remove the tab ...
             this.tabContainer.removeChild(aTab);
 
             // ... and fix up the _tPos properties immediately.
-            for (let i = aTab._tPos; i < this.mTabs.length; i++)
-              this.mTabs[i]._tPos = i;
+            for (let i = aTab._tPos; i < this.tabs.length; i++)
+              this.tabs[i]._tPos = i;
 
             // update tab close buttons state
             if (!this._windowIsClosing)
               this.tabContainer.adjustTabstrip();
 
             // update first-tab/last-tab/beforeselected/afterselected attributes
             this.selectedTab._selected = true;
 
@@ -1674,17 +1543,17 @@
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             if (this.mCurrentTab != aTab)
               return;
 
             if (aTab.owner &&
                 this._removingTabs.indexOf(aTab.owner) == -1 &&
-                this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
+                Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
               this.selectedTab = aTab.owner;
               return;
             }
 
             var tab = aTab;
 
             do {
               tab = tab.nextSibling;
@@ -1787,42 +1656,27 @@
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             this.getBrowserForTab(aTab).reload();
           ]]>
         </body>
       </method>
 
-      <method name="onTabBarDblClick">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            // See hack note in the tabbrowser-close-button binding
-            if (!this._blockDblClick && aEvent.button == 0 &&
-                aEvent.originalTarget.localName == "box")
-              BrowserOpenTab();
-          ]]>
-        </body>
-      </method>
-
       <method name="addProgressListener">
         <parameter name="aListener"/>
         <parameter name="aMask"/>
         <body>
           <![CDATA[
             if (!this.mAddProgressListenerWasCalled) {
               this.mAddProgressListenerWasCalled = true;
-              var autoHide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
-              var tabStripHide = !window.toolbar.visible;
-              if (!autoHide && !tabStripHide)
-                this.setStripVisibilityTo(true);
+              this.tabContainer.updateVisibility();
             }
 
-            if (!this.mTabbedMode && this.mProgressListeners.length == 1) {
+            if (this.mProgressListeners.length == 1) {
               // If we are adding a 2nd progress listener, we need to enter tabbed mode
               // because the browser status filter can only handle one progress listener.
               // In tabbed mode, mTabProgressListener is used which will iterate over all listeners.
               this.enterTabbedMode();
             }
 
             this.mProgressListeners.push(aListener);
 
@@ -1861,18 +1715,17 @@
               this.mTabFilters[0].removeProgressListener(aListener);
          ]]>
         </body>
       </method>
 
       <method name="addTabsProgressListener">
         <parameter name="aListener"/>
         <body>
-          if (!this.mTabbedMode)
-            this.enterTabbedMode();
+          this.enterTabbedMode();
           this.mTabsProgressListeners.push(aListener);
         </body>
       </method>
 
       <method name="removeTabsProgressListener">
         <parameter name="aListener"/>
         <body>
         <![CDATA[
@@ -1894,37 +1747,31 @@
 
       <method name="selectTabAtIndex">
         <parameter name="aIndex"/>
         <parameter name="aEvent"/>
         <body>
         <![CDATA[
           // count backwards for aIndex < 0
           if (aIndex < 0)
-            aIndex += this.mTabs.length;
+            aIndex += this.tabs.length;
 
           if (aIndex >= 0 &&
-              aIndex < this.mTabs.length &&
+              aIndex < this.tabs.length &&
               aIndex != this.tabContainer.selectedIndex)
-            this.selectedTab = this.mTabs[aIndex];
+            this.selectedTab = this.tabs[aIndex];
 
           if (aEvent) {
             aEvent.preventDefault();
             aEvent.stopPropagation();
           }
         ]]>
         </body>
       </method>
 
-      <property name="tabContainer" readonly="true">
-        <getter>
-          return this.mTabContainer;
-        </getter>
-      </property>
-
       <property name="selectedTab">
         <getter>
           return this.mTabBox.selectedTab;
         </getter>
         <setter>
           <![CDATA[
           // Update the tab
           this.mTabBox.selectedTab = val;
@@ -1936,396 +1783,36 @@
       <property name="selectedBrowser"
                 onget="return this.mCurrentBrowser;"
                 readonly="true"/>
 
       <property name="browsers" readonly="true">
        <getter>
           <![CDATA[
             return this._browsers ||
-                   (this._browsers = Array.map(this.mTabs, function (tab) tab.linkedBrowser));
+                   (this._browsers = Array.map(this.tabs, function (tab) tab.linkedBrowser));
           ]]>
         </getter>
       </property>
 
-      <method name="_onDragStart">
-        <parameter name="aEvent"/>
-        <body>
-        <![CDATA[
-          var target = aEvent.target;
-          if (target.localName == "tab" &&
-              aEvent.originalTarget.localName != "toolbarbutton") {
-            var dt = aEvent.dataTransfer;
-            dt.mozSetDataAt(TAB_DROP_TYPE, target, 0);
-            var uri = this.getBrowserForTab(aEvent.target).currentURI;
-            var spec = uri ? uri.spec : "about:blank";
-
-            // We must not set text/x-moz-url or text/plain data here,
-            // otherwise trying to deatch the tab by dropping it on the desktop
-            // may result in an "internet shortcut"
-            dt.mozSetDataAt("text/x-moz-text-internal", spec, 0);
-            
-            // Set the cursor to an arrow during tab drags.
-            dt.mozCursor = "default";
-
-            var canvas = tabPreviews.capture(target, false);
-            dt.setDragImage(canvas, 0, 0);
-            aEvent.stopPropagation();
-          }
-
-          this._dragLeftWindow = false;
-        ]]>
-        </body>
-      </method>
-
-      <field name="mDragTime">0</field>
-      <field name="mDragOverDelay">350</field>
-
-      <field name="_supportedLinkDropTypes"><![CDATA[
-        ["text/x-moz-url", "text/uri-list", "text/plain", "application/x-moz-file"]
-      ]]></field>
-
-      <method name="_setEffectAllowedForDataTransfer">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            var dt = aEvent.dataTransfer;
-            // Disallow dropping multiple items
-            if (dt.mozItemCount > 1)
-              return dt.effectAllowed = "none";
-
-            var types = dt.mozTypesAt(0);
-            var sourceNode = null;
-            // tabs are always added as the first type
-            if (types[0] == TAB_DROP_TYPE) {
-              var sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
-              if (sourceNode instanceof XULElement &&
-                  sourceNode.localName == "tab" &&
-                  (sourceNode.parentNode == this.mTabContainer ||
-                   (sourceNode.ownerDocument.defaultView instanceof ChromeWindow &&
-                    sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser"))) {
-                if (sourceNode.parentNode == this.mTabContainer &&
-                    (aEvent.screenX >= sourceNode.boxObject.screenX &&
-                      aEvent.screenX <= (sourceNode.boxObject.screenX +
-                                         sourceNode.boxObject.width))) {
-                  return dt.effectAllowed = "none";
-                }
-
-                return dt.effectAllowed = "copyMove";
-              }
-            }
-
-            for (var i=0; i < this._supportedLinkDropTypes.length; i++) {
-              if (types.contains(this._supportedLinkDropTypes[i])) {
-                // Here we need to to do this manually
-                return dt.effectAllowed = dt.dropEffect = "link";
-              }
-            }
-            return dt.effectAllowed = "none";
-          ]]>
-        </body>
-      </method>
-
-      <method name="_continueScroll">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          // Workaround for bug 481904: Dragging a tab stops scrolling at
-          // the tab's position when dragging to the first/last tab and back.
-          var t = this.selectedTab;
-          if (aEvent.screenX >= t.boxObject.screenX &&
-              aEvent.screenX <= t.boxObject.screenX + t.boxObject.width &&
-              aEvent.screenY >= t.boxObject.screenY &&
-              aEvent.screenY <= t.boxObject.screenY + t.boxObject.height)
-            this.tabContainer.mTabstrip.ensureElementIsVisible(t);
-        ]]></body>
-      </method>
-
-      <method name="_onDragOver">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            var effects = this._setEffectAllowedForDataTransfer(aEvent);
-
-            var ind = this._tabDropIndicator;
-            if (effects == "" || effects == "none") {
-              ind.collapsed = true;
-              this._continueScroll(aEvent);
-              return;
-            }
-            aEvent.preventDefault();
-            aEvent.stopPropagation();
-
-            var tabStrip = this.mTabContainer.mTabstrip;
-            var ltr = (window.getComputedStyle(this.parentNode, null).direction
-                       == "ltr");
-
-            // autoscroll the tab strip if we drag over the scroll
-            // buttons, even if we aren't dragging a tab, but then
-            // return to avoid drawing the drop indicator
-            var pixelsToScroll = 0;
-            if (this.mTabContainer.getAttribute("overflow") == "true") {
-              var targetAnonid = aEvent.originalTarget.getAttribute("anonid");
-              switch (targetAnonid) {
-                case "scrollbutton-up":
-                  pixelsToScroll = tabStrip.scrollIncrement * -1;
-                  break;
-                case "scrollbutton-down":
-                case "alltabs-button":
-                case "newtab-button":
-                  pixelsToScroll = tabStrip.scrollIncrement;
-                  break;
-              }
-              if (pixelsToScroll)
-                tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll);
-            }
-
-            if (effects == "link" && aEvent.target.localName == "tab") {
-              if (!this.mDragTime) 
-                this.mDragTime = Date.now();
-              if (Date.now() >= this.mDragTime + this.mDragOverDelay)
-                this.mTabContainer.selectedItem = aEvent.target;
-              return;
-            }
-
-            var newIndex = this.getNewIndex(aEvent);
-            var scrollRect = tabStrip.scrollClientRect;
-            var rect = this.getBoundingClientRect();
-            var minMargin = scrollRect.left - rect.left;
-            var maxMargin = Math.min(minMargin + scrollRect.width, 
-                                     scrollRect.right);
-            if (!ltr)
-              [minMargin, maxMargin] = [this.clientWidth - maxMargin,
-                                        this.clientWidth - minMargin];
-            var newMargin;
-            if (pixelsToScroll) {
-              // if we are scrolling, put the drop indicator at the edge
-              // so that it doesn't jump while scrolling
-              newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
-            }
-            else {
-              if (newIndex == this.mTabs.length) {
-                let tabRect = this.mTabs[newIndex-1].getBoundingClientRect();
-                if (ltr)
-                  newMargin = tabRect.right - rect.left;
-                else
-                  newMargin = rect.right - tabRect.left;
-              }
-              else {
-                let tabRect = this.mTabs[newIndex].getBoundingClientRect();
-                if (ltr)
-                  newMargin = tabRect.left - rect.left;
-                else
-                  newMargin = rect.right - tabRect.right;
-              }
-              // ensure we never place the drop indicator beyond our limits
-              if (newMargin < minMargin)
-                newMargin = minMargin;
-              else if (newMargin > maxMargin)
-                newMargin = maxMargin;
-            }
-
-            ind.collapsed = false;
- 
-            newMargin += ind.clientWidth / 2;
-            if (!ltr)
-              newMargin *= -1;
-
-            ind.style.MozTransform = "translate(" + Math.round(newMargin) + "px)";
-            ind.style.MozMarginStart = (-ind.clientWidth) + "px";
-            ind.style.marginTop = (-ind.clientHeight) + "px";
-          ]]>
-        </body>
-      </method>
-
-      <method name="_onDrop">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            var dt = aEvent.dataTransfer;
-            var dropEffect = dt.dropEffect;
-            var draggedTab;
-            if (dropEffect != "link") { // copy or move
-              draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
-              // not our drop then
-              if (!draggedTab)
-                return;
-            }
-
-            this._tabDropIndicator.collapsed = true;
-            aEvent.stopPropagation();
-
-            if (draggedTab && (dropEffect == "copy" ||
-                draggedTab.parentNode == this.mTabContainer)) {
-              var newIndex = this.getNewIndex(aEvent);
-              if (dropEffect == "copy") {
-                // copy the dropped tab (wherever it's from)
-                var newTab = this.duplicateTab(draggedTab);
-                this.moveTabTo(newTab, newIndex);
-                if (draggedTab.parentNode != this.mTabContainer || aEvent.shiftKey)
-                  this.selectedTab = newTab;
-              }
-              else {
-                // move the dropped tab
-                if (newIndex > draggedTab._tPos)
-                  newIndex--;
-                this.moveTabTo(draggedTab, newIndex);
-              }
-            }
-            else if (draggedTab) {
-              // swap the dropped tab with a new one we create and then close
-              // it in the other window (making it seem to have moved between
-              // windows)
-              newIndex = this.getNewIndex(aEvent);
-              newTab = this.addTab("about:blank");
-              var newBrowser = this.getBrowserForTab(newTab);
-              // Stop the about:blank load
-              newBrowser.stop();
-              // make sure it has a docshell
-              newBrowser.docShell;
-              
-              this.moveTabTo(newTab, newIndex);
-              
-              this.swapBrowsersAndCloseOther(newTab, draggedTab);
-
-              // We need to set selectedTab after we've done
-              // swapBrowsersAndCloseOther, so that the updateCurrentBrowser
-              // it triggers will correctly update our URL bar.
-              this.selectedTab = newTab;
-            }
-            else {
-              var url;
-              for (var i=0; i < this._supportedLinkDropTypes.length; i++) {
-                let dataType = this._supportedLinkDropTypes[i];
-                // uri-list: for now, support dropping of the first URL
-                // only
-                var isURLList = dataType == "text/uri-list";
-                let urlData = isURLList ?
-                              dt.mozGetDataAt("URL", 0) : dt.mozGetDataAt(dataType, 0);
-                if (urlData) {
-                  url = transferUtils.retrieveURLFromData(urlData, isURLList ? "text/plain" : dataType);
-                  break;
-                }
-              }
-              NS_ASSERT(url, "In the drop event, at least one mime-type should match our supported types");
-
-              // valid urls don't contain spaces ' '; if we have a space it isn't a valid url.
-              // Also disallow dropping javascript: or data: urls--bail out
-              if (!url || !url.length || url.indexOf(" ", 0) != -1 ||
-                  /^\s*(javascript|data):/.test(url))
-                return;
-
-              // XXXmano: temporary fix until dragDropSecurityCheck make the
-              // drag-session an optional paramter
-              var dragService = Cc["@mozilla.org/widget/dragservice;1"].
-                                getService(Ci.nsIDragService);
-              var dragSession = dragService.getCurrentSession();
-              nsDragAndDrop.dragDropSecurityCheck(aEvent, dragSession, url);
-
-              var bgLoad = true;
-              try {
-                bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
-              }
-              catch (e) { }
-
-              if (aEvent.shiftKey)
-                bgLoad = !bgLoad;
-
-              if (document.getBindingParent(aEvent.originalTarget).localName != "tab" || dropEffect == "copy") {
-                // We're adding a new tab.
-                newIndex = this.getNewIndex(aEvent);
-                newTab = this.loadOneTab(getShortcutOrURI(url), {inBackground: bgLoad});
-                this.moveTabTo(newTab, newIndex);
-              }
-              else {
-                // Load in an existing tab.
-                var tab = aEvent.target;
-                try {
-                  this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
-                  if (this.mCurrentTab != tab && !bgLoad)
-                    this.selectedTab = tab;
-                } catch(ex) {
-                  // Just ignore invalid urls
-                }
-              }
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="_onDragEnd">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            // Note: while this case is correctly handled here, this event
-            // isn't dispatched when the tab is moved within the tabstrip,
-            // see bug 460801.
-
-            // * mozUserCancelled = the user pressed ESC to cancel the drag
-            var dt = aEvent.dataTransfer;
-            if (dt.mozUserCancelled || dt.dropEffect != "none")
-              return;
-
-            // Disable detach within the browser toolbox
-            var eX = aEvent.screenX;
-            var wX = window.screenX;
-            // check if the drop point is horizontally within the window
-            if (eX > wX && eX < (wX + window.outerWidth)) {
-              var bo = this.mTabContainer.mTabstrip.boxObject;
-              // also avoid detaching if the the tab was dropped too close to
-              // the tabbar (half a tab)
-              var endScreenY = bo.screenY + 1.5 * bo.height;
-              var eY = aEvent.screenY;
-              if (eY < endScreenY && eY > window.screenY)
-                return;
-            }
-
-            var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
-            this.replaceTabWithWindow(draggedTab);
-            aEvent.stopPropagation();
-          ]]>
-        </body>
-      </method>
-
       <!-- Moves a tab to a new browser window, unless it's already the only tab
            in the current window, in which case this will do nothing. -->
       <method name="replaceTabWithWindow">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
-            if (this.mTabs.length == 1)
+            if (this.tabs.length == 1)
               return null;
 
             // tell a new window to take the "dropped" tab
-            var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
-                      getService(Ci.nsIWindowWatcher);
-            return ww.openWindow(window,
-                                 getBrowserURL(),
-                                 null,
-                                 "chrome,dialog=no,all",
-                                 aTab);
-          ]]>
-        </body>
-      </method>
-
-      <method name="_onDragLeave">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            this.mDragTime = 0;
-
-            // This does not work at all (see bug 458613)
-            var target = aEvent.relatedTarget;
-            while (target && target != this)
-              target = target.parentNode;
-            if (target)
-              return;
-
-            this._tabDropIndicator.collapsed = true;
-            this._continueScroll(aEvent);
-            aEvent.stopPropagation();
+            return Services.ww.openWindow(window,
+                                          getBrowserURL(),
+                                          null,
+                                          "chrome,dialog=no,all",
+                                          aTab);
           ]]>
         </body>
       </method>
 
       <method name="moveTabTo">
         <parameter name="aTab"/>
         <parameter name="aIndex"/>
         <body>
@@ -2338,54 +1825,34 @@
           this._browsers = null; // invalidate cache
           this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
           this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
 
           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
           this.mCurrentTab._selected = false;
           // use .item() instead of [] because dragging to the end of the strip goes out of
           // bounds: .item() returns null (so it acts like appendChild), but [] throws
-          this.mTabContainer.insertBefore(aTab, this.mTabs.item(aIndex));
-          // invalidate cache, because mTabContainer is about to change
+          this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
+          // invalidate cache, because tabContainer is about to change
           this._browsers = null;
 
-          for (let i = 0; i < this.mTabs.length; i++) {
-            this.mTabs[i]._tPos = i;
-            this.mTabs[i]._selected = false;
+          for (let i = 0; i < this.tabs.length; i++) {
+            this.tabs[i]._tPos = i;
+            this.tabs[i]._selected = false;
           }
           this.mCurrentTab._selected = true;
-          this.mTabContainer.mTabstrip.ensureElementIsVisible(this.mCurrentTab, false);
+          this.tabContainer.mTabstrip.ensureElementIsVisible(this.mCurrentTab, false);
 
           var evt = document.createEvent("UIEvents");
           evt.initUIEvent("TabMove", true, false, window, oldPosition);
           aTab.dispatchEvent(evt);
         ]]>
         </body>
       </method>
 
-      <method name="getNewIndex">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            var i;
-            if (window.getComputedStyle(this.parentNode, null).direction == "ltr") {
-              for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
-                if (aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2) 
-                  return i;
-            } else {
-              for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
-                if (aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2)
-                  return i;
-            }
-            return this.mTabs.length;
-          ]]>
-        </body>
-      </method>
-
-
       <method name="moveTabForward">
         <body>
           <![CDATA[
             var tabPos = this.mCurrentTab._tPos;
             if (tabPos < this.browsers.length - 1) {
               this.moveTabTo(this.mCurrentTab, tabPos + 1);
               this.mCurrentTab.focus();
             }
@@ -2696,17 +2163,17 @@
             var offset = 1;
             switch (aEvent.charCode) {
               case '}'.charCodeAt(0):
                 offset *= -1;
               case '{'.charCodeAt(0):
                 if (window.getComputedStyle(this, null).direction == "ltr")
                   offset *= -1;
 
-                this.mTabContainer.advanceSelectedTab(offset, true);
+                this.tabContainer.advanceSelectedTab(offset, true);
                 aEvent.stopPropagation();
                 aEvent.preventDefault();
                 return;
             }
             if ('shiftKey' in aEvent && aEvent.shiftKey)
               return;
 #else
           if (('ctrlKey' in aEvent && aEvent.ctrlKey) &&
@@ -2756,87 +2223,129 @@
                 onset="return this.mCurrentBrowser.userTypedClear = val;"/>
 
       <property name="userTypedValue"
                 onget="return this.mCurrentBrowser.userTypedValue;"
                 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
 
       <method name="createTooltip">
         <parameter name="event"/>
-        <body>
-          <![CDATA[
-            event.stopPropagation();
-            var tn = document.tooltipNode;
-            if (tn.localName != "tab")
-              return false; // Not a tab, so cancel the tooltip
-            if ("mOverCloseButton" in tn && tn.mOverCloseButton) {
-              event.target.setAttribute("label", tn.getAttribute("closetabtext"));
-              return true;
-            }
-            if (tn.hasAttribute("label")) {
-              event.target.setAttribute("label", tn.getAttribute("label"));
-              return true;
-            }
-            return false;
-          ]]>
-        </body>
+        <body><![CDATA[
+          event.stopPropagation();
+          var tab = document.tooltipNode;
+          if (tab.localName != "tab") {
+            event.preventDefault();
+            return;
+          }
+          event.target.setAttribute("label", tab.mOverCloseButton ?
+                                             tab.getAttribute("closetabtext") :
+                                             tab.getAttribute("label"));
+        ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
             case "keypress":
               this._handleKeyEvent(aEvent);
               break;
           }
         ]]></body>
       </method>
 
       <constructor>
         <![CDATA[
           this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild;
-          this.mCurrentTab = this.mTabContainer.firstChild;
+          this.mCurrentTab = this.tabContainer.firstChild;
           document.addEventListener("keypress", this, false);
 
           var uniqueId = "panel" + Date.now();
           this.mPanelContainer.childNodes[0].id = uniqueId;
-          this.mTabContainer.childNodes[0].linkedPanel = uniqueId;
-          this.mTabContainer.childNodes[0]._tPos = 0;
-          this.mTabContainer.childNodes[0].linkedBrowser = this.mPanelContainer.childNodes[0].firstChild;
+          this.mCurrentTab.linkedPanel = uniqueId;
+          this.mCurrentTab._tPos = 0;
+          this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
 
           // set up the shared autoscroll popup
           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
           this._autoScrollPopup.id = "autoscroller";
           this.appendChild(this._autoScrollPopup);
           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
           for (var i = 0; i < this.mTabListeners.length; ++i) {
-            this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
+            let browser = this.getBrowserAtIndex(i);
+            this.mBrowserHistory.unregisterOpenPage(browser.currentURI);
+            browser.webProgress.removeProgressListener(this.mTabFilters[i]);
             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
             this.mTabFilters[i] = null;
             this.mTabListeners[i].destroy();
             this.mTabListeners[i] = null;
           }
           document.removeEventListener("keypress", this, false);
         ]]>
       </destructor>
+
+      <!-- Deprecated stuff, implemented for backwards compatibility. -->
+      <method name="setStripVisibilityTo">
+        <parameter name="aShow"/>
+        <body>
+          this.tabContainer.visible = aShow;
+        </body>
+      </method>
+      <method name="getStripVisibility">
+        <body>
+          return this.tabContainer.visible;
+        </body>
+      </method>
+      <property name="mContextTab" readonly="true"
+                onget="return this.tabContainer._contextTab;"/>
+      <property name="mPrefs" readonly="true"
+                onget="return Services.prefs;"/>
+      <property name="mTabContainer" readonly="true"
+                onget="return this.tabContainer;"/>
+      <property name="mTabs" readonly="true"
+                onget="return this.tabs;"/>
+      <!--
+        - Compatibility hack: several extensions depend on this property to
+        - access the tab context menu or tab container, so keep that working for
+        - now. Ideally we can remove this once extensions are using
+        - tabbrowser.tabContextMenu and tabbrowser.tabContainer directly.
+        -->
+      <property name="mStrip" readonly="true">
+        <getter>
+        <![CDATA[
+          return ({
+            self: this,
+            childNodes: [null, this.tabContextMenu, this.tabContainer],
+            firstChild: { nextSibling: this.tabContextMenu },
+            getElementsByAttribute: function (attr, attrValue) {
+              if (attr == "anonid" && attrValue == "tabContextMenu")
+                return [this.self.tabContextMenu];
+              return [];
+            },
+            // Also support adding event listeners (forward to the tab container)
+            addEventListener: function (a,b,c) { this.self.tabContainer.addEventListener(a,b,c); },
+            removeEventListener: function (a,b,c) { this.self.tabContainer.removeEventListener(a,b,c); }
+          });
+        ]]>
+        </getter>
+      </property>
     </implementation>
 
     <handlers>
       <handler event="DOMWindowClose" phase="capturing">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
-          if (this.mTabs.length == 1)
+          if (this.tabs.length == 1)
             return;
 
           var tab = this._getTabForContentWindow(event.target);
           if (tab) {
             this.removeTab(tab);
             event.preventDefault();
           }
         ]]>
@@ -2864,16 +2373,24 @@
           this.setTabTitle(tab);
           if (tab == this.mCurrentTab)
             this.updateTitlebar();
         ]]>
       </handler>
     </handlers>
   </binding>
 
+  <binding id="tabbrowser-tabbox"
+           extends="chrome://global/content/bindings/tabbox.xml#tabbox">
+    <implementation>
+      <property name="tabs" readonly="true"
+                onget="return document.getBindingParent(this).tabContainer;"/>
+    </implementation>
+  </binding>
+
   <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
     <implementation>
       <!-- Override scrollbox.xml method, since our scrollbox's children are
            inherited from the binding parent -->
       <method name="_getScrollableElements">
         <body><![CDATA[
           return document.getBindingParent(this).childNodes;
         ]]></body>
@@ -2896,181 +2413,241 @@
          tabs.setAttribute("overflow", "true");
          this.ensureElementIsVisible(tabs.selectedItem, false);
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tabs"
            extends="chrome://global/content/bindings/tabbox.xml#tabs">
-    <content>
-      <xul:stack flex="1" class="tabs-stack">
-        <xul:vbox>
-          <xul:spacer flex="1"/>
-          <xul:hbox class="tabs-bottom" align="center"/>
-        </xul:vbox>
-        <xul:hbox xbl:inherits="overflow" class="tabs-container">
-#ifdef XP_MACOSX
-          <xul:stack>
-            <xul:spacer class="tabs-left"/>
-          </xul:stack>
+    <resources>
+      <stylesheet src="chrome://browser/content/tabbrowser.css"/>
+    </resources>
+
+    <content context="_child">
+      <xul:hbox align="start">
+        <xul:image class="tab-drop-indicator" anonid="tab-drop-indicator" collapsed="true"/>
+      </xul:hbox>
+      <xul:menupopup anonid="tabContextMenu"
+                     onpopupshowing="if (event.target == this) this.parentNode._updateContextMenu(this);"
+                     onpopuphidden="if (event.target == this) this.parentNode._contextTab = null;">
+        <xul:menuitem id="context_newTab" label="&newTab.label;" accesskey="&newTab.accesskey;"
+                      command="cmd_newNavigatorTab"/>
+        <xul:menuseparator/>
+        <xul:menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
+                      oncommand="this.parentNode.parentNode._contextMenuAction('reloadTab');"/>
+        <xul:menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
+                      tbattr="tabbrowser-multiple"
+                      oncommand="this.parentNode.parentNode._contextMenuAction('reloadAllTabs');"/>
+        <xul:menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
+                      tbattr="tabbrowser-multiple"
+                      oncommand="this.parentNode.parentNode._contextMenuAction('removeAllTabsBut');"/>
+        <xul:menuseparator/>
+        <xul:menuitem id="context_openTabInWindow" label="&openTabInNewWindow.label;"
+                      accesskey="&openTabInNewWindow.accesskey;"
+                      tbattr="tabbrowser-multiple"
+                      oncommand="this.parentNode.parentNode._contextMenuAction('replaceTabWithWindow');"/>
+        <xul:menuseparator/>
+        <xul:menuitem id="context_bookmarkTab"
+                      label="&bookmarkThisTab.label;"
+                      accesskey="&bookmarkThisTab.accesskey;"
+                      oncommand="this.parentNode.parentNode._contextMenuAction('BookmarkThisTab', window);"/>
+        <xul:menuitem id="context_bookmarkAllTabs"
+                      label="&bookmarkAllTabs.label;"
+                      accesskey="&bookmarkAllTabs.accesskey;"
+                      command="Browser:BookmarkAllTabs"/>
+        <xul:menuitem id="context_undoCloseTab"
+                      label="&undoCloseTab.label;"
+                      accesskey="&undoCloseTab.accesskey;"
+                      command="History:UndoCloseTab"/>
+        <xul:menuseparator/>
+        <xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
+                      tbattr="tabbrowser-multiple"
+                      oncommand="this.parentNode.parentNode._contextMenuAction('removeTab');"/>
+      </xul:menupopup>
+      <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
+                          style="min-width: 1px;"
+#ifndef XP_MACOSX
+                          clicktoscroll="true"
 #endif
-          <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
-                              style="min-width: 1px;"
-#ifndef XP_MACOSX
-                              clicktoscroll="true"
-#endif
-                              class="tabbrowser-arrowscrollbox">
+                          class="tabbrowser-arrowscrollbox">
 # This is a hack to circumvent bug 472020, otherwise the tabs show up on the
 # right of the newtab button.
-            <children includes="tab"/>
+        <children includes="tab"/>
 # This is to ensure anything extensions put here will go before the newtab
 # button, necessary due to the previous hack.
-            <children/>
-            <xul:toolbarbutton class="tabs-newtab-button"
-                               command="cmd_newNavigatorTab"
-                               tooltiptext="&newTabButton.tooltip;"/>
-          </xul:arrowscrollbox>
-          <xul:toolbarbutton class="tabs-newtab-button" anonid="newtab-button"
-                             command="cmd_newNavigatorTab"
-                             tooltiptext="&newTabButton.tooltip;"/>
-          <xul:toolbarbutton class="tabs-alltabs-button" anonid="alltabs-button"
-                             type="menu"
-                             tooltiptext="&listAllTabs.label;">
-            <xul:menupopup class="tabs-alltabs-popup" anonid="alltabs-popup"
-                           position="after_end"/>
-          </xul:toolbarbutton>
-          <xul:toolbarbutton anonid="tabs-closebutton"
-                             class="close-button tabs-closebutton"/>
-        </xul:hbox>
-      </xul:stack>
+        <children/>
+        <xul:toolbarbutton class="tabs-newtab-button"
+                           command="cmd_newNavigatorTab"
+                           tooltiptext="&newTabButton.tooltip;"/>
+      </xul:arrowscrollbox>
+      <xul:toolbarbutton class="tabs-newtab-button" anonid="newtab-button"
+                         command="cmd_newNavigatorTab"
+                         tooltiptext="&newTabButton.tooltip;"/>
+      <xul:toolbarbutton class="tabs-alltabs-button" anonid="alltabs-button"
+                         type="menu"
+                         tooltiptext="&listAllTabs.label;">
+        <xul:menupopup class="tabs-alltabs-popup" anonid="alltabs-popup"
+                       position="after_end"/>
+      </xul:toolbarbutton>
+      <xul:toolbarbutton anonid="tabs-closebutton"
+                         class="close-button tabs-closebutton"/>
     </content>
+
     <implementation implements="nsIDOMEventListener">
       <constructor>
         <![CDATA[
-          var pb2 =
-              Components.classes['@mozilla.org/preferences-service;1'].
-              getService(Components.interfaces.nsIPrefBranch2);
-
-          try {
-            this.mTabMinWidth = pb2.getIntPref("browser.tabs.tabMinWidth");
-          } catch (e) {
-          }
-          try {
-            this.mTabMaxWidth = pb2.getIntPref("browser.tabs.tabMaxWidth");
-          } catch (e) {
-          }
-          try {
-            this.mTabClipWidth = pb2.getIntPref("browser.tabs.tabClipWidth");
-          } catch (e) {
-          }
-          try {
-            this.mCloseButtons = pb2.getIntPref("browser.tabs.closeButtons");
-          } catch (e) {
-          }
-
-          this.firstChild.minWidth = this.mTabMinWidth;
-          this.firstChild.maxWidth = this.mTabMaxWidth;
+          this.mTabMinWidth = Services.prefs.getIntPref("browser.tabs.tabMinWidth");
+          this.mTabMaxWidth = Services.prefs.getIntPref("browser.tabs.tabMaxWidth");
+          this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
+          this.mCloseButtons = Services.prefs.getIntPref("browser.tabs.closeButtons");
+          this._closeWindowWithLastTab = Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
+
+          var tab = this.firstChild;
+          tab.setAttribute("label",
+                           this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle"));
+          tab.minWidth = this.mTabMinWidth;
+          tab.maxWidth = this.mTabMaxWidth;
+          tab.width = 0;
+          tab.setAttribute("flex", "100");
+          tab.setAttribute("crop", "end");
+          tab.setAttribute("validate", "never");
+          tab.setAttribute("onerror", "this.removeAttribute('image');");
           this.adjustTabstrip();
 
-          pb2.addObserver("browser.tabs.closeButtons", 
-                          this._prefObserver, false);
+          Services.prefs.addObserver("browser.tabs.closeButtons", this._prefObserver, false);
+          Services.prefs.addObserver("browser.tabs.autoHide", this._prefObserver, false);
+          Services.prefs.addObserver("browser.tabs.closeWindowWithLastTab", this._prefObserver, false);
 
           window.addEventListener("resize", this, false);
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
-          var pb2 =
-              Components.classes['@mozilla.org/preferences-service;1'].
-              getService(Components.interfaces.nsIPrefBranch2);
-          pb2.removeObserver("browser.tabs.closeButtons", this._prefObserver);
+          Services.prefs.removeObserver("browser.tabs.closeButtons", this._prefObserver);
+          Services.prefs.removeObserver("browser.tabs.autoHide", this._prefObserver);
+          Services.prefs.removeObserver("browser.tabs.closeWindowWithLastTab", this._prefObserver);
         ]]>
       </destructor>
 
+      <field name="tabbrowser" readonly="true">
+        document.getElementById(this.getAttribute("tabbrowser"));
+      </field>
+
+      <field name="tabbox" readonly="true">
+        this.tabbrowser.mTabBox;
+      </field>
+
+      <field name="contextMenu" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "tabContextMenu");
+      </field>
+
       <field name="mTabstripWidth">0</field>
 
       <field name="mTabstrip">
         document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
       </field>
 
       <field name="mTabstripClosebutton">
         document.getAnonymousElementByAttribute(this, "anonid", "tabs-closebutton");
       </field>
 
-      <field name="_prefObserver">({
-        tabbox: this,
-  
-        observe: function(subject, topic, data)
-        {
-          if (topic == "nsPref:changed") {
-            switch (data) {
+      <field name="_prefObserver"><![CDATA[({
+        tabContainer: this,
+
+        observe: function (subject, topic, data) {
+          switch (data) {
             case "browser.tabs.closeButtons":
-              subject.QueryInterface(Components.interfaces.nsIPrefBranch);
-              this.tabbox.mCloseButtons = subject.getIntPref("browser.tabs.closeButtons");
-              this.tabbox.adjustTabstrip();
+              this.tabContainer.mCloseButtons = Services.prefs.getIntPref(data);
+              this.tabContainer.adjustTabstrip();
               break;
-            }
+            case "browser.tabs.autoHide":
+              this.tabContainer.updateVisibility();
+              break;
+            case "browser.tabs.closeWindowWithLastTab":
+              this.tabContainer._closeWindowWithLastTab = Services.prefs.getBoolPref(data);
+              this.tabContainer.adjustTabstrip();
+              break;
           }
-        },
-  
-        QueryInterface: function(aIID)
-        {
-          if (aIID.equals(Components.interfaces.nsIObserver) ||
-              aIID.equals(Components.interfaces.nsISupports))
-            return this;
-          throw Components.results.NS_NOINTERFACE;
         }
-        });
+      });]]></field>
+      <field name="_blockDblClick">false</field>
+
+      <field name="_tabDropIndicator">
+        document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator");
       </field>
-      <field name="mTabMinWidth">100</field>
-      <field name="mTabMaxWidth">250</field>
-      <field name="mTabClipWidth">140</field>
-      <field name="mCloseButtons">1</field>
+
+      <field name="_dragOverDelay">350</field>
+      <field name="_dragTime">0</field>
+
+      <field name="_supportedLinkDropTypes"><![CDATA[
+        ["text/x-moz-url", "text/uri-list", "text/plain", "application/x-moz-file"]
+      ]]></field>
+
+      <field name="_container" readonly="true"><![CDATA[
+        this.parentNode && this.parentNode.localName == "toolbar" ? this.parentNode : this;
+      ]]></field>
+
+      <property name="visible"
+                onget="return !this._container.collapsed;">
+        <setter><![CDATA[
+          this._container.collapsed = !val;
+
+          if (val)
+            this.tabbrowser.enterTabbedMode();
+
+          document.getElementById("menu_closeWindow").hidden = !val;
+          document.getElementById("menu_close").setAttribute("label",
+            this.tabbrowser.mStringBundle.getString(val ? "tabs.closeTab" : "tabs.close"));
+
+          return val;
+        ]]></setter>
+      </property>
+
+      <method name="updateVisibility">
+        <body><![CDATA[
+          if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1 &&
+              window.toolbar.visible)
+            this.visible = !Services.prefs.getBoolPref("browser.tabs.autoHide");
+          else
+            this.visible = true;
+        ]]></body>
+      </method>
 
       <method name="adjustTabstrip">
         <body><![CDATA[
           // modes for tabstrip
           // 0 - activetab  = close button on active tab only
           // 1 - alltabs    = close buttons on all tabs
           // 2 - noclose    = no close buttons at all
           // 3 - closeatend = close button at the end of the tabstrip
           switch (this.mCloseButtons) {
           case 0:
-            this.setAttribute("closebuttons", "activetab");
+            if (this.childNodes.length == 1 && this._closeWindowWithLastTab)
+              this.setAttribute("closebuttons", "noclose");
+            else
+              this.setAttribute("closebuttons", "activetab");
             break;
           case 1:
-            if (this.firstChild.getBoundingClientRect().width > this.mTabClipWidth)
+            if (this.childNodes.length == 1 && this._closeWindowWithLastTab)
+              this.setAttribute("closebuttons", "noclose");
+            else if (this.firstChild.getBoundingClientRect().width > this.mTabClipWidth)
               this.setAttribute("closebuttons", "alltabs");
             else
               this.setAttribute("closebuttons", "activetab");
             break;
           case 2:
           case 3:
             this.setAttribute("closebuttons", "noclose");
             break;
           }
           this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
         ]]></body>
       </method>
-        
-      <field name="_mPrefs">null</field>
-      <property name="mPrefs" readonly="true">
-        <getter>
-        <![CDATA[
-          if (!this._mPrefs) {
-            this._mPrefs =
-              Components.classes['@mozilla.org/preferences-service;1'].
-              getService(Components.interfaces.nsIPrefBranch2);
-          }
-          return this._mPrefs;
-        ]]>
-        </getter>
-      </property>
 
       <method name="_handleTabSelect">
         <body><![CDATA[
           this.mTabstrip.ensureElementIsVisible(this.selectedItem);
         ]]></body>
       </method>
 
       <method name="_fillTrailingGap">
@@ -3142,98 +2719,493 @@
           if (!this._animateElement.hasAttribute("notifybgtab")) {
             this._animateElement.setAttribute("notifybgtab", "true");
             setTimeout(function (ele) {
               ele.removeAttribute("notifybgtab");
             }, 150, this._animateElement);
           }
         ]]></body>
       </method>
+
+      <field name="_contextTab">null</field>
+      <method name="_updateContextMenu">
+        <parameter name="aPopupMenu"/>
+        <body><![CDATA[
+          this._contextTab = document.popupNode.localName == "tab" ?
+                             document.popupNode : this.selectedItem;
+          var disabled = this.childNodes.length == 1;
+          var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
+          for (var i = 0; i < menuItems.length; i++)
+            menuItems[i].disabled = disabled;
+
+          // Session store
+          // XXXzeniko should't we just disable this item as we disable
+          // the tabbrowser-multiple items above - for consistency?
+          document.getElementById("context_undoCloseTab").hidden =
+            Cc["@mozilla.org/browser/sessionstore;1"].
+            getService(Ci.nsISessionStore).
+            getClosedTabCount(window) == 0;
+        ]]></body>
+      </method>
+
+      <method name="_contextMenuAction">
+        <parameter name="aMethod"/>
+        <parameter name="aContext"/>
+        <body><![CDATA[
+          (aContext || this.tabbrowser)[aMethod](this._contextTab);
+        ]]></body>
+      </method>
+
+      <method name="_getDragTargetTab">
+        <parameter name="event"/>
+        <body><![CDATA[
+          return event.target.localName == "tab" ? event.target : null;
+        ]]></body>
+      </method>
+
+      <method name="_getDropIndex">
+        <parameter name="event"/>
+        <body><![CDATA[
+          var tabs = this.childNodes;
+          var tab = this._getDragTargetTab(event);
+          if (window.getComputedStyle(this, null).direction == "ltr") {
+            for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
+              if (event.screenX < tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2) 
+                return i;
+          } else {
+            for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
+              if (event.screenX > tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
+                return i;
+          }
+          return tabs.length;
+        ]]></body>
+      </method>
+
+      <method name="_setEffectAllowedForDataTransfer">
+        <parameter name="event"/>
+        <body><![CDATA[
+          var dt = event.dataTransfer;
+          // Disallow dropping multiple items
+          if (dt.mozItemCount > 1)
+            return dt.effectAllowed = "none";
+
+          var types = dt.mozTypesAt(0);
+          var sourceNode = null;
+          // tabs are always added as the first type
+          if (types[0] == TAB_DROP_TYPE) {
+            var sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
+            if (sourceNode instanceof XULElement &&
+                sourceNode.localName == "tab" &&
+                (sourceNode.parentNode == this ||
+                 (sourceNode.ownerDocument.defaultView instanceof ChromeWindow &&
+                  sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser"))) {
+              if (sourceNode.parentNode == this &&
+                  (event.screenX >= sourceNode.boxObject.screenX &&
+                    event.screenX <= (sourceNode.boxObject.screenX +
+                                       sourceNode.boxObject.width))) {
+                return dt.effectAllowed = "none";
+              }
+
+              return dt.effectAllowed = "copyMove";
+            }
+          }
+
+          for (let i = 0; i < this._supportedLinkDropTypes.length; i++) {
+            if (types.contains(this._supportedLinkDropTypes[i])) {
+              // Here we need to to do this manually
+              return dt.effectAllowed = dt.dropEffect = "link";
+            }
+          }
+          return dt.effectAllowed = "none";
+        ]]></body>
+      </method>
+
+      <method name="_continueScroll">
+        <parameter name="event"/>
+        <body><![CDATA[
+          // Workaround for bug 481904: Dragging a tab stops scrolling at
+          // the tab's position when dragging to the first/last tab and back.
+          var t = this.selectedItem;
+          if (event.screenX >= t.boxObject.screenX &&
+              event.screenX <= t.boxObject.screenX + t.boxObject.width &&
+              event.screenY >= t.boxObject.screenY &&
+              event.screenY <= t.boxObject.screenY + t.boxObject.height)
+            this.mTabstrip.ensureElementIsVisible(t);
+        ]]></body>
+      </method>
     </implementation>
+
     <handlers>
       <handler event="TabSelect" action="this._handleTabSelect();"/>
+
+      <handler event="dblclick"><![CDATA[
+        // See hack note in the tabbrowser-close-button binding
+        if (!this._blockDblClick && event.button == 0 &&
+            event.originalTarget.localName == "box")
+          BrowserOpenTab();
+      ]]></handler>
+
+      <handler event="click"><![CDATA[
+        if (event.button != 1 || event.target.localName != "tab")
+          return;
+
+        if (this.childNodes.length > 1 ||
+            !this._closeWindowWithLastTab)
+          this.tabbrowser.removeTab(event.target);
+
+        event.stopPropagation();
+      ]]></handler>
+
+      <handler event="dragstart"><![CDATA[
+        var tab = this._getDragTargetTab(event);
+        if (!tab)
+          return;
+
+        let dt = event.dataTransfer;
+        dt.mozSetDataAt(TAB_DROP_TYPE, tab, 0);
+        let uri = this.tabbrowser.getBrowserForTab(tab).currentURI;
+        let spec = uri ? uri.spec : "about:blank";
+
+        // We must not set text/x-moz-url or text/plain data here,
+        // otherwise trying to deatch the tab by dropping it on the desktop
+        // may result in an "internet shortcut"
+        dt.mozSetDataAt("text/x-moz-text-internal", spec, 0);
+        
+        // Set the cursor to an arrow during tab drags.
+        dt.mozCursor = "default";
+
+        let canvas = tabPreviews.capture(tab, false);
+        dt.setDragImage(canvas, 0, 0);
+        event.stopPropagation();
+      ]]></handler>
+
+      <handler event="dragover"><![CDATA[
+        var effects = this._setEffectAllowedForDataTransfer(event);
+
+        var ind = this._tabDropIndicator;
+        if (effects == "" || effects == "none") {
+          ind.collapsed = true;
+          this._continueScroll(event);
+          return;
+        }
+        event.preventDefault();
+        event.stopPropagation();
+
+        var tabStrip = this.mTabstrip;
+        var ltr = (window.getComputedStyle(this, null).direction == "ltr");
+
+        // autoscroll the tab strip if we drag over the scroll
+        // buttons, even if we aren't dragging a tab, but then
+        // return to avoid drawing the drop indicator
+        var pixelsToScroll = 0;
+        if (this.getAttribute("overflow") == "true") {
+          var targetAnonid = event.originalTarget.getAttribute("anonid");
+          switch (targetAnonid) {
+            case "scrollbutton-up":
+              pixelsToScroll = tabStrip.scrollIncrement * -1;
+              break;
+            case "scrollbutton-down":
+            case "alltabs-button":
+            case "newtab-button":
+              pixelsToScroll = tabStrip.scrollIncrement;
+              break;
+          }
+          if (pixelsToScroll)
+            tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll);
+        }
+
+        if (effects == "link") {
+          let tab = this._getDragTargetTab(event);
+          if (tab) {
+            if (!this._dragTime)
+              this._dragTime = Date.now();
+            if (Date.now() >= this._dragTime + this._dragOverDelay)
+              this.selectedItem = tab;
+            return;
+          }
+        }
+
+        var newIndex = this._getDropIndex(event);
+        var scrollRect = tabStrip.scrollClientRect;
+        var rect = this.getBoundingClientRect();
+        var minMargin = scrollRect.left - rect.left;
+        var maxMargin = Math.min(minMargin + scrollRect.width, 
+                                 scrollRect.right);
+        if (!ltr)
+          [minMargin, maxMargin] = [this.clientWidth - maxMargin,
+                                    this.clientWidth - minMargin];
+        var newMargin;
+        if (pixelsToScroll) {
+          // if we are scrolling, put the drop indicator at the edge
+          // so that it doesn't jump while scrolling
+          newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
+        }
+        else {
+          if (newIndex == this.childNodes.length) {
+            let tabRect = this.childNodes[newIndex-1].getBoundingClientRect();
+            if (ltr)
+              newMargin = tabRect.right - rect.left;
+            else
+              newMargin = rect.right - tabRect.left;
+          }
+          else {
+            let tabRect = this.childNodes[newIndex].getBoundingClientRect();
+            if (ltr)
+              newMargin = tabRect.left - rect.left;
+            else
+              newMargin = rect.right - tabRect.right;
+          }
+          // ensure we never place the drop indicator beyond our limits
+          if (newMargin < minMargin)
+            newMargin = minMargin;
+          else if (newMargin > maxMargin)
+            newMargin = maxMargin;
+        }
+
+        ind.collapsed = false;
+
+        newMargin += ind.clientWidth / 2;
+        if (!ltr)
+          newMargin *= -1;
+
+        ind.style.MozTransform = "translate(" + Math.round(newMargin) + "px)";
+        ind.style.MozMarginStart = (-ind.clientWidth) + "px";
+        ind.style.marginTop = (-ind.clientHeight) + "px";
+      ]]></handler>
+
+      <handler event="drop"><![CDATA[
+        var dt = event.dataTransfer;
+        var dropEffect = dt.dropEffect;
+        var draggedTab;
+        if (dropEffect != "link") { // copy or move
+          draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
+          // not our drop then
+          if (!draggedTab)
+            return;
+        }
+
+        this._tabDropIndicator.collapsed = true;
+        event.stopPropagation();
+
+        if (draggedTab && (dropEffect == "copy" ||
+            draggedTab.parentNode == this)) {
+          let newIndex = this._getDropIndex(event);
+          if (dropEffect == "copy") {
+            // copy the dropped tab (wherever it's from)
+            let newTab = this.tabbrowser.duplicateTab(draggedTab);
+            this.tabbrowser.moveTabTo(newTab, newIndex);
+            if (draggedTab.parentNode != this || event.shiftKey)
+              this.selectedItem = newTab;
+          } else {
+            // move the dropped tab
+            if (newIndex > draggedTab._tPos)
+              newIndex--;
+            this.tabbrowser.moveTabTo(draggedTab, newIndex);
+          }
+        } else if (draggedTab) {
+          // swap the dropped tab with a new one we create and then close
+          // it in the other window (making it seem to have moved between
+          // windows)
+          let newIndex = this._getDropIndex(event);
+          let newTab = this.tabbrowser.addTab("about:blank");
+          let newBrowser = this.tabbrowser.getBrowserForTab(newTab);
+          // Stop the about:blank load
+          newBrowser.stop();
+          // make sure it has a docshell
+          newBrowser.docShell;
+
+          this.tabbrowser.moveTabTo(newTab, newIndex);
+
+          this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab);
+
+          // We need to select the tab after we've done
+          // swapBrowsersAndCloseOther, so that the updateCurrentBrowser
+          // it triggers will correctly update our URL bar.
+          this.tabbrowser.selectedTab = newTab;
+        } else {
+          let url;
+          for (let i = 0; i < this._supportedLinkDropTypes.length; i++) {
+            let dataType = this._supportedLinkDropTypes[i];
+            // uri-list: for now, support dropping of the first URL
+            // only
+            let isURLList = dataType == "text/uri-list";
+            let urlData = isURLList ?
+                          dt.mozGetDataAt("URL", 0) : dt.mozGetDataAt(dataType, 0);
+            if (urlData) {
+              url = transferUtils.retrieveURLFromData(urlData, isURLList ? "text/plain" : dataType);
+              break;
+            }
+          }
+          NS_ASSERT(url, "In the drop event, at least one mime-type should match our supported types");
+
+          // valid urls don't contain spaces ' '; if we have a space it isn't a valid url.
+          // Also disallow dropping javascript: or data: urls--bail out
+          if (!url || !url.length || url.indexOf(" ", 0) != -1 ||
+              /^\s*(javascript|data):/.test(url))
+            return;
+
+          // XXXmano: temporary fix until dragDropSecurityCheck make the
+          // drag-session an optional paramter
+          let dragService = Cc["@mozilla.org/widget/dragservice;1"].
+                            getService(Ci.nsIDragService);
+          let dragSession = dragService.getCurrentSession();
+          nsDragAndDrop.dragDropSecurityCheck(event, dragSession, url);
+
+          let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+
+          if (event.shiftKey)
+            bgLoad = !bgLoad;
+
+          let tab = this._getDragTargetTab(event);
+          if (!tab || dropEffect == "copy") {
+            // We're adding a new tab.
+            let newIndex = this._getDropIndex(event);
+            let newTab = this.tabbrowser.loadOneTab(getShortcutOrURI(url), {inBackground: bgLoad});
+            this.tabbrowser.moveTabTo(newTab, newIndex);
+          } else {
+            // Load in an existing tab.
+            try {
+              this.tabbrowser.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
+              if (!bgLoad)
+                this.selectedItem = tab;
+            } catch(ex) {
+              // Just ignore invalid urls
+            }
+          }
+        }
+      ]]></handler>
+
+      <handler event="dragend"><![CDATA[
+        // Note: while this case is correctly handled here, this event
+        // isn't dispatched when the tab is moved within the tabstrip,
+        // see bug 460801.
+
+        // * mozUserCancelled = the user pressed ESC to cancel the drag
+        var dt = event.dataTransfer;
+        if (dt.mozUserCancelled || dt.dropEffect != "none")
+          return;
+
+        // Disable detach within the browser toolbox
+        var eX = event.screenX;
+        var wX = window.screenX;
+        // check if the drop point is horizontally within the window
+        if (eX > wX && eX < (wX + window.outerWidth)) {
+          let bo = this.mTabstrip.boxObject;
+          // also avoid detaching if the the tab was dropped too close to
+          // the tabbar (half a tab)
+          let endScreenY = bo.screenY + 1.5 * bo.height;
+          let eY = event.screenY;
+          if (eY < endScreenY && eY > window.screenY)
+            return;
+        }
+
+        var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
+        this.tabbrowser.replaceTabWithWindow(draggedTab);
+        event.stopPropagation();
+      ]]></handler>
+
+      <handler event="dragleave"><![CDATA[
+        this._dragTime = 0;
+
+        // This does not work at all (see bug 458613)
+        var target = event.relatedTarget;
+        while (target && target != this)
+          target = target.parentNode;
+        if (target)
+          return;
+
+        this._tabDropIndicator.collapsed = true;
+        this._continueScroll(event);
+        event.stopPropagation();
+      ]]></handler>
     </handlers>
   </binding>
 
   <!-- close-tab-button binding
        This binding relies on the structure of the tabbrowser binding.
        Therefore it should only be used as a child of the tab or the tabs
        element (in both cases, when they are anonymous nodes of <tabbrowser>).
-       This binding is exposed as a pseudo-public-API so themes can customize
-       the tabbar appearance without having to be scriptable
-       (see globalBindings.xml in Pinstripe for example).
   -->
   <binding id="tabbrowser-close-tab-button"
            extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image">
     <handlers>
       <handler event="click" button="0"><![CDATA[
         var bindingParent = document.getBindingParent(this);
-        if (bindingParent) {
-          var tabbedBrowser = document.getBindingParent(bindingParent);
-          if (bindingParent.localName == "tab") {
-            /* The only sequence in which a second click event (i.e. dblclik)
-             * can be dispatched on an in-tab close button is when it is shown
-             * after the first click (i.e. the first click event was dispatched
-             * on the tab). This happens when we show the close button only on
-             * the active tab. (bug 352021)
-             * The only sequence in which a third click event can be dispatched
-             * on an in-tab close button is when the tab was opened with a
-             * double click on the tabbar. (bug 378344)
-             * In both cases, it is most likely that the close button area has
-             * been accidentally clicked, therefore we do not close the tab.
-             *
-             * We don't want to ignore processing of more than one click event,
-             * though, since the user might actually be repeatedly clicking to
-             * close many tabs at once.
-             */
-            if (event.detail > 1 && !this._ignoredClick) {
-              this._ignoredClick = true;
+
+        if (bindingParent.localName == "tab") {
+          let tabContainer = bindingParent.parentNode;
+          /* The only sequence in which a second click event (i.e. dblclik)
+           * can be dispatched on an in-tab close button is when it is shown
+           * after the first click (i.e. the first click event was dispatched
+           * on the tab). This happens when we show the close button only on
+           * the active tab. (bug 352021)
+           * The only sequence in which a third click event can be dispatched
+           * on an in-tab close button is when the tab was opened with a
+           * double click on the tabbar. (bug 378344)
+           * In both cases, it is most likely that the close button area has
+           * been accidentally clicked, therefore we do not close the tab.
+           *
+           * We don't want to ignore processing of more than one click event,
+           * though, since the user might actually be repeatedly clicking to
+           * close many tabs at once.
+           */
+          if (event.detail > 1 && !this._ignoredClick) {
+            this._ignoredClick = true;
+            return;
+          }
+
+          // Reset the "ignored click" flag
+          this._ignoredClick = false;
+
+          tabContainer.tabbrowser.removeTab(bindingParent);
+          this._blockDblClick = true;
+
+          /* XXXmano hack (see bug 343628):
+           * Since we're removing the event target, if the user
+           * double-clicks this button, the dblclick event will be dispatched
+           * with the tabbar as its event target (and explicit/originalTarget),
+           * which treats that as a mouse gesture for opening a new tab.
+           * In this context, we're manually blocking the dblclick event
+           * (see dblclick handler).
+           */
+          let self = this;
+          let clickedOnce = false;
+          function enableDblClick(event) {
+            if (event.detail == 1 && !clickedOnce) {
+              clickedOnce = true;
               return;
             }
-
-            // Reset the "ignored click" flag
-            this._ignoredClick = false;
-
-            tabbedBrowser.removeTab(bindingParent);
-            tabbedBrowser._blockDblClick = true;
-
-            /* XXXmano hack (see bug 343628):
-             * Since we're removing the event target, if the user
-             * double-clicks this button, the dblclick event will be dispatched
-             * with the tabbar as its event target (and explicit/originalTarget),
-             * which treats that as a mouse gesture for opening a new tab.
-             * In this context, we're manually blocking the dblclick event
-             * (see onTabBarDblClick).
-             */
-            var clickedOnce = false;
-            function enableDblClick(event) {
-              if (event.detail == 1 && !clickedOnce) {
-                clickedOnce = true;
-                return;
-              }
-              setTimeout(function() {
-                tabbedBrowser._blockDblClick = false;
-              }, 0);
-              tabbedBrowser.removeEventListener("click", enableDblClick, false);
-            }
-            tabbedBrowser.addEventListener("click", enableDblClick, false);
+            setTimeout(function() {
+              self._blockDblClick = false;
+            }, 0);
+            tabContainer.removeEventListener("click", enableDblClick, false);
           }
-          else // "tabs"
-            tabbedBrowser.removeCurrentTab();
+          tabContainer.addEventListener("click", enableDblClick, false);
         }
+        else // "tabs"
+          bindingParent.tabbrowser.removeCurrentTab();
       ]]></handler>
+
       <handler event="dblclick" button="0" phase="capturing">
         // for the one-close-button case
         event.stopPropagation();
       </handler>
+
+      <handler event="dragstart">
+        event.stopPropagation();
+      </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tab" display="xul:hbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
+    <resources>
+      <stylesheet src="chrome://browser/content/tabbrowser.css"/>
+    </resources>
+
     <content closetabtext="&closeTab.label;">
       <xul:image xbl:inherits="validate,src=image"
                  class="tab-icon-image"
                  role="presentation"/>
       <xul:label flex="1"
                  xbl:inherits="value=label,crop,accesskey"
                  class="tab-text"
                  role="presentation"/>
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -112,16 +112,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_bug481560.js \
                  browser_bug484315.js \
                  browser_bug491431.js \
                  browser_bug495058.js \
                  browser_bug517902.js \
                  browser_bug520538.js \
                  browser_bug521216.js \
                  browser_bug537474.js \
+                 browser_bug555224.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_discovery.js \
                  browser_drag.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_overflowScroll.js \
                  browser_pageInfo.js \
@@ -145,16 +146,17 @@ include $(topsrcdir)/config/rules.mk
                  feed_tab.html \
                  plugin_unknown.html \
                  plugin_test.html \
                  plugin_both.html \
                  plugin_both2.html \
                  alltabslistener.html \
                  zoom_test.html \
                  dummy_page.html \
+                 browser_tabMatchesInAwesomebar.js \
     $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += browser_bug462289.js
 else
 _BROWSER_FILES += browser_customize.js
 endif
 
--- a/browser/base/content/test/browser_allTabsPanel.js
+++ b/browser/base/content/test/browser_allTabsPanel.js
@@ -27,18 +27,18 @@ var whenClosed = [
 
 function rand(min, max) {
   return min + Math.floor(Math.random() * (max - min + 1));
 }
 function pickOne(array) {
   return array[rand(0, array.length - 1)];
 }
 function pickOneTab() {
-  var tab = pickOne(gBrowser.tabContainer.childNodes);
-  return [tab, Array.indexOf(gBrowser.tabContainer.childNodes, tab)];
+  var tab = pickOne(gBrowser.tabs);
+  return [tab, Array.indexOf(gBrowser.tabs, tab)];
 }
 function nextSequence() {
   while (gBrowser.browsers.length > 1)
     gBrowser.removeCurrentTab();
   if (sequences-- <= 0) {
     allTabs.close();
     gBrowser.addTab();
     gBrowser.removeCurrentTab();
--- a/browser/base/content/test/browser_bug386835.js
+++ b/browser/base/content/test/browser_bug386835.js
@@ -36,122 +36,109 @@ function secondPageLoaded() {
 }
 
 function thirdPageLoaded() {
   zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
   zoomTest(gTab2, 1, "Tab 2 should still not be affected");
   zoomTest(gTab3, gLevel, "Tab 3 should have zoomed as it was loading in the background");
 
   // Switching to tab 2 should update its zoom setting.
-  gBrowser.selectedTab = gTab2;
+  afterZoom(function() {
+    zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
+    zoomTest(gTab2, gLevel, "Tab 2 should be zoomed now");
+    zoomTest(gTab3, gLevel, "Tab 3 should still be zoomed");
 
-  zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
-  zoomTest(gTab2, gLevel, "Tab 2 should be zoomed now");
-  zoomTest(gTab3, gLevel, "Tab 3 should still be zoomed");
+    load(gTab1, gTestImage, imageLoaded);
+  });
 
-  load(gTab1, gTestImage, imageLoaded);
+  gBrowser.selectedTab = gTab2;
 }
 
 function imageLoaded() {
   zoomTest(gTab1, 1, "Zoom should be 1 when image was loaded in the background");
+  afterZoom(function() {
+    zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
+
+    executeSoon(imageZoomSwitch);
+  });
   gBrowser.selectedTab = gTab1;
-  zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
-
-  executeSoon(imageZoomSwitch);
 }
 
 function imageZoomSwitch() {
   navigate(BACK, function () {
     navigate(FORWARD, function () {
       zoomTest(gTab1, 1, "Tab 1 should not be zoomed when an image loads");
-      gBrowser.selectedTab = gTab2;
-      zoomTest(gTab1, 1, "Tab 1 should still not be zoomed when deselected");
-
-      // Mac OS X does not support print preview, so skip those tests
-      let isOSX = ("nsILocalFileMac" in Components.interfaces);
-      if (isOSX)
-        finishTest();
-      else
-        runPrintPreviewTests();
-    });
-  });
-}
 
-function runPrintPreviewTests() {
-  // test print preview on image document
-  testPrintPreview(gTab1, function() {
-    // test print preview on HTML document
-    testPrintPreview(gTab2, function() {
-      // test print preview on image document with siteSpecific set to false
-      gPrefService.setBoolPref("browser.zoom.siteSpecific", false);
-      testPrintPreview(gTab1, function() {
-        // test print preview of HTML document with siteSpecific set to false
-        testPrintPreview(gTab2, function() {
-          if (gPrefService.prefHasUserValue("browser.zoom.siteSpecific"))
-            gPrefService.clearUserPref("browser.zoom.siteSpecific");
-          finishTest();
-        });
+      afterZoom(function() {
+        zoomTest(gTab1, 1, "Tab 1 should still not be zoomed when deselected");
+        finishTest();
       });
+      gBrowser.selectedTab = gTab2;
+
+      finishTest();
     });
   });
 }
 
-function testPrintPreview(aTab, aCallback) {
-  gBrowser.selectedTab = aTab;
-  FullZoom.enlarge();
-  let level = ZoomManager.zoom;
-
-  let onEnterOrig = PrintPreviewListener.onEnter;
-  PrintPreviewListener.onEnter = function () {
-    PrintPreviewListener.onEnter = onEnterOrig;
-    PrintPreviewListener.onEnter.apply(PrintPreviewListener, arguments);
-    PrintUtils.exitPrintPreview();
-  };
-
-  let onExitOrig = PrintPreviewListener.onExit;
-  PrintPreviewListener.onExit = function () {
-    PrintPreviewListener.onExit = onExitOrig;
-    PrintPreviewListener.onExit.apply(PrintPreviewListener, arguments);
-
-    zoomTest(aTab, level, "Toggling print preview mode should not affect zoom level");
-
-    FullZoom.reset();
-    aCallback();
-  };
-
-  executeSoon(function () {
-    document.getElementById("cmd_printPreview").doCommand();
-  });
-}
-
 function finishTest() {
   gBrowser.selectedTab = gTab1;
   FullZoom.reset();
   gBrowser.removeTab(gTab1);
   FullZoom.reset();
   gBrowser.removeTab(gTab2);
   FullZoom.reset();
   gBrowser.removeTab(gTab3);
   finish();
 }
 
 function zoomTest(tab, val, msg) {
   is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
 }
 
 function load(tab, url, cb) {
+  let didLoad = didZoom = false;
   tab.linkedBrowser.addEventListener("load", function (event) {
     event.currentTarget.removeEventListener("load", arguments.callee, true);
-    cb();
+    didLoad = true;
+    if (didZoom)
+      executeSoon(cb);
   }, true);
+
+  afterZoom(function() {
+    didZoom = true;
+    if (didLoad)
+      executeSoon(cb);
+  });
+
   tab.linkedBrowser.loadURI(url);
 }
 
 function navigate(direction, cb) {
+  let didPs = didZoom = false;
   gBrowser.addEventListener("pageshow", function (event) {
     gBrowser.removeEventListener("pageshow", arguments.callee, true);
-    executeSoon(cb);
+    didPs = true;
+    if (didZoom)
+      executeSoon(cb);
   }, true);
+
+  afterZoom(function() {
+    didZoom = true;
+    if (didPs)
+      executeSoon(cb);
+  });
+
   if (direction == BACK)
     gBrowser.goBack();
   else if (direction == FORWARD)
     gBrowser.goForward();
 }
+
+function afterZoom(cb) {
+  let oldAPTS = FullZoom._applyPrefToSetting;
+  FullZoom._applyPrefToSetting = function(value, browser) {
+    if (!value)
+      value = undefined;
+    oldAPTS.call(FullZoom, value, browser);
+    FullZoom._applyPrefToSetting = oldAPTS;
+    executeSoon(cb);
+  };
+}
--- a/browser/base/content/test/browser_bug416661.js
+++ b/browser/base/content/test/browser_bug416661.js
@@ -1,49 +1,63 @@
 var tabElm, zoomLevel;
 function start_test_prefNotSet() {
-  tabElm.linkedBrowser.removeEventListener("load", start_test_prefNotSet, true);
-  tabElm.linkedBrowser.addEventListener("load", continue_test_prefNotSet, true);
-
   is(ZoomManager.zoom, 1, "initial zoom level should be 1");
   FullZoom.enlarge();
 
   //capture the zoom level to test later
   zoomLevel = ZoomManager.zoom;
   isnot(zoomLevel, 1, "zoom level should have changed");
 
+  afterZoomAndLoad(continue_test_prefNotSet);
   content.location = 
     "http://mochi.test:8888/browser/browser/base/content/test/moz.png";
 }
 
 function continue_test_prefNotSet () {
-  tabElm.linkedBrowser.removeEventListener("load", continue_test_prefNotSet, true);
-  tabElm.linkedBrowser.addEventListener("load", end_test_prefNotSet, true);
-
   is(ZoomManager.zoom, 1, "zoom level pref should not apply to an image");
   FullZoom.reset();
 
+  afterZoomAndLoad(end_test_prefNotSet);
   content.location = 
     "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html";
 }
 
 function end_test_prefNotSet() {
-  tabElm.linkedBrowser.removeEventListener("load", end_test_prefNotSet, true);
   is(ZoomManager.zoom, zoomLevel, "the zoom level should have persisted");
 
   // Reset the zoom so that other tests have a fresh zoom level
   FullZoom.reset();
   gBrowser.removeCurrentTab();
   finish();
 }
 
 
 function test() {
   waitForExplicitFinish();
 
   tabElm = gBrowser.addTab();
   gBrowser.selectedTab = tabElm;
-  tabElm.linkedBrowser.addEventListener("load", start_test_prefNotSet, true);
 
+  afterZoomAndLoad(start_test_prefNotSet);
   content.location = 
     "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html";
+}
 
+function afterZoomAndLoad(cb) {
+  let didLoad = didZoom = false;
+  tabElm.linkedBrowser.addEventListener("load", function() {
+    tabElm.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    didLoad = true;
+    if (didZoom)
+      executeSoon(cb);
+  }, true);
+  let oldAPTS = FullZoom._applyPrefToSetting;
+  FullZoom._applyPrefToSetting = function(value, browser) {
+    if (!value)
+      value = undefined;
+    oldAPTS.call(FullZoom, value, browser);
+    FullZoom._applyPrefToSetting = oldAPTS;
+    didZoom = true;
+    if (didLoad)
+      executeSoon(cb);
+  };
 }
--- a/browser/base/content/test/browser_bug419612.js
+++ b/browser/base/content/test/browser_bug419612.js
@@ -7,29 +7,43 @@ function test() {
     event.currentTarget.removeEventListener("load", arguments.callee, true);
 
     let tab2 = gBrowser.addTab();
     tab2.linkedBrowser.addEventListener("load", (function(event) {
       event.currentTarget.removeEventListener("load", arguments.callee, true);
 
       FullZoom.enlarge();
       let tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
-      gBrowser.selectedTab = tab2;
-      let tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
-      is(tab2Zoom, tab1Zoom, "Zoom should affect background tabs");
+
+      afterZoom(function() {
+        let tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
+        is(tab2Zoom, tab1Zoom, "Zoom should affect background tabs");
 
-      gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", false);
-      FullZoom.reset();
-      gBrowser.selectedTab = tab1;
-      tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
-      tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
-      isnot(tab1Zoom, tab2Zoom, "Zoom should not affect background tabs");
+        gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", false);
+        FullZoom.reset();
+        gBrowser.selectedTab = tab1;
+        tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
+        tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
+        isnot(tab1Zoom, tab2Zoom, "Zoom should not affect background tabs");
 
-      if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
-        gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
-      gBrowser.removeTab(tab1);
-      gBrowser.removeTab(tab2);
-      finish();
+        if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
+          gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
+        gBrowser.removeTab(tab1);
+        gBrowser.removeTab(tab2);
+        finish();
+      });
+      gBrowser.selectedTab = tab2;
     }), true);
     tab2.linkedBrowser.loadURI(testPage);
   }), true);
   content.location = testPage;
 }
+
+function afterZoom(cb) {
+  let oldAPTS = FullZoom._applyPrefToSetting;
+  FullZoom._applyPrefToSetting = function(value, browser) {
+    if (!value)
+      value = undefined;
+    oldAPTS.call(FullZoom, value, browser);
+    FullZoom._applyPrefToSetting = oldAPTS;
+    executeSoon(cb);
+  };
+}
--- a/browser/base/content/test/browser_bug520538.js
+++ b/browser/base/content/test/browser_bug520538.js
@@ -1,15 +1,15 @@
 function test() {
-  var tabs = gBrowser.tabContainer.childElementCount;
+  var tabCount = gBrowser.tabs.length;
   content.focus();
   browserDOMWindow.openURI(makeURI("about:blank"),
                            null,
                            Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
                            Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
-  is(gBrowser.tabContainer.childElementCount, tabs + 1,
+  is(gBrowser.tabs.length, tabCount + 1,
      "'-new-tab about:blank' opens a new tab");
-  is(gBrowser.selectedTab, gBrowser.tabContainer.childNodes[tabs],
+  is(gBrowser.selectedTab, gBrowser.tabs[tabCount],
      "'-new-tab about:blank' selects the new tab");
   is(document.activeElement, gURLBar.inputField,
      "'-new-tab about:blank' focuses the location bar");
   gBrowser.removeCurrentTab();
 }
--- a/browser/base/content/test/browser_bug521216.js
+++ b/browser/base/content/test/browser_bug521216.js
@@ -1,16 +1,16 @@
 var expected = ["TabOpen", "onLocationChange", "onStateChange", "onLinkIconAvailable"];
 var actual = [];
 var tabIndex = -1;
-__defineGetter__("tab", function () gBrowser.tabContainer.childNodes[tabIndex]);
+__defineGetter__("tab", function () gBrowser.tabs[tabIndex]);
 
 function test() {
   waitForExplicitFinish();
-  tabIndex = gBrowser.tabContainer.childElementCount;
+  tabIndex = gBrowser.tabs.length;
   gBrowser.addTabsProgressListener(progressListener);
   gBrowser.tabContainer.addEventListener("TabOpen", TabOpen, false);
   gBrowser.addTab("data:text/html,<html><head><link href='about:logo' rel='shortcut icon'>");
 }
 
 function record(aName) {
   info("got " + aName);
   if (actual.indexOf(aName) == -1)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug555224.js
@@ -0,0 +1,93 @@
+/* ***** 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 Test Code.
+ *
+ * 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):
+ *   Ryan Flint <rflint@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 ***** */
+const TEST_PAGE = "/browser/browser/base/content/test/dummy_page.html";
+var gTestTab, gBgTab, gTestZoom;
+
+function afterZoomAndLoad(aCallback, aTab) {
+  let didLoad = didZoom = false;
+  aTab.linkedBrowser.addEventListener("load", function() {
+    aTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    didLoad = true;
+    if (didZoom)
+      executeSoon(aCallback);
+  }, true);
+  let oldAPTS = FullZoom._applyPrefToSetting;
+  FullZoom._applyPrefToSetting = function(value, browser) {
+    if (!value)
+      value = undefined;
+    oldAPTS.call(FullZoom, value, browser);
+    // Don't reset _applyPrefToSetting until we've seen the about:blank load(s)
+    if (browser && (browser.currentURI.spec.indexOf("http:") != -1)) {
+      FullZoom._applyPrefToSetting = oldAPTS;
+      didZoom = true;
+    }
+    if (didLoad && didZoom)
+      executeSoon(aCallback);
+  };
+}
+
+function testBackgroundLoad() {
+  is(ZoomManager.zoom, gTestZoom, "opening a background tab should not change foreground zoom");
+
+  gBrowser.removeTab(gBgTab);
+
+  FullZoom.reset();
+  gBrowser.removeTab(gTestTab);
+
+  finish();
+}
+
+function testInitialZoom() {
+  is(ZoomManager.zoom, 1, "initial zoom level should be 1");
+  FullZoom.enlarge();
+
+  gTestZoom = ZoomManager.zoom;
+  isnot(gTestZoom, 1, "zoom level should have changed");
+
+  afterZoomAndLoad(testBackgroundLoad,
+                   gBgTab = gBrowser.loadOneTab("http://mochi.test:8888" + TEST_PAGE,
+                                                {inBackground: true}));
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  gTestTab = gBrowser.addTab();
+  gBrowser.selectedTab = gTestTab;
+
+  afterZoomAndLoad(testInitialZoom, gTestTab);
+  content.location = "http://example.org" + TEST_PAGE;
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_tabMatchesInAwesomebar.js
@@ -0,0 +1,231 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim:set ts=2 sw=2 sts=2 et:
+ * ***** 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 Places 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):
+ *   Blair McBride <bmcbride@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const TEST_URL_BASES = [
+  "http://example.org/browser/browser/base/content/test/dummy_page.html#tabmatch",
+  "http://example.org/browser/browser/base/content/test/moz.png#tabmatch"
+];
+
+var gPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
+                         getService(Ci.nsIPrivateBrowsingService);
+
+
+var gTabWaitCount = 0;
+var gTabCounter = 0;
+
+var gTestSteps = [
+  function() {
+    info("Running step 1");
+    for (let i = 0; i < 10; i++) {
+      let tab = gBrowser.addTab();
+      loadTab(tab, TEST_URL_BASES[0] + (++gTabCounter));
+    }
+  },
+  function() {
+    info("Running step 2");
+    gBrowser.selectTabAtIndex(1);
+    gBrowser.removeCurrentTab();
+    gBrowser.selectTabAtIndex(1);
+    gBrowser.removeCurrentTab();
+    for (let i = 1; i < gBrowser.tabs.length; i++)
+      loadTab(gBrowser.tabs[i], TEST_URL_BASES[1] + (++gTabCounter));
+  },
+  function() {
+    info("Running step 3");
+    for (let i = 1; i < gBrowser.tabs.length; i++)
+      loadTab(gBrowser.tabs[i], TEST_URL_BASES[0] + gTabCounter);
+  },
+  function() {
+    info("Running step 4");
+    let ps = Services.prefs;
+    ps.setBoolPref("browser.privatebrowsing.keep_current_session", true);
+    ps.setBoolPref("browser.tabs.warnOnClose", false);
+
+    gPrivateBrowsing.privateBrowsingEnabled = true;
+
+    executeSoon(function() {
+      ensure_opentabs_match_db();
+      nextStep();
+    });
+  },
+  function() {
+    info("Running step 5");
+    gPrivateBrowsing.privateBrowsingEnabled = false;
+
+    executeSoon(function() {
+      let ps = Services.prefs;
+      try {
+        ps.clearUserPref("browser.privatebrowsing.keep_current_session");
+      } catch (ex) {}
+      try {
+        ps.clearUserPref("browser.tabs.warnOnClose");
+      } catch (ex) {}
+
+      ensure_opentabs_match_db();
+      nextStep()
+    });
+  }
+];
+
+
+
+function test() {
+  waitForExplicitFinish();
+  nextStep();
+}
+
+function loadTab(tab, url) {
+  tab.linkedBrowser.addEventListener("load", function (event) {
+    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    if (--gTabWaitCount > 0)
+      return;
+    is(gTabWaitCount, 0,
+       "sanity check, gTabWaitCount should not be decremented below 0");
+
+    try {
+      ensure_opentabs_match_db();
+    } catch (e) {
+      ok(false, "exception from ensure_openpages_match_db: " + e);
+    }
+
+    executeSoon(nextStep);
+  }, true);
+  gTabWaitCount++;
+  tab.linkedBrowser.loadURI(url);
+}
+
+function nextStep() {
+  if (gTestSteps.length == 0) {
+    while (gBrowser.tabs.length > 1) {
+      gBrowser.selectTabAtIndex(1);
+      gBrowser.removeCurrentTab();
+    }
+
+    waitForClearHistory(finish);
+
+    return;
+  }
+
+  var stepFunc = gTestSteps.shift();
+  stepFunc();
+}
+
+function ensure_opentabs_match_db() {
+  var tabs = {};
+
+  var winEnum = Services.wm.getEnumerator("navigator:browser");
+  while (winEnum.hasMoreElements()) {
+    let browserWin = winEnum.getNext();
+    // skip closed-but-not-destroyed windows
+    if (browserWin.closed)
+      continue;
+
+    for (let i = 0; i < browserWin.gBrowser.tabContainer.childElementCount; i++) {
+      let browser = browserWin.gBrowser.getBrowserAtIndex(i);
+      let url = browser.currentURI.spec;
+      if (!(url in tabs))
+        tabs[url] = 1;
+      else
+        tabs[url]++;
+    }
+  }
+
+  var db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+                              .DBConnection;
+
+  try {
+    var stmt = db.createStatement(
+                          "SELECT IFNULL(p_t.url, p.url) AS url, open_count, place_id " +
+                          "FROM moz_openpages_temp " +
+                          "LEFT JOIN moz_places p ON p.id=place_id " +
+                          "LEFT JOIN moz_places_temp p_t ON p_t.id=place_id");
+  } catch (e) {
+    ok(false, "error creating db statement: " + e);
+    return;
+  }
+
+  var dbtabs = [];
+  try {
+    while (stmt.executeStep()) {
+      ok(stmt.row.url in tabs,
+         "url is in db, should be in tab: " + stmt.row.url);
+      is(tabs[stmt.row.url], stmt.row.open_count,
+         "db count (" + stmt.row.open_count + ") " +
+         "should match actual open tab count " +
+         "(" + tabs[stmt.row.url] + "): " + stmt.row.url);
+      dbtabs.push(stmt.row.url);
+    }
+  } finally {
+    stmt.finalize();
+  }
+
+  for (let url in tabs) {
+    // ignore URLs that should never be in the places db
+    if (!is_expected_in_db(url))
+      continue;
+    ok(dbtabs.indexOf(url) > -1,
+       "tab is open (" + tabs[url] + " times) and should recorded in db: " + url);
+  }
+}
+
+function is_expected_in_db(url) {
+  var uri = Services.io.newURI(url, null, null);
+  return PlacesUtils.history.canAddURI(uri);
+}
+
+/**
+ * Clears history invoking callback when done.
+ */
+function waitForClearHistory(aCallback) {
+  const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
+  let observer = {
+    observe: function(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
+      aCallback();
+    }
+  };
+  Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
+
+  let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+           getService(Ci.nsINavHistoryService);
+  hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
+}
--- a/browser/base/content/test/browser_tabs_owner.js
+++ b/browser/base/content/test/browser_tabs_owner.js
@@ -1,14 +1,14 @@
 function test() {
   gBrowser.addTab();
   gBrowser.addTab();
   gBrowser.addTab();
 
-  var tabs = gBrowser.tabContainer.childNodes;
+  var tabs = gBrowser.tabs;
   var owner;
 
   is(tabs.length, 4, "4 tabs are open");
 
   owner = gBrowser.selectedTab = tabs[2];
   BrowserOpenTab();
   is(gBrowser.selectedTab, tabs[4], "newly opened tab is selected");
   gBrowser.removeCurrentTab();
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -56,31 +56,66 @@
         this._setPlaceholder();
 
         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.inputField.addEventListener("overflow", this, false);
+        this.inputField.addEventListener("underflow", this, false);
       ]]></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.inputField.removeEventListener("overflow", this, false);
+        this.inputField.removeEventListener("underflow", 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.
+      -->
+      <method name="onBeforeValueGet">
+        <body><![CDATA[
+          if (this.hasAttribute("actiontype"))
+            return {value: this._value};
+          return null;
+        ]]></body>
+      </method>
+
+      <!--
+        onBeforeValueSet is called by the base-binding's .value setter.
+        It should return the value that the setter should use.
+      -->
+      <method name="onBeforeValueSet">
+        <parameter name="aValue"/>
+        <body><![CDATA[
+          this._value = aValue;
+          var returnValue = aValue;
+          var action = this._parseActionUrl(aValue);
+          if (action) {
+            returnValue = action.param;
+            this.setAttribute("actiontype", action.type);
+          } else {
+            this.removeAttribute("actiontype");
+          }
+          return returnValue;
+        ]]></body>
+      </method>
+
       <method name="handleRevert">
         <body><![CDATA[
           var isScrolling = this.popupOpen;
 
           gBrowser.userTypedValue = null;
 
           // don't revert to last valid url unless page is NOT loading
           // and user is NOT key-scrolling through autocomplete list
@@ -103,16 +138,23 @@
         <body><![CDATA[
           if (aTriggeringEvent instanceof MouseEvent && aTriggeringEvent.button == 2)
             return; // Do nothing for right clicks
 
           var [url, postData] = this._canonizeURL(aTriggeringEvent);
           if (!url)
             return;
 
+          var action = this._parseActionUrl(url);
+          if (action) {
+            if (action.type == "switchtab")
+              switchToTabHavingURI(action.param);
+            return;
+          }
+
           this.value = url;
           gBrowser.userTypedValue = url;
           try {
             addToUrlbarHistory(url);
           } catch (ex) {
             // Things may go wrong when adding url to session history,
             // but don't let that interfere with the loading of the url.
             Cu.reportError(ex);
@@ -211,22 +253,22 @@
           }
 
           var postData = {};
           url = getShortcutOrURI(url, postData);
 
           return [url, postData.value];
         ]]></body>
       </method>
-
-      <field name="_contentIsCropped">false</field>
+
+      <field name="_contentIsCropped">false</field>
 
       <method name="_initURLTooltip">
         <body><![CDATA[
-          if (this.focused || !this._contentIsCropped)
+          if (this.focused || !this._contentIsCropped)
             return;
           if (this._tooltipTimer)
             clearTimeout(this._tooltipTimer);
           this._tooltipTimer = setTimeout(function (self) {
             self._tooltipTimer = 0;
             var label = self._urlTooltip.firstChild;
             label.value = self.value;
             var bO = self.boxObject;
@@ -384,23 +426,23 @@
               }
               break;
             case "mousemove":
               this._initURLTooltip();
               break;
             case "mouseout":
               this._hideURLTooltip();
               break;
-            case "overflow":
-              this._contentIsCropped = true;
-              break;
-            case "underflow":
-              this._contentIsCropped = false;
-              this._hideURLTooltip();
-              break;
+            case "overflow":
+              this._contentIsCropped = true;
+              break;
+            case "underflow":
+              this._contentIsCropped = false;
+              this._hideURLTooltip();
+              break;
           }
         ]]></body>
       </method>
 
       <property name="textValue"
                 onget="return this.value;">
         <setter>
           <![CDATA[
@@ -437,16 +479,28 @@
               case 2:
                 type = "bookmark";
                 break;
             }
           }
           this.placeholder = this.getAttribute(type + "placeholder");
         ]]></body>
       </method>
+
+      <method name="_parseActionUrl">
+        <parameter name="aUrl"/>
+        <body><![CDATA[
+          if (!/^moz-action:/.test(aUrl))
+            return null;
+
+          // url is in the format moz-action:ACTION,PARAM
+          let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
+          return {type: action, param: param};
+        ]]></body>
+      </method>
     </implementation>
 
     <handlers>
       <handler event="draggesture" phase="capturing"><![CDATA[
         // TODO: This should use dragstart but editor code is still using
         //       the old drag & drop APIs, so we have to handle draggesture.
         //       This can be changed once editor code is updated to the new API.
         //       See bug 499008 for details.
@@ -608,16 +662,25 @@
           // Check for middle-click or modified clicks on the URL bar
           if (gURLBar && this.mInput == gURLBar) {
             var url = controller.getValueAt(this.selectedIndex);
 
             // close the autocomplete popup and revert the entered address
             this.closePopup();
             controller.handleEscape();
 
+            // Check if this is meant to be an action
+            let action = this.mInput._parseActionUrl(url);
+            if (action) {
+              if (action.type == "switchtab")
+                url = action.param;
+              else
+                return;
+            }
+
             // respect the usual clicking subtleties
             openUILink(url, aEvent);
           }
         ]]>
         </body>
       </method>
 
       <method name="createResultLabel">
@@ -635,9 +698,10 @@
             }
             return label;
           ]]>
         </body>
       </method>
 
     </implementation>
   </binding>
+
 </bindings>
--- a/browser/components/Makefile.in
+++ b/browser/components/Makefile.in
@@ -57,17 +57,16 @@ EXTRA_PP_COMPONENTS = \
 
 EXTRA_JS_MODULES = distribution.js
 
 PARALLEL_DIRS = \
   about \
   certerror \
   dirprovider \
   feeds \
-  microsummaries \
   places \
   preferences \
   privatebrowsing \
   search \
   sessionstore \
   shell \
   sidebar \
   $(NULL)
--- a/browser/components/build/Makefile.in
+++ b/browser/components/build/Makefile.in
@@ -38,17 +38,16 @@ LOCAL_INCLUDES = \
 	$(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 OS_LIBS += $(call EXPAND_LIBNAME,version)
 endif
 
 SHARED_LIBRARY_LIBS = \
 	../feeds/src/$(LIB_PREFIX)browser_feeds_s.$(LIB_SUFFIX) \
-	../places/src/$(LIB_PREFIX)browserplaces_s.$(LIB_SUFFIX) \
 	../privatebrowsing/src/$(LIB_PREFIX)privatebrowsing_s.$(LIB_SUFFIX) \
 	../about/$(LIB_PREFIX)browserabout_s.$(LIB_SUFFIX) \
 	../dirprovider/$(LIB_PREFIX)browserdir_s.$(LIB_SUFFIX) \
 	$(NULL)
 
 ifneq (,$(filter windows cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))
 SHARED_LIBRARY_LIBS += ../shell/src/$(LIB_PREFIX)shellservice_s.$(LIB_SUFFIX)
 endif
--- a/browser/components/build/nsBrowserCompsCID.h
+++ b/browser/components/build/nsBrowserCompsCID.h
@@ -86,23 +86,16 @@
 { 0x6893e69, 0x71d8, 0x4b23, { 0x81, 0xeb, 0x80, 0x31, 0x4d, 0xaf, 0x3e, 0x66 } }
 
 #define NS_FEEDSNIFFER_CONTRACTID \
   "@mozilla.org/browser/feeds/sniffer;1"
 
 #define NS_ABOUTFEEDS_CID \
 { 0x12ff56ec, 0x58be, 0x402c, { 0xb0, 0x57, 0x1, 0xf9, 0x61, 0xde, 0x96, 0x9b } }
 
-// 6fb0c970-e1b1-11db-8314-0800200c9a66
-#define NS_PLACESIMPORTEXPORTSERVICE_CID \
-{ 0x6fb0c970, 0xe1b1, 0x11db, { 0x83, 0x14, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
-
-#define NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID \
-  "@mozilla.org/browser/places/import-export-service;1"
-
 // 136e2c4d-c5a4-477c-b131-d93d7d704f64
 #define NS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID \
 { 0x136e2c4d, 0xc5a4, 0x477c, { 0xb1, 0x31, 0xd9, 0x3d, 0x7d, 0x70, 0x4f, 0x64 } }
 
 // 7e4bb6ad-2fc4-4dc6-89ef-23e8e5ccf980
 #define NS_BROWSER_ABOUT_REDIRECTOR_CID \
 { 0x7e4bb6ad, 0x2fc4, 0x4dc6, { 0x89, 0xef, 0x23, 0xe8, 0xe5, 0xcc, 0xf9, 0x80 } }
 
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -35,17 +35,16 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIGenericFactory.h"
 
 #include "nsBrowserCompsCID.h"
 #include "DirectoryProvider.h"
-#include "nsPlacesImportExportService.h"
 
 #if defined(XP_WIN)
 #include "nsWindowsShellService.h"
 #elif defined(XP_MACOSX)
 #include "nsMacShellService.h"
 #elif defined(MOZ_WIDGET_GTK2)
 #include "nsGNOMEShellService.h"
 #endif
@@ -81,17 +80,16 @@
 #include "nsPrivateBrowsingServiceWrapper.h"
 #include "nsNetCID.h"
 
 using namespace mozilla::browser;
 
 /////////////////////////////////////////////////////////////////////////////
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(DirectoryProvider)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlacesImportExportService)
 #if defined(XP_WIN)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowsShellService)
 #elif defined(XP_MACOSX)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacShellService)
 #elif defined(MOZ_WIDGET_GTK2)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGNOMEShellService, Init)
 #endif
 
@@ -143,22 +141,16 @@ static const nsModuleComponentInfo compo
 #elif defined(MOZ_WIDGET_GTK2)
   { "Browser Shell Service",
     NS_SHELLSERVICE_CID,
     NS_SHELLSERVICE_CONTRACTID,
     nsGNOMEShellServiceConstructor },
 
 #endif
 
-
-  { "Places Import/Export Service",
-    NS_PLACESIMPORTEXPORTSERVICE_CID,
-    NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID,
-    nsPlacesImportExportServiceConstructor},
-
   { "Feed Sniffer",
     NS_FEEDSNIFFER_CID,
     NS_FEEDSNIFFER_CONTRACTID,
     nsFeedSnifferConstructor,
     nsFeedSniffer::Register },
 
 #ifdef MOZ_SAFE_BROWSING
   { "about:blocked",
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -94,25 +94,16 @@ DirectoryProvider::GetFile(const char *a
     rv = NS_GetSpecialDirectory(NS_APP_DEFAULTS_50_DIR,
                                 getter_AddRefs(file));
     NS_ENSURE_SUCCESS(rv, rv);
 
     file->AppendNative(NS_LITERAL_CSTRING("existing-profile-defaults.js"));
     file.swap(*aResult);
     return NS_OK;
   }
-  else if (!strcmp(aKey, NS_APP_USER_MICROSUMMARY_DIR)) {
-    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                                getter_AddRefs(file));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    file->AppendNative(NS_LITERAL_CSTRING("microsummary-generators"));
-    file.swap(*aResult);
-    return NS_OK;
-  }
   else {
     return NS_ERROR_FAILURE;
   }
 
   nsDependentCString leafstr(leafName);
 
   nsCOMPtr<nsIFile> parentDir;
   if (file) {
--- a/browser/components/dirprovider/nsBrowserDirectoryServiceDefs.h
+++ b/browser/components/dirprovider/nsBrowserDirectoryServiceDefs.h
@@ -49,15 +49,9 @@
 //=============================================================================
 
 // ----------------------------------------------------------------------------
 // Files and directories that exist on a per-browser basis.
 // ----------------------------------------------------------------------------
 
 #define NS_APP_EXISTING_PREF_OVERRIDE           "ExistingPrefOverride"
 
-// ----------------------------------------------------------------------------
-// Files and directories that exist on a per-profile basis.
-// ----------------------------------------------------------------------------
-
-#define NS_APP_USER_MICROSUMMARY_DIR            "UsrMicsumGens"
-         
 #endif
--- a/browser/components/dirprovider/tests/unit/test_keys.js
+++ b/browser/components/dirprovider/tests/unit/test_keys.js
@@ -29,35 +29,16 @@
  * 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 b