Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Mon, 19 Nov 2012 18:10:54 -0800
changeset 126448 79570035f4fa23769505602a4a9e620367e299e3
parent 126447 0f5fb8247f5357a30dbad4db24504c1b03afba9b (current diff)
parent 122776 669ac8371d192b66409e633bde26914a70385766 (diff)
child 126449 7a3e3dc72d8622557be5b2a2c84d6d8ef6887834
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
accessible/src/base/nsAccTreeWalker.cpp
accessible/src/base/nsAccTreeWalker.h
gfx/harfbuzz/src/hb-ot-shape-complex-misc.cc
js/src/yarr/TypeTraits.h
toolkit/components/downloads/test/unit/test_memory_db_support.js
--- a/.hgtags
+++ b/.hgtags
@@ -83,8 +83,9 @@ 357da346ceb705d196a46574804c7c4ec44ac186
 26dcd1b1a20893ad99341c61c6b1239ff1523858 FIREFOX_AURORA_15_BASE
 0accd12a8e7e217836ea3f1ee7c411913fc75d8e FIREFOX_AURORA_16_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_16_BASE
 9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
 9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
 6fdf9985acfe6f939da584b2559464ab22264fe7 FIREFOX_AURORA_16_BASE
 fd72dbbd692012224145be1bf13df1d7675fd277 FIREFOX_AURORA_17_BASE
 2704e441363fe2a48e992dfac694482dfd82664a FIREFOX_AURORA_18_BASE
+cf8750abee06cde395c659f8ecd8ae019d7512e3 FIREFOX_AURORA_19_BASE
--- a/accessible/public/nsIAccessibilityService.h
+++ b/accessible/public/nsIAccessibilityService.h
@@ -7,17 +7,24 @@
 #ifndef _nsIAccessibilityService_h_
 #define _nsIAccessibilityService_h_
 
 #include "nsIAccessibleRetrieval.h"
 #include "nsIAccessibleEvent.h"
 
 #include "nsAutoPtr.h"
 
+namespace mozilla {
+namespace a11y {
+
 class Accessible;
+
+} // namespace a11y
+} // namespace mozilla
+
 class nsINode;
 class nsIContent;
 class nsIFrame;
 class nsIPresShell;
 class nsObjectFrame;
 
 // 10ff6dca-b219-4b64-9a4c-67a62b86edce
 #define NS_IACCESSIBILITYSERVICE_IID \
@@ -32,33 +39,36 @@ public:
   /**
    * Return root document accessible that is or contains a document accessible
    * for the given presshell.
    *
    * @param aPresShell  [in] the presshell
    * @param aCanCreate  [in] points whether the root document accessible
    *                        should be returned from the cache or can be created
    */
-  virtual Accessible* GetRootDocumentAccessible(nsIPresShell* aPresShell,
-                                                bool aCanCreate) = 0;
+  virtual mozilla::a11y::Accessible*
+    GetRootDocumentAccessible(nsIPresShell* aPresShell, bool aCanCreate) = 0;
 
    /**
    * Adds/remove ATK root accessible for gtk+ native window to/from children
    * of the application accessible.
    */
-  virtual Accessible* AddNativeRootAccessible(void* aAtkAccessible) = 0;
-  virtual void RemoveNativeRootAccessible(Accessible* aRootAccessible) = 0;
+  virtual mozilla::a11y::Accessible*
+    AddNativeRootAccessible(void* aAtkAccessible) = 0;
+  virtual void
+    RemoveNativeRootAccessible(mozilla::a11y::Accessible* aRootAccessible) = 0;
 
   /**
    * Fire accessible event of the given type for the given target.
    *
    * @param aEvent   [in] accessible event type
    * @param aTarget  [in] target of accessible event
    */
-  virtual void FireAccessibleEvent(uint32_t aEvent, Accessible* aTarget) = 0;
+  virtual void FireAccessibleEvent(uint32_t aEvent,
+                                   mozilla::a11y::Accessible* aTarget) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIAccessibilityService,
                               NS_IACCESSIBILITYSERVICE_IID)
 
 // for component registration
 // {DE401C37-9A7F-4278-A6F8-3DE2833989EF}
 #define NS_ACCESSIBILITY_SERVICE_CID \
--- a/accessible/src/atk/AccessibleWrap.h
+++ b/accessible/src/atk/AccessibleWrap.h
@@ -36,16 +36,19 @@ enum AtkProperty {
 };
 
 struct AtkPropertyChange {
   int32_t type;     // property type as listed above
   void *oldvalue;
   void *newvalue;
 };
 
+namespace mozilla {
+namespace a11y {
+
 class MaiHyperlink;
 
 /**
  * AccessibleWrap, and its descendents in atk directory provide the
  * implementation of AtkObject.
  */
 class AccessibleWrap : public Accessible
 {
@@ -105,9 +108,12 @@ private:
     eNoNewAtkSignals
   };
 
   static EAvailableAtkSignals gAvailableAtkSignals;
 
   uint16_t CreateMaiInterfaces(void);
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif /* __NS_ACCESSIBLE_WRAP_H__ */
--- a/accessible/src/atk/AtkSocketAccessible.cpp
+++ b/accessible/src/atk/AtkSocketAccessible.cpp
@@ -6,16 +6,18 @@
 
 #include <atk/atk.h>
 #include "AtkSocketAccessible.h"
 
 #include "InterfaceInitFuncs.h"
 #include "nsMai.h"
 #include "mozilla/Likely.h"
 
+using namespace mozilla::a11y;
+
 AtkSocketEmbedType AtkSocketAccessible::g_atk_socket_embed = NULL;
 GType AtkSocketAccessible::g_atk_socket_type = G_TYPE_INVALID;
 const char* AtkSocketAccessible::sATKSocketEmbedSymbol = "atk_socket_embed";
 const char* AtkSocketAccessible::sATKSocketGetTypeSymbol = "atk_socket_get_type";
 
 bool AtkSocketAccessible::gCanEmbed = FALSE;
 
 extern "C" void mai_atk_component_iface_init(AtkComponentIface* aIface);
--- a/accessible/src/atk/AtkSocketAccessible.h
+++ b/accessible/src/atk/AtkSocketAccessible.h
@@ -12,16 +12,19 @@
 // This file gets included by nsAccessibilityService.cpp, which can't include
 // atk.h (or glib.h), so we can't rely on it being included.
 #ifdef __ATK_H__
 extern "C" typedef void (*AtkSocketEmbedType) (AtkSocket*, gchar*);
 #else
 extern "C" typedef void (*AtkSocketEmbedType) (void*, void*);
 #endif
 
+namespace mozilla {
+namespace a11y {
+
 /**
  * Provides a AccessibleWrap wrapper around AtkSocket for out-of-process
  * accessibles.
  */
 class AtkSocketAccessible : public AccessibleWrap
 {
 public:
 
@@ -44,9 +47,12 @@ public:
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsIAccessible
   NS_IMETHODIMP GetNativeInterface(void** aOutAccessible);
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/atk/DocAccessibleWrap.cpp
+++ b/accessible/src/atk/DocAccessibleWrap.cpp
@@ -2,16 +2,18 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsMai.h"
 #include "DocAccessibleWrap.h"
 
+using namespace mozilla::a11y;
+
 ////////////////////////////////////////////////////////////////////////////////
 // DocAccessibleWrap
 ////////////////////////////////////////////////////////////////////////////////
 
 DocAccessibleWrap::
   DocAccessibleWrap(nsIDocument* aDocument, nsIContent* aRootContent,
                     nsIPresShell* aPresShell) :
   DocAccessible(aDocument, aRootContent, aPresShell), mActivated(false)
--- a/accessible/src/atk/DocAccessibleWrap.h
+++ b/accessible/src/atk/DocAccessibleWrap.h
@@ -8,19 +8,25 @@
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 #ifndef mozilla_a11y_DocAccessibleWrap_h__
 #define mozilla_a11y_DocAccessibleWrap_h__
 
 #include "DocAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+
 class DocAccessibleWrap : public DocAccessible
 {
 public:
   DocAccessibleWrap(nsIDocument* aDocument, nsIContent* aRootContent,
                     nsIPresShell* aPresShell);
   virtual ~DocAccessibleWrap();
 
   bool mActivated;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/atk/HyperTextAccessibleWrap.h
+++ b/accessible/src/atk/HyperTextAccessibleWrap.h
@@ -4,12 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_HyperTextAccessibleWrap_h__
 #define mozilla_a11y_HyperTextAccessibleWrap_h__
 
 #include "HyperTextAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+
 typedef class HyperTextAccessible HyperTextAccessibleWrap;
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/atk/InterfaceInitFuncs.h
+++ b/accessible/src/atk/InterfaceInitFuncs.h
@@ -4,18 +4,24 @@
  *  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  *   * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ATK_INTERFACE_INIT_FUNCS_H_
 #define ATK_INTERFACE_INIT_FUNCS_H_
 
 #include <atk/atk.h>
 
+namespace mozilla {
+namespace a11y {
+
 class AccessibleWrap;
 
+} // namespace a11y
+} // namespace mozilla
+
 extern "C" {
 void actionInterfaceInitCB(AtkActionIface* aIface);
 void componentInterfaceInitCB(AtkComponentIface* aIface);
 void documentInterfaceInitCB(AtkDocumentIface *aIface);
 void editableTextInterfaceInitCB(AtkEditableTextIface* aIface);
 void hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface *aIface);
 void hypertextInterfaceInitCB(AtkHypertextIface* aIface);
 void imageInterfaceInitCB(AtkImageIface* aIface);
@@ -23,15 +29,15 @@ void selectionInterfaceInitCB(AtkSelecti
 void tableInterfaceInitCB(AtkTableIface *aIface);
 void textInterfaceInitCB(AtkTextIface* aIface);
 void valueInterfaceInitCB(AtkValueIface *aIface);
 }
 
 /**
  * XXX these should live in a file of utils for atk.
  */
-AtkObject* refAccessibleAtPointHelper(AccessibleWrap* aAccWrap,
+AtkObject* refAccessibleAtPointHelper(mozilla::a11y::AccessibleWrap* aAccWrap,
                                       gint aX, gint aY, AtkCoordType aCoordType);
-void getExtentsHelper(AccessibleWrap* aAccWrap,
+void getExtentsHelper(mozilla::a11y::AccessibleWrap* aAccWrap,
                       gint* aX, gint* aY, gint* aWidth, gint* aHeight,
                       AtkCoordType aCoordType);
 
 #endif // ATK_INTERFACE_INIT_FUNCS_H_
--- a/accessible/src/atk/nsAccessNodeWrap.cpp
+++ b/accessible/src/atk/nsAccessNodeWrap.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAccessNodeWrap.h"
 
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
+using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNodeWrap
 ////////////////////////////////////////////////////////////////////////////////
 
 //-----------------------------------------------------
 // construction 
 //-----------------------------------------------------
--- a/accessible/src/atk/nsAccessNodeWrap.h
+++ b/accessible/src/atk/nsAccessNodeWrap.h
@@ -8,19 +8,25 @@
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 #ifndef _nsAccessNodeWrap_H_
 #define _nsAccessNodeWrap_H_
 
 #include "nsAccessNode.h"
 
-class nsAccessNodeWrap :  public nsAccessNode
+namespace mozilla {
+namespace a11y {
+
+class nsAccessNodeWrap : public nsAccessNode
 {
 public: // construction, destruction
   nsAccessNodeWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~nsAccessNodeWrap();
 
   static void InitAccessibility();
   static void ShutdownAccessibility();
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/atk/nsMai.h
+++ b/accessible/src/atk/nsMai.h
@@ -41,11 +41,11 @@ PR_END_MACRO
 #define IS_MAI_OBJECT(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
                                          MAI_TYPE_ATK_OBJECT))
 #define IS_MAI_OBJECT_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), \
                                          MAI_TYPE_ATK_OBJECT))
 #define MAI_ATK_OBJECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), \
                                          MAI_TYPE_ATK_OBJECT, \
                                          MaiAtkObjectClass))
 GType mai_atk_object_get_type(void);
-AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
+mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
 
 #endif /* __NS_MAI_H__ */
--- a/accessible/src/atk/nsMaiHyperlink.cpp
+++ b/accessible/src/atk/nsMaiHyperlink.cpp
@@ -2,16 +2,18 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIURI.h"
 #include "nsMaiHyperlink.h"
 
+using namespace mozilla::a11y;
+
 /* MaiAtkHyperlink */
 
 #define MAI_TYPE_ATK_HYPERLINK      (mai_atk_hyperlink_get_type ())
 #define MAI_ATK_HYPERLINK(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj),\
                                      MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlink))
 #define MAI_ATK_HYPERLINK_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST ((klass),\
                                  MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlinkClass))
 #define MAI_IS_ATK_HYPERLINK(obj)      (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\
--- a/accessible/src/atk/nsMaiHyperlink.h
+++ b/accessible/src/atk/nsMaiHyperlink.h
@@ -8,16 +8,19 @@
 #define __MAI_HYPERLINK_H__
 
 #include "nsMai.h"
 #include "Accessible.h"
 
 struct _AtkHyperlink;
 typedef struct _AtkHyperlink                      AtkHyperlink;
 
+namespace mozilla {
+namespace a11y {
+
 /*
  * MaiHyperlink is a auxiliary class for MaiInterfaceHyperText.
  */
 
 class MaiHyperlink
 {
 public:
   MaiHyperlink(Accessible* aHyperLink);
@@ -29,9 +32,13 @@ public:
     { return mHyperlink && mHyperlink->IsLink() ? mHyperlink : nullptr; }
 
 protected:
   Accessible* mHyperlink;
   AtkHyperlink* mMaiAtkHyperlink;
 public:
   static nsresult Initialize(AtkHyperlink *aObj, MaiHyperlink *aClass);
 };
+
+} // namespace a11y
+} // namespace mozilla
+
 #endif /* __MAI_HYPERLINK_H__ */
--- a/accessible/src/atk/nsMaiInterfaceComponent.cpp
+++ b/accessible/src/atk/nsMaiInterfaceComponent.cpp
@@ -7,16 +7,18 @@
 #include "InterfaceInitFuncs.h"
 
 #include "AccessibleWrap.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsMai.h"
 #include "mozilla/Likely.h"
 
+using namespace mozilla::a11y;
+
 extern "C" {
 
 static AtkObject*
 refAccessibleAtPointCB(AtkComponent* aComponent, gint aAccX, gint aAccY,
                        AtkCoordType aCoordType)
 {
   return refAccessibleAtPointHelper(GetAccessibleWrap(ATK_OBJECT(aComponent)),
                                     aAccX, aAccY, aCoordType);
--- a/accessible/src/atk/nsMaiInterfaceDocument.cpp
+++ b/accessible/src/atk/nsMaiInterfaceDocument.cpp
@@ -6,16 +6,18 @@
 
 #include "InterfaceInitFuncs.h"
 
 #include "AccessibleWrap.h"
 #include "DocAccessible.h"
 #include "nsMai.h"
 #include "mozilla/Likely.h"
 
+using namespace mozilla::a11y;
+
 static const char* const kDocTypeName = "W3C-doctype";
 static const char* const kDocUrlName = "DocURL";
 static const char* const kMimeTypeName = "MimeType";
 
 // below functions are vfuncs on an ATK  interface so they need to be C call
 extern "C" {
 
 static const gchar* getDocumentLocaleCB(AtkDocument* aDocument);
--- a/accessible/src/atk/nsMaiInterfaceEditableText.cpp
+++ b/accessible/src/atk/nsMaiInterfaceEditableText.cpp
@@ -7,16 +7,18 @@
 #include "InterfaceInitFuncs.h"
 
 #include "HyperTextAccessible.h"
 #include "nsMai.h"
 
 #include "nsString.h"
 #include "mozilla/Likely.h"
 
+using namespace mozilla::a11y;
+
 extern "C" {
 static void
 setTextContentsCB(AtkEditableText *aText, const gchar *aString)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
   if (!accWrap)
     return;
 
--- a/accessible/src/atk/nsMaiInterfaceHyperlinkImpl.cpp
+++ b/accessible/src/atk/nsMaiInterfaceHyperlinkImpl.cpp
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InterfaceInitFuncs.h"
 
 #include "nsMaiHyperlink.h"
 #include "mozilla/Likely.h"
 
+using namespace mozilla::a11y;
+
 extern "C" {
 static AtkHyperlink*
 getHyperlinkCB(AtkHyperlinkImpl* aImpl)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImpl));
   if (!accWrap)
     return nullptr;
 
--- a/accessible/src/atk/nsMaiInterfaceHypertext.cpp
+++ b/accessible/src/atk/nsMaiInterfaceHypertext.cpp
@@ -6,16 +6,18 @@
 
 #include "InterfaceInitFuncs.h"
 
 #include "HyperTextAccessible.h"
 #include "nsMai.h"
 #include "nsMaiHyperlink.h"
 #include "mozilla/Likely.h"
 
+using namespace mozilla::a11y;
+
 extern "C" {
 
 static AtkHyperlink*
 getLinkCB(AtkHypertext *aText, gint aLinkIndex)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
   if (!accWrap)
     return nullptr;
--- a/accessible/src/atk/nsMaiInterfaceSelection.cpp
+++ b/accessible/src/atk/nsMaiInterfaceSelection.cpp
@@ -7,16 +7,18 @@
 #include "InterfaceInitFuncs.h"
 
 #include "AccessibleWrap.h"
 #include "nsMai.h"
 #include "mozilla/Likely.h"
 
 #include <atk/atk.h>
 
+using namespace mozilla::a11y;
+
 extern "C" {
 
 static gboolean
 addSelectionCB(AtkSelection *aSelection, gint i)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
   if (!accWrap || !accWrap->IsSelect())
     return FALSE;
--- a/accessible/src/atk/nsMaiInterfaceValue.cpp
+++ b/accessible/src/atk/nsMaiInterfaceValue.cpp
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InterfaceInitFuncs.h"
 #include "mozilla/Likely.h"
 
 #include "AccessibleWrap.h"
 #include "nsMai.h"
 
+using namespace mozilla::a11y;
+
 extern "C" {
 
 static void
 getCurrentValueCB(AtkValue *obj, GValue *value)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
   if (!accWrap)
     return;
--- a/accessible/src/base/AccCollector.h
+++ b/accessible/src/base/AccCollector.h
@@ -4,21 +4,21 @@
 
 #ifndef mozilla_a11y_AccCollector_h__
 #define mozilla_a11y_AccCollector_h__
 
 #include "Filters.h"
 
 #include "nsTArray.h"
 
-class Accessible;
-
 namespace mozilla {
 namespace a11y {
 
+class Accessible;
+
 /**
  * Collect accessible children complying with filter function. Provides quick
  * access to accessible by index.
  */
 class AccCollector
 {
 public:
   AccCollector(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc);
@@ -81,15 +81,15 @@ public:
 
 protected:
   // Make sure it's used by Accessible class only.
   EmbeddedObjCollector(Accessible* aRoot) :
     AccCollector(aRoot, filters::GetEmbeddedObject) { }
 
   virtual void AppendObject(Accessible* aAccessible);
 
-  friend class ::Accessible;
+  friend class Accessible;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -5,18 +5,22 @@
 
 #ifndef _AccEvent_H_
 #define _AccEvent_H_
 
 #include "nsIAccessibleEvent.h"
 
 #include "mozilla/a11y/Accessible.h"
 
+namespace mozilla {
+namespace a11y {
+
+class DocAccessible;
+
 class nsAccEvent;
-class DocAccessible;
 
 // 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,
@@ -481,10 +485,13 @@ public:
     return mRawPtr->GetEventGroups() & (1U << Destination::kEventGroup) ?
       static_cast<Destination*>(mRawPtr) : nullptr;
   }
 
 private:
   AccEvent* mRawPtr;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/base/AccGroupInfo.h
+++ b/accessible/src/base/AccGroupInfo.h
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef AccGroupInfo_h_
 #define AccGroupInfo_h_
 
 #include "Accessible-inl.h"
 #include "nsAccUtils.h"
 
+namespace mozilla {
+namespace a11y {
+
 /**
  * Calculate and store group information.
  */
 class AccGroupInfo
 {
 public:
   AccGroupInfo(Accessible* aItem, mozilla::a11y::role aRole);
   ~AccGroupInfo() { MOZ_COUNT_DTOR(AccGroupInfo); }
@@ -71,9 +74,12 @@ private:
   static bool IsConceptualParent(mozilla::a11y::role aRole,
 				 mozilla::a11y::role aParentRole);
 
   uint32_t mPosInSet;
   uint32_t mSetSize;
   Accessible* mParent;
 };
 
+} // namespace mozilla
+} // namespace a11y
+
 #endif
--- a/accessible/src/base/AccIterator.h
+++ b/accessible/src/base/AccIterator.h
@@ -19,17 +19,17 @@ namespace a11y {
  */
 class AccIterable
 {
 public:
   virtual ~AccIterable() { }
   virtual Accessible* Next() = 0;
 
 private:
-  friend class mozilla::a11y::Relation;
+  friend class Relation;
   nsAutoPtr<AccIterable> mNextIter;
 };
 
 /**
  * Allows to iterate through accessible children or subtree complying with
  * filter function.
  */
 class AccIterator : public AccIterable
--- a/accessible/src/base/Filters.cpp
+++ b/accessible/src/base/Filters.cpp
@@ -33,17 +33,17 @@ filters::GetSelectable(Accessible* aAcce
 uint32_t
 filters::GetRow(Accessible* aAccessible)
 {
   a11y::role role = aAccessible->Role();
   if (role == roles::ROW)
     return eMatch | eSkipSubtree;
 
   // Look for rows inside rowgroup.
-  if (role == roles::SECTION)
+  if (role == roles::GROUPING)
     return eSkip;
 
   return eSkipSubtree;
 }
 
 uint32_t
 filters::GetCell(Accessible* aAccessible)
 {
--- a/accessible/src/base/Filters.h
+++ b/accessible/src/base/Filters.h
@@ -2,23 +2,24 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_Filters_h__
 #define mozilla_a11y_Filters_h__
 
 #include "mozilla/StandardInteger.h"
 
-class Accessible;
-
 /**
  * Predefined filters used for nsAccIterator and nsAccCollector.
  */
 namespace mozilla {
 namespace a11y {
+
+class Accessible;
+
 namespace filters {
 
 enum EResult {
   eSkip = 0,
   eMatch = 1,
   eSkipSubtree = 2
 };
 
--- a/accessible/src/base/FocusManager.h
+++ b/accessible/src/base/FocusManager.h
@@ -3,23 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_FocusManager_h_
 #define mozilla_a11y_FocusManager_h_
 
 #include "nsAutoPtr.h"
 #include "mozilla/dom/Element.h"
 
+namespace mozilla {
+namespace a11y {
+
 class AccEvent;
 class Accessible;
 class DocAccessible;
 
-namespace mozilla {
-namespace a11y {
-
 /**
  * Manage the accessible focus. Used to fire and process accessible events.
  */
 class FocusManager
 {
 public:
   virtual ~FocusManager();
 
--- a/accessible/src/base/Logging.h
+++ b/accessible/src/base/Logging.h
@@ -5,29 +5,28 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_logs_h__
 #define mozilla_a11y_logs_h__
 
 #include "nscore.h"
 #include "nsAString.h"
 
-class AccEvent;
-class Accessible;
-class DocAccessible;
-
 class nsIDocument;
 class nsINode;
 class nsIRequest;
 class nsISelection;
 class nsIWebProgress;
 
 namespace mozilla {
 namespace a11y {
 
+class AccEvent;
+class Accessible;
+class DocAccessible;
 class OuterDocAccessible;
 
 namespace logging {
 
 enum EModules {
   eDocLoad = 1 << 0,
   eDocCreate = 1 << 1,
   eDocDestroy = 1 << 2,
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -25,24 +25,24 @@ CPPSRCS = \
   NotificationController.cpp \
   nsAccDocManager.cpp \
   nsAccessNode.cpp \
   nsARIAMap.cpp \
   nsCoreUtils.cpp \
   nsAccUtils.cpp \
   nsAccessibilityService.cpp \
   nsAccessiblePivot.cpp \
-  nsAccTreeWalker.cpp \
   nsEventShell.cpp \
   nsCaretAccessible.cpp \
   nsTextEquivUtils.cpp \
   RoleAsserts.cpp \
   StyleInfo.cpp \
   TextAttrs.cpp \
   TextUpdater.cpp \
+  TreeWalker.cpp \
   $(NULL)
 
 ifneq ($(A11Y_LOG),0)
 CPPSRCS += \
   Logging.cpp \
   $(NULL)
 endif
 
--- a/accessible/src/base/NotificationController.h
+++ b/accessible/src/base/NotificationController.h
@@ -9,19 +9,23 @@
 #include "AccEvent.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefreshDriver.h"
 
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
+class nsIContent;
+
+namespace mozilla {
+namespace a11y {
+
 class Accessible;
 class DocAccessible;
-class nsIContent;
 
 /**
  * Notification interface.
  */
 class Notification
 {
 public:
   virtual ~Notification() { };
@@ -342,9 +346,12 @@ private:
 
   /**
    * Pending events array. Don't make this an nsAutoTArray; we use
    * SwapElements() on it.
    */
   nsTArray<nsRefPtr<AccEvent> > mEvents;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/base/RoleMap.h
+++ b/accessible/src/base/RoleMap.h
@@ -32,17 +32,17 @@ ROLE(MENUBAR,
      eNoNameRule)
 
 ROLE(SCROLLBAR,
      "scrollbar",
      ATK_ROLE_SCROLL_BAR,
      NSAccessibilityScrollBarRole,  //We might need to make this its own mozAccessible, to support the children objects (valueindicator, down/up buttons).
      ROLE_SYSTEM_SCROLLBAR,
      ROLE_SYSTEM_SCROLLBAR,
-     eNoNameRule)
+     eNameFromValueRule)
 
 ROLE(GRIP,
      "grip",
      ATK_ROLE_UNKNOWN,
      NSAccessibilitySplitterRole,
      ROLE_SYSTEM_GRIP,
      ROLE_SYSTEM_GRIP,
      eNoNameRule)
@@ -425,25 +425,25 @@ ROLE(HOTKEYFIELD,
      eNoNameRule)
 
 ROLE(SLIDER,
      "slider",
      ATK_ROLE_SLIDER,
      NSAccessibilitySliderRole,
      ROLE_SYSTEM_SLIDER,
      ROLE_SYSTEM_SLIDER,
-     eNoNameRule)
+     eNameFromValueRule)
 
 ROLE(SPINBUTTON,
      "spinbutton",
      ATK_ROLE_SPIN_BUTTON,
      NSAccessibilityIncrementorRole,  //Subroles: Increment/Decrement.
      ROLE_SYSTEM_SPINBUTTON,
      ROLE_SYSTEM_SPINBUTTON,
-     eNoNameRule)
+     eNameFromValueRule)
 
 ROLE(DIAGRAM,
      "diagram",
      ATK_ROLE_IMAGE,
      NSAccessibilityUnknownRole,
      ROLE_SYSTEM_DIAGRAM,
      ROLE_SYSTEM_DIAGRAM,
      eNoNameRule)
--- a/accessible/src/base/TextAttrs.h
+++ b/accessible/src/base/TextAttrs.h
@@ -5,26 +5,27 @@
 
 #ifndef nsTextAttrs_h_
 #define nsTextAttrs_h_
 
 #include "nsCOMPtr.h"
 #include "nsColor.h"
 #include "nsStyleConsts.h"
 
-class HyperTextAccessible;
 class nsIFrame;
 class nsIPersistentProperties;
 class nsIContent;
-class Accessible;
 class nsDeviceContext;
 
 namespace mozilla {
 namespace a11y {
 
+class Accessible;
+class HyperTextAccessible;
+
 /**
  * Used to expose text attributes for the hyper text accessible (see
  * HyperTextAccessible class).
  *
  * @note "invalid: spelling" text attribute is implemented entirely in
  *       HyperTextAccessible class.
  */
 class TextAttrsMgr
rename from accessible/src/base/nsAccTreeWalker.cpp
rename to accessible/src/base/TreeWalker.cpp
--- a/accessible/src/base/nsAccTreeWalker.cpp
+++ b/accessible/src/base/TreeWalker.cpp
@@ -1,72 +1,80 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsAccTreeWalker.h"
+#include "TreeWalker.h"
 
 #include "Accessible.h"
 #include "nsAccessibilityService.h"
 #include "DocAccessible.h"
 
 #include "nsINodeList.h"
 
+using namespace mozilla::a11y;
+
 ////////////////////////////////////////////////////////////////////////////////
 // WalkState
 ////////////////////////////////////////////////////////////////////////////////
 
+namespace mozilla {
+namespace a11y {
+
 struct WalkState
 {
   WalkState(nsIContent *aContent) :
     content(aContent), childIdx(0), prevState(nullptr) {}
 
   nsCOMPtr<nsIContent> content;
   nsCOMPtr<nsINodeList> childList;
   uint32_t childIdx;
   WalkState *prevState;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 ////////////////////////////////////////////////////////////////////////////////
-// nsAccTreeWalker
+// TreeWalker
 ////////////////////////////////////////////////////////////////////////////////
 
-nsAccTreeWalker::
-  nsAccTreeWalker(DocAccessible* aDoc, nsIContent* aContent,
-                  bool aWalkAnonContent, bool aWalkCache) :
-  mDoc(aDoc), mWalkCache(aWalkCache), mState(nullptr)
+TreeWalker::
+  TreeWalker(Accessible* aContext, nsIContent* aContent, bool aWalkCache) :
+  mDoc(aContext->Document()), mContext(aContext),
+  mWalkCache(aWalkCache), mState(nullptr)
 {
   NS_ASSERTION(aContent, "No node for the accessible tree walker!");
 
   if (aContent)
     mState = new WalkState(aContent);
 
-  mChildFilter = aWalkAnonContent ? nsIContent::eAllChildren :
-                                  nsIContent::eAllButXBL;
+  mChildFilter = mContext->CanHaveAnonChildren() ?
+    nsIContent::eAllChildren : nsIContent::eAllButXBL;
 
   mChildFilter |= nsIContent::eSkipPlaceholderContent;
 
-  MOZ_COUNT_CTOR(nsAccTreeWalker);
+  MOZ_COUNT_CTOR(TreeWalker);
 }
 
-nsAccTreeWalker::~nsAccTreeWalker()
+TreeWalker::~TreeWalker()
 {
   // Clear state stack from memory
   while (mState)
     PopState();
 
-  MOZ_COUNT_DTOR(nsAccTreeWalker);
+  MOZ_COUNT_DTOR(TreeWalker);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsAccTreeWalker: private
+// TreeWalker: private
 
 Accessible*
-nsAccTreeWalker::NextChildInternal(bool aNoWalkUp)
+TreeWalker::NextChildInternal(bool aNoWalkUp)
 {
   if (!mState || !mState->content)
     return nullptr;
 
   if (!mState->childList)
     mState->childList = mState->content->GetChildren(mChildFilter);
 
   uint32_t length = 0;
@@ -97,25 +105,25 @@ nsAccTreeWalker::NextChildInternal(bool 
 
   // No more children, get back to the parent.
   PopState();
 
   return aNoWalkUp ? nullptr : NextChildInternal(false);
 }
 
 void
-nsAccTreeWalker::PopState()
+TreeWalker::PopState()
 {
   WalkState* prevToLastState = mState->prevState;
   delete mState;
   mState = prevToLastState;
 }
 
 bool
-nsAccTreeWalker::PushState(nsIContent* aContent)
+TreeWalker::PushState(nsIContent* aContent)
 {
   WalkState* nextToLastState = new WalkState(aContent);
   if (!nextToLastState)
     return false;
 
   nextToLastState->prevState = mState;
   mState = nextToLastState;
 
rename from accessible/src/base/nsAccTreeWalker.h
rename to accessible/src/base/TreeWalker.h
--- a/accessible/src/base/nsAccTreeWalker.h
+++ b/accessible/src/base/TreeWalker.h
@@ -1,46 +1,52 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef _nsAccTreeWalker_H_
-#define _nsAccTreeWalker_H_
+#ifndef mozilla_a11y_TreeWalker_h_
+#define mozilla_a11y_TreeWalker_h_
 
 #include "nsAutoPtr.h"
 #include "nsIContent.h"
 
+namespace mozilla {
+namespace a11y {
+
 class Accessible;
 class DocAccessible;
+
 struct WalkState;
 
 /**
  * This class is used to walk the DOM tree to create accessible tree.
  */
-class nsAccTreeWalker
+class TreeWalker
 {
 public:
-  nsAccTreeWalker(DocAccessible* aDoc, nsIContent* aNode, 
-                  bool aWalkAnonymousContent, bool aWalkCache = false);
-  virtual ~nsAccTreeWalker();
+  TreeWalker(Accessible* aContext, nsIContent* aNode, bool aWalkCache = false);
+  virtual ~TreeWalker();
 
   /**
    * Return the next child accessible.
    *
    * @note Returned accessible is bound to the document, if the accessible is
    *       rejected during tree creation then the caller should be unbind it
    *       from the document.
    */
   Accessible* NextChild()
   {
     return NextChildInternal(false);
   }
 
 private:
+  TreeWalker();
+  TreeWalker(const TreeWalker&);
+  TreeWalker& operator =(const TreeWalker&);
 
   /**
    * Return the next child accessible.
    *
    * @param  aNoWalkUp  [in] specifies the walk direction, true means we
    *                     shouldn't go up through the tree if we failed find
    *                     accessible children.
    */
@@ -55,14 +61,18 @@ private:
   bool PushState(nsIContent *aNode);
 
   /**
    * Pop state from stack.
    */
   void PopState();
 
   DocAccessible* mDoc;
+  Accessible* mContext;
   int32_t mChildFilter;
   bool mWalkCache;
   WalkState* mState;
 };
 
-#endif 
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_TreeWalker_h_
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -428,16 +428,26 @@ static nsRoleMapEntry sWAIRoleMaps[] =
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eARIASelectable
   },
+  { // rowgroup
+    &nsGkAtoms::rowgroup,
+    roles::GROUPING,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
   { // rowheader
     &nsGkAtoms::rowheader,
     roles::ROWHEADER,
     kUseMapRole,
     eNoValue,
     eSortAction,
     eNoLiveAttr,
     kGenericAccType,
--- a/accessible/src/base/nsAccCache.h
+++ b/accessible/src/base/nsAccCache.h
@@ -28,19 +28,19 @@ ClearCacheEntry(const void* aKey, nsRefP
   return PL_DHASH_REMOVE;
 }
 
 /**
  * Clear the cache and shutdown the accessibles.
  */
 
 static void
-ClearCache(AccessibleHashtable& aCache)
+ClearCache(mozilla::a11y::AccessibleHashtable& aCache)
 {
-  aCache.Enumerate(ClearCacheEntry<Accessible>, nullptr);
+  aCache.Enumerate(ClearCacheEntry<mozilla::a11y::Accessible>, nullptr);
 }
 
 /**
  * Traverse the accessible cache entry for cycle collector.
  */
 template <class T>
 static PLDHashOperator
 CycleCollectorTraverseCacheEntry(const void *aKey, T *aAccessible,
@@ -56,15 +56,16 @@ CycleCollectorTraverseCacheEntry(const v
   return PL_DHASH_NEXT;
 }
 
 /**
  * Traverse the accessible cache for cycle collector.
  */
 
 static void
-CycleCollectorTraverseCache(AccessibleHashtable& aCache,
+CycleCollectorTraverseCache(mozilla::a11y::AccessibleHashtable& aCache,
                             nsCycleCollectionTraversalCallback *aCallback)
 {
-  aCache.EnumerateRead(CycleCollectorTraverseCacheEntry<Accessible>, aCallback);
+  aCache.EnumerateRead(CycleCollectorTraverseCacheEntry<mozilla::a11y::Accessible>,
+                       aCallback);
 }
 
 #endif
--- a/accessible/src/base/nsAccDocManager.h
+++ b/accessible/src/base/nsAccDocManager.h
@@ -8,27 +8,36 @@
 #include "nsIDocument.h"
 #include "nsIDOMEventListener.h"
 #include "nsRefPtrHashtable.h"
 #include "nsIWebProgress.h"
 #include "nsIWebProgressListener.h"
 #include "nsWeakReference.h"
 #include "nsIPresShell.h"
 
+namespace mozilla {
+namespace a11y {
+
 class Accessible;
 class DocAccessible;
 
+} // namespace a11y
+} // namespace mozilla
+
 /**
  * Manage the document accessible life cycle.
  */
 class nsAccDocManager : public nsIWebProgressListener,
                         public nsIDOMEventListener,
                         public nsSupportsWeakReference
 {
 public:
+  typedef mozilla::a11y::Accessible Accessible;
+  typedef mozilla::a11y::DocAccessible DocAccessible;
+
   virtual ~nsAccDocManager() { }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIWEBPROGRESSLISTENER
   NS_DECL_NSIDOMEVENTLISTENER
 
   /**
    * Return document accessible for the given DOM node.
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -15,21 +15,25 @@
 
 #include "mozilla/dom/Element.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIPresShell.h"
 #include "nsPoint.h"
 
+struct nsRoleMapEntry;
+
+namespace mozilla {
+namespace a11y {
+
 class nsAccessNode;
 class Accessible;
 class HyperTextAccessible;
 class DocAccessible;
-struct nsRoleMapEntry;
 
 class nsAccUtils
 {
 public:
   /**
    * Returns value of attribute from the given attributes container.
    *
    * @param aAttributes - attributes container
@@ -288,9 +292,12 @@ public:
 
   /**
    * Return true if the given accessible can't have children. Used when exposing
    * to platform accessibility APIs, should the children be pruned off?
    */
   static bool MustPrune(Accessible* aAccessible);
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -8,33 +8,29 @@
  */
 
 #ifndef _nsAccessNode_H_
 #define _nsAccessNode_H_
 
 #include "nsIAccessibleTypes.h"
 #include "nsINode.h"
 
-class nsAccessNode;
-class DocAccessible;
-class nsIAccessibleDocument;
 class nsIContent;
+class nsIDocShellTreeItem;
+class nsIFrame;
+class nsIPresShell;
+class nsPresContext;
 
 namespace mozilla {
 namespace a11y {
-class RootAccessible;
-}
-}
 
-class nsIPresShell;
-class nsPresContext;
-class nsIFrame;
-class nsIDocShellTreeItem;
+class DocAccessible;
+class RootAccessible;
 
-class nsAccessNode: public nsISupports
+class nsAccessNode : public nsISupports
 {
 public:
 
   nsAccessNode(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~nsAccessNode();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsAccessNode)
@@ -42,27 +38,28 @@ public:
   /**
    * Return the document accessible for this access node.
    */
   DocAccessible* Document() const { return mDoc; }
 
   /**
    * Return the root document accessible for this accessnode.
    */
-  mozilla::a11y::RootAccessible* RootAccessible() const;
+  a11y::RootAccessible* RootAccessible() const;
 
   /**
    * Shutdown the access node object.
    */
   virtual void Shutdown();
 
   /**
    * Return frame for the given access node object.
    */
   virtual nsIFrame* GetFrame() const;
+
   /**
    * Return DOM node associated with the accessible.
    */
   virtual nsINode* GetNode() const;
   nsIContent* GetContent() const { return mContent; }
 
   /**
    * Return node type information of DOM node associated with the accessible.
@@ -98,10 +95,13 @@ protected:
   DocAccessible* mDoc;
 
 private:
   nsAccessNode() MOZ_DELETE;
   nsAccessNode(const nsAccessNode&) MOZ_DELETE;
   nsAccessNode& operator =(const nsAccessNode&) MOZ_DELETE;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/base/nsAccessiblePivot.h
+++ b/accessible/src/base/nsAccessiblePivot.h
@@ -9,25 +9,34 @@
 
 #include "nsIAccessiblePivot.h"
 
 #include "nsAutoPtr.h"
 #include "nsTObserverArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 
+namespace mozilla {
+namespace a11y {
+
 class Accessible;
+
+} // namespace a11y
+} // namespace mozilla
+
 class nsIAccessibleTraversalRule;
 
 /**
  * Class represents an accessible pivot.
  */
 class nsAccessiblePivot MOZ_FINAL : public nsIAccessiblePivot
 {
 public:
+  typedef mozilla::a11y::Accessible Accessible;
+
   nsAccessiblePivot(Accessible* aRoot);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAccessiblePivot, nsIAccessiblePivot)
 
   NS_DECL_NSIACCESSIBLEPIVOT
 
   /*
--- a/accessible/src/base/nsCaretAccessible.h
+++ b/accessible/src/base/nsCaretAccessible.h
@@ -112,15 +112,15 @@ private:
 
   // Currently focused control.
   nsCOMPtr<nsIContent> mCurrentControl;
 
   // Info for the the last selection event.
   // If it was on a control, then its control's selection. Otherwise, it's for
   // a document where the selection changed.
   nsCOMPtr<nsIWeakReference> mLastUsedSelection; // Weak ref to nsISelection
-  nsRefPtr<HyperTextAccessible> mLastTextAccessible;
+  nsRefPtr<mozilla::a11y::HyperTextAccessible> mLastTextAccessible;
   int32_t mLastCaretOffset;
 
   mozilla::a11y::RootAccessible* mRootAccessible;
 };
 
 #endif
--- a/accessible/src/base/nsEventShell.cpp
+++ b/accessible/src/base/nsEventShell.cpp
@@ -2,16 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsEventShell.h"
 
 #include "nsAccUtils.h"
 
+using namespace mozilla::a11y;
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsEventShell
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 nsEventShell::FireEvent(AccEvent* aEvent)
 {
   if (!aEvent)
--- a/accessible/src/base/nsEventShell.h
+++ b/accessible/src/base/nsEventShell.h
@@ -15,26 +15,27 @@ class nsIPersistentProperties;
  */
 class nsEventShell
 {
 public:
 
   /**
    * Fire the accessible event.
    */
-  static void FireEvent(AccEvent* aEvent);
+  static void FireEvent(mozilla::a11y::AccEvent* aEvent);
 
   /**
    * Fire accessible event of the given type for the given accessible.
    *
    * @param  aEventType   [in] the event type
    * @param  aAccessible  [in] the event target
    */
-  static void FireEvent(uint32_t aEventType, Accessible* aAccessible,
-                        EIsFromUserInput aIsFromUserInput = eAutoDetect);
+  static void FireEvent(uint32_t aEventType,
+                        mozilla::a11y::Accessible* aAccessible,
+                        mozilla::a11y::EIsFromUserInput aIsFromUserInput = mozilla::a11y::eAutoDetect);
 
   /**
    * Append 'event-from-input' object attribute if the accessible event has
    * been fired just now for the given node.
    *
    * @param  aNode        [in] the DOM node
    * @param  aAttributes  [in, out] the attributes
    */
--- a/accessible/src/base/nsTextEquivUtils.h
+++ b/accessible/src/base/nsTextEquivUtils.h
@@ -37,16 +37,17 @@ enum ETextEquivRule
 
 /**
  * The class provides utils methods to compute the accessible name and
  * description.
  */
 class nsTextEquivUtils
 {
 public:
+  typedef mozilla::a11y::Accessible Accessible;
 
   /**
    * Calculates the name from accessible subtree if allowed.
    *
    * @param aAccessible [in] the given accessible
    * @param aName       [out] accessible name
    */
   static nsresult GetNameFromSubtree(Accessible* aAccessible,
--- a/accessible/src/generic/ARIAGridAccessible-inl.h
+++ b/accessible/src/generic/ARIAGridAccessible-inl.h
@@ -6,48 +6,54 @@
 
 #ifndef mozilla_a11y_ARIAGridAccessible_inl_h__
 #define mozilla_a11y_ARIAGridAccessible_inl_h__
 
 #include "ARIAGridAccessible.h"
 
 #include "AccIterator.h"
 
+namespace mozilla {
+namespace a11y {
+
 inline Accessible*
-mozilla::a11y::ARIAGridCellAccessible::TableFor(Accessible* aRow) const
+ARIAGridCellAccessible::TableFor(Accessible* aRow) const
 {
   if (aRow) {
     Accessible* table = aRow->Parent();
     if (table) {
       roles::Role tableRole = table->Role();
-      if (tableRole == roles::SECTION) { // if there's a rowgroup.
+      if (tableRole == roles::GROUPING) { // if there's a rowgroup.
         table = table->Parent();
         if (table)
           tableRole = table->Role();
       }
 
       return tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ?
         table : nullptr;
     }
   }
 
   return nullptr;
 }
 
 inline int32_t
-mozilla::a11y::ARIAGridCellAccessible::RowIndexFor(Accessible* aRow) const
+ARIAGridCellAccessible::RowIndexFor(Accessible* aRow) const
 {
   Accessible* table = TableFor(aRow);
   if (table) {
     int32_t rowIdx = 0;
     Accessible* row = nullptr;
     AccIterator rowIter(table, filters::GetRow);
     while ((row = rowIter.Next()) && row != aRow)
       rowIdx++;
 
     if (row)
       return rowIdx;
   }
 
   return -1;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/generic/Accessible-inl.h
+++ b/accessible/src/generic/Accessible-inl.h
@@ -5,16 +5,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_Accessible_inl_h_
 #define mozilla_a11y_Accessible_inl_h_
 
 #include "Accessible.h"
 #include "nsARIAMap.h"
 
+namespace mozilla {
+namespace a11y {
+
 inline mozilla::a11y::role
 Accessible::Role()
 {
   if (!mRoleMapEntry || mRoleMapEntry->roleRule != kUseMapRole)
     return ARIATransformRole(NativeRole());
 
   return ARIATransformRole(mRoleMapEntry->role);
 }
@@ -40,9 +43,12 @@ inline bool
 Accessible::HasNumericValue() const
 {
   if (mFlags & eHasNumericValue)
     return true;
 
   return mRoleMapEntry && mRoleMapEntry->valueRule != eNoValue;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -9,25 +9,25 @@
 
 #include "AccCollector.h"
 #include "AccGroupInfo.h"
 #include "AccIterator.h"
 #include "nsAccUtils.h"
 #include "nsAccEvent.h"
 #include "nsAccessibleRelation.h"
 #include "nsAccessibilityService.h"
-#include "nsAccTreeWalker.h"
 #include "nsIAccessibleRelation.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "States.h"
 #include "StyleInfo.h"
+#include "TreeWalker.h"
 
 #include "nsContentUtils.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMTreeWalker.h"
 #include "nsIDOMXULButtonElement.h"
@@ -616,21 +616,27 @@ Accessible::VisibilityState()
 
   nsIFrame* curFrame = frame;
   nsPoint framePos(0, 0);
   do {
     nsIView* view = curFrame->GetView();
     if (view && view->GetVisibility() == nsViewVisibility_kHide)
       return states::INVISIBLE;
 
-    // Offscreen state for background tab content.
+    // Offscreen state for background tab content and invisible for not selected
+    // deck panel.
     nsIFrame* parentFrame = curFrame->GetParent();
     nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
-    if (deckFrame && deckFrame->GetSelectedBox() != curFrame)
-      return states::OFFSCREEN;
+    if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
+      if (deckFrame->GetContent()->IsXUL() &&
+          deckFrame->GetContent()->Tag() == nsGkAtoms::tabpanels)
+        return states::OFFSCREEN;
+
+      return states::INVISIBLE;
+    }
 
     // If contained by scrollable frame then check that at least 12 pixels
     // around the object is visible, otherwise the object is offscreen.
     framePos += curFrame->GetPosition();
     nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
     if (scrollableFrame) {
       nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
       nsRect frameRect(framePos, frame->GetSize());
@@ -2938,17 +2944,17 @@ Accessible::ContainerWidget() const
 // Accessible protected methods
 
 void
 Accessible::CacheChildren()
 {
   DocAccessible* doc = Document();
   NS_ENSURE_TRUE_VOID(doc);
 
-  nsAccTreeWalker walker(doc, mContent, CanHaveAnonChildren());
+  TreeWalker walker(this, mContent);
 
   Accessible* child = nullptr;
   while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 void
 Accessible::TestChildCache(Accessible* aCachedChild) const
 {
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -16,30 +16,36 @@
 #include "nsIAccessibleValue.h"
 #include "nsIAccessibleStates.h"
 #include "nsIContent.h"
 
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
 
-class AccEvent;
-class AccGroupInfo;
-class KeyBinding;
-class Accessible;
-class HyperTextAccessible;
 struct nsRoleMapEntry;
 
+struct nsRect;
+class nsIContent;
+class nsIFrame;
+class nsIAtom;
+class nsIView;
+
 namespace mozilla {
 namespace a11y {
 
+class Accessible;
+class AccEvent;
+class AccGroupInfo;
 class EmbeddedObjCollector;
 class HTMLImageMapAccessible;
 class HTMLLIAccessible;
+class HyperTextAccessible;
 class ImageAccessible;
+class KeyBinding;
 class Relation;
 class TableAccessible;
 class TableCellAccessible;
 class TextLeafAccessible;
 class XULTreeAccessible;
 
 /**
  * Name type flags.
@@ -76,25 +82,16 @@ struct GroupPos
 {
   GroupPos() : level(0), posInSet(0), setSize(0) { }
 
   int32_t level;
   int32_t posInSet;
   int32_t setSize;
 };
 
-} // namespace a11y
-} // namespace mozilla
-
-struct nsRect;
-class nsIContent;
-class nsIFrame;
-class nsIAtom;
-class nsIView;
-
 typedef nsRefPtrHashtable<nsPtrHashKey<const void>, Accessible>
   AccessibleHashtable;
 
 
 #define NS_ACCESSIBLE_IMPL_IID                          \
 {  /* 133c8bf4-4913-4355-bd50-426bd1d6e1ad */           \
   0x133c8bf4,                                           \
   0x4913,                                               \
@@ -145,17 +142,17 @@ public:
   virtual void Value(nsString& aValue);
 
   /**
    * Get the name of this accessible.
    *
    * Note: aName.IsVoid() when name was left empty by the author on purpose.
    * aName.IsEmpty() when the author missed name, AT can try to repair a name.
    */
-  virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
+  virtual ENameValueFlag Name(nsString& aName);
 
   /**
    * Return DOM node associated with this accessible.
    */
   inline already_AddRefed<nsIDOMNode> DOMNode() const
   {
     nsIDOMNode *DOMNode = nullptr;
     if (GetNode())
@@ -906,17 +903,17 @@ protected:
   static const uint32_t kChildrenFlagsMask =
     eChildrenUninitialized | eMixedChildren | eEmbeddedChildren;
 
   uint32_t mFlags;
   friend class DocAccessible;
 
   nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
   int32_t mIndexOfEmbeddedChild;
-  friend class mozilla::a11y::EmbeddedObjCollector;
+  friend class EmbeddedObjCollector;
 
   nsAutoPtr<AccGroupInfo> mGroupInfo;
   friend class AccGroupInfo;
 
   /**
    * Non-null indicates author-supplied role; possibly state & value as well
    */
   nsRoleMapEntry* mRoleMapEntry;
@@ -978,9 +975,12 @@ public:
 private:
   void ToPlatformFormat(nsAString& aValue) const;
   void ToAtkFormat(nsAString& aValue) const;
 
   uint32_t mKey;
   uint32_t mModifierMask;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/generic/DocAccessible-inl.h
+++ b/accessible/src/generic/DocAccessible-inl.h
@@ -5,16 +5,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_DocAccessible_inl_h_
 #define mozilla_a11y_DocAccessible_inl_h_
 
 #include "DocAccessible.h"
 #include "nsAccessibilityService.h"
 #include "NotificationController.h"
+#include "States.h"
+
+namespace mozilla {
+namespace a11y {
 
 inline void
 DocAccessible::BindChildDocument(DocAccessible* aDocument)
 {
   mNotificationController->ScheduleChildDocBinding(aDocument);
 }
 
 template<class Class, class Arg>
@@ -35,21 +39,38 @@ DocAccessible::UpdateText(nsIContent* aT
   NS_ASSERTION(mNotificationController, "The document was shut down!");
 
   // Ignore the notification if initial tree construction hasn't been done yet.
   if (mNotificationController && HasLoadState(eTreeConstructed))
     mNotificationController->ScheduleTextUpdate(aTextNode);
 }
 
 inline void
+DocAccessible::NotifyOfLoad(uint32_t aLoadEventType)
+{
+  mLoadState |= eDOMLoaded;
+  mLoadEventType = aLoadEventType;
+
+  // If the document is loaded completely then network activity was presumingly
+  // caused by file loading. Fire busy state change event.
+  if (HasLoadState(eCompletelyLoaded) && IsLoadEventTarget()) {
+    nsRefPtr<AccEvent> stateEvent =
+      new AccStateChangeEvent(this, states::BUSY, false);
+    FireDelayedAccessibleEvent(stateEvent);
+  }
+}
+
+inline void
 DocAccessible::MaybeNotifyOfValueChange(Accessible* aAccessible)
 {
   mozilla::a11y::role role = aAccessible->Role();
-  if (role == mozilla::a11y::roles::ENTRY ||
-      role == mozilla::a11y::roles::COMBOBOX) {
+  if (role == roles::ENTRY || role == roles::COMBOBOX) {
     nsRefPtr<AccEvent> valueChangeEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible,
                    eAutoDetect, AccEvent::eRemoveDupes);
     FireDelayedAccessibleEvent(valueChangeEvent);
   }
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -2,25 +2,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Accessible-inl.h"
 #include "AccIterator.h"
 #include "DocAccessible-inl.h"
 #include "nsAccCache.h"
-#include "nsAccessibilityService.h"
 #include "nsAccessiblePivot.h"
-#include "nsAccTreeWalker.h"
 #include "nsAccUtils.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Role.h"
 #include "RootAccessible.h"
-#include "States.h"
+#include "TreeWalker.h"
 
 #include "nsIMutableArray.h"
 #include "nsICommandManager.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocument.h"
 #include "nsIDOMAttr.h"
 #include "nsIDOMCharacterData.h"
@@ -1508,18 +1506,17 @@ DocAccessible::ProcessInvalidationList()
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible protected
 
 void
 DocAccessible::CacheChildren()
 {
   // Search for accessible children starting from the document element since
   // some web pages tend to insert elements under it rather than document body.
-  nsAccTreeWalker walker(this, mDocument->GetRootElement(),
-                         CanHaveAnonChildren());
+  TreeWalker walker(this, mDocument->GetRootElement());
 
   Accessible* child = nullptr;
   while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
@@ -1854,18 +1851,17 @@ DocAccessible::UpdateTree(Accessible* aC
 #endif
 
   nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
 
   if (child) {
     updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
 
   } else {
-    nsAccTreeWalker walker(this, aChildNode,
-                           aContainer->CanHaveAnonChildren(), true);
+    TreeWalker walker(aContainer, aChildNode, true);
 
     while ((child = walker.NextChild()))
       updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
   }
 
   // Content insertion/removal is not cause of accessible tree change.
   if (updateFlags == eNoAccessible)
     return;
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -19,32 +19,30 @@
 #include "nsIDocumentObserver.h"
 #include "nsIEditor.h"
 #include "nsIObserver.h"
 #include "nsIScrollPositionListener.h"
 #include "nsITimer.h"
 #include "nsIWeakReference.h"
 #include "nsIDocShellTreeNode.h"
 
-template<class Class, class Arg>
-class TNotification;
-class NotificationController;
+class nsAccDocManager;
+class nsAccessiblePivot;
 
 class nsIScrollableView;
-class nsAccessiblePivot;
 
 const uint32_t kDefaultCacheSize = 256;
 
 namespace mozilla {
 namespace a11y {
 
+class NotificationController;
 class RelatedAccIterator;
-
-} // namespace a11y
-} // namespace mozilla
+template<class Class, class Arg>
+class TNotification;
 
 class DocAccessible : public HyperTextAccessibleWrap,
                       public nsIAccessibleDocument,
                       public nsIDocumentObserver,
                       public nsIObserver,
                       public nsIScrollPositionListener,
                       public nsSupportsWeakReference,
                       public nsIAccessibleCursorable,
@@ -325,25 +323,20 @@ protected:
 
   // DocAccessible
   virtual nsresult AddEventListeners();
   virtual nsresult RemoveEventListeners();
 
   /**
    * Marks this document as loaded or loading.
    */
-  void NotifyOfLoad(uint32_t aLoadEventType)
-  {
-    mLoadState |= eDOMLoaded;
-    mLoadEventType = aLoadEventType;
-  }
-
+  void NotifyOfLoad(uint32_t aLoadEventType);
   void NotifyOfLoading(bool aIsReloading);
 
-  friend class nsAccDocManager;
+  friend class ::nsAccDocManager;
 
   /**
    * Perform initial update (create accessible tree).
    * Can be overridden by wrappers to prepare initialization work.
    */
   virtual void DoInitialUpdate();
 
   /**
@@ -563,17 +556,17 @@ protected:
   };
 
   /**
    * The cache of IDs pointed by relation attributes.
    */
   typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
   nsClassHashtable<nsStringHashKey, AttrRelProviderArray> mDependentIDsHash;
 
-  friend class mozilla::a11y::RelatedAccIterator;
+  friend class RelatedAccIterator;
 
   /**
    * Used for our caching algorithm. We store the list of nodes that should be
    * invalidated.
    *
    * @see ProcessInvalidationList
    */
   nsTArray<nsIContent*> mInvalidationList;
@@ -591,9 +584,12 @@ private:
 
 inline DocAccessible*
 Accessible::AsDoc()
 {
   return mFlags & eDocAccessible ?
     static_cast<DocAccessible*>(this) : nullptr;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -12,22 +12,21 @@
 
 #include "AccessibleWrap.h"
 
 #include "nsFrameSelection.h"
 #include "nsISelectionController.h"
 
 namespace mozilla {
 namespace a11y {
+
 struct DOMPoint {
   nsINode* node;
   int32_t idx;
 };
-}
-}
 
 enum EGetTextType { eGetBefore=-1, eGetAt=0, eGetAfter=1 };
 
 // This character marks where in the text returned via nsIAccessibleText(),
 // that embedded object characters exist
 const PRUnichar kEmbeddedObjectChar = 0xfffc;
 const PRUnichar kImaginaryEmbeddedObjectChar = ' ';
 const PRUnichar kForcedNewLineChar = '\n';
@@ -238,17 +237,17 @@ public:
 
   /**
    * Return the editor associated with the accessible.
    */
   virtual already_AddRefed<nsIEditor> GetEditor() const;
 
 protected:
   // Accessible
-  virtual mozilla::a11y::ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
+  virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
 
   // HyperTextAccessible
 
   /**
    * Transform magic offset into text offset.
    */
   int32_t ConvertMagicOffset(int32_t aOffset)
   {
@@ -400,10 +399,13 @@ private:
 
 inline HyperTextAccessible*
 Accessible::AsHyperText()
 {
   return mFlags & eHyperTextAccessible ?
     static_cast<HyperTextAccessible*>(this) : nullptr;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/generic/ImageAccessible.h
+++ b/accessible/src/generic/ImageAccessible.h
@@ -72,23 +72,22 @@ private:
    * @param aIndex  The 0-based index to be tested.
    *
    * @returns  true if index is valid for longdesc action.
    */
   inline bool IsLongDescIndex(uint8_t aIndex);
 
 };
 
-} // namespace a11y
-} // namespace mozilla
-
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcasting method
 
-inline mozilla::a11y::ImageAccessible*
+inline ImageAccessible*
 Accessible::AsImage()
 {
-  return IsImage() ?
-    static_cast<mozilla::a11y::ImageAccessible*>(this) : nullptr;
+  return IsImage() ? static_cast<ImageAccessible*>(this) : nullptr;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/generic/RootAccessible.h
+++ b/accessible/src/generic/RootAccessible.h
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_RootAccessible_h__
 #define mozilla_a11y_RootAccessible_h__
 
 #include "nsCaretAccessible.h"
 #include "DocAccessibleWrap.h"
 
-
 #include "nsHashtable.h"
 #include "nsCaretAccessible.h"
 #include "nsIDocument.h"
 #include "nsIDOMEventListener.h"
 
 namespace mozilla {
 namespace a11y {
 
@@ -78,19 +77,19 @@ protected:
                                     XULTreeAccessible* aAccessible);
 
     uint32_t GetChromeFlags();
 #endif
 
     nsRefPtr<nsCaretAccessible> mCaretAccessible;
 };
 
-} // namespace a11y
-} // namespace mozilla
-
-inline mozilla::a11y::RootAccessible*
+inline RootAccessible*
 Accessible::AsRoot()
 {
   return mFlags & eRootAccessible ?
     static_cast<mozilla::a11y::RootAccessible*>(this) : nullptr;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/generic/TableAccessible.h
+++ b/accessible/src/generic/TableAccessible.h
@@ -6,21 +6,21 @@
 
 #ifndef TABLE_ACCESSIBLE_H
 #define TABLE_ACCESSIBLE_H
 
 #include "nsString.h"
 #include "nsTArray.h"
 #include "prtypes.h"
 
-class Accessible;
-
 namespace mozilla {
 namespace a11y {
 
+class Accessible;
+
 /**
  * Accessible table interface.
  */
 class TableAccessible
 {
 public:
 
   /**
--- a/accessible/src/generic/TableCellAccessible.h
+++ b/accessible/src/generic/TableCellAccessible.h
@@ -5,21 +5,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_TableCellAccessible_h__
 #define mozilla_a11y_TableCellAccessible_h__
 
 #include "nsTArray.h"
 #include "mozilla/StandardInteger.h"
 
-class Accessible;
-
 namespace mozilla {
 namespace a11y {
 
+class Accessible;
 class TableAccessible;
 
 /**
  * Abstract interface implemented by table cell accessibles.
  */
 class TableCellAccessible
 {
 public:
--- a/accessible/src/generic/TextLeafAccessible.h
+++ b/accessible/src/generic/TextLeafAccessible.h
@@ -33,23 +33,24 @@ public:
 protected:
   // Accessible
   virtual void CacheChildren();
 
 protected:
   nsString mText;
 };
 
-} // namespace a11y
-} // namespace mozilla
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcast method
 
-inline mozilla::a11y::TextLeafAccessible*
+inline TextLeafAccessible*
 Accessible::AsTextLeaf()
 {
   return mFlags & eTextLeafAccessible ?
-    static_cast<mozilla::a11y::TextLeafAccessible*>(this) : nullptr;
+    static_cast<TextLeafAccessible*>(this) : nullptr;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/html/HTMLImageMapAccessible.h
+++ b/accessible/src/html/HTMLImageMapAccessible.h
@@ -64,22 +64,23 @@ public:
   virtual uint32_t EndOffset();
 
 protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
   virtual void CacheChildren();
 };
 
-} // namespace a11y
-} // namespace mozilla
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcasting method
 
-inline mozilla::a11y::HTMLImageMapAccessible*
+inline HTMLImageMapAccessible*
 Accessible::AsImageMap()
 {
   return IsImageMapAccessible() ?
-    static_cast<mozilla::a11y::HTMLImageMapAccessible*>(this) : nullptr;
+    static_cast<HTMLImageMapAccessible*>(this) : nullptr;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/html/HTMLListAccessible.h
+++ b/accessible/src/html/HTMLListAccessible.h
@@ -91,20 +91,20 @@ public:
   // HTMLListBulletAccessible
 
   /**
    * Return true if the bullet is inside of list item element boundaries.
    */
   bool IsInside() const;
 };
 
+
+inline HTMLLIAccessible*
+Accessible::AsHTMLListItem()
+{
+  return mFlags & eHTMLListItemAccessible ?
+    static_cast<HTMLLIAccessible*>(this) : nullptr;
+}
+
 } // namespace a11y
 } // namespace mozilla
 
-
-inline mozilla::a11y::HTMLLIAccessible*
-Accessible::AsHTMLListItem()
-{
-  return mFlags & eHTMLListItemAccessible ?
-    static_cast<mozilla::a11y::HTMLLIAccessible*>(this) : nullptr;
-}
-
 #endif
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -2,38 +2,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HTMLTableAccessible.h"
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
-#include "nsAccTreeWalker.h"
 #include "nsAccUtils.h"
 #include "DocAccessible.h"
+#include "nsIAccessibleRelation.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
-#include "nsIMutableArray.h"
+#include "TreeWalker.h"
 
-#include "nsIAccessibleRelation.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMRange.h"
 #include "nsISelectionPrivate.h"
 #include "nsINameSpaceManager.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDOMHTMLTableCellElement.h"
 #include "nsIDOMHTMLTableElement.h"
 #include "nsIDOMHTMLTableRowElement.h"
 #include "nsIDOMHTMLTableSectionElem.h"
 #include "nsIDocument.h"
+#include "nsIMutableArray.h"
 #include "nsIPresShell.h"
 #include "nsITableLayout.h"
 #include "nsITableCellLayout.h"
 #include "nsFrameSelection.h"
 #include "nsError.h"
 #include "nsArrayUtils.h"
 #include "nsComponentManagerUtils.h"
 
@@ -353,17 +353,17 @@ HTMLTableAccessible::Shutdown()
 
 void
 HTMLTableAccessible::CacheChildren()
 {
   // Move caption accessible so that it's the first child. Check for the first
   // caption only, because nsAccessibilityService ensures we don't create
   // accessibles for the other captions, since only the first is actually
   // visible.
-  nsAccTreeWalker walker(mDoc, mContent, CanHaveAnonChildren());
+  TreeWalker walker(this, mContent);
 
   Accessible* child = nullptr;
   while ((child = walker.NextChild())) {
     if (child->Role() == roles::CAPTION) {
       InsertChildAt(0, child);
       while ((child = walker.NextChild()) && AppendChild(child));
       break;
     }
--- a/accessible/src/mac/AccessibleWrap.h
+++ b/accessible/src/mac/AccessibleWrap.h
@@ -20,16 +20,19 @@
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 #if defined(__OBJC__)
 @class mozAccessible;
 #endif
 
+namespace mozilla {
+namespace a11y {
+
 class AccessibleWrap : public Accessible
 {
 public: // construction, destruction
   AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~AccessibleWrap();
     
   /**
    * Get the native Obj-C object (mozAccessible).
@@ -103,9 +106,12 @@ private:
   /**
    * We have created our native. This does not mean there is one.
    * This can never go back to false.
    * We need it because checking whether we need a native object cost time.
    */
   bool mNativeInited;  
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/mac/DocAccessibleWrap.h
+++ b/accessible/src/mac/DocAccessibleWrap.h
@@ -3,18 +3,24 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_DocAccessibleWrap_h__
 #define mozilla_a11y_DocAccessibleWrap_h__
 
 #include "DocAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+
 class DocAccessibleWrap : public DocAccessible
 {
 public:
   DocAccessibleWrap(nsIDocument* aDocument, nsIContent* aRootContent,
                     nsIPresShell* aPresShell);
   virtual ~DocAccessibleWrap();
 
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/mac/DocAccessibleWrap.mm
+++ b/accessible/src/mac/DocAccessibleWrap.mm
@@ -2,16 +2,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DocAccessibleWrap.h"
 
 #import "mozAccessible.h"
 
+using namespace mozilla::a11y;
+
 DocAccessibleWrap::
   DocAccessibleWrap(nsIDocument* aDocument, nsIContent* aRootContent,
                     nsIPresShell* aPresShell) :
   DocAccessible(aDocument, aRootContent, aPresShell)
 {
 }
 
 DocAccessibleWrap::~DocAccessibleWrap()
--- a/accessible/src/mac/HyperTextAccessibleWrap.h
+++ b/accessible/src/mac/HyperTextAccessibleWrap.h
@@ -3,12 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_HyperTextAccessibleWrap_h__
 #define mozilla_a11y_HyperTextAccessibleWrap_h__
 
 #include "HyperTextAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+
 typedef class HyperTextAccessible HyperTextAccessibleWrap;
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/mac/mozAccessible.h
+++ b/accessible/src/mac/mozAccessible.h
@@ -31,17 +31,17 @@ GetNativeFromGeckoAccessible(nsIAccessib
   return native;
 }
 
 @interface mozAccessible : NSObject <mozAccessible>
 {
   /**
    * Weak reference; it owns us.
    */
-  AccessibleWrap* mGeckoAccessible;
+  mozilla::a11y::AccessibleWrap* mGeckoAccessible;
   
   /**
    * Strong ref to array of children
    */
   NSMutableArray* mChildren;
   
   /** 
    * Weak reference to the parent
@@ -50,17 +50,17 @@ GetNativeFromGeckoAccessible(nsIAccessib
 
   /**
    * The nsIAccessible role of our gecko accessible.
    */
   mozilla::a11y::role        mRole;
 }
 
 // inits with the gecko owner.
-- (id)initWithAccessible:(AccessibleWrap*)geckoParent;
+- (id)initWithAccessible:(mozilla::a11y::AccessibleWrap*)geckoParent;
 
 // our accessible parent (AXParent)
 - (id <mozAccessible>)parent;
 
 // a lazy cache of our accessible children (AXChildren). updated
 - (NSArray*)children;
 
 // returns the size of this accessible.
@@ -111,17 +111,17 @@ GetNativeFromGeckoAccessible(nsIAccessib
 #pragma mark -
 
 // invalidates and removes all our children from our cached array.
 - (void)invalidateChildren;
 
 /** 
  * Append a child if they are already cached.
  */
-- (void)appendChild:(Accessible*)aAccessible;
+- (void)appendChild:(mozilla::a11y::Accessible*)aAccessible;
 
 // makes ourselves "expired". after this point, we might be around if someone
 // has retained us (e.g., a third-party), but we really contain no information.
 - (void)expire;
 - (BOOL)isExpired;
 
 #ifdef DEBUG
 - (void)printHierarchy;
--- a/accessible/src/mac/mozTextAccessible.h
+++ b/accessible/src/mac/mozTextAccessible.h
@@ -5,17 +5,17 @@
 #import "mozAccessible.h"
 
 #import "HyperTextAccessible.h"
 
 @interface mozTextAccessible : mozAccessible
 {
   // both of these are the same old mGeckoAccessible, but already
   // QI'd for us, to the right type, for convenience.
-  HyperTextAccessible *mGeckoTextAccessible; // strong
+  mozilla::a11y::HyperTextAccessible* mGeckoTextAccessible; // strong
   nsIAccessibleEditableText *mGeckoEditableTextAccessible; // strong
 }
 @end
 
 @interface mozTextLeafAccessible : mozAccessible
 {
 }
 @end
--- a/accessible/src/mac/nsAccessNodeWrap.h
+++ b/accessible/src/mac/nsAccessNodeWrap.h
@@ -7,20 +7,26 @@
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 #ifndef _nsAccessNodeWrap_H_
 #define _nsAccessNodeWrap_H_
 
 #include "nsAccessNode.h"
 
-class nsAccessNodeWrap :  public nsAccessNode
+namespace mozilla {
+namespace a11y {
+
+class nsAccessNodeWrap : public nsAccessNode
 {
 public:
   nsAccessNodeWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~nsAccessNodeWrap();
 
   static void InitAccessibility();
   static void ShutdownAccessibility();
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/mac/nsAccessNodeWrap.mm
+++ b/accessible/src/mac/nsAccessNodeWrap.mm
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAccessNodeWrap.h"
 
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
+using namespace mozilla::a11y;
 
 /*
  * Class nsAccessNodeWrap
  */
 
 //-----------------------------------------------------
 // construction 
 //-----------------------------------------------------
--- a/accessible/src/msaa/AccessibleWrap.h
+++ b/accessible/src/msaa/AccessibleWrap.h
@@ -111,16 +111,20 @@ Class::QueryInterface(REFIID aIID, void*
 
 #define IMPL_IUNKNOWN_INHERITED2(Class, Super0, Super1, Super2)                \
   IMPL_IUNKNOWN_QUERY_HEAD(Class)                                              \
   IMPL_IUNKNOWN_QUERY_CLASS(Super1);                                           \
   IMPL_IUNKNOWN_QUERY_CLASS(Super2);                                           \
   IMPL_IUNKNOWN_QUERY_CLASS(Super0)                                            \
   IMPL_IUNKNOWN_QUERY_TAIL
 
+
+namespace mozilla {
+namespace a11y {
+
 class AccessibleWrap : public Accessible,
                        public ia2AccessibleComponent,
                        public ia2AccessibleHyperlink,
                        public ia2AccessibleValue,
                        public IAccessible2
 {
 public: // construction, destruction
   AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
@@ -365,9 +369,12 @@ protected:
     NAVRELATION_POPUP_FOR = 0x100b,
     NAVRELATION_PARENT_WINDOW_OF = 0x100c,
     NAVRELATION_DEFAULT_BUTTON = 0x100d,
     NAVRELATION_DESCRIBED_BY = 0x100e,
     NAVRELATION_DESCRIPTION_FOR = 0x100f
   };
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/msaa/Compatibility.h
+++ b/accessible/src/msaa/Compatibility.h
@@ -4,21 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef COMPATIBILITY_MANAGER_H
 #define COMPATIBILITY_MANAGER_H
 
 #include "mozilla/StandardInteger.h"
 
-class nsAccessNodeWrap;
-
 namespace mozilla {
 namespace a11y {
 
+class nsAccessNodeWrap;
+
 /**
  * Used to get compatibility modes. Note, modes are computed at accessibility
  * start up time and aren't changed during lifetime.
  */
 class Compatibility
 {
 public:
   /**
--- a/accessible/src/msaa/DocAccessibleWrap.h
+++ b/accessible/src/msaa/DocAccessibleWrap.h
@@ -10,16 +10,19 @@
 #ifndef mozilla_a11y_DocAccessibleWrap_h__
 #define mozilla_a11y_DocAccessibleWrap_h__
 
 #include "ISimpleDOMDocument.h"
 
 #include "DocAccessible.h"
 #include "nsIDocShellTreeItem.h"
 
+namespace mozilla {
+namespace a11y {
+
 class DocAccessibleWrap : public DocAccessible,
                           public ISimpleDOMDocument
 {
 public:
   DocAccessibleWrap(nsIDocument* aDocument, nsIContent* aRootContent,
                     nsIPresShell* aPresShell);
   virtual ~DocAccessibleWrap();
 
@@ -64,9 +67,12 @@ public:
 protected:
   // DocAccessible
   virtual void DoInitialUpdate();
 
 protected:
   void* mHWND;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/msaa/HyperTextAccessibleWrap.cpp
+++ b/accessible/src/msaa/HyperTextAccessibleWrap.cpp
@@ -4,16 +4,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HyperTextAccessibleWrap.h"
 
 #include "nsEventShell.h"
 
+using namespace mozilla;
+using namespace mozilla::a11y;
+
 NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAccessibleWrap,
                              HyperTextAccessible)
 
 STDMETHODIMP
 HyperTextAccessibleWrap::QueryInterface(REFIID aIID, void** aInstancePtr)
 {
   if (!aInstancePtr)
     return E_FAIL;
--- a/accessible/src/msaa/HyperTextAccessibleWrap.h
+++ b/accessible/src/msaa/HyperTextAccessibleWrap.h
@@ -7,16 +7,19 @@
 
 #ifndef mozilla_a11y_HyperTextAccessibleWrap_h__
 #define mozilla_a11y_HyperTextAccessibleWrap_h__
 
 #include "HyperTextAccessible.h"
 #include "ia2AccessibleEditableText.h"
 #include "ia2AccessibleHyperText.h"
 
+namespace mozilla {
+namespace a11y {
+
 class HyperTextAccessibleWrap : public HyperTextAccessible,
                                 public ia2AccessibleHypertext,
                                 public ia2AccessibleEditableText
 {
 public:
   HyperTextAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
     HyperTextAccessible(aContent, aDoc) {}
 
@@ -30,10 +33,12 @@ public:
   virtual nsresult HandleAccEvent(AccEvent* aEvent);
 
 protected:
   virtual nsresult GetModifiedText(bool aGetInsertedText, nsAString& aText,
                                    uint32_t *aStartOffset,
                                    uint32_t *aEndOffset);
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
-
--- a/accessible/src/msaa/nsAccessNodeWrap.h
+++ b/accessible/src/msaa/nsAccessNodeWrap.h
@@ -38,22 +38,25 @@
   __try {
 
 #define A11Y_TRYBLOCK_END                                                      \
   } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(),      \
                                                     GetExceptionInformation()))\
   { }                                                                          \
   return E_FAIL;
 
+namespace mozilla {
+namespace a11y {
+
 class AccTextChangeEvent;
 
-class nsAccessNodeWrap :  public nsAccessNode,
-                          public nsIWinAccessNode,
-                          public ISimpleDOMNode,
-                          public IServiceProvider
+class nsAccessNodeWrap : public nsAccessNode,
+                         public nsIWinAccessNode,
+                         public ISimpleDOMNode,
+                         public IServiceProvider
 {
   public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIWINACCESSNODE
 
 public: // construction, destruction
   nsAccessNodeWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~nsAccessNodeWrap();
@@ -143,15 +146,18 @@ protected:
 
     /**
      * It is used in HyperTextAccessibleWrap for IA2::newText/oldText
      * implementation.
      */
     static AccTextChangeEvent* gTextEvent;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 /**
  * Converts nsresult to HRESULT.
  */
 HRESULT GetHRESULT(nsresult aResult);
 
 #endif
 
--- a/accessible/src/other/AccessibleWrap.cpp
+++ b/accessible/src/other/AccessibleWrap.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AccessibleWrap.h"
 
+using namespace mozilla::a11y;
+
 //-----------------------------------------------------
 // construction 
 //-----------------------------------------------------
 AccessibleWrap::
   AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
   Accessible(aContent, aDoc)
 {
 }
--- a/accessible/src/other/AccessibleWrap.h
+++ b/accessible/src/other/AccessibleWrap.h
@@ -2,28 +2,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
-#ifndef _AccessibleWrap_H_
-#define _AccessibleWrap_H_
+#ifndef mozilla_a11y_AccessibleWrap_h_
+#define mozilla_a11y_AccessibleWrap_h_
 
 #include "nsCOMPtr.h"
 #include "Accessible.h"
 
+namespace mozilla {
+namespace a11y {
+
 class AccessibleWrap : public Accessible
 {
 public: // construction, destruction
   AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~AccessibleWrap();
 
   protected:
     virtual nsresult FirePlatformEvent(AccEvent* aEvent)
     {
       return NS_OK;
     }
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/other/DocAccessibleWrap.h
+++ b/accessible/src/other/DocAccessibleWrap.h
@@ -7,11 +7,17 @@
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 #ifndef mozilla_a11y_DocAccessibleWrap_h__
 #define mozilla_a11y_DocAccessibleWrap_h__
 
 #include "DocAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+
 typedef DocAccessible DocAccessibleWrap;
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/other/HyperTextAccessibleWrap.h
+++ b/accessible/src/other/HyperTextAccessibleWrap.h
@@ -3,12 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_HyperTextAccessibleWrap_h__
 #define mozilla_a11y_HyperTextAccessibleWrap_h__
 
 #include "HyperTextAccessible.h"
 
+namespace mozilla {
+namespace a11y {
+
 typedef class HyperTextAccessible HyperTextAccessibleWrap;
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/other/nsAccessNodeWrap.cpp
+++ b/accessible/src/other/nsAccessNodeWrap.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAccessNodeWrap.h"
 
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
+using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Class nsAccessNodeWrap
 ////////////////////////////////////////////////////////////////////////////////
 
 //-----------------------------------------------------
 // construction 
 //-----------------------------------------------------
--- a/accessible/src/other/nsAccessNodeWrap.h
+++ b/accessible/src/other/nsAccessNodeWrap.h
@@ -7,20 +7,26 @@
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 #ifndef _nsAccessNodeWrap_H_
 #define _nsAccessNodeWrap_H_
 
 #include "nsAccessNode.h"
 
+namespace mozilla {
+namespace a11y {
+
 class nsAccessNodeWrap :  public nsAccessNode
 {
 public: // construction, destruction
   nsAccessNodeWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~nsAccessNodeWrap();
 
     static void InitAccessibility();
     static void ShutdownAccessibility();
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/windows/ia2/ia2AccessibleAction.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleAction.cpp
@@ -6,16 +6,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ia2AccessibleAction.h"
 
 #include "AccessibleAction_i.c"
 
 #include "AccessibleWrap.h"
 
+using namespace mozilla::a11y;
+
 // IUnknown
 
 STDMETHODIMP
 ia2AccessibleAction::QueryInterface(REFIID iid, void** ppv)
 {
   *ppv = NULL;
 
   if (IID_IAccessibleAction == iid) {
--- a/accessible/src/windows/ia2/ia2AccessibleEditableText.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleEditableText.cpp
@@ -8,16 +8,18 @@
 #include "ia2AccessibleEditableText.h"
 
 #include "AccessibleEditableText_i.c"
 #include "HyperTextAccessibleWrap.h"
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
+using namespace mozilla::a11y;
+
 // IAccessibleEditableText
 
 STDMETHODIMP
 ia2AccessibleEditableText::copyText(long aStartOffset, long aEndOffset)
 {
 __try {
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
--- a/accessible/src/windows/ia2/ia2AccessibleHyperlink.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleHyperlink.cpp
@@ -7,16 +7,18 @@
 
 #include "Accessible2.h"
 #include "AccessibleHyperlink.h"
 #include "AccessibleHyperlink_i.c"
 
 #include "AccessibleWrap.h"
 #include "nsIWinAccessNode.h"
 
+using namespace mozilla::a11y;
+
 // IUnknown
 
 STDMETHODIMP
 ia2AccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
 {
   *ppv = NULL;
 
   if (IID_IAccessibleHyperlink == iid) {
--- a/accessible/src/windows/ia2/ia2AccessibleHypertext.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleHypertext.cpp
@@ -6,16 +6,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ia2AccessibleHypertext.h"
 
 #include "AccessibleHypertext_i.c"
 
 #include "HyperTextAccessibleWrap.h"
 
+using namespace mozilla::a11y;
+
 // IAccessibleHypertext
 
 STDMETHODIMP
 ia2AccessibleHypertext::get_nHyperlinks(long* aHyperlinkCount)
 {
 __try {
   *aHyperlinkCount = 0;
 
--- a/accessible/src/windows/ia2/ia2AccessibleTableCell.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleTableCell.cpp
@@ -15,16 +15,18 @@
 #include "nsIAccessibleTable.h"
 #include "nsIWinAccessNode.h"
 #include "nsAccessNodeWrap.h"
 #include "nsWinUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
+using namespace mozilla::a11y;
+
 #define TABLECELL_INTERFACE_UNSUPPORTED_MSG \
 "Subclass of ia2AccessibleTableCell doesn't implement nsIAccessibleTableCell"\
 
 // IUnknown
 
 STDMETHODIMP
 ia2AccessibleTableCell::QueryInterface(REFIID iid, void** ppv)
 {
--- a/accessible/src/windows/ia2/ia2AccessibleText.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleText.cpp
@@ -9,16 +9,18 @@
 
 #include "Accessible2.h"
 #include "AccessibleText_i.c"
 
 #include "HyperTextAccessibleWrap.h"
 
 #include "nsIPersistentProperties2.h"
 
+using namespace mozilla::a11y;
+
 // IAccessibleText
 
 STDMETHODIMP
 ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset)
 {
 __try {
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
--- a/accessible/src/windows/ia2/ia2AccessibleValue.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleValue.cpp
@@ -6,16 +6,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ia2AccessibleValue.h"
 
 #include "AccessibleValue_i.c"
 
 #include "AccessibleWrap.h"
 
+using namespace mozilla::a11y;
+
 // IUnknown
 
 STDMETHODIMP
 ia2AccessibleValue::QueryInterface(REFIID iid, void** ppv)
 {
   *ppv = NULL;
 
   if (IID_IAccessibleValue == iid) {
--- a/accessible/src/windows/uia/uiaRawElmProvider.h
+++ b/accessible/src/windows/uia/uiaRawElmProvider.h
@@ -6,21 +6,21 @@
 
 #ifndef mozilla_a11y_uiaRawElmProvider_h__
 #define mozilla_a11y_uiaRawElmProvider_h__
 
 #include "objbase.h"
 #include "AccessibleWrap.h"
 #include "UIAutomation.h"
 
-class AccessibleWrap;
-
 namespace mozilla {
 namespace a11y {
 
+class AccessibleWrap;
+
 /**
  * IRawElementProviderSimple implementation (maintains IAccessibleEx approach).
  */
 class uiaRawElmProvider MOZ_FINAL : public IAccessibleEx,
                                     public IRawElementProviderSimple
 {
 public:
   uiaRawElmProvider(AccessibleWrap* aAcc) : mAcc(aAcc), mRefCnt(0) { }
--- a/accessible/src/xpcom/nsAccEvent.cpp
+++ b/accessible/src/xpcom/nsAccEvent.cpp
@@ -2,16 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAccEvent.h"
 #include "nsAccUtils.h"
 #include "DocAccessible.h"
 
+using namespace mozilla::a11y;
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS1(nsAccEvent, nsIAccessibleEvent)
 
 NS_IMETHODIMP
 nsAccEvent::GetIsFromUserInput(bool* aIsFromUserInput)
--- a/accessible/src/xpcom/nsAccEvent.h
+++ b/accessible/src/xpcom/nsAccEvent.h
@@ -5,16 +5,19 @@
 
 #ifndef _nsAccEvent_H_
 #define _nsAccEvent_H_
 
 #include "nsIAccessibleEvent.h"
 
 #include "AccEvent.h"
 
+namespace mozilla {
+namespace a11y {
+
 /**
  * Generic accessible event.
  */
 class nsAccEvent: public nsIAccessibleEvent
 {
 public:
   nsAccEvent(AccEvent* aEvent) : mEvent(aEvent) { }
   virtual ~nsAccEvent() { }
@@ -146,10 +149,13 @@ public:
   NS_DECL_NSIACCESSIBLEVIRTUALCURSORCHANGEEVENT
 
 private:
   nsAccVirtualCursorChangeEvent() MOZ_DELETE;
   nsAccVirtualCursorChangeEvent(const nsAccVirtualCursorChangeEvent&) MOZ_DELETE;
   nsAccVirtualCursorChangeEvent& operator =(const nsAccVirtualCursorChangeEvent&) MOZ_DELETE;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
 
--- a/accessible/src/xpcom/xpcAccessibleTable.cpp
+++ b/accessible/src/xpcom/xpcAccessibleTable.cpp
@@ -7,16 +7,18 @@
 #include "xpcAccessibleTable.h"
 
 #include "Accessible.h"
 #include "TableAccessible.h"
 
 #include "nsIMutableArray.h"
 #include "nsComponentManagerUtils.h"
 
+using namespace mozilla::a11y;
+
 static const uint32_t XPC_TABLE_DEFAULT_SIZE = 40;
 
 nsresult
 xpcAccessibleTable::GetCaption(nsIAccessible** aCaption)
 {
   NS_ENSURE_ARG_POINTER(aCaption);
   *aCaption = nullptr;
   if (!mTable)
--- a/accessible/src/xpcom/xpcAccessibleTable.h
+++ b/accessible/src/xpcom/xpcAccessibleTable.h
@@ -5,24 +5,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_A11Y_XPCOM_XPACCESSIBLETABLE_H_
 #define MOZILLA_A11Y_XPCOM_XPACCESSIBLETABLE_H_
 
 #include "nsAString.h"
 #include "nscore.h"
 
-
 class nsIAccessible;
 class nsIArray;
+
 namespace mozilla {
 namespace a11y {
+
 class TableAccessible;
-}
-}
 
 class xpcAccessibleTable
 {
 public:
   xpcAccessibleTable(mozilla::a11y::TableAccessible* aTable) : mTable(aTable) { }
 
   nsresult GetCaption(nsIAccessible** aCaption);
   nsresult GetSummary(nsAString& aSummary);
@@ -60,9 +59,12 @@ public:
   nsresult UnselectColumn(int32_t aColIdx);
   nsresult UnselectRow(int32_t aRowIdx);
   nsresult IsProbablyForLayout(bool* aIsForLayout);
 
 protected:
   mozilla::a11y::TableAccessible* mTable;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif // MOZILLA_A11Y_XPCOM_XPACCESSIBLETABLE_H_
--- a/accessible/src/xpcom/xpcAccessibleTableCell.h
+++ b/accessible/src/xpcom/xpcAccessibleTableCell.h
@@ -35,12 +35,13 @@ public:
   nsresult GetRowExtent(int32_t* aExtent);
   nsresult GetColumnHeaderCells(nsIArray** aHeaderCells);
   nsresult GetRowHeaderCells(nsIArray** aHeaderCells);
   nsresult IsSelected(bool* aSelected);
 
 protected:
   mozilla::a11y::TableCellAccessible* mTableCell;
 };
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_xpcom_xpcAccessibletableCell_h_
--- a/accessible/src/xul/XULColorPickerAccessible.cpp
+++ b/accessible/src/xul/XULColorPickerAccessible.cpp
@@ -2,21 +2,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "XULColorPickerAccessible.h"
 
 #include "Accessible-inl.h"
 #include "nsAccUtils.h"
-#include "nsAccTreeWalker.h"
 #include "nsCoreUtils.h"
 #include "DocAccessible.h"
 #include "Role.h"
 #include "States.h"
+#include "TreeWalker.h"
 
 #include "nsIDOMElement.h"
 #include "nsMenuPopupFrame.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULColorPickerTileAccessible
@@ -136,17 +136,17 @@ XULColorPickerAccessible::AreItemsOperab
 ////////////////////////////////////////////////////////////////////////////////
 // XULColorPickerAccessible: protected Accessible
 
 void
 XULColorPickerAccessible::CacheChildren()
 {
   NS_ENSURE_TRUE_VOID(mDoc);
 
-  nsAccTreeWalker walker(mDoc, mContent, true);
+  TreeWalker walker(this, mContent);
 
   Accessible* child = nullptr;
   while ((child = walker.NextChild())) {
     uint32_t role = child->Role();
 
     // Get an accessible for menupopup or panel elements.
     if (role == roles::ALERT) {
       AppendChild(child);
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -3,23 +3,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "XULFormControlAccessible.h"
 
 #include "Accessible-inl.h"
 #include "HTMLFormControlAccessible.h"
 #include "nsAccUtils.h"
-#include "nsAccTreeWalker.h"
 #include "nsCoreUtils.h"
 #include "DocAccessible.h"
 #include "nsIAccessibleRelation.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
+#include "TreeWalker.h"
 #include "XULMenuAccessible.h"
 
 #include "nsIDOMNSEditableElement.h"
 #include "nsIDOMXULButtonElement.h"
 #include "nsIDOMXULCheckboxElement.h"
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULTextboxElement.h"
@@ -183,17 +183,17 @@ XULButtonAccessible::CacheChildren()
 
   NS_ENSURE_TRUE_VOID(mDoc);
   if (!isMenu && !isMenuButton)
     return;
 
   Accessible* menupopup = nullptr;
   Accessible* button = nullptr;
 
-  nsAccTreeWalker walker(mDoc, mContent, true);
+  TreeWalker walker(this, mContent);
 
   Accessible* child = nullptr;
   while ((child = walker.NextChild())) {
     roles::Role role = child->Role();
 
     if (role == roles::MENUPOPUP) {
       // Get an accessible for menupopup or panel elements.
       menupopup = child;
@@ -822,17 +822,17 @@ XULTextFieldAccessible::CacheChildren()
 {
   NS_ENSURE_TRUE_VOID(mDoc);
   // Create child accessibles for native anonymous content of underlying HTML
   // input element.
   nsCOMPtr<nsIContent> inputContent(GetInputField());
   if (!inputContent)
     return;
 
-  nsAccTreeWalker walker(mDoc, inputContent, false);
+  TreeWalker walker(this, inputContent);
 
   Accessible* child = nullptr;
   while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTextFieldAccessible: HyperTextAccessible protected
 
--- a/accessible/src/xul/XULTreeAccessible.h
+++ b/accessible/src/xul/XULTreeAccessible.h
@@ -264,22 +264,22 @@ public:
 
 protected:
 
   // Accessible
   virtual Accessible* GetSiblingAtOffset(int32_t aOffset,
                                          nsresult *aError = nullptr) const;
 };
 
-} // namespace a11y
-} // namespace mozilla
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcasting method
 
-inline mozilla::a11y::XULTreeAccessible*
+inline XULTreeAccessible*
 Accessible::AsXULTree()
 {
-  return IsXULTree() ?
-    static_cast<mozilla::a11y::XULTreeAccessible*>(this) : nullptr;
+  return IsXULTree() ? static_cast<XULTreeAccessible*>(this) : nullptr;
 }
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -34,16 +34,17 @@ DIRS	= \
   tree \
   treeupdate \
   value \
   $(null)
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES =\
+		dumbfile.xpi \
 		formimage.png \
 		letters.gif \
 		moz.png \
 		$(topsrcdir)/content/media/test/bug461281.ogg \
 		longdesc_src.html \
 		actions.js \
 		attributes.js \
 		autocomplete.js \
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7
GIT binary patch
literal 22
Nc$^FHW@Tf*000g10H*)|
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -1412,17 +1412,18 @@ function caretMoveChecker(aCaretOffset, 
        "Wrong caret offset for " + prettyName(aEvent.accessible));
   }
 }
 
 /**
  * State change checker.
  */
 function stateChangeChecker(aState, aIsExtraState, aIsEnabled,
-                            aTargetOrFunc, aTargetFuncArg, aIsAsync)
+                            aTargetOrFunc, aTargetFuncArg, aIsAsync,
+                            aSkipCurrentStateCheck)
 {
   this.__proto__ = new invokerChecker(EVENT_STATE_CHANGE, aTargetOrFunc,
                                       aTargetFuncArg, aIsAsync);
 
   this.check = function stateChangeChecker_check(aEvent)
   {
     var event = null;
     try {
@@ -1436,16 +1437,21 @@ function stateChangeChecker(aState, aIsE
 
     is(event.isExtraState(), aIsExtraState,
        "Wrong extra state bit of the statechange event.");
     isState(event.state, aState, aIsExtraState,
             "Wrong state of the statechange event.");
     is(event.isEnabled(), aIsEnabled,
       "Wrong state of statechange event state");
 
+    if (aSkipCurrentStateCheck) {
+      todo(false, "State checking was skipped!");
+      return;
+    }
+
     var state = aIsEnabled ? (aIsExtraState ? 0 : aState) : 0;
     var extraState = aIsEnabled ? (aIsExtraState ? aState : 0) : 0;
     var unxpdState = aIsEnabled ? 0 : (aIsExtraState ? 0 : aState);
     var unxpdExtraState = aIsEnabled ? 0 : (aIsExtraState ? aState : 0);
     testStates(event.accessible, state, extraState, unxpdState, unxpdExtraState);
   }
 
   this.match = function stateChangeChecker_match(aEvent)
--- a/accessible/tests/mochitest/name/test_general.html
+++ b/accessible/tests/mochitest/name/test_general.html
@@ -159,16 +159,25 @@
       testName("textareawithchild", "Story Bar is ended.");
 
       //////////////////////////////////////////////////////////////////////////
       // button name (specific cases not covered by test_name_markup.html)
 
       testName("submit", "Submit Query");
       testName("image_submit", "Submit Query");
 
+      //////////////////////////////////////////////////////////////////////////
+      // controls having a value used as a part of computed name
+
+      testName("ctrlvalue_progressbar:input", "foo 5 baz");
+      testName("ctrlvalue_scrollbar:input", "foo 5 baz");
+      testName("ctrlvalue_slider:input", "foo 5 baz");
+      testName("ctrlvalue_spinbutton:input", "foo 5 baz");
+
+
       /////////////////////////////////////////////////////////////////////////
       // label with nested combobox (test for 'f' item of name computation guide)
 
       testName("comboinstart", "One day(s).");
       testName("combo3", "day(s).");
 
       testName("textboxinstart", "Two days.");
       testName("textbox1", "days.");
@@ -241,16 +250,21 @@
      title="Accessible name is duplicated when input has a label associated uisng for/id and is wrapped around the input">
     Mozilla Bug 669312
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=704416"
      title="HTML acronym and abbr names should be provided by @title">
     Mozilla Bug 704416
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=812041"
+     title="ARIA slider and spinbutton don't provide a value for name computation">
+    Mozilla Bug 812041
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- aria-label, simple label -->
   <span id="btn_simple_aria_label" role="button" aria-label="I am a button"/>
   <br/>
@@ -440,16 +454,48 @@
       is ended.
     </label>
   </form>
 
   <!-- submit buttons -->
   <input type="submit" id="submit">
   <input type="image" id="image_submit">
 
+  <!-- controls having a value used as part of computed name -->
+  <input type="checkbox" id="ctrlvalue_progressbar:input">
+  <label for="ctrlvalue_progressbar:input">
+    foo <span role="progressbar"
+               aria-valuenow="5" aria-valuemin="1"
+               aria-valuemax="10">5</span> baz
+  </label>
+
+  <input type="checkbox" id="ctrlvalue_scrollbar:input" />
+  <label for="ctrlvalue_scrollbar:input">
+    foo <span role="scrollbar"
+              aria-valuenow="5" aria-valuemin="1"
+              aria-valuemax="10">5</span> baz
+  </label>
+
+  <input type="checkbox" id="ctrlvalue_slider:input">
+  <label for="ctrlvalue_slider:input">
+    foo <input role="slider" type="range"
+               value="5" min="1" max="10"
+               aria-valuenow="5" aria-valuemin="1"
+               aria-valuemax="10"> baz
+  </label>
+
+  <input type="checkbox" id="ctrlvalue_spinbutton:input">
+  <label for="ctrlvalue_spinbutton:input">
+    foo <input role="spinbutton" type="number"
+               value="5" min="1" max="10"
+               aria-valuenow="5" aria-valuemin="1"
+               aria-valuemax="10">
+    baz
+  </label>
+
   <!-- a label with a nested control in the start, middle and end -->
   <form>
     <!-- at the start (without and with whitespaces) -->
     <label id="comboinstart"><select id="combo3">
         <option>One</option>
         <option>Two</option>
       </select>
       day(s).
--- a/accessible/tests/mochitest/states/Makefile.in
+++ b/accessible/tests/mochitest/states/Makefile.in
@@ -14,28 +14,30 @@ include $(DEPTH)/config/autoconf.mk
 MOCHITEST_A11Y_FILES =\
 		test_aria.html \
 		test_aria_imgmap.html \
 		test_aria_widgetitems.html \
 		test_buttons.html \
 		test_controls.html \
 		test_controls.xul \
 		test_doc.html \
+		test_doc_busy.html \
 		test_docarticle.html \
 		test_editablebody.html \
 		test_expandable.xul \
 		test_frames.html \
 		test_inputs.html \
 		test_link.html \
 		test_popup.xul \
 		test_selects.html \
 		test_stale.html \
 		test_textbox.xul \
 		test_tree.xul \
 		test_visibility.html \
+		test_visibility.xul \
 		z_frames.html \
 		z_frames_article.html \
 		z_frames_checkbox.html \
 		z_frames_textbox.html \
 		z_frames_update.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_doc_busy.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>states of document</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    //gA11yEventDumpToConsole = true; // debugging stuff
+
+    function loadFile()
+    {
+      // XXX: state change busy false event might be delievered when document
+      // has state busy true already (these events should be coalesced actually
+      // in this case as nothing happened).
+      this.eventSeq = [
+        new stateChangeChecker(STATE_BUSY, false, true, document, null, false, true),
+        new stateChangeChecker(STATE_BUSY, false, false, document)
+      ];
+
+      this.invoke = function loadFile_invoke()
+      {
+        synthesizeMouse(getNode("link"), 1, 1, {});
+      }
+
+      this.getID = function loadFile_getID()
+      {
+        return "load file: state busy change events on document";
+      }
+    }
+
+    var gQueue = null;
+    function doTest()
+    {
+      // State busy change event on file loading.
+      //enableLogging("docload"); // debugging
+      gQueue = new eventQueue();
+      gQueue.push(new loadFile());
+      //gQueue.onFinish = function() { disableLogging(); } // debugging
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     title="Missing busy state change event when downloading files"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=446469">Bug 446469</a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <a id="link" href="http://example.com/a11y/accessible/dumbfile.xpi">a file</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_visibility.xul
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="XUL elements visibility states testing">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../role.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+
+  <script type="application/javascript">
+  <![CDATA[
+    function doTest()
+    {
+      testStates("deck_pane1", STATE_INVISIBLE, 0, STATE_OFFSCREEN);
+      testStates("deck_pane2", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+      testStates("tabs_pane1", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+      testStates("tabs_pane2", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  ]]>
+  </script>
+
+  <hbox flex="1" style="overflow: auto;">
+    <body xmlns="http://www.w3.org/1999/xhtml">
+     <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=810260"
+         title=" xul:deck hidden pages shouldn't be offscreen">
+        Mozilla Bug 810260
+     </a>
+
+      <p id="display"></p>
+      <div id="content" style="display: none">
+      </div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+
+      <deck selectedIndex="1">
+        <description value="This is the first page" id="deck_pane1"/>
+        <button label="This is the second page" id="deck_pane2"/>
+      </deck>
+
+      <tabbox>
+        <tabs>
+          <tab>tab1</tab>
+          <tab>tab2</tab>
+        </tabs>
+        <tabpanels>
+          <description value="This is the first page" id="tabs_pane1"/>
+          <button label="This is the second page" id="tabs_pane2"/>
+        </tabpanels>
+      </tabbox>
+
+    </vbox>
+  </hbox>
+
+</window>
+
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -10,16 +10,17 @@ VPATH		= @srcdir@
 relativesrcdir  = accessible/tree
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES =\
 		dockids.html \
 	$(warning test_applicationacc.xul temporarily disabled, see bug 561508) \
 		test_aria_globals.html \
+		test_aria_grid.html \
 		test_aria_imgmap.html \
 		test_aria_menu.html \
 		test_aria_presentation.html \
 		test_brokencontext.html \
 		test_button.xul \
 		test_canvas.html \
 		test_combobox.xul \
 		test_cssoverflow.html \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_grid.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>HTML table tests</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+
+  <script type="application/javascript">
+    function doTest()
+    {
+      //////////////////////////////////////////////////////////////////////////
+      // grid having rowgroups
+
+      var accTree =
+        { TABLE: [
+          { GROUPING: [
+            { ROW: [
+              { GRID_CELL: [
+                { TEXT_LEAF: [ ] }
+              ] }
+            ] }
+          ] },
+        ] };
+
+      testAccessibleTree("grid", accTree);
+
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Support ARIA role rowgroup"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=525909">
+    Mozilla Bug 525909
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="grid" role="grid">
+    <div role="rowgroup">
+      <div role="row">
+        <div role="gridcell">cell</div>
+      </div>
+    </div>
+  </div>
+</body>
+</html>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -577,17 +577,13 @@ pref("ui.mouse.radius.bottommm", 2);
 pref("browser.prompt.allowNative", false);
 
 // Minimum delay in milliseconds between network activity notifications (0 means
 // no notifications). The delay is the same for both download and upload, though
 // they are handled separately. This pref is only read once at startup:
 // a restart is required to enable a new value.
 pref("network.activity.blipIntervalMilliseconds", 250);
 
-// Send some sites a custom user-agent.
-pref("general.useragent.override.facebook.com", "\(Mobile#(Android; Mobile");
-pref("general.useragent.override.youtube.com", "\(Mobile#(Android; Mobile");
-
 pref("jsloader.reuseGlobal", true);
 
 // Enable freeing dirty pages when minimizing memory; this reduces memory
 // consumption when applications are sent to the background.
 pref("memory.free_dirty_pages", true);
--- a/b2g/chrome/content/identity.js
+++ b/b2g/chrome/content/identity.js
@@ -38,17 +38,17 @@ if (typeof kIdentityJSLoaded === 'undefi
   const kIdentityDelegateLogout = "identity-delegate-logout";
   const kIdentityDelegateReady = "identity-delegate-ready";
   const kIdentityDelegateFinished = "identity-delegate-finished";
   const kIdentityControllerDoMethod = "identity-controller-doMethod";
   const kIdentktyJSLoaded = true;
 }
 
 var showUI = false;
-var options = null;
+var options = {};
 var isLoaded = false;
 var func = null;
 
 /*
  * Message back to the SignInToWebsite pipe.  Message should be an
  * object with the following keys:
  *
  *   method:             one of 'login', 'logout', 'ready'
@@ -60,68 +60,62 @@ function identityCall(message) {
 
 /*
  * To close the dialog, we first tell the gecko SignInToWebsite manager that it
  * can clean up.  Then we tell the gaia component that we are finished.  It is
  * necessary to notify gecko first, so that the message can be sent before gaia
  * destroys our context.
  */
 function closeIdentityDialog() {
-  log('ready to close');
   // tell gecko we're done.
   func = null; options = null;
   sendAsyncMessage(kIdentityDelegateFinished);
 }
 
 /*
  * doInternalWatch - call the internal.watch api and relay the results
  * up to the controller.
  */
 function doInternalWatch() {
   log("doInternalWatch:", options, isLoaded);
   if (options && isLoaded) {
-    log("internal watch options:", options);
     let BrowserID = content.wrappedJSObject.BrowserID;
     BrowserID.internal.watch(function(aParams) {
-        log("sending watch method message:", aParams.method);
         identityCall(aParams);
         if (aParams.method === "ready") {
-          log("watch finished.");
           closeIdentityDialog();
         }
       },
-      JSON.stringify({loggedInUser: options.loggedInUser, origin: options.origin}),
+      JSON.stringify(options),
       function(...things) {
         log("internal: ", things);
       }
     );
   }
 }
 
 function doInternalRequest() {
   log("doInternalRequest:", options && isLoaded);
   if (options && isLoaded) {
     content.wrappedJSObject.BrowserID.internal.get(
       options.origin,
       function(assertion) {
         if (assertion) {
-          log("request -> assertion, so do login");
-          identityCall({method:'login',assertion:assertion});
+          identityCall({method: 'login', assertion: assertion});
         }
         closeIdentityDialog();
       },
       options);
   }
 }
 
 function doInternalLogout(aOptions) {
   log("doInternalLogout:", (options && isLoaded));
   if (options && isLoaded) {
     let BrowserID = content.wrappedJSObject.BrowserID;
-    log("logging you out of ", options.origin);
     BrowserID.internal.logout(options.origin, function() {
       identityCall({method:'logout'});
       closeIdentityDialog();
     });
   }
 }
 
 addEventListener("DOMContentLoaded", function(e) {
@@ -129,32 +123,32 @@ addEventListener("DOMContentLoaded", fun
     isLoaded = true;
      // bring da func
      if (func) func();
   });
 });
 
 // listen for request
 addMessageListener(kIdentityDelegateRequest, function(aMessage) {
-    log("\n\n* * * * injected identity.js received", kIdentityDelegateRequest, "\n\n\n");
+  log("injected identity.js received", kIdentityDelegateRequest, "\n\n\n");
   options = aMessage.json;
   showUI = true;
   func = doInternalRequest;
   func();
 });
 
 // listen for watch
 addMessageListener(kIdentityDelegateWatch, function(aMessage) {
-    log("\n\n* * * * injected identity.js received", kIdentityDelegateWatch, "\n\n\n");
+  log("injected identity.js received", kIdentityDelegateWatch, "\n\n\n");
   options = aMessage.json;
   showUI = false;
   func = doInternalWatch;
   func();
 });
 
 // listen for logout
 addMessageListener(kIdentityDelegateLogout, function(aMessage) {
-    log("\n\n* * * * injected identity.js received", kIdentityDelegateLogout, "\n\n\n");
+  log("injected identity.js received", kIdentityDelegateLogout, "\n\n\n");
   options = aMessage.json;
   showUI = false;
   func = doInternalLogout;
   func();
 });
--- a/b2g/components/SignInToWebsite.jsm
+++ b/b2g/components/SignInToWebsite.jsm
@@ -71,16 +71,17 @@
 
 this.EXPORTED_SYMBOLS = ["SignInToWebsiteController"];
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/IdentityUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
                                   "resource://gre/modules/identity/MinimalIdentity.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Logger",
                                   "resource://gre/modules/identity/LogUtils.jsm");
 
 // JS shim that contains the callback functions that
@@ -96,28 +97,20 @@ const kReceivedIdentityAssertion = "rece
 const kIdentityDelegateWatch = "identity-delegate-watch";
 const kIdentityDelegateRequest = "identity-delegate-request";
 const kIdentityDelegateLogout = "identity-delegate-logout";
 const kIdentityDelegateFinished = "identity-delegate-finished";
 const kIdentityDelegateReady = "identity-delegate-ready";
 
 const kIdentityControllerDoMethod = "identity-controller-doMethod";
 
-XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
-                                   "@mozilla.org/uuid-generator;1",
-                                   "nsIUUIDGenerator");
-
 function log(...aMessageArgs) {
   Logger.log.apply(Logger, ["SignInToWebsiteController"].concat(aMessageArgs));
 }
 
-function getRandomId() {
-  return uuidgen.generateUUID().toString();
-}
-
 /*
  * GaiaInterface encapsulates the our gaia functions.  There are only two:
  *
  * getContent       - return the current content window
  * sendChromeEvent  - send a chromeEvent from the browser shell
  */
 let GaiaInterface = {
   _getBrowser: function SignInToWebsiteController__getBrowser() {
@@ -202,18 +195,18 @@ let Pipe = {
       // Try to load the identity shim file containing the callbacks
       // in the content script.  This could be either the visible
       // popup that the user interacts with, or it could be an invisible
       // frame.
       let frame = evt.detail.frame;
       let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
       let mm = frameLoader.messageManager;
       try {
-        log("about to load frame script");
         mm.loadFrameScript(kIdentityShimFile, true);
+        log("Loaded shim " + kIdentityShimFile + "\n");
       } catch (e) {
         log("Error loading ", kIdentityShimFile, " as a frame script: ", e);
       }
 
       // There are two messages that the delegate can send back: a "do
       // method" event, and a "finished" event.  We pass the do-method
       // events straight to the caller for interpretation and handling.
       // If we receive a "finished" event, then the delegate is done, so
@@ -225,17 +218,17 @@ let Pipe = {
         mm.removeMessageListener(kIdentityControllerDoMethod, aMessageCallback);
 
         let id = kReceivedIdentityAssertion + "-" + getRandomId();
         let detail = {
           type: kReceivedIdentityAssertion,
           showUI: aGaiaOptions.showUI || false,
           id: id
         };
-        log('tell gaia to close the dialog');
+        log('telling gaia to close the dialog');
         // tell gaia to close the dialog
         GaiaInterface.sendChromeEvent(detail);
       });
 
       mm.sendAsyncMessage(aGaiaOptions.message, aRpOptions);
     });
 
     // Tell gaia to open the identity iframe or trusty popup
@@ -300,17 +293,17 @@ this.SignInToWebsiteController = {
    *             assertion       optional
    */
   _makeDoMethodCallback: function SignInToWebsiteController__makeDoMethodCallback(aRpId) {
     return function SignInToWebsiteController_methodCallback(aOptions) {
       let message = aOptions.json;
       if (typeof message === 'string') {
         message = JSON.parse(message);
       }
-      log("doMethod:", message.method);
+
       switch(message.method) {
         case "ready":
           IdentityService.doReady(aRpId);
           break;
 
         case "login":
            IdentityService.doLogin(aRpId, message.assertion);
           break;
@@ -321,44 +314,43 @@ this.SignInToWebsiteController = {
 
         default:
           log("WARNING: wonky method call:", message.method);
           break;
       }
     };
   },
 
-  doWatch: function SignInToWebsiteController_doWatch(aOptions) {
+  doWatch: function SignInToWebsiteController_doWatch(aRpOptions) {
     // dom prevents watch from  being called twice
-    log("doWatch:", aOptions);
     var gaiaOptions = {
       message: kIdentityDelegateWatch,
       showUI: false
     };
-    this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
+    this.pipe.communicate(aRpOptions, gaiaOptions, this._makeDoMethodCallback(aRpOptions.id));
   },
 
   /**
    * The website is requesting login so the user must choose an identity to use.
    */
-  doRequest: function SignInToWebsiteController_doRequest(aOptions) {
-    log("doRequest", aOptions);
+  doRequest: function SignInToWebsiteController_doRequest(aRpOptions) {
+    log("doRequest", aRpOptions);
     // tell gaia to open the identity popup
     var gaiaOptions = {
       message: kIdentityDelegateRequest,
       showUI: true
     };
-    this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
+    this.pipe.communicate(aRpOptions, gaiaOptions, this._makeDoMethodCallback(aRpOptions.id));
   },
 
   /*
    *
    */
-  doLogout: function SignInToWebsiteController_doLogout(aOptions) {
-    log("doLogout", aOptions);
+  doLogout: function SignInToWebsiteController_doLogout(aRpOptions) {
+    log("doLogout", aRpOptions);
     var gaiaOptions = {
       message: kIdentityDelegateLogout,
       showUI: false
     };
-    this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
+    this.pipe.communicate(aRpOptions, gaiaOptions, this._makeDoMethodCallback(aRpOptions.id));
   }
 
 };
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -86,16 +86,17 @@ UpdateCheckListener.prototype = {
     Services.aus.QueryInterface(Ci.nsIUpdateCheckListener);
     Services.aus.onProgress(request, position, totalSize);
   }
 };
 
 function UpdatePrompt() {
   this.wrappedJSObject = this;
   this._updateCheckListener = new UpdateCheckListener(this);
+  Services.obs.addObserver(this, "update-check-start", false);
 }
 
 UpdatePrompt.prototype = {
   classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt,
                                          Ci.nsIUpdateCheckListener,
                                          Ci.nsIRequestObserver,
                                          Ci.nsIProgressEventSink,
@@ -352,28 +353,82 @@ UpdatePrompt.prototype = {
         this._update = null;
         break;
       case "update-prompt-apply-result":
         this.handleApplyPromptResult(detail);
         break;
     }
   },
 
+  appsUpdated: function UP_appsUpdated(aApps) {
+    log("appsUpdated: " + aApps.length + " apps to update");
+    let lock = Services.settings.createLock();
+    lock.set("apps.updateStatus", "check-complete", null);
+    this.sendChromeEvent("apps-update-check", { apps: aApps });
+    this._checkingApps = false;
+  },
+
+  // Trigger apps update check and wait for all to be done before
+  // notifying gaia.
+  onUpdateCheckStart: function UP_onUpdateCheckStart() {
+    // Don't start twice.
+    if (this._checkingApps) {
+      return;
+    }
+
+    this._checkingApps = true;
+
+    let self = this;
+
+    let window = Services.wm.getMostRecentWindow("navigator:browser");
+    let all = window.navigator.mozApps.mgmt.getAll();
+
+    all.onsuccess = function() {
+      let appsCount = this.result.length;
+      let appsChecked = 0;
+      let appsToUpdate = [];
+      this.result.forEach(function updateApp(aApp) {
+        let update = aApp.checkForUpdate();
+        update.onsuccess = function() {
+          appsChecked += 1;
+          appsToUpdate.push(aApp.manifestURL);
+          if (appsChecked == appsCount) {
+            self.appsUpdated(appsToUpdate);
+          }
+        }
+        update.onerror = function() {
+          appsChecked += 1;
+          if (appsChecked == appsCount) {
+            self.appsUpdated(appsToUpdate);
+          }
+        }
+      });
+    }
+
+    all.onerror = function() {
+      // Could not get the app list, just notify to update nothing.
+      self.appsUpdated([]);
+    }
+  },
+
   // nsIObserver
 
   observe: function UP_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "idle":
         this._waitingForIdle = false;
         this.showApplyPrompt(this._update);
         // Fall through
       case "quit-application":
         Services.idle.removeIdleObserver(this, APPLY_IDLE_TIMEOUT_SECONDS);
         Services.obs.removeObserver(this, "quit-application");
         break;
+      case "update-check-start":
+        this.onUpdateCheckStart();
+        break;
     }
   },
 
   // nsITimerCallback
 
   notify: function UP_notify(aTimer) {
     if (aTimer == this._applyPromptTimer) {
       log("Timed out waiting for result, restarting");
--- a/b2g/components/test/unit/head_identity.js
+++ b/b2g/components/test/unit/head_identity.js
@@ -42,37 +42,51 @@ function partial(fn) {
 }
 
 function uuid() {
   return uuidGenerator.generateUUID().toString();
 }
 
 // create a mock "doc" object, which the Identity Service
 // uses as a pointer back into the doc object
-function mockDoc(aIdentity, aOrigin, aDoFunc) {
+function mockDoc(aParams, aDoFunc) {
   let mockedDoc = {};
   mockedDoc.id = uuid();
-  mockedDoc.loggedInUser = aIdentity;
-  mockedDoc.origin = aOrigin;
+
+  // Properties of aParams may include loggedInUser
+  Object.keys(aParams).forEach(function(param) {
+    mockedDoc[param] = aParams[param];
+  });
+
+  // the origin is set inside nsDOMIdentity by looking at the
+  // document.nodePrincipal.origin.  Here we, we must satisfy
+  // ourselves with pretending.
+  mockedDoc.origin = "https://jedp.gov";
+
   mockedDoc['do'] = aDoFunc;
   mockedDoc.doReady = partial(aDoFunc, 'ready');
   mockedDoc.doLogin = partial(aDoFunc, 'login');
   mockedDoc.doLogout = partial(aDoFunc, 'logout');
   mockedDoc.doError = partial(aDoFunc, 'error');
   mockedDoc.doCancel = partial(aDoFunc, 'cancel');
   mockedDoc.doCoffee = partial(aDoFunc, 'coffee');
 
   return mockedDoc;
 }
 
 // create a mock "pipe" object that would normally communicate
 // messages up to gaia (either the trusty ui or the hidden iframe),
 // and convey messages back down from gaia to the controller through
 // the message callback.
-function mockPipe() {
+
+// The mock receiving pipe simulates gaia which, after receiving messages
+// through the pipe, will call back with instructions to invoke
+// certain methods.  It mocks what comes back from the other end of
+// the pipe.
+function mockReceivingPipe() {
   let MockedPipe = {
     communicate: function(aRpOptions, aGaiaOptions, aMessageCallback) {
       switch (aGaiaOptions.message) {
         case "identity-delegate-watch":
           aMessageCallback({json: {method: "ready"}});
           break;
         case "identity-delegate-request":
           aMessageCallback({json: {method: "login", assertion: TEST_CERT}});
@@ -84,16 +98,27 @@ function mockPipe() {
           throw("what the what?? " + aGaiaOptions.message);
           break;
       }
     }
   };
   return MockedPipe;
 }
 
+// The mock sending pipe lets us test what's actually getting put in the
+// pipe.
+function mockSendingPipe(aMessageCallback) {
+  let MockedPipe = {
+    communicate: function(aRpOptions, aGaiaOptions, aDummyCallback) {
+      aMessageCallback(aRpOptions, aGaiaOptions);
+    }
+  };
+  return MockedPipe;
+}
+
 // mimicking callback funtionality for ease of testing
 // this observer auto-removes itself after the observe function
 // is called, so this is meant to observe only ONE event.
 function makeObserver(aObserveTopic, aObserveFunc) {
   let observer = {
     // nsISupports provides type management in C++
     // nsIObserver is to be an observer
     QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
--- a/b2g/components/test/unit/test_signintowebsite.js
+++ b/b2g/components/test/unit/test_signintowebsite.js
@@ -19,144 +19,243 @@ function log(...aMessageArgs) {
   Logger.log.apply(Logger, ["test_signintowebsite"].concat(aMessageArgs));
 }
 
 function test_overall() {
   do_check_neq(MinimalIDService, null);
   run_next_test();
 }
 
+function objectContains(object, subset) {
+  let objectKeys = Object.keys(object);
+  let subsetKeys = Object.keys(subset);
+
+  // can't have fewer keys than the subset
+  if (objectKeys.length < subsetKeys.length) {
+    return false;
+  }
+
+  let key;
+  let success = true;
+  if (subsetKeys.length > 0) {
+    for (let i=0; i<subsetKeys.length; i++) {
+      key = subsetKeys[i];
+
+      // key exists in the source object
+      if (typeof object[key] === 'undefined') {
+        success = false;
+        break;
+      }
+
+      // recursively check object values
+      else if (typeof subset[key] === 'object') {
+        if (typeof object[key] !== 'object') {
+          success = false;
+          break;
+        }
+        if (! objectContains(object[key], subset[key])) {
+          success = false;
+          break;
+        }
+      }
+
+      else if (object[key] !== subset[key]) {
+        success = false;
+        break;
+      }
+    }
+  }
+
+  return success;
+}
+
+function test_object_contains() {
+  do_test_pending();
+
+  let someObj = {
+    pies: 42,
+    green: "spam",
+    flan: {yes: "please"}
+  };
+  let otherObj = {
+    pies: 42,
+    flan: {yes: "please"}
+  };
+  do_check_true(objectContains(someObj, otherObj));
+  do_test_finished();
+  run_next_test();
+}
+
 function test_mock_doc() {
   do_test_pending();
-  let mockedDoc = mockDoc(null, TEST_URL, function(action, params) {
+  let mockedDoc = mockDoc({loggedInUser: null}, function(action, params) {
     do_check_eq(action, 'coffee');
     do_test_finished();
     run_next_test();
   });
 
   // A smoke test to ensure that mockedDoc is functioning correctly.
   // There is presently no doCoffee method in Persona.
   mockedDoc.doCoffee();
 }
 
 function test_watch() {
   do_test_pending();
 
   setup_test_identity("pie@food.gov", TEST_CERT, function() {
     let controller = SignInToWebsiteController;
 
-    let mockedDoc = mockDoc(null, TEST_URL, function(action, params) {
+    let mockedDoc = mockDoc({loggedInUser: null}, function(action, params) {
       do_check_eq(action, 'ready');
       controller.uninit();
       do_test_finished();
       run_next_test();
     });
 
-    controller.init({pipe: mockPipe()});
-
+    controller.init({pipe: mockReceivingPipe()});
     MinimalIDService.RP.watch(mockedDoc, {});
   });
 }
 
 function test_request_login() {
   do_test_pending();
 
   setup_test_identity("flan@food.gov", TEST_CERT, function() {
     let controller = SignInToWebsiteController;
 
-    let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
+    let mockedDoc = mockDoc({loggedInUser: null}, call_sequentially(
       function(action, params) {
         do_check_eq(action, 'ready');
         do_check_eq(params, undefined);
       },
       function(action, params) {
         do_check_eq(action, 'login');
         do_check_eq(params, TEST_CERT);
         controller.uninit();
         do_test_finished();
         run_next_test();
       }
     ));
 
-    controller.init({pipe: mockPipe()});
+    controller.init({pipe: mockReceivingPipe()});
     MinimalIDService.RP.watch(mockedDoc, {});
     MinimalIDService.RP.request(mockedDoc.id, {});
   });
 }
 
 function test_request_logout() {
   do_test_pending();
 
   setup_test_identity("flan@food.gov", TEST_CERT, function() {
     let controller = SignInToWebsiteController;
 
-    let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
+    let mockedDoc = mockDoc({loggedInUser: null}, call_sequentially(
       function(action, params) {
         do_check_eq(action, 'ready');
         do_check_eq(params, undefined);
       },
       function(action, params) {
         do_check_eq(action, 'logout');
         do_check_eq(params, undefined);
         controller.uninit();
         do_test_finished();
         run_next_test();
       }
     ));
 
-    controller.init({pipe: mockPipe()});
+    controller.init({pipe: mockReceivingPipe()});
     MinimalIDService.RP.watch(mockedDoc, {});
     MinimalIDService.RP.logout(mockedDoc.id, {});
   });
 }
 
 function test_request_login_logout() {
   do_test_pending();
 
   setup_test_identity("unagi@food.gov", TEST_CERT, function() {
     let controller = SignInToWebsiteController;
 
-    let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
+    let mockedDoc = mockDoc({loggedInUser: null}, call_sequentially(
       function(action, params) {
         do_check_eq(action, 'ready');
         do_check_eq(params, undefined);
       },
       function(action, params) {
         do_check_eq(action, 'login');
         do_check_eq(params, TEST_CERT);
       },
       function(action, params) {
         do_check_eq(action, 'logout');
         do_check_eq(params, undefined);
         controller.uninit();
         do_test_finished();
         run_next_test();
       }
-      /*
-      ,function(action, params) {
-        do_check_eq(action, 'ready');
-        do_test_finished();
-        run_next_test();
-      }
-       */
     ));
 
-    controller.init({pipe: mockPipe()});
+    controller.init({pipe: mockReceivingPipe()});
     MinimalIDService.RP.watch(mockedDoc, {});
     MinimalIDService.RP.request(mockedDoc.id, {});
     MinimalIDService.RP.logout(mockedDoc.id, {});
   });
 }
 
+function test_options_pass_through() {
+  do_test_pending();
+
+  // An meaningless structure for testing that RP messages preserve
+  // objects and their parameters as they are passed back and forth.
+  let randomMixedParams = {
+    loggedInUser: "juanita@mozilla.com",
+    forceAuthentication: true,
+    issuer: "https://foo.com",
+    someThing: {
+      name: "Pertelote",
+      legs: 4,
+      nested: {bee: "Eric", remaining: "1/2"}
+      }
+    };
+
+  let mockedDoc = mockDoc(randomMixedParams, function(action, params) {});
+
+  function pipeOtherEnd(rpOptions, gaiaOptions) {
+    // Ensure that every time we receive a message, our mixed
+    // random params are contained in that message
+    do_check_true(objectContains(rpOptions, randomMixedParams));
+
+    switch (gaiaOptions.message) {
+      case "identity-delegate-watch":
+        MinimalIDService.RP.request(mockedDoc.id, {});
+        break;
+      case "identity-delegate-request":
+        MinimalIDService.RP.logout(mockedDoc.id, {});
+        break;
+      case "identity-delegate-logout":
+        do_test_finished();
+        run_next_test();
+        break;
+    }
+  }
+
+  let controller = SignInToWebsiteController;
+  controller.init({pipe: mockSendingPipe(pipeOtherEnd)});
+
+  MinimalIDService.RP.watch(mockedDoc, {});
+}
+
 let TESTS = [
   test_overall,
   test_mock_doc,
+  test_object_contains,
+
   test_watch,
   test_request_login,
   test_request_logout,
-  test_request_login_logout
+  test_request_login_logout,
+
+  test_options_pass_through
 ];
 
 TESTS.forEach(add_test);
 
 function run_test() {
   run_next_test();
 }
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=19.0a1
+MOZ_APP_VERSION=20.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=1.0.0-prerelease
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1352136253000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1352745754000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
@@ -506,24 +506,24 @@
                   <match name="filename" exp="nppdf32\.dll" />                      <versionRange  minVersion="10.0" maxVersion="10.1.3" severity="0" vulnerabilitystatus="1"></versionRange>
                   </pluginItem>
       <pluginItem  blockID="p160">
                   <match name="filename" exp="NPSWF32\.dll" />                      <versionRange  minVersion="0" maxVersion="10.2.9999" severity="0" vulnerabilitystatus="1"></versionRange>
                   </pluginItem>
       <pluginItem  blockID="p176">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="0" maxVersion="10.3.183.18.999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="17.0" maxVersion="*" />
+                              <versionRange  minVersion="18.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p178">
                   <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="11.0" maxVersion="11.4.402.286.999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="17.0" maxVersion="*" />
+                              <versionRange  minVersion="18.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p180">
                   <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 07" maxVersion="Java 7 Update 08" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
@@ -577,12 +577,18 @@
             <feature>DIRECT3D_9_LAYERS</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.17.12.5896</driverVersion>      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g37">      <os>WINNT 5.1</os>      <vendor>0x10de</vendor>            <feature>DIRECT3D_9_LAYERS</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>7.0.0.0</driverVersion>      <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g144">      <os>All</os>      <vendor>0x1002</vendor>            <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.982.0.0</driverVersion>      <driverVersionComparator>EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g146">      <os>All</os>      <vendor>0x1022</vendor>            <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.982.0.0</driverVersion>      <driverVersionComparator>EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g148">      <os>All</os>      <vendor>0x1022</vendor>            <feature>DIRECT3D_9_LAYERS</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.982.0.0</driverVersion>      <driverVersionComparator>EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g150">      <os>All</os>      <vendor>0x1002</vendor>            <feature>DIRECT3D_9_LAYERS</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.982.0.0</driverVersion>      <driverVersionComparator>EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g192">      <os>WINNT 6.2</os>      <vendor>0x1002</vendor>            <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>9.10.8.0</driverVersion>      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g194">      <os>WINNT 6.2</os>      <vendor>0x1022</vendor>            <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>9.10.8.0</driverVersion>      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
+    <gfxBlacklistEntry  blockID="g198">      <os>Darwin 10</os>      <vendor>0x10de</vendor>            <feature>WEBGL_MSAA</feature>      <featureStatus>BLOCKED_DEVICE</featureStatus>    </gfxBlacklistEntry>
+    <gfxBlacklistEntry  blockID="g200">      <os>Darwin 11</os>      <vendor>0x10de</vendor>            <featureStatus>WEBGL_MSAA</featureStatus>      <driverVersion>BLOCKED_DEVICE</driverVersion>    </gfxBlacklistEntry>
+    <gfxBlacklistEntry  blockID="g202">      <os>Darwin 12</os>      <vendor>0x10de</vendor>            <feature>WEBGL_MSAA</feature>      <featureStatus>BLOCKED_DEVICE</featureStatus>    </gfxBlacklistEntry>
+    <gfxBlacklistEntry  blockID="g204">      <os>Darwin 10</os>      <vendor>0x8086</vendor>            <feature>WEBGL_MSAA</feature>      <featureStatus>BLOCKED_DEVICE</featureStatus>    </gfxBlacklistEntry>
+    <gfxBlacklistEntry  blockID="g206">      <os>Darwin 11</os>      <vendor>0x8086</vendor>            <feature>WEBGL_MSAA</feature>      <featureStatus>BLOCKED_DEVICE</featureStatus>    </gfxBlacklistEntry>
+    <gfxBlacklistEntry  blockID="g208">      <os>Darwin 12</os>      <vendor>0x8086</vendor>            <feature>WEBGL_MSAA</feature>      <featureStatus>BLOCKED_DEVICE</featureStatus>    </gfxBlacklistEntry>
     </gfxItems>
 
 
 </blocklist>
\ No newline at end of file
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -346,20 +346,16 @@
             <menuitem id="appmenu_preferences"
 #ifdef XP_UNIX
                       label="&preferencesCmdUnix.label;"
 #else
                       label="&preferencesCmd2.label;"
 #endif
                       oncommand="openPreferences();"/>
             <menuseparator/>
-            <menuitem id="appmenu_socialToggle"
-                      type="checkbox"
-                      autocheck="false"
-                      command="Social:Toggle"/>
             <menuseparator id="appmenu_toggleToolbarsSeparator"/>
             <menuitem id="appmenu_toggleTabsOnTop"
                       label="&viewTabsOnTop.label;"
                       type="checkbox"
                       command="cmd_ToggleTabsOnTop"/>
             <menuitem id="appmenu_toolbarLayout"
                       label="&appMenuToolbarLayout.label;"
                       command="cmd_CustomizeToolbars"/>
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -500,37 +500,47 @@
                         accesskey="&downloads.accesskey;"
                         key="key_openDownloads"
                         command="Tools:Downloads"/>
               <menuitem id="menu_openAddons"
                         label="&addons.label;"
                         accesskey="&addons.accesskey;"
                         key="key_openAddons"
                         command="Tools:Addons"/>
-              <menuitem id="menu_socialToggle"
-                        type="checkbox"
-                        autocheck="false"
-                        command="Social:Toggle"/>
-              <menu id="menu_socialAmbientMenu"
-                    class="show-only-for-keyboard">
-                <menupopup id="menu_socialAmbientMenuPopup">
+              <menu id="menu_socialAmbientMenu">
+                <menupopup id="menu_social-statusarea-popup">
+                  <menuitem class="social-statusarea-user menuitem-iconic" pack="start" align="center"
+                            oncommand="SocialUI.showProfile(); document.getElementById('social-statusarea-popup').hidePopup();">
+                    <image class="social-statusarea-user-portrait"
+                           observes="socialBroadcaster_userPortrait"/>
+                    <vbox>
+                      <label class="social-statusarea-loggedInStatus"
+                             observes="socialBroadcaster_loggedInStatus"/>
+                    </vbox>
+                  </menuitem>
+#ifndef XP_WIN
+                  <menuseparator class="social-statusarea-separator"/>
+#endif
                   <menuseparator id="socialAmbientMenuSeparator"
                                  hidden="true"/>
-                  <menuitem id="social-toggle-sidebar-keyboardmenuitem"
+                  <menuitem class="social-toggle-sidebar-menuitem"
                             type="checkbox"
                             autocheck="false"
                             command="Social:ToggleSidebar"
                             label="&social.toggleSidebar.label;"
                             accesskey="&social.toggleSidebar.accesskey;"/>
-                  <menuitem id="social-toggle-notifications-keyboardmenuitem"
+                  <menuitem class="social-toggle-notifications-menuitem"
                             type="checkbox"
                             autocheck="false"
                             command="Social:ToggleNotifications"
                             label="&social.toggleNotifications.label;"
                             accesskey="&social.toggleNotifications.accesskey;"/>
+                  <menuseparator class="social-statusarea-separator"/>
+                  <menuitem class="social-toggle-menuitem" command="Social:Toggle"/>
+                  <menuitem class="social-remove-menuitem" command="Social:Remove"/>
                 </menupopup>
               </menu>
 #ifdef MOZ_SERVICES_SYNC
               <!-- only one of sync-setup or sync-menu will be showing at once -->
               <menuitem id="sync-setup"
                         label="&syncSetup.label;"
                         accesskey="&syncSetup.accesskey;"
                         observes="sync-setup-state"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -95,33 +95,34 @@
     <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
     <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/>
     <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
-    <command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" hidden="true"/>
+    <command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing"
 #ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
       oncommand="OpenBrowserWindow({private: true});"/>
 #else
       oncommand="gPrivateBrowsingUI.toggleMode();"/>
 #endif
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
     <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();"/>
     <command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/>
     <command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();"/>
     <command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
+    <command id="Social:Remove" oncommand="SocialUI.disableWithConfirmation();"/>
   </commandset>
 
   <commandset id="placesCommands">
     <command id="Browser:ShowAllBookmarks"
              oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
     <command id="Browser:ShowAllHistory"
              oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
   </commandset>
@@ -244,16 +245,21 @@
                  command="View:PageSource"/>
     <broadcaster id="devtoolsMenuBroadcaster_ErrorConsole"
                  label="&errorConsoleCmd.label;"
                  key="key_errorConsole"
                  command="Tools:ErrorConsole"/>
     <broadcaster id="devtoolsMenuBroadcaster_GetMoreTools"
                  label="&getMoreDevtoolsCmd.label;"
                  oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
+
+    <!-- SocialAPI broadcasters -->
+    <broadcaster id="socialBroadcaster_userPortrait"/>
+    <broadcaster id="socialBroadcaster_loggedInStatus"
+                 notLoggedInLabel="&social.notLoggedIn.label;"/>
   </broadcasterset>
 
   <keyset id="mainKeyset">
     <key id="key_newNavigator"
          key="&newNavigatorCmd.key;"
          command="cmd_newNavigator"
          modifiers="accel"/>
     <key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTab"/>
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -100,43 +100,38 @@ let SocialUI = {
   // Called once Social.jsm's provider has been set
   _providerReady: function SocialUI_providerReady() {
     // If we couldn't find a provider, nothing to do here.
     if (!Social.provider)
       return;
 
     this.updateToggleCommand();
 
-    let toggleCommand = this.toggleCommand;
-    let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
-    let label = gNavigatorBundle.getFormattedString("social.toggle.label",
-                                                    [Social.provider.name,
-                                                     brandShortName]);
-    let accesskey = gNavigatorBundle.getString("social.toggle.accesskey");
-    toggleCommand.setAttribute("label", label);
-    toggleCommand.setAttribute("accesskey", accesskey);
-
-    let kbMenuitem = document.getElementById("menu_socialAmbientMenu");
-    kbMenuitem.hidden = !Social.enabled;
-    kbMenuitem.setAttribute("label", label);
-    kbMenuitem.setAttribute("accesskey", accesskey);
-
-    // The View->Sidebar menu.
-    document.getElementById("menu_socialSidebar").setAttribute("label", Social.provider.name);
+    // The View->Sidebar and Menubar->Tools menu.
+    for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
+      document.getElementById(id).setAttribute("label", Social.provider.name);
 
     SocialToolbar.init();
     SocialShareButton.init();
     SocialSidebar.init();
     SocialMenu.populate();
   },
 
   updateToggleCommand: function SocialUI_updateToggleCommand() {
+    if (!Social.provider)
+      return;
+
     let toggleCommand = this.toggleCommand;
     // We only need to update the command itself - all our menu items use it.
-    toggleCommand.setAttribute("checked", Services.prefs.getBoolPref("social.enabled"));
+    let enabled = Services.prefs.getBoolPref("social.enabled");
+    let label = gNavigatorBundle.getFormattedString(enabled ? "social.turnOff.label" : "social.turnOn.label",
+                                                    [Social.provider.name]);
+    let accesskey = gNavigatorBundle.getString(enabled ? "social.turnOff.accesskey" : "social.turnOn.accesskey");
+    toggleCommand.setAttribute("label", label);
+    toggleCommand.setAttribute("accesskey", accesskey);
     toggleCommand.setAttribute("hidden", Social.active ? "false" : "true");
   },
 
   // This handles "ActivateSocialFeature" events fired against content documents
   // in this window.
   _activationEventHandler: function SocialUI_activationHandler(e) {
     // Nothing to do if Social is already enabled, or we don't have a provider
     // to enable yet.
@@ -596,17 +591,17 @@ let SocialShareButton = {
     }
     shareButton.src = imageURL;
   }
 };
 
 var SocialMenu = {
   populate: function SocialMenu_populate() {
     // This menu is only accessible through keyboard navigation.
-    let submenu = document.getElementById("menu_socialAmbientMenuPopup");
+    let submenu = document.getElementById("menu_social-statusarea-popup");
     let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem");
     while (ambientMenuItems.length)
       submenu.removeChild(ambientMenuItems.item(0));
 
     let separator = document.getElementById("socialAmbientMenuSeparator");
     separator.hidden = true;
     let provider = Social.provider;
     if (Social.active && provider) {
@@ -628,64 +623,67 @@ var SocialMenu = {
   }
 };
 
 var SocialToolbar = {
   // Called once, after window load, when the Social.provider object is initialized
   init: function SocialToolbar_init() {
     this.button.setAttribute("image", Social.provider.iconURL);
 
-    let removeItem = document.getElementById("social-remove-menuitem");
     let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
     let label = gNavigatorBundle.getFormattedString("social.remove.label",
                                                     [brandShortName]);
     let accesskey = gNavigatorBundle.getString("social.remove.accesskey");
-    removeItem.setAttribute("label", label);
-    removeItem.setAttribute("accesskey", accesskey);
+
+    let removeCommand = document.getElementById("Social:Remove");
+    removeCommand.setAttribute("label", label);
+    removeCommand.setAttribute("accesskey", accesskey);
 
     this.updateButton();
     this.updateProfile();
     this._dynamicResizer = new DynamicResizeWatcher();
   },
 
   get button() {
     return document.getElementById("social-provider-button");
   },
 
   updateButtonHiddenState: function SocialToolbar_updateButtonHiddenState() {
     let tbi = document.getElementById("social-toolbar-item");
-    tbi.hidden = !Social.uiVisible;
-    if (!SocialUI.haveLoggedInUser()) {
+    tbi.hidden = !Social.active;
+    let socialEnabled = Social.enabled;
+    for (let className of ["social-statusarea-separator", "social-statusarea-user"]) {
+      for (let element of document.getElementsByClassName(className))
+        element.hidden = !socialEnabled;
+    }
+    let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
+    toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
+
+    if (!SocialUI.haveLoggedInUser() || !socialEnabled) {
       let parent = document.getElementById("social-notification-panel");
       while (parent.hasChildNodes())
         parent.removeChild(parent.firstChild);
 
       while (tbi.lastChild != tbi.firstChild)
         tbi.removeChild(tbi.lastChild);
     }
   },
 
   updateProfile: function SocialToolbar_updateProfile() {
     // Profile may not have been initialized yet, since it depends on a worker
     // response. In that case we'll be called again when it's available, via
     // social:profile-changed
     let profile = Social.provider.profile || {};
     let userPortrait = profile.portrait || "chrome://global/skin/icons/information-32.png";
-    document.getElementById("social-statusarea-user-portrait").setAttribute("src", userPortrait);
+    document.getElementById("socialBroadcaster_userPortrait").setAttribute("src", userPortrait);
 
-    let notLoggedInLabel = document.getElementById("social-statusarea-notloggedin");
-    let userNameBtn = document.getElementById("social-statusarea-username");
-    if (profile.userName) {
-      notLoggedInLabel.hidden = true;
-      userNameBtn.hidden = false;
-      userNameBtn.value = profile.userName;
-    } else {
-      notLoggedInLabel.hidden = false;
-      userNameBtn.hidden = true;
-    }
+    let loggedInStatusBroadcaster = document.getElementById("socialBroadcaster_loggedInStatus");
+    let notLoggedInString = loggedInStatusBroadcaster.getAttribute("notLoggedInLabel");
+    let loggedInStatusValue = profile.userName ? profile.userName : notLoggedInString;
+    loggedInStatusBroadcaster.setAttribute("value", loggedInStatusValue);
   },
 
   updateButton: function SocialToolbar_updateButton() {
     this.updateButtonHiddenState();
     let provider = Social.provider;
     let icons = provider.ambientNotificationIcons;
     let iconNames = Object.keys(icons);
     let iconBox = document.getElementById("social-toolbar-item");
@@ -922,17 +920,21 @@ var SocialSidebar = {
 
   // Whether the user has toggled the sidebar on (for windows where it can appear)
   get opened() {
     return Services.prefs.getBoolPref("social.sidebar.open") && !document.mozFullScreen;
   },
 
   setSidebarVisibilityState: function(aEnabled) {
     let sbrowser = document.getElementById("social-sidebar-browser");
-    sbrowser.docShell.isActive = aEnabled;
+    // it's possible we'll be called twice with aEnabled=false so let's
+    // just assume we may often be called with the same state.
+    if (aEnabled == sbrowser.docShellIsActive)
+      return;
+    sbrowser.docShellIsActive = aEnabled;
     let evt = sbrowser.contentDocument.createEvent("CustomEvent");
     evt.initCustomEvent(aEnabled ? "socialFrameShow" : "socialFrameHide", true, true, {});
     sbrowser.contentDocument.documentElement.dispatchEvent(evt);
   },
 
   update: function SocialSidebar_update() {
     clearTimeout(this._unloadTimeoutId);
     // Hide the toggle menu item if the sidebar cannot appear
@@ -984,37 +986,18 @@ var SocialSidebar = {
     SocialSidebar.setSidebarVisibilityState(true);
   },
 
   unloadSidebar: function SocialSidebar_unloadSidebar() {
     let sbrowser = document.getElementById("social-sidebar-browser");
     if (!sbrowser.hasAttribute("origin"))
       return;
 
-    // Bug 803255 - If we don't remove the sidebar browser from the DOM,
-    // the previous document leaks because it's only released when the
-    // sidebar is made visible again.
-    let container = sbrowser.parentNode;
-    container.removeChild(sbrowser);
     sbrowser.removeAttribute("origin");
-    sbrowser.removeAttribute("src");
-
-    function resetDocShell(docshellSupports) {
-      let docshell = docshellSupports.QueryInterface(Ci.nsIDocShell);
-      if (docshell.chromeEventHandler != sbrowser)
-        return;
-
-      SocialSidebar.configureSidebarDocShell(docshell);
-
-      Services.obs.removeObserver(resetDocShell, "webnavigation-create");
-    }
-    Services.obs.addObserver(resetDocShell, "webnavigation-create", false);
-
-    container.appendChild(sbrowser);
-
+    sbrowser.setAttribute("src", "about:blank");
     SocialFlyout.unload();
   },
 
   _unloadTimeoutId: 0,
 
   setSidebarErrorMessage: function(aType) {
     let sbrowser = document.getElementById("social-sidebar-browser");
     switch (aType) {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1466,20 +1466,22 @@ var gBrowserInit = {
                   gPrefService.getBoolPref("devtools.debugger.remote-enabled");
     if (enabled) {
       let cmd = document.getElementById("Tools:ChromeDebugger");
       cmd.removeAttribute("disabled");
       cmd.removeAttribute("hidden");
     }
 
     // Enable Error Console?
-    let consoleEnabled = gPrefService.getBoolPref("devtools.errorconsole.enabled") ||
+    // Temporarily enabled. See bug 798925.
+    let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled") ||
                          gPrefService.getBoolPref("devtools.chrome.enabled");
     if (consoleEnabled) {
       let cmd = document.getElementById("Tools:ErrorConsole");
+      cmd.removeAttribute("disabled");
       cmd.removeAttribute("hidden");
     }
 
     // Enable Scratchpad in the UI, if the preference allows this.
     let scratchpadEnabled = gPrefService.getBoolPref(Scratchpad.prefEnabledName);
     if (scratchpadEnabled) {
       let cmd = document.getElementById("Tools:Scratchpad");
       cmd.removeAttribute("disabled");
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -662,43 +662,43 @@
                    class="chromeclass-toolbar-additional"
                    removable="false"
                    title="&socialToolbar.title;"
                    hidden="true">
         <toolbarbutton id="social-provider-button"
                        class="toolbarbutton-1"
                        type="menu">
           <menupopup id="social-statusarea-popup">
-            <menuitem id="social-statusarea-user" pack="start" align="center" class="menuitem-iconic"
-                  onclick="SocialUI.showProfile(); document.getElementById('social-statusarea-popup').hidePopup();">
-              <image id="social-statusarea-user-portrait"/>
+            <menuitem class="social-statusarea-user menuitem-iconic" pack="start" align="center"
+                      oncommand="SocialUI.showProfile(); document.getElementById('social-statusarea-popup').hidePopup();">
+              <image class="social-statusarea-user-portrait"
+                     observes="socialBroadcaster_userPortrait"/>
               <vbox>
-                <label id="social-statusarea-notloggedin"
-                       value="&social.notLoggedIn.label;"/>
-                <label id="social-statusarea-username"/>
+                <label class="social-statusarea-loggedInStatus"
+                       observes="socialBroadcaster_loggedInStatus"/>
               </vbox>
             </menuitem>
 #ifndef XP_WIN
-            <menuseparator/>
+            <menuseparator class="social-statusarea-separator"/>
 #endif
-            <menuitem id="social-toggle-sidebar-menuitem"
+            <menuitem class="social-toggle-sidebar-menuitem"
                       type="checkbox"
                       autocheck="false"
                       command="Social:ToggleSidebar"
                       label="&social.toggleSidebar.label;"
                       accesskey="&social.toggleSidebar.accesskey;"/>
-            <menuitem id="social-toggle-notifications-menuitem"
+            <menuitem class="social-toggle-notifications-menuitem"
                       type="checkbox"
                       autocheck="false"
                       command="Social:ToggleNotifications"
                       label="&social.toggleNotifications.label;"
                       accesskey="&social.toggleNotifications.accesskey;"/>
-            <menuseparator/>
-            <menuitem id="social-remove-menuitem"
-                      oncommand="SocialUI.disableWithConfirmation();"/>
+            <menuseparator class="social-statusarea-separator"/>
+            <menuitem class="social-toggle-menuitem" command="Social:Toggle"/>
+            <menuitem class="social-remove-menuitem" command="Social:Remove"/>
           </menupopup>
         </toolbarbutton>
       </toolbaritem>
 
       <toolbaritem id="bookmarks-menu-button-container"
                    class="chromeclass-toolbar-additional"
                    removable="true"
                    title="&bookmarksMenuButton.label;">
@@ -1055,17 +1055,17 @@
     <splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
     <vbox id="appcontent" flex="1">
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="contentAreaClick(event, false);"/>
-      <chatbar id="pinnedchats" layer="true" mousethrough="always"/>
+      <chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
       <statuspanel id="statusbar-display" inactive="true"/>
     </vbox>
     <splitter id="devtools-side-splitter" hidden="true"/>
     <vbox id="devtools-sidebar-box" hidden="true"
           style="min-width: 18em; width: 22em; max-width: 42em;" persist="width">
       <toolbar id="devtools-sidebar-toolbar"
                class="devtools-toolbar"
                nowindowdrag="true">
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -264,51 +264,54 @@ Sanitizer.prototype = {
     },
     
     downloads: {
       clear: function ()
       {
         var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                               .getService(Components.interfaces.nsIDownloadManager);
 
-        var dlIDsToRemove = [];
+        var dlsToRemove = [];
         if (this.range) {
           // First, remove the completed/cancelled downloads
           dlMgr.removeDownloadsByTimeframe(this.range[0], this.range[1]);
-          
+
           // Queue up any active downloads that started in the time span as well
-          var dlsEnum = dlMgr.activeDownloads;
-          while(dlsEnum.hasMoreElements()) {
-            var dl = dlsEnum.next();
-            if(dl.startTime >= this.range[0])
-              dlIDsToRemove.push(dl.id);
+          for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) {
+            while (dlsEnum.hasMoreElements()) {
+              var dl = dlsEnum.next();
+              if (dl.startTime >= this.range[0])
+                dlsToRemove.push(dl);
+            }
           }
         }
         else {
           // Clear all completed/cancelled downloads
           dlMgr.cleanUp();
+          dlMgr.cleanUpPrivate();
           
           // Queue up all active ones as well
-          var dlsEnum = dlMgr.activeDownloads;
-          while(dlsEnum.hasMoreElements()) {
-            dlIDsToRemove.push(dlsEnum.next().id);
+          for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) {
+            while (dlsEnum.hasMoreElements()) {
+              dlsToRemove.push(dlsEnum.next());
+            }
           }
         }
-        
+
         // Remove any queued up active downloads
-        dlIDsToRemove.forEach(function(id) {
-          dlMgr.removeDownload(id);
+        dlsToRemove.forEach(function (dl) {
+          dl.remove();
         });
       },
 
       get canClear()
       {
         var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                               .getService(Components.interfaces.nsIDownloadManager);
-        return dlMgr.canCleanUp;
+        return dlMgr.canCleanUp || dlMgr.canCleanUpPrivate;
       }
     },
     
     passwords: {
       clear: function ()
       {
         var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
                               .getService(Components.interfaces.nsILoginManager);
--- a/browser/base/content/test/browser_sanitize-download-history.js
+++ b/browser/base/content/test/browser_sanitize-download-history.js
@@ -45,25 +45,26 @@ function test()
     let testPath = Services.io.newFileURI(file).spec;
     let data = {
       name: "381603.patch",
       source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
       target: testPath,
       startTime: 1180493839859230,
       endTime: 1180493839859239,
       state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-      currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+      currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+      guid: "a1bcD23eF4g5"
     };
     let db = Cc["@mozilla.org/download-manager;1"].
              getService(Ci.nsIDownloadManager).DBConnection;
     let stmt = db.createStatement(
       "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
-        "state, currBytes, maxBytes, preferredAction, autoResume) " +
+        "state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
       "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
-        ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+        ":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
     try {
       for (let prop in data)
         stmt.params[prop] = data[prop];
       stmt.execute();
     }
     finally {
       stmt.finalize();
     }
--- a/browser/base/content/test/browser_sanitize-timespans.js
+++ b/browser/base/content/test/browser_sanitize-timespans.js
@@ -389,25 +389,26 @@ function setupDownloads() {
   let data = {
     id:   "5555555",
     name: "fakefile-10-minutes",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
     target: "fakefile-10-minutes",
     startTime: now_uSec - 10 * kUsecPerMin, // 10 minutes ago, in uSec
     endTime: now_uSec - 11 * kUsecPerMin, // 1 minute later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "a1bcD23eF4g5"
   };
 
   let db = dm.DBConnection;
   let stmt = db.createStatement(
     "INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
-      "state, currBytes, maxBytes, preferredAction, autoResume) " +
+      "state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
     "VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
-      ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+      ":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
     stmt.reset();
   }
@@ -416,17 +417,18 @@ function setupDownloads() {
   data = {
     id:   "5555551",
     name: "fakefile-1-hour",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
     target: "fakefile-1-hour",
     startTime: now_uSec - 45 * kUsecPerMin, // 45 minutes ago, in uSec
     endTime: now_uSec - 44 * kUsecPerMin, // 1 minute later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "1bcD23eF4g5a"
   };
 
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
@@ -437,17 +439,18 @@ function setupDownloads() {
   data = {
     id:   "5555556",
     name: "fakefile-1-hour-10-minutes",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
     target: "fakefile-1-hour-10-minutes",
     startTime: now_uSec - 70 * kUsecPerMin, // 70 minutes ago, in uSec
     endTime: now_uSec - 71 * kUsecPerMin, // 1 minute later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "a1cbD23e4Fg5"
   };
 
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
@@ -458,17 +461,18 @@ function setupDownloads() {
   data = {
     id:   "5555552",
     name: "fakefile-2-hour",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
     target: "fakefile-2-hour",
     startTime: now_uSec - 90 * kUsecPerMin, // 90 minutes ago, in uSec
     endTime: now_uSec - 89 * kUsecPerMin, // 1 minute later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "b1aDc23eFg54"
   };
 
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
@@ -479,17 +483,18 @@ function setupDownloads() {
   data = {
     id:   "5555557",
     name: "fakefile-2-hour-10-minutes",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
     target: "fakefile-2-hour-10-minutes",
     startTime: now_uSec - 130 * kUsecPerMin, // 130 minutes ago, in uSec
     endTime: now_uSec - 131 * kUsecPerMin, // 1 minute later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "z1bcD23eF4g5"
   };
 
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
@@ -500,17 +505,18 @@ function setupDownloads() {
   data = {
     id:   "5555553",
     name: "fakefile-4-hour",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
     target: "fakefile-4-hour",
     startTime: now_uSec - 180 * kUsecPerMin, // 180 minutes ago, in uSec
     endTime: now_uSec - 179 * kUsecPerMin, // 1 minute later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "zzzcD23eF4g5"
   };
   
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
@@ -521,17 +527,18 @@ function setupDownloads() {
   data = {
     id:   "5555558",
     name: "fakefile-4-hour-10-minutes",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
     target: "fakefile-4-hour-10-minutes",
     startTime: now_uSec - 250 * kUsecPerMin, // 250 minutes ago, in uSec
     endTime: now_uSec - 251 * kUsecPerMin, // 1 minute later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "z1bzz23eF4gz"
   };
   
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
@@ -547,17 +554,18 @@ function setupDownloads() {
   data = {
     id:   "5555554",
     name: "fakefile-today",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
     target: "fakefile-today",
     startTime: today.getTime() * 1000,  // 12:00:30am this morning, in uSec
     endTime: (today.getTime() + 1000) * 1000, // 1 second later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "ffffD23eF4g5"
   };
   
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
@@ -570,17 +578,18 @@ function setupDownloads() {
   data = {
     id:   "5555550",
     name: "fakefile-old",
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
     target: "fakefile-old",
     startTime: lastYear.getTime() * 1000, // 1 year ago, in uSec
     endTime: (lastYear.getTime() + 1000) * 1000, // 1 second later
     state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "ggggg23eF4g5"
   };
   
   try {
     for (let prop in data)
       stmt.params[prop] = data[prop];
     stmt.execute();
   }
   finally {
--- a/browser/base/content/test/browser_sanitizeDialog_treeView.js
+++ b/browser/base/content/test/browser_sanitizeDialog_treeView.js
@@ -409,25 +409,26 @@ function addDownloadWithMinutesAgo(aMinu
   let data = {
     id:        gDownloadId,
     name:      name,
     source:   "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
     target:    name,
     startTime: now_uSec - (aMinutesAgo * 60 * 1000000),
     endTime:   now_uSec - ((aMinutesAgo + 1) *60 * 1000000),
     state:     Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
-    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
+    currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
+    guid: "a1bcD23eF4g5"
   };
 
   let db = dm.DBConnection;
   let stmt = db.createStatement(
     "INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
-      "state, currBytes, maxBytes, preferredAction, autoResume) " +
+      "state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
     "VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
-      ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+      ":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
   try {
     for (let prop in data) {
       stmt.params[prop] = data[prop];
     }
     stmt.execute();
   }
   finally {
     stmt.reset();
--- a/browser/base/content/test/browser_social_toolbar.js
+++ b/browser/base/content/test/browser_social_toolbar.js
@@ -21,19 +21,19 @@ var tests = {
     let profile = {
       portrait: "https://example.com/portrait.jpg",
       userName: "trickster",
       displayName: "Kuma Lisa",
       profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
     }
     Social.provider.updateUserProfile(profile);
     // check dom values
-    let portrait = document.getElementById("social-statusarea-user-portrait").getAttribute("src");
+    let portrait = document.getElementsByClassName("social-statusarea-user-portrait")[0].getAttribute("src");
     is(profile.portrait, portrait, "portrait is set");
-    let userButton = document.getElementById("social-statusarea-username");
+    let userButton = document.getElementsByClassName("social-statusarea-loggedInStatus")[0];
     ok(!userButton.hidden, "username is visible");
     is(userButton.value, profile.userName, "username is set");
     next();
   },
   testNoAmbientNotificationsIsNoKeyboardMenu: function(next) {
     // The menu bar isn't as easy to instrument on Mac.
     if (navigator.platform.contains("Mac")) {
       info("Skipping checking the menubar on Mac OS");
@@ -41,17 +41,17 @@ var tests = {
     }
 
     // Test that keyboard accessible menuitem doesn't exist when no ambient icons specified.
     let toolsPopup = document.getElementById("menu_ToolsPopup");
     toolsPopup.addEventListener("popupshown", function ontoolspopupshownNoAmbient() {
       toolsPopup.removeEventListener("popupshown", ontoolspopupshownNoAmbient);
       let socialToggleMore = document.getElementById("menu_socialAmbientMenu");
       ok(socialToggleMore, "Keyboard accessible social menu should exist");
-      is(socialToggleMore.querySelectorAll("menuitem").length, 2, "The minimum number of menuitems is two when there are no ambient notifications.");
+      is(socialToggleMore.querySelectorAll("menuitem").length, 5, "The minimum number of menuitems is two when there are no ambient notifications.");
       is(socialToggleMore.hidden, false, "Menu should be visible since we show some non-ambient notifications in the menu.");
       toolsPopup.hidePopup();
       next();
     }, false);
     document.getElementById("menu_ToolsPopup").openPopup();
   },
   testAmbientNotifications: function(next) {
     let ambience = {
@@ -99,46 +99,41 @@ var tests = {
         next();
 
       // Test that keyboard accessible menuitem was added.
       let toolsPopup = document.getElementById("menu_ToolsPopup");
       toolsPopup.addEventListener("popupshown", function ontoolspopupshownAmbient() {
         toolsPopup.removeEventListener("popupshown", ontoolspopupshownAmbient);
         let socialToggleMore = document.getElementById("menu_socialAmbientMenu");
         ok(socialToggleMore, "Keyboard accessible social menu should exist");
-        is(socialToggleMore.querySelectorAll("menuitem").length, 5, "The number of menuitems is minimum plus three ambient notification menuitems.");
+        is(socialToggleMore.querySelectorAll("menuitem").length, 8, "The number of menuitems is minimum plus three ambient notification menuitems.");
         is(socialToggleMore.hidden, false, "Menu is visible when ambient notifications have label & menuURL");
-        let menuitem = socialToggleMore.querySelector("menuitem");
+        let menuitem = socialToggleMore.querySelector(".ambient-menuitem");
         is(menuitem.getAttribute("label"), "Test Ambient 1", "Keyboard accessible ambient menuitem should have specified label");
         toolsPopup.hidePopup();
         next();
       }, false);
       document.getElementById("menu_ToolsPopup").openPopup();
     }, "statusIcon was never found");
   },
   testProfileUnset: function(next) {
     Social.provider.updateUserProfile({});
     // check dom values
-    let userButton = document.getElementById("social-statusarea-username");
-    ok(userButton.hidden, "username is not visible");
     let ambientIcons = document.querySelectorAll("#social-toolbar-item > box");
     for (let ambientIcon of ambientIcons) {
       ok(ambientIcon.collapsed, "ambient icon (" + ambientIcon.id + ") is collapsed");
     }
     
     next();
   },
-  testShowSidebarMenuitemExists: function(next) {
-    let toggleSidebarMenuitem = document.getElementById("social-toggle-sidebar-menuitem");
-    ok(toggleSidebarMenuitem, "Toggle Sidebar menuitem exists");
-    let toggleSidebarKeyboardMenuitem = document.getElementById("social-toggle-sidebar-keyboardmenuitem");
-    ok(toggleSidebarKeyboardMenuitem, "Toggle Sidebar keyboard menuitem exists");
-    next();
-  },
-  testShowDesktopNotificationsMenuitemExists: function(next) {
-    let toggleDesktopNotificationsMenuitem = document.getElementById("social-toggle-notifications-menuitem");
-    ok(toggleDesktopNotificationsMenuitem, "Toggle notifications menuitem exists");
-    let toggleDesktopNotificationsKeyboardMenuitem = document.getElementById("social-toggle-notifications-keyboardmenuitem");
-    ok(toggleDesktopNotificationsKeyboardMenuitem, "Toggle notifications keyboard menuitem exists");
+  testMenuitemsExist: function(next) {
+    let toggleSidebarMenuitems = document.getElementsByClassName("social-toggle-sidebar-menuitem");
+    is(toggleSidebarMenuitems.length, 2, "Toggle Sidebar menuitems exist");
+    let toggleDesktopNotificationsMenuitems = document.getElementsByClassName("social-toggle-notifications-menuitem");
+    is(toggleDesktopNotificationsMenuitems.length, 2, "Toggle notifications menuitems exist");
+    let toggleSocialMenuitems = document.getElementsByClassName("social-toggle-menuitem");
+    is(toggleSocialMenuitems.length, 2, "Toggle Social menuitems exist");
+    let removeSocialMenuitems = document.getElementsByClassName("social-remove-menuitem");
+    is(removeSocialMenuitems.length, 2, "Remove Social menuitems exist");
     next();
   }
 }
 
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -19,18 +19,26 @@
              align="center"
              onclick="DownloadsView.onDownloadClick(event);">
       <xul:image class="downloadTypeIcon"
                  validate="always"
                  xbl:inherits="src=image"/>
       <xul:image class="downloadTypeIcon blockedIcon"/>
       <xul:vbox pack="center"
                 flex="1">
+        <!-- We're letting localizers put a min-width in here primarily
+             because of the downloads summary at the bottom of the list of
+             download items. An element in the summary has the same min-width
+             on a description, and we don't want the panel to change size if the
+             summary isn't being displayed, so we ensure that items share the
+             same minimum width.
+             -->
         <xul:description class="downloadTarget"
                          crop="center"
+                         style="min-width: &downloadsSummary.minWidth;"
                          xbl:inherits="value=target,tooltiptext=target"/>
         <xul:progressmeter anonid="progressmeter"
                            class="downloadProgress"
                            min="0"
                            max="100"
                            xbl:inherits="mode=progressmode,value=progress"/>
         <xul:description class="downloadDetails"
                          style="width: &downloadDetails.width;"
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -84,8 +84,13 @@ richlistitem[type="download"]:not([selec
 #downloads-indicator:not(:-moz-any([progress],
                                    [counter],
                                    [paused]))
                                            #downloads-indicator-progress-area
 
 {
   visibility: hidden;
 }
+
+#downloadsSummary:not([inprogress="true"]) #downloadsSummaryProgress,
+#downloadsSummary:not([inprogress="true"]) #downloadsSummaryDetails {
+  display: none;
+}
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -535,21 +535,20 @@ const DownloadsView = {
     let hiddenCount = count - this.kItemCountLimit;
 
     if (count > 0) {
       DownloadsPanel.panel.setAttribute("hasdownloads", "true");
     } else {
       DownloadsPanel.panel.removeAttribute("hasdownloads");
     }
 
-    let s = DownloadsCommon.strings;
-    this.downloadsHistory.label = (hiddenCount > 0)
-                                  ? s.showMoreDownloads(hiddenCount)
-                                  : s.showAllDownloads;
-    this.downloadsHistory.accessKey = s.showDownloadsAccessKey;
+    // If we've got some hidden downloads, we should show the summary just
+    // below the list.
+    this.downloadsHistory.collapsed = hiddenCount > 0;
+    DownloadsSummary.visible = this.downloadsHistory.collapsed;
   },
 
   /**
    * Element corresponding to the list of downloads.
    */
   get richListBox()
   {
     delete this.richListBox;
@@ -1404,8 +1403,160 @@ DownloadsViewItemController.prototype = 
    */
   _openExternal: function DVIC_openExternal(aFile)
   {
     let protocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
                       .getService(Ci.nsIExternalProtocolService);
     protocolSvc.loadUrl(makeFileURI(aFile));
   }
 };
+
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsSummary
+
+/**
+ * Manages the summary at the bottom of the downloads panel list if the number
+ * of items in the list exceeds the panels limit.
+ */
+const DownloadsSummary = {
+
+  /**
+   * Sets the collapsed state of the summary, and automatically subscribes or
+   * unsubscribes from the DownloadsCommon DownloadsSummaryData singleton.
+   *
+   * @param aVisible
+   *        True if the summary should be shown.
+   */
+  set visible(aVisible)
+  {
+    if (aVisible == this._visible || !this._summaryNode) {
+      return;
+    }
+    if (aVisible) {
+      DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
+                     .addView(this);
+    } else {
+      DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
+                     .removeView(this);
+    }
+    this._summaryNode.collapsed = !aVisible;
+    return this._visible = aVisible;
+  },
+  _visible: false,
+
+  /**
+   * Sets whether or not we show the progress bar.
+   *
+   * @param aShowingProgress
+   *        True if we should show the progress bar.
+   */
+  set showingProgress(aShowingProgress)
+  {
+    if (aShowingProgress) {
+      this._summaryNode.setAttribute("inprogress", "true");
+    } else {
+      this._summaryNode.removeAttribute("inprogress");
+    }
+  },
+
+  /**
+   * Sets the amount of progress that is visible in the progress bar.
+   *
+   * @param aValue
+   *        A value between 0 and 100 to represent the progress of the
+   *        summarized downloads.
+   */
+  set percentComplete(aValue)
+  {
+    if (this._progressNode) {
+      this._progressNode.setAttribute("value", aValue);
+    }
+    return aValue;
+  },
+
+  /**
+   * Sets the description for the download summary.
+   *
+   * @param aValue
+   *        A string representing the description of the summarized
+   *        downloads.
+   */
+  set description(aValue)
+  {
+    if (this._descriptionNode) {
+      this._descriptionNode.setAttribute("value", aValue);
+      this._descriptionNode.setAttribute("tooltiptext", aValue);
+    }
+    return aValue;
+  },
+
+  /**
+   * Sets the details for the download summary, such as the time remaining,
+   * the amount of bytes transferred, etc.
+   *
+   * @param aValue
+   *        A string representing the details of the summarized
+   *        downloads.
+   */
+  set details(aValue)
+  {
+    if (this._detailsNode) {
+      this._detailsNode.setAttribute("value", aValue);
+      this._detailsNode.setAttribute("tooltiptext", aValue);
+    }
+    return aValue;
+  },
+
+  /**
+   * Element corresponding to the root of the downloads summary.
+   */
+  get _summaryNode()
+  {
+    let node = document.getElementById("downloadsSummary");
+    if (!node) {
+      return null;
+    }
+    delete this._summaryNode;
+    return this._summaryNode = node;
+  },
+
+  /**
+   * Element corresponding to the progress bar in the downloads summary.
+   */
+  get _progressNode()
+  {
+    let node = document.getElementById("downloadsSummaryProgress");
+    if (!node) {
+      return null;
+    }
+    delete this._progressNode;
+    return this._progressNode = node;
+  },
+
+  /**
+   * Element corresponding to the main description of the downloads
+   * summary.
+   */
+  get _descriptionNode()
+  {
+    let node = document.getElementById("downloadsSummaryDescription");
+    if (!node) {
+      return null;
+    }
+    delete this._descriptionNode;
+    return this._descriptionNode = node;
+  },
+
+  /**
+   * Element corresponding to the secondary description of the downloads
+   * summary.
+   */
+  get _detailsNode()
+  {
+    let node = document.getElementById("downloadsSummaryDetails");
+    if (!node) {
+      return null;
+    }
+    delete this._detailsNode;
+    return this._detailsNode = node;
+  }
+}
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -100,14 +100,38 @@
       <richlistbox id="downloadsListBox"
                    class="plain"
                    flex="1"
                    context="downloadsContextMenu"
                    onkeypress="DownloadsView.onDownloadKeyPress(event);"
                    oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
                    ondragstart="DownloadsView.onDownloadDragStart(event);"/>
 
+      <hbox id="downloadsSummary"
+            collapsed="true"
+            align="center"
+            orient="horizontal"
+            onclick="DownloadsPanel.showDownloadsHistory();">
+        <image class="downloadTypeIcon" />
+        <vbox>
+          <description id="downloadsSummaryDescription"
+                       class="downloadTarget"
+                       style="min-width: &downloadsSummary.minWidth;"/>
+          <progressmeter id="downloadsSummaryProgress"
+                         class="downloadProgress"
+                         min="0"
+                         max="100"
+                         mode="normal" />
+          <description id="downloadsSummaryDetails"
+                       class="downloadDetails"
+                       style="width: &downloadDetails.width;"
+                       crop="end"/>
+        </vbox>
+      </hbox>
+
       <button id="downloadsHistory"
               class="plain"
+              label="&downloadsHistory.label;"
+              accesskey="&downloadsHistory.accesskey;"
               oncommand="DownloadsPanel.showDownloadsHistory();"/>
     </panel>
   </popupset>
 </overlay>
--- a/browser/components/downloads/src/DownloadsCommon.jsm
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -49,16 +49,18 @@ Cu.import("resource://gre/modules/Servic
 
 XPCOMUtils.defineLazyServiceGetter(this, "gBrowserGlue",
                                    "@mozilla.org/browser/browserglue;1",
                                    "nsIBrowserGlue");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
+                                  "resource://gre/modules/DownloadUtils.jsm");
 
 const nsIDM = Ci.nsIDownloadManager;
 
 const kDownloadsStringBundleUrl =
   "chrome://browser/locale/downloads/downloads.properties";
 
 const kDownloadsStringsRequiringFormatting = {
   sizeWithUnits: true,
@@ -67,17 +69,17 @@ const kDownloadsStringsRequiringFormatti
   shortTimeLeftHours: true,
   shortTimeLeftDays: true,
   statusSeparator: true,
   statusSeparatorBeforeNumber: true,
   fileExecutableSecurityWarning: true
 };
 
 const kDownloadsStringsRequiringPluralForm = {
-  showMoreDownloads: true
+  otherDownloads: true
 };
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsLocalFileCtor", function () {
   return Components.Constructor("@mozilla.org/file/local;1",
                                 "nsILocalFile", "initWithPath");
 });
 
 const kPartialDownloadSuffix = ".part";
@@ -179,17 +181,155 @@ this.DownloadsCommon = {
   get data() DownloadsData,
 
   /**
    * Returns a reference to the DownloadsData singleton.
    *
    * This does not need to be a lazy getter, since no initialization is required
    * at present.
    */
-  get indicatorData() DownloadsIndicatorData
+  get indicatorData() DownloadsIndicatorData,
+
+  /**
+   * Returns a reference to the DownloadsSummaryData singleton - creating one
+   * in the process if one hasn't been instantiated yet.
+   *
+   * @param aNumToExclude
+   *        The number of items on the top of the downloads list to exclude
+   *        from the summary.
+   */
+  _summary: null,
+  getSummary: function DC_getSummary(aNumToExclude)
+  {
+    if (this._summary) {
+      return this._summary;
+    }
+    return this._summary = new DownloadsSummaryData(aNumToExclude);
+  },
+
+  /**
+   * Given an iterable collection of nsIDownload's, generates and returns
+   * statistics about that collection.
+   *
+   * @param aDownloads An iterable collection of nsIDownloads.
+   *
+   * @return Object whose properties are the generated statistics. Currently,
+   *         we return the following properties:
+   *
+   *         numActive       : The total number of downloads.
+   *         numPaused       : The total number of paused downloads.
+   *         numScanning     : The total number of downloads being scanned.
+   *         numDownloading  : The total number of downloads being downloaded.
+   *         totalSize       : The total size of all downloads once completed.
+   *         totalTransferred: The total amount of transferred data for these
+   *                           downloads.
+   *         slowestSpeed    : The slowest download rate.
+   *         rawTimeLeft     : The estimated time left for the downloads to
+   *                           complete.
+   *         percentComplete : The percentage of bytes successfully downloaded.
+   */
+  summarizeDownloads: function DC_summarizeDownloads(aDownloads)
+  {
+    let summary = {
+      numActive: 0,
+      numPaused: 0,
+      numScanning: 0,
+      numDownloading: 0,
+      totalSize: 0,
+      totalTransferred: 0,
+      // slowestSpeed is Infinity so that we can use Math.min to
+      // find the slowest speed. We'll set this to 0 afterwards if
+      // it's still at Infinity by the time we're done iterating all
+      // downloads.
+      slowestSpeed: Infinity,
+      rawTimeLeft: -1,
+      percentComplete: -1
+    }
+
+    // If no download has been loaded, don't use the methods of the Download
+    // Manager service, so that it is not initialized unnecessarily.
+    for (let download of aDownloads) {
+      summary.numActive++;
+      switch (download.state) {
+        case nsIDM.DOWNLOAD_PAUSED:
+          summary.numPaused++;
+          break;
+        case nsIDM.DOWNLOAD_SCANNING:
+          summary.numScanning++;
+          break;
+        case nsIDM.DOWNLOAD_DOWNLOADING:
+          summary.numDownloading++;
+          if (download.size > 0 && download.speed > 0) {
+            let sizeLeft = download.size - download.amountTransferred;
+            summary.rawTimeLeft = Math.max(summary.rawTimeLeft,
+                                           sizeLeft / download.speed);
+            summary.slowestSpeed = Math.min(summary.slowestSpeed,
+                                            download.speed);
+          }
+          break;
+      }
+      // Only add to total values if we actually know the download size.
+      if (download.size > 0 &&
+          download.state != nsIDM.DOWNLOAD_CANCELED &&
+          download.state != nsIDM.DOWNLOAD_FAILED) {
+        summary.totalSize += download.size;
+        summary.totalTransferred += download.amountTransferred;
+      }
+    }
+
+    if (summary.numActive != 0 && summary.totalSize != 0 &&
+        summary.numActive != summary.numScanning) {
+      summary.percentComplete = (summary.totalTransferred /
+                                 summary.totalSize) * 100;
+    }
+
+    if (summary.slowestSpeed == Infinity) {
+      summary.slowestSpeed = 0;
+    }
+
+    return summary;
+  },
+
+  /**
+   * If necessary, smooths the estimated number of seconds remaining for one
+   * or more downloads to complete.
+   *
+   * @param aSeconds
+   *        Current raw estimate on number of seconds left for one or more
+   *        downloads. This is a floating point value to help get sub-second
+   *        accuracy for current and future estimates.
+   */
+  smoothSeconds: function DC_smoothSeconds(aSeconds, aLastSeconds)
+  {
+    // We apply an algorithm similar to the DownloadUtils.getTimeLeft function,
+    // though tailored to a single time estimation for all downloads.  We never
+    // apply sommothing if the new value is less than half the previous value.
+    let shouldApplySmoothing = aLastSeconds >= 0 &&
+                               aSeconds > aLastSeconds / 2;
+    if (shouldApplySmoothing) {
+      // Apply hysteresis to favor downward over upward swings.  Trust only 30%
+      // of the new value if lower, and 10% if higher (exponential smoothing).
+      let (diff = aSeconds - aLastSeconds) {
+        aSeconds = aLastSeconds + (diff < 0 ? .3 : .1) * diff;
+      }
+
+      // If the new time is similar, reuse something close to the last time
+      // left, but subtract a little to provide forward progress.
+      let diff = aSeconds - aLastSeconds;
+      let diffPercent = diff / aLastSeconds * 100;
+      if (Math.abs(diff) < 5 || Math.abs(diffPercent) < 5) {
+        aSeconds = aLastSeconds - (diff < 0 ? .4 : .2);
+      }
+    }
+
+    // In the last few seconds of downloading, we are always subtracting and
+    // never adding to the time left.  Ensure that we never fall below one
+    // second left until all downloads are actually finished.
+    return aLastSeconds = Math.max(aSeconds, 1);
+  }
 };
 
 /**
  * Returns true if we are executing on Windows Vista or a later version.
  */
 XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
   let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
   if (os != "WINNT") {
@@ -608,16 +748,17 @@ const DownloadsData = {
     }
 
     dataItem.state = aDownload.state;
     dataItem.referrer = aDownload.referrer && aDownload.referrer.spec;
     dataItem.resumable = aDownload.resumable;
     dataItem.startTime = Math.round(aDownload.startTime / 1000);
     dataItem.currBytes = aDownload.amountTransferred;
     dataItem.maxBytes = aDownload.size;
+    dataItem.download = aDownload;
 
     this._views.forEach(
       function (view) view.getViewItem(dataItem).onStateChange()
     );
 
     if (isNew && !dataItem.newDownloadNotified) {
       dataItem.newDownloadNotified = true;
       this._notifyNewDownload();
@@ -914,112 +1055,231 @@ DownloadsDataItem.prototype = {
       // The downloads database contains a native path.  Try to create a local
       // file, though this may throw an exception if the path is invalid.
       return new DownloadsLocalFileCtor(aFilename);
     }
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
-//// DownloadsIndicatorData
+//// DownloadsViewPrototype
 
 /**
- * This object registers itself with DownloadsData as a view, and transforms the
- * notifications it receives into overall status data, that is then broadcast to
- * the registered download status indicators.
- *
- * Note that using this object does not automatically start the Download Manager
- * service.  Consumers will see an empty list of downloads until the service is
- * actually started.  This is useful to display a neutral progress indicator in
- * the main browser window until the autostart timeout elapses.
+ * A prototype for an object that registers itself with DownloadsData as soon
+ * as a view is registered with it.
  */
-const DownloadsIndicatorData = {
+const DownloadsViewPrototype = {
   //////////////////////////////////////////////////////////////////////////////
   //// Registration of views
 
   /**
    * Array of view objects that should be notified when the available status
    * data changes.
    */
   _views: [],
 
   /**
    * Adds an object to be notified when the available status data changes.
    * The specified object is initialized with the currently available status.
    *
    * @param aView
-   *        DownloadsIndicatorView object to be added.  This reference must be
+   *        View object to be added.  This reference must be
    *        passed to removeView before termination.
    */
-  addView: function DID_addView(aView)
+  addView: function DVP_addView(aView)
   {
     // Start receiving events when the first of our views is registered.
     if (this._views.length == 0) {
       DownloadsCommon.data.addView(this);
     }
 
     this._views.push(aView);
     this.refreshView(aView);
   },
 
   /**
    * Updates the properties of an object previously added using addView.
    *
    * @param aView
-   *        DownloadsIndicatorView object to be updated.
+   *        View object to be updated.
    */
-  refreshView: function DID_refreshView(aView)
+  refreshView: function DVP_refreshView(aView)
   {
     // Update immediately even if we are still loading data asynchronously.
+    // Subclasses must provide these two functions!
     this._refreshProperties();
     this._updateView(aView);
   },
 
   /**
    * Removes an object previously added using addView.
    *
    * @param aView
-   *        DownloadsIndicatorView object to be removed.
+   *        View object to be removed.
    */
-  removeView: function DID_removeView(aView)
+  removeView: function DVP_removeView(aView)
   {
     let index = this._views.indexOf(aView);
     if (index != -1) {
       this._views.splice(index, 1);
     }
 
     // Stop receiving events when the last of our views is unregistered.
     if (this._views.length == 0) {
       DownloadsCommon.data.removeView(this);
-      this._itemCount = 0;
     }
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Callback functions from DownloadsData
 
   /**
    * Indicates whether we are still loading downloads data asynchronously.
    */
   _loading: false,
 
   /**
    * Called before multiple downloads are about to be loaded.
    */
-  onDataLoadStarting: function DID_onDataLoadStarting()
+  onDataLoadStarting: function DVP_onDataLoadStarting()
   {
     this._loading = true;
   },
 
   /**
    * Called after data loading finished.
    */
+  onDataLoadCompleted: function DVP_onDataLoadCompleted()
+  {
+    this._loading = false;
+  },
+
+  /**
+   * Called when the downloads database becomes unavailable (for example, we
+   * entered Private Browsing Mode and the database backend changed).
+   * References to existing data should be discarded.
+   *
+   * @note Subclasses should override this.
+   */
+  onDataInvalidated: function DVP_onDataInvalidated()
+  {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  /**
+   * Called when a new download data item is available, either during the
+   * asynchronous data load or when a new download is started.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object that was just added.
+   * @param aNewest
+   *        When true, indicates that this item is the most recent and should be
+   *        added in the topmost position.  This happens when a new download is
+   *        started.  When false, indicates that the item is the least recent
+   *        with regard to the items that have been already added. The latter
+   *        generally happens during the asynchronous data load.
+   *
+   * @note Subclasses should override this.
+   */
+  onDataItemAdded: function DVP_onDataItemAdded(aDataItem, aNewest)
+  {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  /**
+   * Called when a data item is removed, ensures that the widget associated with
+   * the view item is removed from the user interface.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object that is being removed.
+   *
+   * @note Subclasses should override this.
+   */
+  onDataItemRemoved: function DVP_onDataItemRemoved(aDataItem)
+  {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  /**
+   * Returns the view item associated with the provided data item for this view.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object for which the view item is requested.
+   *
+   * @return Object that can be used to notify item status events.
+   *
+   * @note Subclasses should override this.
+   */
+  getViewItem: function DID_getViewItem(aDataItem)
+  {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  /**
+   * Private function used to refresh the internal properties being sent to
+   * each registered view.
+   *
+   * @note Subclasses should override this.
+   */
+  _refreshProperties: function DID_refreshProperties()
+  {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  /**
+   * Private function used to refresh an individual view.
+   *
+   * @note Subclasses should override this.
+   */
+  _updateView: function DID_updateView()
+  {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsIndicatorData
+
+/**
+ * This object registers itself with DownloadsData as a view, and transforms the
+ * notifications it receives into overall status data, that is then broadcast to
+ * the registered download status indicators.
+ *
+ * Note that using this object does not automatically start the Download Manager
+ * service.  Consumers will see an empty list of downloads until the service is
+ * actually started.  This is useful to display a neutral progress indicator in
+ * the main browser window until the autostart timeout elapses.
+ */
+const DownloadsIndicatorData = {
+  __proto__: DownloadsViewPrototype,
+
+  /**
+   * Removes an object previously added using addView.
+   *
+   * @param aView
+   *        DownloadsIndicatorView object to be removed.
+   */
+  removeView: function DID_removeView(aView)
+  {
+    DownloadsViewPrototype.removeView.call(this, aView);
+
+    if (this._views.length == 0) {
+      this._itemCount = 0;
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Callback functions from DownloadsData
+
+  /**
+   * Called after data loading finished.
+   */
   onDataLoadCompleted: function DID_onDataLoadCompleted()
   {
-    this._loading = false;
+    DownloadsViewPrototype.onDataLoadCompleted.call(this);
     this._updateViews();
   },
 
   /**
    * Called when the downloads database becomes unavailable (for example, we
    * entered Private Browsing Mode and the database backend changed).
    * References to existing data should be discarded.
    */
@@ -1174,115 +1434,263 @@ const DownloadsIndicatorData = {
    * Last number of seconds estimated until all in-progress downloads with a
    * known size and speed will finish.  This value is stored to allow smoothing
    * in case of small variations.  This is set to -1 if the previous value is
    * unknown.
    */
   _lastTimeLeft: -1,
 
   /**
-   * Update the estimated time until all in-progress downloads will finish.
-   *
-   * @param aSeconds
-   *        Current raw estimate on number of seconds left for all downloads.
-   *        This is a floating point value to help get sub-second accuracy for
-   *        current and future estimates.
+   * A generator function for the downloads that this summary is currently
+   * interested in. This generator is passed off to summarizeDownloads in order
+   * to generate statistics about the downloads we care about - in this case,
+   * it's all active downloads.
    */
-  _updateTimeLeft: function DID_updateTimeLeft(aSeconds)
+  _activeDownloads: function DID_activeDownloads()
   {
-    // We apply an algorithm similar to the DownloadUtils.getTimeLeft function,
-    // though tailored to a single time estimation for all downloads.  We never
-    // apply sommothing if the new value is less than half the previous value.
-    let shouldApplySmoothing = this._lastTimeLeft >= 0 &&
-                               aSeconds > this._lastTimeLeft / 2;
-    if (shouldApplySmoothing) {
-      // Apply hysteresis to favor downward over upward swings.  Trust only 30%
-      // of the new value if lower, and 10% if higher (exponential smoothing).
-      let (diff = aSeconds - this._lastTimeLeft) {
-        aSeconds = this._lastTimeLeft + (diff < 0 ? .3 : .1) * diff;
-      }
-
-      // If the new time is similar, reuse something close to the last time
-      // left, but subtract a little to provide forward progress.
-      let diff = aSeconds - this._lastTimeLeft;
-      let diffPercent = diff / this._lastTimeLeft * 100;
-      if (Math.abs(diff) < 5 || Math.abs(diffPercent) < 5) {
-        aSeconds = this._lastTimeLeft - (diff < 0 ? .4 : .2);
+    // If no download has been loaded, don't use the methods of the Download
+    // Manager service, so that it is not initialized unnecessarily.
+    if (this._itemCount > 0) {
+      let downloads = Services.downloads.activeDownloads;
+      while (downloads.hasMoreElements()) {
+        yield downloads.getNext().QueryInterface(Ci.nsIDownload);
       }
     }
-
-    // In the last few seconds of downloading, we are always subtracting and
-    // never adding to the time left.  Ensure that we never fall below one
-    // second left until all downloads are actually finished.
-    this._lastTimeLeft = Math.max(aSeconds, 1);
   },
 
   /**
    * Computes aggregate values based on the current state of downloads.
    */
   _refreshProperties: function DID_refreshProperties()
   {
-    let numActive = 0;
-    let numPaused = 0;
-    let numScanning = 0;
-    let totalSize = 0;
-    let totalTransferred = 0;
-    let rawTimeLeft = -1;
-
-    // If no download has been loaded, don't use the methods of the Download
-    // Manager service, so that it is not initialized unnecessarily.
-    if (this._itemCount > 0) {
-      let downloads = Services.downloads.activeDownloads;
-      while (downloads.hasMoreElements()) {
-        let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
-        numActive++;
-        switch (download.state) {
-          case nsIDM.DOWNLOAD_PAUSED:
-            numPaused++;
-            break;
-          case nsIDM.DOWNLOAD_SCANNING:
-            numScanning++;
-            break;
-          case nsIDM.DOWNLOAD_DOWNLOADING:
-            if (download.size > 0 && download.speed > 0) {
-              let sizeLeft = download.size - download.amountTransferred;
-              rawTimeLeft = Math.max(rawTimeLeft, sizeLeft / download.speed);
-            }
-            break;
-        }
-        // Only add to total values if we actually know the download size.
-        if (download.size > 0) {
-          totalSize += download.size;
-          totalTransferred += download.amountTransferred;
-        }
-      }
-    }
+    let summary =
+      DownloadsCommon.summarizeDownloads(this._activeDownloads());
 
     // Determine if the indicator should be shown or get attention.
     this._hasDownloads = (this._itemCount > 0);
 
-    if (numActive == 0 || totalSize == 0 || numActive == numScanning) {
-      // Don't display the current progress.
-      this._percentComplete = -1;
-    } else {
-      // Display the current progress.
-      this._percentComplete = (totalTransferred / totalSize) * 100;
-    }
+    // If all downloads are paused, show the progress indicator as paused.
+    this._paused = summary.numActive > 0 &&
+                   summary.numActive == summary.numPaused;
 
-    // If all downloads are paused, show the progress indicator as paused.
-    this._paused = numActive > 0 && numActive == numPaused;
+    this._percentComplete = summary.percentComplete;
 
     // Display the estimated time left, if present.
-    if (rawTimeLeft == -1) {
+    if (summary.rawTimeLeft == -1) {
       // There are no downloads with a known time left.
       this._lastRawTimeLeft = -1;
       this._lastTimeLeft = -1;
       this._counter = "";
     } else {
       // Compute the new time left only if state actually changed.
-      if (this._lastRawTimeLeft != rawTimeLeft) {
-        this._lastRawTimeLeft = rawTimeLeft;
-        this._updateTimeLeft(rawTimeLeft);
+      if (this._lastRawTimeLeft != summary.rawTimeLeft) {
+        this._lastRawTimeLeft = summary.rawTimeLeft;
+        this._lastTimeLeft = DownloadsCommon.smoothSeconds(summary.rawTimeLeft,
+                                                           this._lastTimeLeft);
       }
       this._counter = DownloadsCommon.formatTimeLeft(this._lastTimeLeft);
     }
   }
 }
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsSummaryData
+
+/**
+ * DownloadsSummaryData is a view for DownloadsData that produces a summary
+ * of all downloads after a certain exclusion point aNumToExclude. For example,
+ * if there were 5 downloads in progress, and a DownloadsSummaryData was
+ * constructed with aNumToExclude equal to 3, then that DownloadsSummaryData
+ * would produce a summary of the last 2 downloads.
+ *
+ * @param aNumToExclude
+ *        The number of items to exclude from the summary, starting from the
+ *        top of the list.
+ */
+function DownloadsSummaryData(aNumToExclude) {
+  this._numToExclude = aNumToExclude;
+  // Since we can have multiple instances of DownloadsSummaryData, we
+  // override these values from the prototype so that each instance can be
+  // completely separated from one another.
+  this._views = [];
+  this._loading = false;
+
+  this._dataItems = [];
+
+  // Floating point value indicating the last number of seconds estimated until
+  // the longest download will finish.  We need to store this value so that we
+  // don't continuously apply smoothing if the actual download state has not
+  // changed.  This is set to -1 if the previous value is unknown.
+  this._lastRawTimeLeft = -1;
+
+  // Last number of seconds estimated until all in-progress downloads with a
+  // known size and speed will finish.  This value is stored to allow smoothing
+  // in case of small variations.  This is set to -1 if the previous value is
+  // unknown.
+  this._lastTimeLeft = -1;
+
+  // The following properties are updated by _refreshProperties and are then
+  // propagated to the views.
+  this._showingProgress = false;
+  this._details = "";
+  this._description = "";
+  this._numActive = 0;
+  this._percentComplete = -1;
+}
+
+DownloadsSummaryData.prototype = {
+  __proto__: DownloadsViewPrototype,
+
+  /**
+   * Removes an object previously added using addView.
+   *
+   * @param aView
+   *        DownloadsSummary view to be removed.
+   */
+  removeView: function DSD_removeView(aView)
+  {
+    DownloadsViewPrototype.removeView.call(this, aView);
+
+    if (this._views.length == 0) {
+      // Clear out our collection of DownloadsDataItems. If we ever have
+      // another view registered with us, this will get re-populated.
+      this._dataItems = [];
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Callback functions from DownloadsData - see the documentation in
+  //// DownloadsViewPrototype for more information on what these functions
+  //// are used for.
+
+  onDataLoadCompleted: function DSD_onDataLoadCompleted()
+  {
+    DownloadsViewPrototype.onDataLoadCompleted.call(this);
+    this._updateViews();
+  },
+
+  onDataInvalidated: function DSD_onDataInvalidated()
+  {
+    this._dataItems = [];
+  },
+
+  onDataItemAdded: function DSD_onDataItemAdded(aDataItem, aNewest)
+  {
+    if (aNewest) {
+      this._dataItems.unshift(aDataItem);
+    } else {
+      this._dataItems.push(aDataItem);
+    }
+
+    this._updateViews();
+  },
+
+  onDataItemRemoved: function DSD_onDataItemRemoved(aDataItem)
+  {
+    let itemIndex = this._dataItems.indexOf(aDataItem);
+    this._dataItems.splice(itemIndex, 1);
+    this._updateViews();
+  },
+
+  getViewItem: function DSD_getViewItem(aDataItem)
+  {
+    let self = this;
+    return Object.freeze({
+      onStateChange: function DIVI_onStateChange()
+      {
+        // Since the state of a download changed, reset the estimated time left.
+        self._lastRawTimeLeft = -1;
+        self._lastTimeLeft = -1;
+        self._updateViews();
+      },
+      onProgressChange: function DIVI_onProgressChange()
+      {
+        self._updateViews();
+      }
+    });
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Propagation of properties to our views
+
+  /**
+   * Computes aggregate values and propagates the changes to our views.
+   */
+  _updateViews: function DSD_updateViews()
+  {
+    // Do not update the status indicators during batch loads of download items.
+    if (this._loading) {
+      return;
+    }
+
+    this._refreshProperties();
+    this._views.forEach(this._updateView, this);
+  },
+
+  /**
+   * Updates the specified view with the current aggregate values.
+   *
+   * @param aView
+   *        DownloadsIndicatorView object to be updated.
+   */
+  _updateView: function DSD_updateView(aView)
+  {
+    aView.showingProgress = this._showingProgress;
+    aView.percentComplete = this._percentComplete;
+    aView.description = this._description;
+    aView.details = this._details;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Property updating based on current download status
+
+  /**
+   * A generator function for the downloads that this summary is currently
+   * interested in. This generator is passed off to summarizeDownloads in order
+   * to generate statistics about the downloads we care about - in this case,
+   * it's the downloads in this._dataItems after the first few to exclude,
+   * which was set when constructing this DownloadsSummaryData instance.
+   */
+  _downloadsForSummary: function DSD_downloadsForSummary()
+  {
+    if (this._dataItems.length > 0) {
+      for (let i = this._numToExclude; i < this._dataItems.length; ++i) {
+        yield this._dataItems[i].download;
+      }
+    }
+  },
+
+  /**
+   * Computes aggregate values based on the current state of downloads.
+   */
+  _refreshProperties: function DSD_refreshProperties()
+  {
+    // Pre-load summary with default values.
+    let summary =
+      DownloadsCommon.summarizeDownloads(this._downloadsForSummary());
+
+    this._description = DownloadsCommon.strings
+                                       .otherDownloads(summary.numActive);
+    this._percentComplete = summary.percentComplete;
+
+    // If all downloads are paused, show the progress indicator as paused.
+    this._showingProgress = summary.numDownloading > 0 ||
+                            summary.numPaused > 0;
+
+    // Display the estimated time left, if present.
+    if (summary.rawTimeLeft == -1) {
+      // There are no downloads with a known time left.
+      this._lastRawTimeLeft = -1;
+      this._lastTimeLeft = -1;
+      this._details = "";
+    } else {
+      // Compute the new time left only if state actually changed.
+      if (this._lastRawTimeLeft != summary.rawTimeLeft) {
+        this._lastRawTimeLeft = summary.rawTimeLeft;
+        this._lastTimeLeft = DownloadsCommon.smoothSeconds(summary.rawTimeLeft,
+                                                           this._lastTimeLeft);
+      }
+      [this._details] = DownloadUtils.getDownloadStatusNoRate(
+        summary.totalTransferred, summary.totalSize, summary.slowestSpeed,
+        this._lastTimeLeft);
+    }
+  }
+}
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -334,16 +334,19 @@ PrivateBrowsingService.prototype = {
     return !cancelEnter.data;
   },
 
   _canLeavePrivateBrowsingMode: function PBS__canLeavePrivateBrowsingMode() {
     let cancelLeave = Cc["@mozilla.org/supports-PRBool;1"].
                       createInstance(Ci.nsISupportsPRBool);
     cancelLeave.data = false;
     this._obs.notifyObservers(cancelLeave, "private-browsing-cancel-vote", "exit");
+    if (!cancelLeave.data) {
+      this._obs.notifyObservers(cancelLeave, "last-pb-context-exiting", null);
+    }
     return !cancelLeave.data;
   },
 
   _getBrowserWindow: function PBS__getBrowserWindow() {
     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
              getService(Ci.nsIWindowMediator);
 
     var win = wm.getMostRecentWindow("navigator:browser");
--- a/browser/components/privatebrowsing/test/browser/obsolete/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/obsolete/Makefile.in
@@ -18,17 +18,17 @@ MOCHITEST_BROWSER_FILES =  \
 		browser_privatebrowsing_findbar.js \
 		browser_privatebrowsing_forgetthissite.js \
 		browser_privatebrowsing_import.js \
 		browser_privatebrowsing_newwindow_stopcmd.js \
 		browser_privatebrowsing_pageinfo.js \
 		browser_privatebrowsing_popupmode.js \
 		browser_privatebrowsing_searchbar.js \
 		browser_privatebrowsing_sslsite_transition.js \
-		browser_privatebrowsing_transition.js \
+		$(warning browser_privatebrowsing_transition.js disabled since it no longer makes sense) \
 		browser_privatebrowsing_urlbarundo.js \
 		browser_privatebrowsing_viewsource.js \
 		staller.sjs \
 		$(NULL)
 
 # Disabled until bug 564934 is fixed:
 #		browser_privatebrowsing_downloadmonitor.js \
 
--- a/browser/components/privatebrowsing/test/browser/perwindow/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/perwindow/Makefile.in
@@ -26,15 +26,19 @@ MOCHITEST_BROWSER_FILES =  \
 		browser_privatebrowsing_lastpbcontextexited.js \
 		browser_privatebrowsing_localStorage.js \
 		browser_privatebrowsing_localStorage_page1.html \
 		browser_privatebrowsing_localStorage_page2.html \
 		browser_privatebrowsing_opendir.js \
 		browser_privatebrowsing_openlocation.js \
 		browser_privatebrowsing_openLocationLastURL.js \
 		browser_privatebrowsing_popupblocker.js \
+		browser_privatebrowsing_protocolhandler.js \
+		browser_privatebrowsing_protocolhandler_page.html \
 		browser_privatebrowsing_theming.js \
 		browser_privatebrowsing_urlbarfocus.js \
+		browser_privatebrowsing_windowtitle.js \
+		browser_privatebrowsing_windowtitle_page.html \
 		browser_privatebrowsing_zoomrestore.js \
 		popup.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_protocolhandler.js
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_protocolhandler.js
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_protocolhandler.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_protocolhandler.js
@@ -2,53 +2,64 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure that the web pages can't register protocol handlers
 // inside the private browsing mode.
 
 function test() {
   // initialization
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
+  waitForExplicitFinish();
+  let windowsToClose = [];
+  let notificationValue = "Protocol Registration: testprotocol";
+  let testURI = "http://example.com/browser/" +
+    "browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_protocolhandler_page.html";
 
-  const testPageURL = "http://example.com/browser/" +
-    "browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_protocolhandler_page.html";
-  waitForExplicitFinish();
-
-  const notificationValue = "Protocol Registration: testprotocol";
+  function doTest(aIsPrivateMode, aWindow, aCallback) {
+    aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+      aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function () {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+      setTimeout(function() {
+        let notificationBox = aWindow.gBrowser.getNotificationBox();
+        let notification = notificationBox.getNotificationWithValue(notificationValue);
 
-    setTimeout(function() {
-      // Make sure the notification is correctly displayed with a remember control
-      let notificationBox = gBrowser.getNotificationBox();
-      let notification = notificationBox.getNotificationWithValue(notificationValue);
-      ok(notification, "Notification box should be displaying outside of private browsing mode");
-      gBrowser.removeCurrentTab();
+        if (aIsPrivateMode) {
+          // Make sure the notification is correctly displayed without a remember control
+          ok(!notification, "Notification box should not be displayed inside of private browsing mode");
+        } else {
+          // Make sure the notification is correctly displayed with a remember control
+          ok(notification, "Notification box should be displaying outside of private browsing mode");
+        }
 
-      // enter the private browsing mode
-      pb.privateBrowsingEnabled = true;
+        aCallback();
+      }, 100); // remember control is added in a setTimeout(0) call
+    }, true);
 
-      gBrowser.selectedTab = gBrowser.addTab();
-      gBrowser.selectedBrowser.addEventListener("load", function () {
-        gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    aWindow.gBrowser.selectedBrowser.loadURI(testURI);
+  }
 
-        setTimeout(function () {
-          // Make sure the notification is correctly displayed without a remember control
-          let notificationBox = gBrowser.getNotificationBox();
-          let notification = notificationBox.getNotificationWithValue(notificationValue);
-          ok(!notification, "Notification box should not be displayed inside of private browsing mode");
+  function testOnWindow(aOptions, aCallback) {
+    whenNewWindowLoaded(aOptions, function(aWin) {
+      windowsToClose.push(aWin);
+      // execute should only be called when need, like when you are opening
+      // web pages on the test. If calling executeSoon() is not necesary, then
+      // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+      executeSoon(function() aCallback(aWin));
+    });
+  };
 
-          gBrowser.removeCurrentTab();
+   // this function is called after calling finish() on the test.
+  registerCleanupFunction(function() {
+    windowsToClose.forEach(function(aWin) {
+      aWin.close();
+    });
+  });
 
-          // cleanup
-          pb.privateBrowsingEnabled = false;
-          finish();
-        }, 100); // remember control is added in a setTimeout(0) call
-      }, true);
-      content.location = testPageURL;
-    }, 100); // remember control is added in a setTimeout(0) call
-  }, true);
-  content.location = testPageURL;
+  // test first when not on private mode
+  testOnWindow({}, function(aWin) {
+    doTest(false, aWin, function() {
+      // then test when on private mode
+      testOnWindow({private: true}, function(aWin) {
+        doTest(true, aWin, finish);
+      });
+    });
+  });
 }
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_protocolhandler_page.html
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_protocolhandler_page.html
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_windowtitle.js
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_windowtitle.js
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_windowtitle.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_windowtitle.js
@@ -1,23 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure that the window title changes correctly while switching
 // from and to private browsing mode.
 
 function test() {
-  // initialization
-  gPrefService.setBoolPref("browser.privatebrowsing.keep_current_session", true);
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-
   const testPageURL = "http://mochi.test:8888/browser/" +
-    "browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_windowtitle_page.html";
+    "browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_windowtitle_page.html";
   waitForExplicitFinish();
   requestLongerTimeout(2);
 
   // initialization of expected titles
   let test_title = "Test title";
   let app_name = document.documentElement.getAttribute("title");
   const isOSX = ("nsILocalFileMac" in Ci);
   let page_with_title;
@@ -38,60 +33,67 @@ function test() {
     page_with_title = test_title + " - " + app_name;
     page_without_title = app_name;
     about_pb_title = "Would you like to start Private Browsing?" + " - " + app_name;
     pb_page_with_title = test_title + " - " + app_name + " (Private Browsing)";
     pb_page_without_title = app_name + " (Private Browsing)";
     pb_about_pb_title = "Private Browsing - " + app_name + " (Private Browsing)";
   }
 
-  function testTabTitle(url, insidePB, expected_title, funcNext) {
-    pb.privateBrowsingEnabled = insidePB;
-
-    let tab = gBrowser.selectedTab = gBrowser.addTab();
-    let browser = gBrowser.selectedBrowser;
-    browser.stop();
-    // ensure that the test is run after the titlebar has been updated
-    browser.addEventListener("pageshow", function () {
-      browser.removeEventListener("pageshow", arguments.callee, false);
-      executeSoon(function () {
-        is(document.title, expected_title, "The window title for " + url +
-           " is correct (" + (insidePB ? "inside" : "outside") +
-           " private browsing mode)");
+  function testTabTitle(aWindow, url, insidePB, expected_title, funcNext) {
+    executeSoon(function () {
+      let tab = aWindow.gBrowser.selectedTab = aWindow.gBrowser.addTab();
+      let browser = aWindow.gBrowser.selectedBrowser;
+      browser.stop();
+      // ensure that the test is run after the titlebar has been updated
+      browser.addEventListener("pageshow", function () {
+        browser.removeEventListener("pageshow", arguments.callee, false);
+        executeSoon(function () {
+          is(aWindow.document.title, expected_title, "The window title for " + url +
+             " is correct (" + (insidePB ? "inside" : "outside") +
+             " private browsing mode)");
 
-        let win = gBrowser.replaceTabWithWindow(tab);
-        win.addEventListener("load", function() {
-          win.removeEventListener("load", arguments.callee, false);
+          let win = aWindow.gBrowser.replaceTabWithWindow(tab);
+          win.addEventListener("load", function() {
+            win.removeEventListener("load", arguments.callee, false);
 
-          executeSoon(function() {
-            is(win.document.title, expected_title, "The window title for " + url +
-               " detached tab is correct (" + (insidePB ? "inside" : "outside") +
-               " private browsing mode)");
-            win.close();
+            executeSoon(function() {
+              is(win.document.title, expected_title, "The window title for " + url +
+                 " detached tab is correct (" + (insidePB ? "inside" : "outside") +
+                 " private browsing mode)");
+              win.close();
+              aWindow.close();
 
-            setTimeout(funcNext, 0);
-          });
-        }, false);
-      });
-    }, false);
+              setTimeout(funcNext, 0);
+            });
+          }, false);
+        });
+      }, false);
 
-    browser.loadURI(url);
+      browser.loadURI(url);
+    });
   }
 
-  function cleanup() {
-    pb.privateBrowsingEnabled = false;
-    gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
-    finish();
-  }
-
-  testTabTitle("about:blank", false, page_without_title, function() {
-    testTabTitle(testPageURL, false, page_with_title, function() {
-      testTabTitle("about:privatebrowsing", false, about_pb_title, function() {
-        testTabTitle("about:blank", true, pb_page_without_title, function() {
-          testTabTitle(testPageURL, true, pb_page_with_title, function() {
-            testTabTitle("about:privatebrowsing", true, pb_about_pb_title, cleanup);
+  whenNewWindowLoaded({private: false}, function(win) {
+    testTabTitle(win, "about:blank", false, page_without_title, function() {
+      whenNewWindowLoaded({private: false}, function(win) {
+        testTabTitle(win, testPageURL, false, page_with_title, function() {
+          whenNewWindowLoaded({private: false}, function(win) {
+            testTabTitle(win, "about:privatebrowsing", false, about_pb_title, function() {
+              whenNewWindowLoaded({private: true}, function(win) {
+                testTabTitle(win, "about:blank", true, pb_page_without_title, function() {
+                  whenNewWindowLoaded({private: true}, function(win) {
+                    testTabTitle(win, testPageURL, true, pb_page_with_title, function() {
+                      whenNewWindowLoaded({private: true}, function(win) {
+                        testTabTitle(win, "about:privatebrowsing", true, pb_about_pb_title, finish);
+                      });
+                    });
+                  });
+                });
+              });
+            });
           });
         });
       });
     });
   });
   return;
 }
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_windowtitle_page.html
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_windowtitle_page.html
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -493,22 +493,25 @@
         // Speculatively connect to the current engine's search URI (and
         // suggest URI, if different) to reduce request latency
 
         const SUGGEST_TYPE = "application/x-suggestions+json";
         var engine = this.currentEngine;
 	var connector =
             Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect);
         var searchURI = engine.getSubmission("dummy").uri;
-        connector.speculativeConnect(searchURI, null);
+        let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                              .getInterface(Components.interfaces.nsIWebNavigation)
+                              .QueryInterface(Components.interfaces.nsILoadContext);
+        connector.speculativeConnect(searchURI, callbacks);
 
         if (engine.supportsResponseType(SUGGEST_TYPE)) {
 	  var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE).uri;
           if (suggestURI.prePath != searchURI.prePath)
-            connector.speculativeConnect(suggestURI, null);
+            connector.speculativeConnect(suggestURI, callbacks);
         }
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="searchbar-textbox"
       extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
     <implementation implements="nsIObserver">
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-19.0a1
+20.0a1
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -47,16 +47,17 @@ let DebuggerController = {
     if (this._isInitialized) {
       return;
     }
     this._isInitialized = true;
     window.removeEventListener("load", this._startupDebugger, true);
 
     DebuggerView.initialize(function() {
       DebuggerView._isInitialized = true;
+
       window.dispatchEvent("Debugger:Loaded");
       this._connect();
     }.bind(this));
   },
 
   /**
    * Destroys the view and disconnects the debugger client from the server.
    */
@@ -366,17 +367,22 @@ function StackFrames() {
   this._onFrames = this._onFrames.bind(this);
   this._onFramesCleared = this._onFramesCleared.bind(this);
   this._afterFramesCleared = this._afterFramesCleared.bind(this);
   this.evaluate = this.evaluate.bind(this);
 }
 
 StackFrames.prototype = {
   get activeThread() DebuggerController.activeThread,
+  autoScopeExpand: false,
   currentFrame: null,
+  syncedWatchExpressions: null,
+  currentWatchExpressions: null,
+  currentBreakpointLocation: null,
+  currentEvaluation: null,
   currentException: null,
 
   /**
    * Connect to the current thread client.
    */
   connect: function SF_connect() {
     dumpn("StackFrames is connecting...");
     this.activeThread.addListener("paused", this._onPaused);
@@ -412,58 +418,132 @@ StackFrames.prototype = {
    * Handler for the thread client's paused notification.
    *
    * @param string aEvent
    *        The name of the notification ("paused" in this case).
    * @param object aPacket
    *        The response packet.
    */
   _onPaused: function SF__onPaused(aEvent, aPacket) {
-    // In case the pause was caused by an exception, store the exception value.
-    if (aPacket.why.type == "exception") {
-      this.currentException = aPacket.why.exception;
+    switch (aPacket.why.type) {
+      // If paused by a breakpoint, store the breakpoint location.
+      case "breakpoint":
+        this.currentBreakpointLocation = aPacket.frame.where;
+        break;
+      // If paused by a client evaluation, store the evaluated value.
+      case "clientEvaluated":
+        this.currentEvaluation = aPacket.why.frameFinished;
+        break;
+      // If paused by an exception, store the exception value.
+      case "exception":
+        this.currentException = aPacket.why.exception;
+        break;
     }
 
     this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
     DebuggerView.editor.focus();
   },
 
   /**
    * Handler for the thread client's resumed notification.
    */
   _onResumed: function SF__onResumed() {
     DebuggerView.editor.setDebugLocation(-1);
+
+    // Prepare the watch expression evaluation string for the next pause.
+    if (!this._isWatchExpressionsEvaluation) {
+      this.currentWatchExpressions = this.syncedWatchExpressions;
+    }
   },
 
   /**
    * Handler for the thread client's framesadded notification.
    */
   _onFrames: function SF__onFrames() {
     // Ignore useless notifications.
     if (!this.activeThread.cachedFrames.length) {
       return;
     }
+
+    // Conditional breakpoints are { breakpoint, expression } tuples. The
+    // boolean evaluation of the expression decides if the active thread
+    // automatically resumes execution or not.
+    if (this.currentBreakpointLocation) {
+      let { url, line } = this.currentBreakpointLocation;
+      let breakpointClient = DebuggerController.Breakpoints.getBreakpoint(url, line);
+      let conditionalExpression = breakpointClient.conditionalExpression;
+      if (conditionalExpression) {
+        // Evaluating the current breakpoint's conditional expression will
+        // cause the stack frames to be cleared and active thread to pause,
+        // sending a 'clientEvaluated' packed and adding the frames again.
+        this.evaluate(conditionalExpression, 0);
+        this._isConditionalBreakpointEvaluation = true;
+        return;
+      }
+    }
+    // Got our evaluation of the current breakpoint's conditional expression.
+    if (this._isConditionalBreakpointEvaluation) {
+      this._isConditionalBreakpointEvaluation = false;
+      // If the breakpoint's conditional expression evaluation is falsy,
+      // automatically resume execution.
+      if (VariablesView.isFalsy({ value: this.currentEvaluation.return })) {
+        this.activeThread.resume();
+        return;
+      }
+    }
+
+
+    // Watch expressions are evaluated in the context of the topmost frame,
+    // and the results and displayed in the variables view.
+    if (this.currentWatchExpressions) {
+      // Evaluation causes the stack frames to be cleared and active thread to
+      // pause, sending a 'clientEvaluated' packed and adding the frames again.
+      this.evaluate(this.currentWatchExpressions, 0);
+      this._isWatchExpressionsEvaluation = true;
+      return;
+    }
+    // Got our evaluation of the current watch expressions.
+    if (this._isWatchExpressionsEvaluation) {
+      this._isWatchExpressionsEvaluation = false;
+      // If an error was thrown during the evaluation of the watch expressions,
+      // then at least one expression evaluation could not be performed.
+      if (this.currentEvaluation.throw) {
+        DebuggerView.WatchExpressions.removeExpression(0);
+        DebuggerController.StackFrames.syncWatchExpressions();
+        return;
+      }
+      // If the watch expressions were evaluated successfully, attach
+      // the results to the topmost frame.
+      let topmostFrame = this.activeThread.cachedFrames[0];
+      topmostFrame.watchExpressionsEvaluation = this.currentEvaluation.return;
+    }
+
+
+    // Make sure all the previous stackframes are removed before re-adding them.
     DebuggerView.StackFrames.empty();
 
     for (let frame of this.activeThread.cachedFrames) {
       this._addFrame(frame);
     }
-    if (!this.currentFrame) {
+    if (this.currentFrame == null) {
       this.selectFrame(0);
     }
     if (this.activeThread.moreFrames) {
       DebuggerView.StackFrames.dirty = true;
     }
   },
 
   /**
    * Handler for the thread client's framescleared notification.
    */
   _onFramesCleared: function SF__onFramesCleared() {
     this.currentFrame = null;
+    this.currentWatchExpressions = null;
+    this.currentBreakpointLocation = null;
+    this.currentEvaluation = null;
     this.currentException = null;
     // After each frame step (in, over, out), framescleared is fired, which
     // forces the UI to be emptied and rebuilt on framesadded. Most of the times
     // this is not necessary, and will result in a brief redraw flicker.
     // To avoid it, invalidate the UI only after a short time if necessary.
     window.setTimeout(this._afterFramesCleared, FRAME_STEP_CLEAR_DELAY);
   },
 
@@ -473,249 +553,405 @@ StackFrames.prototype = {
   _afterFramesCleared: function SF__afterFramesCleared() {
     // Ignore useless notifications.
     if (this.activeThread.cachedFrames.length) {
       return;
     }
     DebuggerView.StackFrames.empty();
     DebuggerView.Variables.empty(0);
     DebuggerView.Breakpoints.unhighlightBreakpoint();
+    DebuggerView.WatchExpressions.toggleContents(true);
     window.dispatchEvent("Debugger:AfterFramesCleared");
   },
 
   /**
    * Marks the stack frame at the specified depth as selected and updates the
    * properties view with the stack frame's data.
    *
    * @param number aDepth
    *        The depth of the frame in the stack.
    */
   selectFrame: function SF_selectFrame(aDepth) {
     let frame = this.activeThread.cachedFrames[this.currentFrame = aDepth];
     if (!frame) {
       return;
     }
-    let env = frame.environment;
+    let { environment, watchExpressionsEvaluation } = frame;
     let { url, line } = frame.where;
 
     // Check if the frame does not represent the evaluation of debuggee code.
-    if (!env) {
+    if (!environment) {
       return;
     }
 
     // Move the editor's caret to the proper url and line.
     DebuggerView.updateEditor(url, line);
     // Highlight the stack frame at the specified depth.
     DebuggerView.StackFrames.highlightFrame(aDepth);
     // Highlight the breakpoint at the specified url and line if it exists.
     DebuggerView.Breakpoints.highlightBreakpoint(url, line);
+    // Don't display the watch expressions textbox inputs in the pane.
+    DebuggerView.WatchExpressions.toggleContents(false);
     // Start recording any added variables or properties in any scope.
     DebuggerView.Variables.createHierarchy();
     // Clear existing scopes and create each one dynamically.
     DebuggerView.Variables.empty();
 
-    let self = this;
-    let name = "";
+    // If watch expressions evaluation results are available, create a scope
+    // to contain all the values.
+    if (watchExpressionsEvaluation) {
+      let label = L10N.getStr("watchExpressionsScopeLabel");
+      let arrow = L10N.getStr("watchExpressionsSeparatorLabel");
+      let scope = DebuggerView.Variables.addScope(label);
+      scope.separator = arrow;
+
+      // The evaluation hasn't thrown, so display the returned results and
+      // always expand the watch expressions scope by default.
+      this._fetchWatchExpressions(scope, watchExpressionsEvaluation);
+      scope.expand();
+    }
 
     do {
-      // Name the outermost scope Global.
-      if (!env.parent) {
-        name = L10N.getStr("globalScopeLabel");
-      }
-      // Otherwise construct the scope name.
-      else {
-        name = env.type.charAt(0).toUpperCase() + env.type.slice(1);
-      }
-
-      let label = L10N.getFormatStr("scopeLabel", [name]);
-      switch (env.type) {
-        case "with":
-        case "object":
-          label += " [" + env.object.class + "]";
-          break;
-        case "function":
-          label += " [" + env.functionName + "]";
-          break;
-      }
-
       // Create a scope to contain all the inspected variables.
+      let label = this._getScopeLabel(environment);
       let scope = DebuggerView.Variables.addScope(label);
 
       // Special additions to the innermost scope.
-      if (env == frame.environment) {
-        // Add any thrown exception.
-        if (aDepth == 0 && this.currentException) {
-          let excVar = scope.addVar("<exception>", { value: this.currentException });
-          this._addExpander(excVar, this.currentException);
-        }
-        // Add "this".
-        if (frame.this) {
-          let thisVar = scope.addVar("this", { value: frame.this });
-          this._addExpander(thisVar, frame.this);
-        }
-        // Expand the innermost scope by default.
-        scope.expand(true);
+      if (environment == frame.environment) {
+        this._insertScopeFrameReferences(scope, frame);
+        this._fetchScopeVariables(scope, environment);
+        // Always expand the innermost scope by default.
+        scope.expand();
       }
-
-      switch (env.type) {
-        case "with":
-        case "object":
-          // Add nodes for all variables in the environment object scope.
-          this.activeThread.pauseGrip(env.object).getPrototypeAndProperties(function(aResponse) {
-            self._addScopeVariables(aResponse.ownProperties, scope);
-
-            // Signal that variables have been fetched.
-            window.dispatchEvent("Debugger:FetchedVariables");
-            DebuggerView.Variables.commitHierarchy();
-          });
-          break;
-        case "block":
-        case "function":
-          // Add nodes for every argument.
-          for (let variable of env.bindings.arguments) {
-            let name = Object.getOwnPropertyNames(variable)[0];
-            let paramVar = scope.addVar(name, variable[name]);
-            let paramVal = variable[name].value;
-            this._addExpander(paramVar, paramVal);
-          }
-          // Add nodes for every other variable in scope.
-          this._addScopeVariables(env.bindings.variables, scope);
-          break;
-        default:
-          Cu.reportError("Unknown Debugger.Environment type: " + env.type);
-          break;
+      // Lazily add nodes for every other environment scope.
+      else {
+        this._addScopeExpander(scope, environment);
+        this.autoScopeExpand && scope.expand();
       }
-    } while (env = env.parent);
+    } while (environment = environment.parent);
 
     // Signal that variables have been fetched.
     window.dispatchEvent("Debugger:FetchedVariables");
     DebuggerView.Variables.commitHierarchy();
   },
 
   /**
+   * Adds an 'onexpand' callback for a scope, lazily handling
+   * the addition of new variables.
+   *
+   * @param Scope aScope
+   *        The scope where the variables will be placed into.
+   * @param object aEnv
+   *        The scope's environment.
+   */
+  _addScopeExpander: function SF__addScopeExpander(aScope, aEnv) {
+    let callback = this._fetchScopeVariables.bind(this, aScope, aEnv);
+
+    // It's a good idea to be prepared in case of an expansion.
+    aScope.onmouseover = callback;
+    // Make sure that variables are always available on expansion.
+    aScope.onexpand = callback;
+  },
+
+  /**
+   * Adds an 'onexpand' callback for a variable, lazily handling
+   * the addition of new properties.
+   *
+   * @param Variable aVar
+   *        The variable where the properties will be placed into.
+   * @param any aGrip
+   *        The grip of the variable.
+   */
+  _addVarExpander: function SF__addVarExpander(aVar, aGrip) {
+    // No need for expansion for primitive values.
+    if (VariablesView.isPrimitive({ value: aGrip })) {
+      return;
+    }
+    let callback = this._fetchVarProperties.bind(this, aVar, aGrip);
+
+    // Some variables are likely to contain a very large number of properties.
+    // It's a good idea to be prepared in case of an expansion.
+    if (aVar.name == "window" || aVar.name == "this") {
+      aVar.onmouseover = callback;
+    }
+    // Make sure that properties are always available on expansion.
+    aVar.onexpand = callback;
+  },
+
+  /**
+   * Adds the watch expressions evaluation results to a scope in the view.
+   *
+   * @param Scope aScope
+   *        The scope where the watch expressions will be placed into.
+   * @param object aExp
+   *        The grip of the evaluation results.
+   */
+  _fetchWatchExpressions: function SF__fetchWatchExpressions(aScope, aExp) {
+    // Retrieve the expressions only once.
+    if (aScope.fetched) {
+      return;
+    }
+    aScope.fetched = true;
+
+    // Add nodes for every watch expression in scope.
+    this.activeThread.pauseGrip(aExp).getPrototypeAndProperties(function(aResponse) {
+      let ownProperties = aResponse.ownProperties;
+      let totalExpressions = DebuggerView.WatchExpressions.totalItems;
+
+      for (let i = 0; i < totalExpressions; i++) {
+        let name = DebuggerView.WatchExpressions.getExpression(i);
+        let expVal = ownProperties[i].value;
+        let expRef = aScope.addVar(name, ownProperties[i]);
+        this._addVarExpander(expRef, expVal);
+      }
+
+      // Signal that watch expressions have been fetched.
+      window.dispatchEvent("Debugger:FetchedWatchExpressions");
+      DebuggerView.Variables.commitHierarchy();
+    }.bind(this));
+  },
+
+  /**
+   * Adds variables to a scope in the view. Triggered when a scope is
+   * expanded or is hovered. It does not expand the scope.
+   *
+   * @param Scope aScope
+   *        The scope where the variables will be placed into.
+   * @param object aEnv
+   *        The scope's environment.
+   */
+  _fetchScopeVariables: function SF__fetchScopeVariables(aScope, aEnv) {
+    // Retrieve the variables only once.
+    if (aScope.fetched) {
+      return;
+    }
+    aScope.fetched = true;
+
+    switch (aEnv.type) {
+      case "with":
+      case "object":
+        // Add nodes for every variable in scope.
+        this.activeThread.pauseGrip(aEnv.object).getPrototypeAndProperties(function(aResponse) {
+          this._insertScopeVariables(aResponse.ownProperties, aScope);
+
+          // Signal that variables have been fetched.
+          window.dispatchEvent("Debugger:FetchedVariables");
+          DebuggerView.Variables.commitHierarchy();
+        }.bind(this));
+        break;
+      case "block":
+      case "function":
+        // Add nodes for every argument and every other variable in scope.
+        this._insertScopeArguments(aEnv.bindings.arguments, aScope);
+        this._insertScopeVariables(aEnv.bindings.variables, aScope);
+        break;
+      default:
+        Cu.reportError("Unknown Debugger.Environment type: " + aEnv.type);
+        break;
+    }
+  },
+
+  /**
+   * Add nodes for special frame references in the innermost scope.
+   *
+   * @param Scope aScope
+   *        The scope where the references will be placed into.
+   * @param object aFrame
+   *        The frame to get some references from.
+   */
+  _insertScopeFrameReferences: function SF__insertScopeFrameReferences(aScope, aFrame) {
+    // Add any thrown exception.
+    if (this.currentException) {
+      let excRef = aScope.addVar("<exception>", { value: this.currentException });
+      this._addVarExpander(excRef, this.currentException);
+    }
+    // Add "this".
+    if (aFrame.this) {
+      let thisRef = aScope.addVar("this", { value: aFrame.this });
+      this._addVarExpander(thisRef, aFrame.this);
+    }
+  },
+
+  /**
+   * Add nodes for every argument in scope.
+   *
+   * @param object aArguments
+   *        The map of names to arguments, as specified in the protocol.
+   * @param Scope aScope
+   *        The scope where the nodes will be placed into.
+   */
+  _insertScopeArguments: function SF__insertScopeArguments(aArguments, aScope) {
+    if (!aArguments) {
+      return;
+    }
+    for (let argument of aArguments) {
+      let name = Object.getOwnPropertyNames(argument)[0];
+      let argRef = aScope.addVar(name, argument[name]);
+      let argVal = argument[name].value;
+      this._addVarExpander(argRef, argVal);
+    }
+  },
+
+  /**
    * Add nodes for every variable in scope.
    *
    * @param object aVariables
-   *        The map of names to variables, as specified in the Remote
-   *        Debugging Protocol.
+   *        The map of names to variables, as specified in the protocol.
    * @param Scope aScope
    *        The scope where the nodes will be placed into.
    */
-  _addScopeVariables: function SF_addScopeVariables(aVariables, aScope) {
+  _insertScopeVariables: function SF__insertScopeVariables(aVariables, aScope) {
     if (!aVariables) {
       return;
     }
     let variableNames = Object.keys(aVariables);
 
     // Sort all of the variables before adding them if preferred.
     if (Prefs.variablesSortingEnabled) {
       variableNames.sort();
     }
     // Add the sorted variables to the specified scope.
     for (let name of variableNames) {
-      let paramVar = aScope.addVar(name, aVariables[name]);
-      let paramVal = aVariables[name].value;
-      this._addExpander(paramVar, paramVal);
+      let varRef = aScope.addVar(name, aVariables[name]);
+      let varVal = aVariables[name].value;
+      this._addVarExpander(varRef, varVal);
     }
   },
 
   /**
-   * Adds an 'onexpand' callback for a variable, lazily handling
-   * the addition of new properties.
+   * Adds properties to a variable in the view. Triggered when a variable is
+   * expanded or certain variables are hovered. It does not expand the variable.
    *
    * @param Variable aVar
    *        The variable where the properties will be placed into.
    * @param any aGrip
    *        The grip of the variable.
    */
-  _addExpander: function SF__addExpander(aVar, aGrip) {
-    // No need for expansion for primitive values.
-    if (VariablesView.isPrimitive({ value: aGrip })) {
-      return;
-    }
-    aVar.onexpand = this._addVarProperties.bind(this, aVar, aGrip);
-  },
-
-  /**
-   * Adds properties to a variable in the view. Triggered when a variable is
-   * expanded.
-   *
-   * @param Variable aVar
-   *        The variable where the properties will be placed into.
-   * @param any aGrip
-   *        The grip of the variable.
-   */
-  _addVarProperties: function SF__addVarProperties(aVar, aGrip) {
+  _fetchVarProperties: function SF__fetchVarProperties(aVar, aGrip) {
     // Retrieve the properties only once.
     if (aVar.fetched) {
       return;
     }
+    aVar.fetched = true;
 
     this.activeThread.pauseGrip(aGrip).getPrototypeAndProperties(function(aResponse) {
       let { ownProperties, prototype } = aResponse;
 
       // Add all the variable properties.
       if (ownProperties) {
         aVar.addProperties(ownProperties);
         // Expansion handlers must be set after the properties are added.
         for (let name in ownProperties) {
-          this._addExpander(aVar.get(name), ownProperties[name].value);
+          this._addVarExpander(aVar.get(name), ownProperties[name].value);
         }
       }
 
       // Add the variable's __proto__.
-      if (prototype.type != "null") {
+      if (prototype && prototype.type != "null") {
         aVar.addProperty("__proto__", { value: prototype });
         // Expansion handlers must be set after the properties are added.
-        this._addExpander(aVar.get("__proto__"), prototype);
+        this._addVarExpander(aVar.get("__proto__"), prototype);
       }
 
-      aVar.fetched = true;
+      aVar._retrieved = true;
 
       // Signal that properties have been fetched.
       window.dispatchEvent("Debugger:FetchedProperties");
       DebuggerView.Variables.commitHierarchy();
     }.bind(this));
   },
 
   /**
+   * Constructs a scope label based on its environment.
+   *
+   * @param object aEnv
+   *        The scope's environment.
+   * @return string
+   *         The scope's label.
+   */
+  _getScopeLabel: function SV__getScopeLabel(aEnv) {
+    let name = "";
+
+    // Name the outermost scope Global.
+    if (!aEnv.parent) {
+      name = L10N.getStr("globalScopeLabel");
+    }
+    // Otherwise construct the scope name.
+    else {
+      name = aEnv.type.charAt(0).toUpperCase() + aEnv.type.slice(1);
+    }
+
+    let label = L10N.getFormatStr("scopeLabel", [name]);
+    switch (aEnv.type) {
+      case "with":
+      case "object":
+        label += " [" + aEnv.object.class + "]";
+        break;
+      case "function":
+        label += " [" + aEnv.functionName + "]";
+        break;
+    }
+    return label;
+  },
+
+  /**
    * Adds the specified stack frame to the list.
    *
    * @param object aFrame
    *        The new frame to add.
    */
   _addFrame: function SF__addFrame(aFrame) {
     let depth = aFrame.depth;
     let { url, line } = aFrame.where;
 
     let startText = StackFrameUtils.getFrameTitle(aFrame);
     let endText = SourceUtils.getSourceLabel(url) + ":" + line;
-
-    DebuggerView.StackFrames.addFrame(startText, endText, depth, {
-      attachment: aFrame
-    });
+    DebuggerView.StackFrames.addFrame(startText, endText, depth);
   },
 
   /**
    * Loads more stack frames from the debugger server cache.
    */
   addMoreFrames: function SF_addMoreFrames() {
     this.activeThread.fillFrames(
       this.activeThread.cachedFrames.length + CALL_STACK_PAGE_SIZE);
   },
 
   /**
+   * Updates a list of watch expressions to evaluate on each pause.
+   */
+  syncWatchExpressions: function SF_syncWatchExpressions() {
+    let list = DebuggerView.WatchExpressions.getExpressions();
+
+    if (list.length) {
+      this.syncedWatchExpressions =
+        this.currentWatchExpressions = "[" + list.map(function(str)
+          "(function() {" +
+            "try { return eval(\"" + str.replace(/"/g, "\\$&") + "\"); }" +
+            "catch(e) { return e.name + ': ' + e.message; }" +
+          "})()"
+        ).join(",") + "]";
+    } else {
+      this.syncedWatchExpressions =
+        this.currentWatchExpressions = null;
+    }
+    this._onFrames();
+  },
+
+  /**
    * Evaluate an expression in the context of the selected frame. This is used
    * for modifying the value of variables or properties in scope.
    *
    * @param string aExpression
    *        The expression to evaluate.
+   * @param number aFrame [optional]
+   *        The frame depth used for evaluation.
    */
-  evaluate: function SF_evaluate(aExpression) {
-    let frame = this.activeThread.cachedFrames[this.currentFrame];
+  evaluate: function SF_evaluate(aExpression, aFrame = this.currentFrame || 0) {
+    let frame = this.activeThread.cachedFrames[aFrame];
     this.activeThread.eval(frame.actor, aExpression);
   }
 };
 
 /**
  * Keeps the source script list up-to-date, using the thread client's
  * source script cache.
  *
@@ -823,16 +1059,20 @@ SourceScripts.prototype = {
   },
 
   /**
    * Callback for the debugger's active thread getScripts() method.
    */
   _onScriptsAdded: function SS__onScriptsAdded(aResponse) {
     // Add all the sources in the debugger view sources container.
     for (let script of aResponse.scripts) {
+      // Ignore scripts generated from 'clientEvaluate' packets.
+      if (script.url == "debugger eval code") {
+        continue;
+      }
       this._addSource(script);
     }
 
     let container = DebuggerView.Sources;
     let preferredValue = container.preferredValue;
 
     // Flushes all the prepared sources into the sources container.
     container.commit();
@@ -1012,51 +1252,60 @@ Breakpoints.prototype = {
   /**
    * Update the breakpoints in the editor view. This function takes the list of
    * breakpoints in the debugger and adds them back into the editor view.
    * This is invoked when the selected script is changed.
    */
   updateEditorBreakpoints: function BP_updateEditorBreakpoints() {
     for each (let breakpointClient in this.store) {
       if (DebuggerView.Sources.selectedValue == breakpointClient.location.url) {
-        this._showBreakpoint(breakpointClient, { noPaneUpdate: true });
+        this._showBreakpoint(breakpointClient, {
+          noPaneUpdate: true,
+          noPaneHighlight: true
+        });
       }
     }
   },
 
   /**
    * Update the breakpoints in the pane view. This function takes the list of
    * breakpoints in the debugger and adds them back into the breakpoints pane.
    * This is invoked when scripts are added.
    */
   updatePaneBreakpoints: function BP_updatePaneBreakpoints() {
     for each (let breakpointClient in this.store) {
       if (DebuggerView.Sources.containsValue(breakpointClient.location.url)) {
-        this._showBreakpoint(breakpointClient, { noEditorUpdate: true });
+        this._showBreakpoint(breakpointClient, {
+          noEditorUpdate: true,
+          noPaneHighlight: true
+        });
       }
     }
   },
 
   /**
    * Add a breakpoint.
    *
    * @param object aLocation
    *        The location where you want the breakpoint. This object must have
    *        two properties:
-   *          - url - the url of the source.
-   *          - line - the line number (starting from 1).
+   *          - url: the url of the source.
+   *          - line: the line number (starting from 1).
    * @param function aCallback [optional]
    *        Optional function to invoke once the breakpoint is added. The
    *        callback is invoked with two arguments:
    *          - aBreakpointClient: the BreakpointActor client object
    *          - aResponseError: if there was any error
    * @param object aFlags [optional]
    *        An object containing some of the following boolean properties:
+   *          - conditionalExpression: tells this breakpoint's conditional expression
+   *          - openPopup: tells if the expression popup should be shown
    *          - noEditorUpdate: tells if you want to skip editor updates
    *          - noPaneUpdate: tells if you want to skip breakpoint pane updates
+   *          - noPaneHighlight: tells if you don't want to highlight the breakpoint
    */
   addBreakpoint:
   function BP_addBreakpoint(aLocation, aCallback, aFlags = {}) {
     let breakpointClient = this.getBreakpoint(aLocation.url, aLocation.line);
 
     // If the breakpoint was already added, callback immediately.
     if (breakpointClient) {
       aCallback && aCallback(breakpointClient);
@@ -1087,16 +1336,19 @@ Breakpoints.prototype = {
         // Update the breakpoint client with the actual location.
         aBreakpointClient.location.url = aResponse.actualLocation.url;
         aBreakpointClient.location.line = aResponse.actualLocation.line;
       }
 
       // Remember the breakpoint client in the store.
       this.store[aBreakpointClient.actor] = aBreakpointClient;
 
+      // Attach any specified conditional expression to the breakpoint client.
+      aBreakpointClient.conditionalExpression = aFlags.conditionalExpression;
+
       // Preserve some information about the breakpoint's source url and line
       // to display in the breakpoints pane.
       aBreakpointClient.lineText = DebuggerView.getEditorLine(line - 1);
       aBreakpointClient.lineInfo = SourceUtils.getSourceLabel(url) + ":" + line;
 
       // Show the breakpoint in the editor and breakpoints pane.
       this._showBreakpoint(aBreakpointClient, aFlags);
 
@@ -1110,19 +1362,17 @@ Breakpoints.prototype = {
    *
    * @param object aBreakpointClient
    *        The BreakpointActor client object to remove.
    * @param function aCallback [optional]
    *        Optional function to invoke once the breakpoint is removed. The
    *        callback is invoked with one argument
    *          - aBreakpointClient: the breakpoint location (url and line)
    * @param object aFlags [optional]
-   *        An object containing some of the following boolean properties:
-   *          - noEditorUpdate: tells if you want to skip editor updates
-   *          - noPaneUpdate: tells if you want to skip breakpoint pane updates
+   *        @see DebuggerController.Breakpoints.addBreakpoint
    */
   removeBreakpoint:
   function BP_removeBreakpoint(aBreakpointClient, aCallback, aFlags = {}) {
     let breakpointActor = (aBreakpointClient || {}).actor;
 
     // If the breakpoint was already removed, callback immediately.
     if (!this.store[breakpointActor]) {
       aCallback && aCallback(aBreakpointClient.location);
@@ -1142,59 +1392,66 @@ Breakpoints.prototype = {
   },
 
   /**
    * Update the editor and breakpoints pane to show a specified breakpoint.
    *
    * @param object aBreakpointClient
    *        The BreakpointActor client object to show.
    * @param object aFlags [optional]
-   *        An object containing some of the following boolean properties:
-   *          - noEditorUpdate: tells if you want to skip editor updates
-   *          - noPaneUpdate: tells if you want to skip breakpoint pane updates
+   *        @see DebuggerController.Breakpoints.addBreakpoint
    */
   _showBreakpoint: function BP__showBreakpoint(aBreakpointClient, aFlags = {}) {
     let currentSourceUrl = DebuggerView.Sources.selectedValue;
     let { url, line } = aBreakpointClient.location;
 
+    // Update the editor if required.
     if (!aFlags.noEditorUpdate) {
       if (url == currentSourceUrl) {
         this._skipEditorBreakpointCallbacks = true;
         this.editor.addBreakpoint(line - 1);
         this._skipEditorBreakpointCallbacks = false;
       }
     }
+    // Update the breakpoints pane if required.
     if (!aFlags.noPaneUpdate) {
-      let { lineText, lineInfo } = aBreakpointClient;
-      let actor = aBreakpointClient.actor;
-      DebuggerView.Breakpoints.addBreakpoint(lineInfo, lineText, url, line, actor);
+      let { lineText, lineInfo, actor } = aBreakpointClient;
+      let conditionalFlag = aBreakpointClient.conditionalExpression !== undefined;
+      let openPopupFlag = aFlags.openPopup;
+
+      DebuggerView.Breakpoints.addBreakpoint(
+        url, line, actor, lineInfo, lineText, conditionalFlag, openPopupFlag);
+    }
+    // Highlight the breakpoint in the pane if required.
+    if (!aFlags.noPaneHighlight) {
+      DebuggerView.Breakpoints.highlightBreakpoint(url, line);
     }
   },
 
   /**
    * Update the editor and breakpoints pane to hide a specified breakpoint.
    *
    * @param object aBreakpointClient
    *        The BreakpointActor client object to hide.
    * @param object aFlags [optional]
-   *        An object containing some of the following boolean properties:
-   *          - noEditorUpdate: tells if you want to skip editor updates
-   *          - noPaneUpdate: tells if you want to skip breakpoint pane updates
+   *        @see DebuggerController.Breakpoints.addBreakpoint
    */
   _hideBreakpoint: function BP__hideBreakpoint(aBreakpointClient, aFlags = {}) {
     let currentSourceUrl = DebuggerView.Sources.selectedValue;
     let { url, line } = aBreakpointClient.location;
 
+    // Update the editor if required.
     if (!aFlags.noEditorUpdate) {
       if (url == currentSourceUrl) {
         this._skipEditorBreakpointCallbacks = true;
         this.editor.removeBreakpoint(line - 1);
         this._skipEditorBreakpointCallbacks = false;
       }
     }
+    // Update the breakpoints pane if required.
     if (!aFlags.noPaneUpdate) {
       DebuggerView.Breakpoints.removeBreakpoint(url, line);
     }
   },
 
   /**
    * Get the breakpoint object at the given location.
    *
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -31,45 +31,44 @@ create({ constructor: StackFramesView, p
     this._cache = new Map();
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function DVSF_destroy() {
     dumpn("Destroying the StackFramesView");
-    this._container.removeEventListener("click", this._onClick, true);
+    this._container.removeEventListener("click", this._onClick, false);
     this._container.removeEventListener("scroll", this._onScroll, true);
     window.removeEventListener("resize", this._onScroll, true);
   },
 
   /**
    * Adds a frame in this stackframes container.
    *
    * @param string aFrameName
    *        Name to be displayed in the list.
    * @param string aFrameDetails
    *        Details to be displayed in the list.
    * @param number aDepth
    *        The frame depth specified by the debugger.
-   * @param object aOptions [optional]
-   *        Additional options or flags supported by this operation:
-   *          - attachment: any kind of primitive/object to attach
    */
   addFrame:
-  function DVSF_addFrame(aFrameName, aFrameDetails, aDepth, aOptions = {}) {
+  function DVSF_addFrame(aFrameName, aFrameDetails, aDepth) {
     // Stackframes are UI elements which benefit from visible panes.
     DebuggerView.showPanesSoon();
 
     // Append a stackframe item to this container.
     let stackframeItem = this.push(aFrameName, aFrameDetails, {
       forced: true,
       unsorted: true,
       relaxed: true,
-      attachment: aOptions.attachment
+      attachment: {
+        depth: aDepth
+      }
     });
 
     // Check if stackframe was already appended.
     if (!stackframeItem) {
       return;
     }
 
     let element = stackframeItem.target;
@@ -149,86 +148,122 @@ let StackFrameUtils = {
 /**
  * Functions handling the breakpoints UI.
  */
 function BreakpointsView() {
   dumpn("BreakpointsView was instantiated");
   MenuContainer.call(this);
   this._createItemView = this._createItemView.bind(this);
   this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
-  this._onClick = this._onClick.bind(this);
+  this._onEditorLoad = this._onEditorLoad.bind(this);
+  this._onEditorUnload = this._onEditorUnload.bind(this);
+  this._onEditorSelection = this._onEditorSelection.bind(this);
+  this._onEditorContextMenu = this._onEditorContextMenu.bind(this);
+  this._onBreakpointClick = this._onBreakpointClick.bind(this);
   this._onCheckboxClick = this._onCheckboxClick.bind(this);
+  this._onConditionalPopupShowing = this._onConditionalPopupShowing.bind(this);
+  this._onConditionalPopupShown = this._onConditionalPopupShown.bind(this);
+  this._onConditionalPopupHiding = this._onConditionalPopupHiding.bind(this);
+  this._onConditionalTextboxKeyPress = this._onConditionalTextboxKeyPress.bind(this);
 }
 
 create({ constructor: BreakpointsView, proto: MenuContainer.prototype }, {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function DVB_initialize() {
     dumpn("Initializing the BreakpointsView");
     this._container = new StackList(document.getElementById("breakpoints"));
+    this._commandset = document.getElementById("debuggerCommands");
     this._popupset = document.getElementById("debuggerPopupset");
+    this._cbPanel = document.getElementById("conditional-breakpoint-panel");
+    this._cbTextbox = document.getElementById("conditional-breakpoint-textbox");
 
     this._container.emptyText = L10N.getStr("emptyBreakpointsText");
     this._container.itemFactory = this._createItemView;
     this._container.uniquenessQualifier = 2;
-    this._container.addEventListener("click", this._onClick, false);
+
+    window.addEventListener("Debugger:EditorLoaded", this._onEditorLoad, false);
+    window.addEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
+    this._container.addEventListener("click", this._onBreakpointClick, false);
+    this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false)
+    this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false)
+    this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding, false)
+    this._cbTextbox.addEventListener("keypress", this._onConditionalTextboxKeyPress, false);
 
     this._cache = new Map();
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function DVB_destroy() {
     dumpn("Destroying the BreakpointsView");
-    this._container.removeEventListener("click", this._onClick, false);
+    window.removeEventListener("Debugger:EditorLoaded", this._onEditorLoad, false);
+    window.removeEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
+    this._container.removeEventListener("click", this._onBreakpointClick, false);
+    this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShowing, false);
+    this._cbPanel.removeEventListener("popupshown", this._onConditionalPopupShown, false);
+    this._cbPanel.removeEventListener("popuphiding", this._onConditionalPopupHiding, false)
+    this._cbTextbox.removeEventListener("keypress", this._onConditionalTextboxKeyPress, false);
   },
 
   /**
    * Adds a breakpoint in this breakpoints container.
    *
+   * @param string aSourceLocation
+   *        The breakpoint source location specified by the debugger controller.
+   * @param number aLineNumber
+   *        The breakpoint line number specified by the debugger controller.
+   * @param string aActor
+   *        A breakpoint identifier specified by the debugger controller.
    * @param string aLineInfo
    *        Line information (parent source etc.) to be displayed in the list.
    * @param string aLineText
    *        Line text to be displayed in the list.
-   * @param string aSourceLocation
-   *        The breakpoint source location specified by the debugger controller.
-   * @param number aLineNumber
-   *        The breakpoint line number specified by the debugger controller.
-   * @parm string aId
-   *       A breakpoint identifier specified by the debugger controller.
+   * @param boolean aConditionalFlag [optional]
+   *        A flag specifying if this is a conditional breakpoint.
+   * @param boolean aOpenPopupFlag [optional]
+   *        A flag specifying if the expression popup should be shown.
    */
-  addBreakpoint:
-  function DVB_addBreakpoint(aLineInfo, aLineText, aSourceLocation, aLineNumber, aId) {
+  addBreakpoint: function DVB_addBreakpoint(aSourceLocation, aLineNumber,
+                                            aActor, aLineInfo, aLineText,
+                                            aConditionalFlag, aOpenPopupFlag) {
     // Append a breakpoint item to this container.
     let breakpointItem = this.push(aLineInfo.trim(), aLineText.trim(), {
       forced: true,
       attachment: {
         enabled: true,
         sourceLocation: aSourceLocation,
-        lineNumber: aLineNumber
+        lineNumber: aLineNumber,
+        isConditional: aConditionalFlag
       }
     });
 
     // Check if breakpoint was already appended.
     if (!breakpointItem) {
-      this.enableBreakpoint(aSourceLocation, aLineNumber, { id: aId });
+      this.enableBreakpoint(aSourceLocation, aLineNumber, { id: aActor });
       return;
     }
 
     let element = breakpointItem.target;
-    element.id = "breakpoint-" + aId;
+    element.id = "breakpoint-" + aActor;
     element.className = "dbg-breakpoint list-item";
     element.infoNode.className = "dbg-breakpoint-info plain";
     element.textNode.className = "dbg-breakpoint-text plain";
     element.setAttribute("contextmenu", this._createContextMenu(element));
 
     breakpointItem.finalize = this._onBreakpointRemoved;
     this._cache.set(this._key(aSourceLocation, aLineNumber), breakpointItem);
+
+    // If this is a conditional breakpoint, display the panes and a panel
+    // to input the corresponding conditional expression.
+    if (aConditionalFlag && aOpenPopupFlag) {
+      this.highlightBreakpoint(aSourceLocation, aLineNumber, { openPopup: true });
+    }
   },
 
   /**
    * Removes a breakpoint from this breakpoints container.
    *
    * @param string aSourceLocation
    *        The breakpoint source location.
    * @param number aLineNumber
@@ -262,16 +297,17 @@ create({ constructor: BreakpointsView, p
   enableBreakpoint:
   function DVB_enableBreakpoint(aSourceLocation, aLineNumber, aOptions = {}) {
     let breakpointItem = this.getBreakpoint(aSourceLocation, aLineNumber);
     if (breakpointItem) {
       // Set a new id to the corresponding breakpoint element if required.
       if (aOptions.id) {
         breakpointItem.target.id = "breakpoint-" + aOptions.id;
       }
+
       // Update the checkbox state if necessary.
       if (!aOptions.silent) {
         breakpointItem.target.checkbox.setAttribute("checked", "true");
       }
 
       let { sourceLocation: url, lineNumber: line } = breakpointItem.attachment;
       let breakpointLocation = { url: url, line: line };
       DebuggerController.Breakpoints.addBreakpoint(breakpointLocation, aOptions.callback, {
@@ -325,23 +361,57 @@ create({ constructor: BreakpointsView, p
 
   /**
    * Highlights a breakpoint in this breakpoints container.
    *
    * @param string aSourceLocation
    *        The breakpoint source location.
    * @param number aLineNumber
    *        The breakpoint line number.
+   * @param object aFlags [optional]
+   *        An object containing some of the following boolean properties:
+   *          - updateEditor: true if editor updates should be allowed
+   *          - openPopup: true if the expression popup should be shown
    */
-  highlightBreakpoint: function DVB_highlightBreakpoint(aSourceLocation, aLineNumber) {
+  highlightBreakpoint:
+  function DVB_highlightBreakpoint(aSourceLocation, aLineNumber, aFlags = {}) {
     let breakpointItem = this.getBreakpoint(aSourceLocation, aLineNumber);
     if (breakpointItem) {
+      // Update the editor source location and line number if necessary.
+      if (aFlags.updateEditor) {
+        DebuggerView.updateEditor(aSourceLocation, aLineNumber, { noDebug: true });
+      }
+
+      // If the breakpoint requires a new conditional expression, display
+      // the panes and the panel to input the corresponding expression.
+      if (aFlags.openPopup && breakpointItem.attachment.isConditional) {
+        let { sourceLocation: url, lineNumber: line } = breakpointItem.attachment;
+        let breakpointClient = DebuggerController.Breakpoints.getBreakpoint(url, line);
+
+        // The conditional expression popup can only be shown with visible panes.
+        DebuggerView.showPanesSoon(function() {
+          // Verify if the breakpoint wasn't removed before the panes were shown.
+          if (this.getBreakpoint(aSourceLocation, aLineNumber)) {
+            this._cbTextbox.value = breakpointClient.conditionalExpression || "";
+            this._cbPanel.openPopup(breakpointItem.target,
+              BREAKPOINT_CONDITIONAL_POPUP_POSITION,
+              BREAKPOINT_CONDITIONAL_POPUP_OFFSET);
+          }
+        }.bind(this));
+      } else {
+        this._cbPanel.hidePopup();
+      }
+
+      // Breakpoint is now highlighted.
       this._container.selectedItem = breakpointItem.target;
-    } else {
+    }
+    // Can't find a breakpoint at the requested source location and line number.
+    else {
       this._container.selectedIndex = -1;
+      this._cbPanel.hidePopup();
     }
   },
 
   /**
    * Unhighlights the current breakpoint in this breakpoints container.
    */
   unhighlightBreakpoint: function DVB_highlightBreakpoint() {
     this.highlightBreakpoint(null);
@@ -359,16 +429,29 @@ create({ constructor: BreakpointsView, p
    * @return object
    *         The corresponding item.
    */
   getBreakpoint: function DVB_getBreakpoint(aSourceLocation, aLineNumber) {
     return this._cache.get(this._key(aSourceLocation, aLineNumber));
   },
 
   /**
+   * Gets the currently selected breakpoint client.
+   * @return object
+   */
+  get selectedClient() {
+    let selectedItem = this.selectedItem;
+    if (selectedItem) {
+      let { sourceLocation: url, lineNumber: line } = selectedItem.attachment;
+      return DebuggerController.Breakpoints.getBreakpoint(url, line);
+    }
+    return null;
+  },
+
+  /**
    * Customization function for creating an item's UI.
    *
    * @param nsIDOMNode aElementNode
    *        The element associated with the displayed item.
    * @param string aInfo
    *        The breakpoint's line info.
    * @param string aText
    *        The breakpoint's line text.
@@ -387,16 +470,17 @@ create({ constructor: BreakpointsView, p
     let lineText = document.createElement("label");
     lineText.setAttribute("value", aText);
     lineText.setAttribute("crop", "end");
     lineText.setAttribute("tooltiptext",
       aText.substr(0, BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH));
 
     let state = document.createElement("vbox");
     state.className = "state";
+    state.setAttribute("pack", "center");
     state.appendChild(checkbox);
 
     let content = document.createElement("vbox");
     content.className = "content";
     content.setAttribute("flex", "1");
     content.appendChild(lineInfo);
     content.appendChild(lineText);
 
@@ -430,22 +514,24 @@ create({ constructor: BreakpointsView, p
     createMenuSeparator();
     createMenuItem.call(this, "enableAll");
     createMenuItem.call(this, "disableAll");
     createMenuSeparator();
     createMenuItem.call(this, "enableOthers");
     createMenuItem.call(this, "disableOthers");
     createMenuItem.call(this, "deleteOthers");
     createMenuSeparator();
+    createMenuItem.call(this, "setConditional");
+    createMenuSeparator();
     createMenuItem.call(this, "enableSelf", true);
     createMenuItem.call(this, "disableSelf");
     createMenuItem.call(this, "deleteSelf");
 
     this._popupset.appendChild(menupopup);
-    document.documentElement.appendChild(commandset);
+    this._commandset.appendChild(commandset);
 
     aElementNode.commandset = commandset;
     aElementNode.menupopup = menupopup;
     return menupopupId;
 
     /**
      * Creates a menu item specified by a name with the appropriate attributes
      * (label and handler).
@@ -506,48 +592,194 @@ create({ constructor: BreakpointsView, p
   /**
    * Function called each time a breakpoint item is removed.
    */
   _onBreakpointRemoved: function DVB__onBreakpointRemoved(aItem) {
     this._destroyContextMenu(aItem.target);
   },
 
   /**
+   * The load listener for the source editor.
+   */
+  _onEditorLoad: function DVB__onEditorLoad({ detail: editor }) {
+    editor.addEventListener("Selection", this._onEditorSelection, false);
+    editor.addEventListener("ContextMenu", this._onEditorContextMenu, false);
+  },
+
+  /**
+   * The unload listener for the source editor.
+   */
+  _onEditorUnload: function DVB__onEditorUnload({ detail: editor }) {
+    editor.removeEventListener("Selection", this._onEditorSelection, false);
+    editor.removeEventListener("ContextMenu", this._onEditorContextMenu, false);
+  },
+
+  /**
+   * The selection listener for the source editor.
+   */
+  _onEditorSelection: function DVB__onEditorSelection(e) {
+    let { start, end } = e.newValue;
+
+    let sourceLocation = DebuggerView.Sources.selectedValue;
+    let lineStart = DebuggerView.editor.getLineAtOffset(start) + 1;
+    let lineEnd = DebuggerView.editor.getLineAtOffset(end) + 1;
+
+    if (this.getBreakpoint(sourceLocation, lineStart) && lineStart == lineEnd) {
+      this.highlightBreakpoint(sourceLocation, lineStart);
+    } else {
+      this.unhighlightBreakpoint();
+    }
+  },
+
+  /**
+   * The context menu listener for the source editor.
+   */
+  _onEditorContextMenu: function DVB__onEditorContextMenu({ x, y }) {
+    let offset = DebuggerView.editor.getOffsetAtLocation(x, y);
+    let line = DebuggerView.editor.getLineAtOffset(offset);
+    this._editorContextMenuLineNumber = line;
+  },
+
+  /**
+   * Called when the add breakpoint key sequence was pressed.
+   */
+  _onCmdAddBreakpoint: function BP__onCmdAddBreakpoint() {
+    // If this command was executed via the context menu, add the breakpoint
+    // on the currently hovered line in the source editor.
+    if (this._editorContextMenuLineNumber >= 0) {
+      DebuggerView.editor.setCaretPosition(this._editorContextMenuLineNumber);
+    }
+    // Avoid placing breakpoints incorrectly when using key shortcuts.
+    this._editorContextMenuLineNumber = -1;
+
+    let url = DebuggerView.Sources.selectedValue;
+    let line = DebuggerView.editor.getCaretPosition().line + 1;
+    let breakpointItem = this.getBreakpoint(url, line);
+
+    // If a breakpoint already existed, remove it now.
+    if (breakpointItem) {
+      let breakpointClient = DebuggerController.Breakpoints.getBreakpoint(url, line)
+      DebuggerController.Breakpoints.removeBreakpoint(breakpointClient);
+      DebuggerView.Breakpoints.unhighlightBreakpoint();
+    }
+    // No breakpoint existed at the required location, add one now.
+    else {
+      let breakpointLocation = { url: url, line: line };
+      DebuggerController.Breakpoints.addBreakpoint(breakpointLocation);
+    }
+  },
+
+  /**
+   * Called when the add conditional breakpoint key sequence was pressed.
+   */
+  _onCmdAddConditionalBreakpoint: function BP__onCmdAddConditionalBreakpoint() {
+    // If this command was executed via the context menu, add the breakpoint
+    // on the currently hovered line in the source editor.
+    if (this._editorContextMenuLineNumber >= 0) {
+      DebuggerView.editor.setCaretPosition(this._editorContextMenuLineNumber);
+    }
+    // Avoid placing breakpoints incorrectly when using key shortcuts.
+    this._editorContextMenuLineNumber = -1;
+
+    let url =  DebuggerView.Sources.selectedValue;
+    let line = DebuggerView.editor.getCaretPosition().line + 1;
+    let breakpointItem = this.getBreakpoint(url, line);
+
+    // If a breakpoint already existed or wasn't a conditional, morph it now.
+    if (breakpointItem) {
+      breakpointItem.attachment.isConditional = true;
+      this.selectedClient.conditionalExpression = "";
+      this.highlightBreakpoint(url, line, { openPopup: true });
+    }
+    // No breakpoint existed at the required location, add one now.
+    else {
+      DebuggerController.Breakpoints.addBreakpoint({ url: url, line: line }, null, {
+        conditionalExpression: "",
+        openPopup: true
+      });
+    }
+  },
+
+  /**
+   * The popup showing listener for the breakpoints conditional expression panel.
+   */
+  _onConditionalPopupShowing: function DVB__onConditionalPopupShowing() {
+    this._popupShown = true;
+  },
+
+  /**
+   * The popup shown listener for the breakpoints conditional expression panel.
+   */
+  _onConditionalPopupShown: function DVB__onConditionalPopupShown() {
+    this._cbTextbox.focus();
+    this._cbTextbox.select();
+  },
+
+  /**
+   * The popup hiding listener for the breakpoints conditional expression panel.
+   */
+  _onConditionalPopupHiding: function DVB__onConditionalPopupHiding() {
+    this._popupShown = false;
+    this.selectedClient.conditionalExpression = this._cbTextbox.value;
+  },
+
+  /**
+   * The keypress listener for the breakpoints conditional expression textbox.
+   */
+  _onConditionalTextboxKeyPress: function DVB__onConditionalTextboxKeyPress(e) {
+    if (e.keyCode == e.DOM_VK_RETURN || e.keyCode == e.DOM_VK_ENTER) {
+      this._cbPanel.hidePopup();
+    }
+  },
+
+  /**
    * The click listener for the breakpoints container.
    */
-  _onClick: function DVB__onClick(e) {
+  _onBreakpointClick: function DVB__onBreakpointClick(e) {
     let breakpointItem = this.getItemForElement(e.target);
     if (!breakpointItem) {
       // The container is empty or we didn't click on an actual item.
       return;
     }
     let { sourceLocation: url, lineNumber: line } = breakpointItem.attachment;
-
-    DebuggerView.updateEditor(url, line, { noDebug: true });
-    this.highlightBreakpoint(url, line);
+    this.highlightBreakpoint(url, line, { updateEditor: true, openPopup: e.button == 0 });
   },
 
   /**
    * The click listener for a breakpoint checkbox.
    */
   _onCheckboxClick: function DVB__onCheckboxClick(e) {
     let breakpointItem = this.getItemForElement(e.target);
     if (!breakpointItem) {
       // The container is empty or we didn't click on an actual item.
       return;
     }
     let { sourceLocation: url, lineNumber: line, enabled } = breakpointItem.attachment;
+    this[enabled ? "disableBreakpoint" : "enableBreakpoint"](url, line, { silent: true });
 
     // Don't update the editor location.
     e.preventDefault();
     e.stopPropagation();
+  },
 
-    this[enabled
-      ? "disableBreakpoint"
-      : "enableBreakpoint"](url, line, { silent: true });
+  /**
+   * Listener handling the "setConditional" menuitem command.
+   *
+   * @param object aTarget
+   *        The corresponding breakpoint element node.
+   */
+  _onSetConditional: function DVB__onSetConditional(aTarget) {
+    if (!aTarget) {
+      return;
+    }
+    let breakpointItem = this.getItemForElement(aTarget);
+    let { sourceLocation: url, lineNumber: line } = breakpointItem.attachment;
+
+    breakpointItem.attachment.isConditional = true;
+    this.highlightBreakpoint(url, line, { openPopup: true });
   },
 
   /**
    * Listener handling the "enableSelf" menuitem command.
    *
    * @param object aTarget
    *        The corresponding breakpoint element node.
    */
@@ -672,23 +904,270 @@ create({ constructor: BreakpointsView, p
    *        The corresponding breakpoint element node.
    */
   _onDeleteAll: function DVB__onDeleteAll(aTarget) {
     this._onDeleteOthers(aTarget);
     this._onDeleteSelf(aTarget);
   },
 
   /**
-   * Gets an identifier for a breakpoint item for the current cache.
+   * Gets an identifier for a breakpoint item in the current cache.
+   * @return string
    */
   _key: function DVB__key(aSourceLocation, aLineNumber) {
     return aSourceLocation + aLineNumber;
   },
 
   _popupset: null,
+  _commandset: null,
+  _cbPanel: null,
+  _cbTextbox: null,
+  _popupShown: false,
+  _cache: null,
+  _editorContextMenuLineNumber: -1
+});
+
+/**
+ * Functions handling the watch expressions UI.
+ */
+function WatchExpressionsView() {
+  dumpn("WatchExpressionsView was instantiated");
+  MenuContainer.call(this);
+  this._createItemView = this._createItemView.bind(this);
+  this._onClick = this._onClick.bind(this);
+  this._onClose = this._onClose.bind(this);
+  this._onBlur = this._onBlur.bind(this);
+  this._onKeyPress = this._onKeyPress.bind(this);
+  this._onMouseOver = this._onMouseOver.bind(this);
+  this._onMouseOut = this._onMouseOut.bind(this);
+}
+
+create({ constructor: WatchExpressionsView, proto: MenuContainer.prototype }, {
+  /**
+   * Initialization function, called when the debugger is started.
+   */
+  initialize: function DVWE_initialize() {
+    dumpn("Initializing the WatchExpressionsView");
+    this._container = new StackList(document.getElementById("expressions"));
+    this._variables = document.getElementById("variables");
+
+    this._container.permaText = L10N.getStr("addWatchExpressionText");
+    this._container.itemFactory = this._createItemView;
+    this._container.addEventListener("click", this._onClick, false);
+
+    this._cache = [];
+  },
+
+  /**
+   * Destruction function, called when the debugger is closed.
+   */
+  destroy: function DVWE_destroy() {
+    dumpn("Destroying the WatchExpressionsView");
+    this._container.removeEventListener("click", this._onClick, false);
+  },
+
+  /**
+   * Adds a watch expression in this container.
+   *
+   * @param string aExpression [optional]
+   *        An optional initial watch expression text.
+   */
+  addExpression: function DVWE_addExpression(aExpression = "") {
+    // Watch expressions are UI elements which benefit from visible panes.
+    DebuggerView.showPanesSoon();
+
+    // Append a watch expression item to this container.
+    let expressionItem = this.push("", aExpression, {
+      forced: { atIndex: 0 },
+      unsorted: true,
+      relaxed: true,
+      attachment: {
+        expression: "",
+        initialExpression: aExpression,
+        id: this._generateId()
+      }
+    });
+
+    // Check if watch expression was already appended.
+    if (!expressionItem) {
+      return;
+    }
+
+    let element = expressionItem.target;
+    element.id = "expression-" + expressionItem.attachment.id;
+    element.className = "dbg-expression list-item";
+    element.arrowNode.className = "dbg-expression-arrow";
+    element.inputNode.className = "dbg-expression-input plain";
+    element.closeNode.className = "dbg-expression-delete plain devtools-closebutton";
+
+    // Automatically focus the new watch expression input and
+    // scroll the variables view to top.
+    element.inputNode.value = aExpression;
+    element.inputNode.select();
+    element.inputNode.focus();
+    this._variables.scrollTop = 0;
+
+    this._cache.splice(0, 0, expressionItem);
+  },
+
+  /**
+   * Removes the watch expression with the specified index from this container.
+   *
+   * @param number aIndex
+   *        The index used to identify the watch expression.
+   */
+  removeExpression: function DVWE_removeExpression(aIndex) {
+    this.remove(this._cache[aIndex]);
+    this._cache.splice(aIndex, 1);
+  },
+
+  /**
+   * Gets the watch expression code string for an item in this container.
+   *
+   * @param number aIndex
+   *        The index used to identify the watch expression.
+   * @return string
+   *         The watch expression code string.
+   */
+  getExpression: function DVWE_getExpression(aIndex) {
+    return this._cache[aIndex].attachment.expression;
+  },
+
+  /**
+   * Gets the watch expressions code strings for all items in this container.
+   *
+   * @return array
+   *         The watch expressions code strings.
+   */
+  getExpressions: function DVWE_getExpressions() {
+    return [item.attachment.expression for (item of this._cache)];
+  },
+
+  /**
+   * Customization function for creating an item's UI.
+   *
+   * @param nsIDOMNode aElementNode
+   *        The element associated with the displayed item.
+   * @param string aExpression
+   *        The initial watch expression text.
+   */
+  _createItemView: function DVWE__createItemView(aElementNode, aExpression) {
+    let arrowNode = document.createElement("box");
+    let inputNode = document.createElement("textbox");
+    let closeNode = document.createElement("toolbarbutton");
+
+    inputNode.setAttribute("value", aExpression);
+    inputNode.setAttribute("flex", "1");
+
+    closeNode.addEventListener("click", this._onClose, false);
+    inputNode.addEventListener("blur", this._onBlur, false);
+    inputNode.addEventListener("keypress", this._onKeyPress, false);
+    aElementNode.addEventListener("mouseover", this._onMouseOver, false);
+    aElementNode.addEventListener("mouseout", this._onMouseOut, false);
+
+    aElementNode.appendChild(arrowNode);
+    aElementNode.appendChild(inputNode);
+    aElementNode.appendChild(closeNode);
+    aElementNode.arrowNode = arrowNode;
+    aElementNode.inputNode = inputNode;
+    aElementNode.closeNode = closeNode;
+  },
+
+  /**
+   * The click listener for this container.
+   */
+  _onClick: function DVWE__onClick(e) {
+    let expressionItem = this.getItemForElement(e.target);
+    if (!expressionItem) {
+      // The container is empty or we didn't click on an actual item.
+      this.addExpression();
+    }
+  },
+
+  /**
+   * The click listener for a watch expression's close button.
+   */
+  _onClose: function DVWE__onClose(e) {
+    let expressionItem = this.getItemForElement(e.target);
+    this.removeExpression(this._cache.indexOf(expressionItem));
+
+    // Synchronize with the controller's watch expressions store.
+    DebuggerController.StackFrames.syncWatchExpressions();
+
+    e.preventDefault();
+    e.stopPropagation();
+  },
+
+  /**
+   * The blur listener for a watch expression's textbox.
+   */
+  _onBlur: function DVWE__onBlur({ target: textbox }) {
+    let expressionItem = this.getItemForElement(textbox);
+    let oldExpression = expressionItem.attachment.expression;
+    let newExpression = textbox.value;
+
+    // Remove the watch expression if it's empty.
+    if (!newExpression) {
+      this.removeExpression(this._cache.indexOf(expressionItem));
+    }
+    // Remove the watch expression if it's a duplicate.
+    else if (!oldExpression && this.getExpressions().indexOf(newExpression) != -1) {
+      this.removeExpression(this._cache.indexOf(expressionItem));
+    }
+    // Expression is eligible.
+    else {
+      // Save the watch expression code string.
+      expressionItem.attachment.expression = newExpression;
+      // Make sure the close button is hidden when the textbox is unfocused.
+      expressionItem.target.closeNode.hidden = true;
+    }
+
+    // Synchronize with the controller's watch expressions store.
+    DebuggerController.StackFrames.syncWatchExpressions();
+  },
+
+  /**
+   * The keypress listener for a watch expression's textbox.
+   */
+  _onKeyPress: function DVWE__onKeyPress(e) {
+    switch(e.keyCode) {
+      case e.DOM_VK_RETURN:
+      case e.DOM_VK_ENTER:
+      case e.DOM_VK_ESCAPE:
+        DebuggerView.editor.focus();
+        return;
+    }
+  },
+
+  /**
+   * The mouse over listener for a watch expression.
+   */
+  _onMouseOver: function DVWE__onMouseOver({ target: element }) {
+    this.getItemForElement(element).target.closeNode.hidden = false;
+  },
+
+  /**
+   * The mouse out listener for a watch expression.
+   */
+  _onMouseOut: function DVWE__onMouseOut({ target: element }) {
+    this.getItemForElement(element).target.closeNode.hidden = true;
+  },
+
+  /**
+   * Gets an identifier for a new watch expression item in the current cache.
+   * @return string
+   */
+  _generateId: (function() {
+    let count = 0;
+    return function DVWE__generateId() {
+      return (++count) + "";
+    };
+  })(),
+
+  _variables: null,
   _cache: null
 });
 
 /**
  * Functions handling the global search UI.
  */
 function GlobalSearchView() {
   dumpn("GlobalSearchView was instantiated");
@@ -1039,17 +1518,17 @@ create({ constructor: GlobalSearchView, 
     if (e instanceof Event) {
       e.preventDefault();
       e.stopPropagation();
     }
     let target = e.target;
     let sourceResultsItem = SourceResults.getItemForElement(target);
     let lineResultsItem = LineResults.getItemForElement(target);
 
-    sourceResultsItem.instance.expand(true);
+    sourceResultsItem.instance.expand();
     this._currentlyFocusedMatch = LineResults.indexOfElement(target);
     this._scrollMatchIntoViewIfNeeded(target);
     this._bounceMatch(target);
 
     let location = sourceResultsItem.location;
     let lineNumber = lineResultsItem.lineNumber;
     DebuggerView.updateEditor(location, lineNumber + 1, { noDebug: true });
 
@@ -1079,17 +1558,17 @@ create({ constructor: GlobalSearchView, 
     if (sourceResultsItem.instance.toggled ||
         sourceResultsItem.instance.expanded) {
       return;
     }
     let { top, height } = aTarget.getBoundingClientRect();
     let { clientHeight } = this._container._parent;
 
     if (top - height <= clientHeight || this._forceExpandResults) {
-      sourceResultsItem.instance.expand(true);
+      sourceResultsItem.instance.expand();
     }
   },
 
   /**
    * Scrolls a match into view.
    *
    * @param nsIDOMNode aMatch
    *        The match to scroll into view.
@@ -1298,17 +1777,17 @@ SourceResults.prototype = {
       lineResults.createView(resultsContainer, lineNumber, aCallbacks)
     }
 
     aElementNode.arrow = arrow;
     aElementNode.resultsHeader = resultsHeader;
     aElementNode.resultsContainer = resultsContainer;
 
     if (aExpandFlag && aMatchCount < GLOBAL_SEARCH_EXPAND_MAX_RESULTS) {
-      this.expand(true);
+      this.expand();
     }
 
     let resultsBox = document.createElement("vbox");
     resultsBox.setAttribute("flex", "1");
     resultsBox.appendChild(resultsHeader);
     resultsBox.appendChild(resultsContainer);
 
     aElementNode.id = "source-results-" + aLocation;
@@ -1523,22 +2002,23 @@ LineResults.indexOfElement = function DV
  * Gets the number of cached items associated with a specified element.
  *
  * @return number
  *         The number of key/value pairs in the corresponding map.
  */
 SourceResults.size =
 LineResults.size = function DVGS_size() {
   let count = 0;
-  for (let [_, item] of this._itemsByElement) {
+  for (let [, item] of this._itemsByElement) {
     if (!item.nonenumerable) {
       count++;
     }
   }
   return count;
 };
 
 /**
  * Preliminary setup for the DebuggerView object.
  */
 DebuggerView.StackFrames = new StackFramesView();
 DebuggerView.Breakpoints = new BreakpointsView();
+DebuggerView.WatchExpressions = new WatchExpressionsView();
 DebuggerView.GlobalSearch = new GlobalSearchView();
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -879,16 +879,17 @@ FilterView.prototype = {
       } else {
         DebuggerView.GlobalSearch[["focusNextMatch", "focusPrevMatch"][action]]();
       }
       return;
     }
 
     // Perform a variable search based on the specified operator.
     if (isVariable) {
+      DebuggerView.Variables.performSearch(token);
       DebuggerView.Variables.expandFirstSearchResults();
       return;
     }
 
     let editor = DebuggerView.editor;
     let offset = editor[["findNext", "findPrevious"][action]](true);
     if (offset > -1) {
       editor.setSelection(offset, offset + token.length)
@@ -908,17 +909,16 @@ FilterView.prototype = {
    * Called when a filtering key sequence was pressed.
    *
    * @param string aOperator
    *        The operator to use for filtering.
    */
   _doSearch: function DVF__doSearch(aOperator = "") {
     this._searchbox.focus();
     this._searchbox.value = aOperator;
-    DebuggerView.GlobalSearch.clearView();
   },
 
   /**
    * Called when the source location filter key sequence was pressed.
    */
   _doFileSearch: function DVF__doFileSearch() {
     this._doSearch();
     this._searchboxPanel.openPopup(this._searchbox);
@@ -947,16 +947,17 @@ FilterView.prototype = {
     this._doSearch(SEARCH_GLOBAL_FLAG);
     this._searchboxPanel.hidePopup();
   },
 
   /**
    * Called when the variable search filter key sequence was pressed.
    */
   _doVariableSearch: function DVF__doVariableSearch() {
+    DebuggerView.Variables.performSearch("");
     this._doSearch(SEARCH_VARIABLE_FLAG);
     this._searchboxPanel.hidePopup();
   },
 
   _searchbox: null,
   _searchboxPanel: null,
   _globalOperatorButton: null,
   _globalOperatorLabel: null,
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const SOURCE_URL_MAX_LENGTH = 64; // chars
 const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 1048576; // 1 MB in bytes
 const PANES_APPEARANCE_DELAY = 50; // ms
 const BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH = 1000; // chars
+const BREAKPOINT_CONDITIONAL_POPUP_POSITION = "after_start";
+const BREAKPOINT_CONDITIONAL_POPUP_OFFSET = 50; // px
 const GLOBAL_SEARCH_LINE_MAX_LENGTH = 300; // chars
 const GLOBAL_SEARCH_EXPAND_MAX_RESULTS = 50;
 const GLOBAL_SEARCH_ACTION_DELAY = 150; // ms
 const SEARCH_GLOBAL_FLAG = "!";
 const SEARCH_LINE_FLAG = ":";
 const SEARCH_TOKEN_FLAG = "#";
 const SEARCH_VARIABLE_FLAG = "*";
 
@@ -35,16 +37,17 @@ let DebuggerView = {
 
     this.Toolbar.initialize();
     this.Options.initialize();
     this.ChromeGlobals.initialize();
     this.Sources.initialize();
     this.Filtering.initialize();
     this.StackFrames.initialize();
     this.Breakpoints.initialize();
+    this.WatchExpressions.initialize();
     this.GlobalSearch.initialize();
 
     this.Variables = new VariablesView(document.getElementById("variables"));
     this.Variables.searchPlaceholder = L10N.getStr("emptyVariablesFilterText");
     this.Variables.emptyText = L10N.getStr("emptyVariablesText");
     this.Variables.nonEnumVisible = Prefs.variablesNonEnumVisible;
     this.Variables.searchEnabled = Prefs.variablesSearchboxVisible;
     this.Variables.eval = DebuggerController.StackFrames.evaluate;
@@ -64,16 +67,17 @@ let DebuggerView = {
 
     this.Toolbar.destroy();
     this.Options.destroy();
     this.ChromeGlobals.destroy();
     this.Sources.destroy();
     this.Filtering.destroy();
     this.StackFrames.destroy();
     this.Breakpoints.destroy();
+    this.WatchExpressions.destroy();
     this.GlobalSearch.destroy();
 
     this._destroyWindow();
     this._destroyPanes();
     this._destroyEditor();
     aCallback();
   },
 
@@ -115,38 +119,38 @@ let DebuggerView = {
   /**
    * Initializes the UI for all the displayed panes.
    */
   _initializePanes: function DV__initializePanes() {
     dumpn("Initializing the DebuggerView panes");
 
     this._togglePanesButton = document.getElementById("toggle-panes");
     this._stackframesAndBreakpoints = document.getElementById("stackframes+breakpoints");
-    this._variables = document.getElementById("variables");
+    this._variablesAndExpressions = document.getElementById("variables+expressions");
 
     this._stackframesAndBreakpoints.setAttribute("width", Prefs.stackframesWidth);
-    this._variables.setAttribute("width", Prefs.variablesWidth);
+    this._variablesAndExpressions.setAttribute("width", Prefs.variablesWidth);
     this.togglePanes({
       visible: Prefs.panesVisibleOnStartup,
       animated: false
     });
   },
 
   /**
    * Destroys the UI for all the displayed panes.
    */
   _destroyPanes: function DV__initializePanes() {
     dumpn("Destroying the DebuggerView panes");
 
     Prefs.stackframesWidth = this._stackframesAndBreakpoints.getAttribute("width");
-    Prefs.variablesWidth = this._variables.getAttribute("width");
+    Prefs.variablesWidth = this._variablesAndExpressions.getAttribute("width");
 
     this._togglePanesButton = null;
     this._stackframesAndBreakpoints = null;
-    this._variables = null;
+    this._variablesAndExpressions = null;
   },
 
   /**
    * Initializes the SourceEditor instance.
    *
    * @param function aCallback
    *        Called after the editor finishes initializing.
    */
@@ -172,27 +176,29 @@ let DebuggerView = {
   /**
    * The load event handler for the source editor, also executing any necessary
    * post-load operations.
    */
   _onEditorLoad: function DV__onEditorLoad() {
     dumpn("Finished loading the DebuggerView editor");
 
     DebuggerController.Breakpoints.initialize();
+    window.dispatchEvent("Debugger:EditorLoaded", this.editor);
     this.editor.focus();
   },
 
   /**
    * Destroys the SourceEditor instance and also executes any necessary
    * post-unload operations.
    */
   _destroyEditor: function DV__destroyEditor() {
     dumpn("Destroying the DebuggerView editor");
 
     DebuggerController.Breakpoints.destroy();
+    window.dispatchEvent("Debugger:EditorUnloaded", this.editor);
     this.editor = null;
   },
 
   /**
    * Sets the proper editor mode (JS or HTML) according to the specified
    * content type, or by determining the type from the url.
    *
    * @param string aUrl
@@ -232,17 +238,17 @@ let DebuggerView = {
 
   /**
    * Load the editor with the specified source text.
    *
    * @param object aSource
    *        The source object coming from the active thread.
    * @param object aOptions [optional]
    *        Additional options for showing the source. Supported options:
-   *        - targetLine: place the caret position at the given line number
+   *        - caretLine: place the caret position at the given line number
    *        - debugLine: place the debug location at the given line number
    *        - callback: function called when the source is shown
    */
   setEditorSource: function DV_setEditorSource(aSource, aOptions = {}) {
     if (!this.editor) {
       return;
     }
     dumpn("Setting the DebuggerView editor source: " + aSource.url +
@@ -259,31 +265,35 @@ let DebuggerView = {
       DebuggerController.SourceScripts.getText(aSource, function(aUrl, aText) {
         aSource.loaded = true;
         aSource.text = aText;
         this.setEditorSource(aSource, aOptions);
       }.bind(this));
     }
     // If the source is already loaded, display it immediately.
     else {
-      if (aSource.text.length < SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
-        this.setEditorMode(aSource.url, aSource.contentType, aSource.text);
-      } else {
-        this.editor.setMode(SourceEditor.MODES.TEXT);
+      if (this._editorSource != aSource) {
+        // Avoid setting the editor mode for very large files.
+        if (aSource.text.length < SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
+          this.setEditorMode(aSource.url, aSource.contentType, aSource.text);
+        } else {
+          this.editor.setMode(SourceEditor.MODES.TEXT);
+        }
+        this.editor.setText(aSource.text);
+        this.editor.resetUndo();
       }
-      this.editor.setText(aSource.text);
-      this.editor.resetUndo();
+      this._editorSource = aSource;
       this.updateEditor();
 
       DebuggerView.Sources.selectedValue = aSource.url;
       DebuggerController.Breakpoints.updateEditorBreakpoints();
 
       // Handle any additional options for showing the source.
-      if (aOptions.targetLine) {
-        editor.setCaretPosition(aOptions.targetLine - 1);
+      if (aOptions.caretLine) {
+        editor.setCaretPosition(aOptions.caretLine - 1);
       }
       if (aOptions.debugLine) {
         editor.setDebugLocation(aOptions.debugLine - 1);
       }
       if (aOptions.callback) {
         aOptions.callback(aSource);
       }
       // Notify that we've shown a source file.
@@ -377,65 +387,73 @@ let DebuggerView = {
 
   /**
    * Sets all the panes hidden or visible.
    *
    * @param object aFlags [optional]
    *        An object containing some of the following boolean properties:
    *        - visible: true if the pane should be shown, false for hidden
    *        - animated: true to display an animation on toggle
+   *        - callback: a function to invoke when the panes toggle finishes
    */
   togglePanes: function DV__togglePanes(aFlags = {}) {
     // Avoid useless toggles.
     if (aFlags.visible == !this.panesHidden) {
+      aFlags.callback && aFlags.callback();
       return;
     }
 
     if (aFlags.visible) {
       this._stackframesAndBreakpoints.style.marginLeft = "0";
-      this._variables.style.marginRight = "0";
+      this._variablesAndExpressions.style.marginRight = "0";
       this._togglePanesButton.removeAttribute("panesHidden");
       this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes"));
     } else {
       let marginL = ~~(this._stackframesAndBreakpoints.getAttribute("width")) + 1;
-      let marginR = ~~(this._variables.getAttribute("width")) + 1;
+      let marginR = ~~(this._variablesAndExpressions.getAttribute("width")) + 1;
       this._stackframesAndBreakpoints.style.marginLeft = -marginL + "px";
-      this._variables.style.marginRight = -marginR + "px";
+      this._variablesAndExpressions.style.marginRight = -marginR + "px";
       this._togglePanesButton.setAttribute("panesHidden", "true");
       this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes"));
     }
 
     if (aFlags.animated) {
       this._stackframesAndBreakpoints.setAttribute("animated", "");
-      this._variables.setAttribute("animated", "");
+      this._variablesAndExpressions.setAttribute("animated", "");
 
       // Displaying the panes may have the effect of triggering scrollbars to
       // appear in the source editor, which would render the currently
       // highlighted line to appear behind them in some cases.
       let self = this;
 
       window.addEventListener("transitionend", function onEvent() {
         window.removeEventListener("transitionend", onEvent, false);
+        aFlags.callback && aFlags.callback();
         self.updateEditor();
       }, false);
     } else {
       this._stackframesAndBreakpoints.removeAttribute("animated");
-      this._variables.removeAttribute("animated");
+      this._variablesAndExpressions.removeAttribute("animated");
+      aFlags.callback && aFlags.callback();
     }
   },
 
   /**
    * Sets all the panes visible after a short period of time.
+   *
+   * @param function aCallback
+   *        A function to invoke when the panes toggle finishes.
    */
-  showPanesSoon: function DV__showPanesSoon() {
+  showPanesSoon: function DV__showPanesSoon(aCallback) {
     // Try to keep animations as smooth as possible, so wait a few cycles.
     window.setTimeout(function() {
       DebuggerView.togglePanes({
         visible: true,
-        animated: true
+        animated: true,
+        callback: aCallback
       });
     }, PANES_APPEARANCE_DELAY);
   },
 
   /**
    * Handles any initialization on a tab navigation event issued by the client.
    */
   _handleTabNavigation: function DV__handleTabNavigation() {
@@ -443,37 +461,40 @@ let DebuggerView = {
 
     this.ChromeGlobals.empty();
     this.Sources.empty();
     this.Filtering.clearSearch();
     this.GlobalSearch.clearView();
     this.GlobalSearch.clearCache();
     this.StackFrames.empty();
     this.Breakpoints.empty();
+    this.Breakpoints.unhighlightBreakpoint();
     this.Variables.empty();
     SourceUtils.clearLabelsCache();
 
     if (this.editor) {
       this.editor.setText("");
+      this._editorSource = null;
     }
   },
 
   Toolbar: null,
   Options: null,
   ChromeGlobals: null,
   Sources: null,
   Filtering: null,
   StackFrames: null,
   Breakpoints: null,
   GlobalSearch: null,
   Variables: null,
   _editor: null,
+  _editorSource: null,
   _togglePanesButton: null,
   _stackframesAndBreakpoints: null,
-  _variables: null,
+  _variablesAndExpressions: null,
   _isInitialized: false,
   _isDestroyed: false
 };
 
 /**
  * A generic item used to describe elements present in views like the
  * ChromeGlobals, Sources, Stackframes, Breakpoints etc.
  *
@@ -585,33 +606,37 @@ MenuContainer.prototype = {
    * overridden via the "relaxed" flag.
    *
    * @param string aLabel
    *        The label displayed in the container.
    * @param string aValue
    *        The actual internal value of the item.
    * @param object aOptions [optional]
    *        Additional options or flags supported by this operation:
-   *          - forced: true to force the item to be immediately added
-   *          - unsorted: true if the items should not remain sorted
+   *          - forced: true to force the item to be immediately appended
+   *          - unsorted: true if the items should not always remain sorted
    *          - relaxed: true if this container should allow dupes & degenerates
    *          - description: an optional description of the item
    *          - attachment: some attached primitive/object
    * @return MenuItem
    *         The item associated with the displayed element if a forced push,
    *         undefined if the item was staged for a later commit.
    */
   push: function DVMC_push(aLabel, aValue, aOptions = {}) {
     let item = new MenuItem(
       aLabel, aValue, aOptions.description, aOptions.attachment);
 
     // Batch the item to be added later.
     if (!aOptions.forced) {
       this._stagedItems.push(item);
     }
+    // Immediately insert the item at the specified index.
+    else if (aOptions.forced && aOptions.forced.atIndex !== undefined) {
+      return this._insertItemAt(aOptions.forced.atIndex, item, aOptions);
+    }
     // Find the target position in this container and insert the item there.
     else if (!aOptions.unsorted) {
       return this._insertItemAt(this._findExpectedIndex(aLabel), item, aOptions);
     }
     // Just append the item in this container.
     else {
       return this._appendItem(item, aOptions);
     }
@@ -675,27 +700,39 @@ MenuContainer.prototype = {
    */
   empty: function DVMC_empty() {
     this._preferredValue = this.selectedValue;
     this._container.selectedIndex = -1;
     this._container.setAttribute("label", this._emptyLabel);
     this._container.removeAttribute("tooltiptext");
     this._container.removeAllItems();
 
-    for (let [_, item] of this._itemsByElement) {
+    for (let [, item] of this._itemsByElement) {
       this._untangleItem(item);
     }
 
     this._itemsByLabel = new Map();
     this._itemsByValue = new Map();
     this._itemsByElement = new Map();
     this._stagedItems = [];
   },
 
   /**
+   * Toggles all the items in this container hidden or visible.
+   *
+   * @param boolean aVisibleFlag
+   *        Specifies the intended visibility.
+   */
+  toggleContents: function DVMC_toggleContents(aVisibleFlag) {
+    for (let [, item] of this._itemsByElement) {
+      item.target.hidden = !aVisibleFlag;
+    }
+  },
+
+  /**
    * Does not remove any item in this container. Instead, it overrides the
    * current label to signal that it is unavailable and removes the tooltip.
    */
   setUnavailable: function DVMC_setUnavailable() {
     this._container.setAttribute("label", this._unavailableLabel);
     this._container.removeAttribute("tooltiptext");
   },
 
@@ -817,16 +854,28 @@ MenuContainer.prototype = {
   set selectedValue(aValue) {
     let item = this._itemsByValue.get(aValue);
     if (item) {
       this._container.selectedItem = item.target;
     }
   },
 
   /**
+   * Gets the item in the container having the specified index.
+   *
+   * @param number aIndex
+   *        The index used to identify the element.
+   * @return MenuItem
+   *         The matched item, or null if nothing is found.
+   */
+  getItemAtIndex: function DVMC_getItemAtIndex(aIndex) {
+    return this.getItemForElement(this._container.getItemAtIndex(aIndex));
+  },
+
+  /**
    * Gets the item in the container having the specified label.
    *
    * @param string aLabel
    *        The label used to identify the element.
    * @return MenuItem
    *         The matched item, or null if nothing is found.
    */
   getItemByLabel: function DVMC_getItemByLabel(aLabel) {
@@ -885,16 +934,24 @@ MenuContainer.prototype = {
     let values = [];
     for (let [value] of this._itemsByValue) {
       values.push(value);
     }
     return values;
   },
 
   /**
+   * Gets the total items in this container.
+   * @return number
+   */
+  get totalItems() {
+    return this._itemsByElement.size;
+  },
+
+  /**
    * Gets the total visible (non-hidden) items in this container.
    * @return number
    */
   get visibleItems() {
     let count = 0;
     for (let [element] of this._itemsByElement) {
       count += element.hidden ? 0 : 1;
     }
@@ -1052,17 +1109,17 @@ MenuContainer.prototype = {
     aItem._target = null;
     return aItem;
   },
 
   /**
    * A generator-iterator over all the items in this container.
    */
   __iterator__: function DVMC_iterator() {
-    for (let [_, item] of this._itemsByElement) {
+    for (let [, item] of this._itemsByElement) {
       yield item;
     }
   },
 
   _container: null,
   _stagedItems: null,
   _itemsByLabel: null,
   _itemsByValue: null,
@@ -1073,27 +1130,27 @@ MenuContainer.prototype = {
 };
 
 /**
  * A stacked list of items, compatible with MenuContainer instances, used for
  * displaying views like the StackFrames, Breakpoints etc.
  *
  * Custom methods introduced by this view, not necessary for a MenuContainer:
  * set emptyText(aValue:string)
+ * set permaText(aValue:string)
  * set itemType(aType:string)
  * set itemFactory(aCallback:function)
  *
  * TODO: Use this in #796135 - "Provide some obvious UI for scripts filtering".
  *
  * @param nsIDOMNode aAssociatedNode
  *        The element associated with the displayed container.
  */
 function StackList(aAssociatedNode) {
   this._parent = aAssociatedNode;
-  this._appendEmptyNotice();
 
   // Create an internal list container.
   this._list = document.createElement("vbox");
   this._parent.appendChild(this._list);
 }
 
 StackList.prototype = {
   /**
@@ -1296,24 +1353,37 @@ StackList.prototype = {
    *        True if the event was bubbling.
    */
   removeEventListener:
   function DVSL_removeEventListener(aName, aCallback, aBubbleFlag) {
     this._parent.removeEventListener(aName, aCallback, aBubbleFlag);
   },
 
   /**
+   * Sets the text displayed permanently in this container's header.
+   * @param string aValue
+   */
+  set permaText(aValue) {
+    if (this._permaTextNode) {
+      this._permaTextNode.setAttribute("value", aValue);
+    }
+    this._permaTextValue = aValue;
+    this._appendPermaNotice();
+  },
+
+  /**
    * Sets the text displayed in this container when there are no available items.
    * @param string aValue
    */
   set emptyText(aValue) {
     if (this._emptyTextNode) {
       this._emptyTextNode.setAttribute("value", aValue);
     }
     this._emptyTextValue = aValue;
+    this._appendEmptyNotice();
   },
 
   /**
    * Overrides the item's element type (e.g. "vbox" or "hbox").
    * @param string aType
    */
   itemType: "hbox",
 
@@ -1346,20 +1416,36 @@ StackList.prototype = {
     aElementNode.appendChild(spacer);
     aElementNode.appendChild(valueNode);
 
     aElementNode.labelNode = labelNode;
     aElementNode.valueNode = valueNode;
   },
 
   /**
+   * Creates and appends a label displayed permanently in this container's header.
+   */
+  _appendPermaNotice: function DVSL__appendPermaNotice() {
+    if (this._permaTextNode || !this._permaTextValue) {
+      return;
+    }
+
+    let label = document.createElement("label");
+    label.className = "empty list-item";
+    label.setAttribute("value", this._permaTextValue);
+
+    this._parent.insertBefore(label, this._list);
+    this._permaTextNode = label;
+  },
+
+  /**
    * Creates and appends a label signaling that this container is empty.
    */
   _appendEmptyNotice: function DVSL__appendEmptyNotice() {
-    if (this._emptyTextNode) {
+    if (this._emptyTextNode || !this._emptyTextValue) {
       return;
     }
 
     let label = document.createElement("label");
     label.className = "empty list-item";
     label.setAttribute("value", this._emptyTextValue);
 
     this._parent.appendChild(label);
@@ -1377,16 +1463,18 @@ StackList.prototype = {
     this._parent.removeChild(this._emptyTextNode);
     this._emptyTextNode = null;
   },
 
   _parent: null,
   _list: null,
   _selectedIndex: -1,
   _selectedItem: null,
+  _permaTextNode: null,
+  _permaTextValue: "",
   _emptyTextNode: null,
   _emptyTextValue: ""
 };
 
 /**
  * A simple way of displaying a "Connect to..." prompt.
  */
 function RemoteDebuggerPrompt() {
--- a/browser/devtools/debugger/debugger.css
+++ b/browser/devtools/debugger/debugger.css
@@ -8,24 +8,20 @@
  * Sources searching
  */
 
 #globalsearch {
   overflow-x: hidden;
   overflow-y: auto;
 }
 
-.dbg-results-container {
+.dbg-results-container:not([open]) {
   display: none;
 }
 
-.dbg-results-container[open] {
-  display: -moz-box;
-}
-
 /**
  * Stack frames
  */
 
 #stackframes {
   overflow: auto;
 }
 
@@ -43,31 +39,28 @@
   overflow: hidden;
 }
 
 /**
  * Variables view
  */
 
 #variables {
-  overflow: auto;
+  overflow-x: hidden;
+  overflow-y: auto;
 }
 
 /**
  * Scope, variable and property elements
  */
 
-#variables .details {
+#variables .details:not([open]) {
   display: none;
 }
 
-#variables .details[open] {
-  display: -moz-box;
-}
-
 .scope[non-header] > .title,
 .variable[non-header] > .title,
 .property[non-header] > .title {
   display: none;
 }
 
 /**
  * Variables and properties searching
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -38,29 +38,42 @@
     <command id="lineSearchCommand"
              oncommand="DebuggerView.Filtering._doLineSearch()"/>
     <command id="tokenSearchCommand"
              oncommand="DebuggerView.Filtering._doTokenSearch()"/>
     <command id="globalSearchCommand"
              oncommand="DebuggerView.Filtering._doGlobalSearch()"/>
     <command id="variableSearchCommand"
              oncommand="DebuggerView.Filtering._doVariableSearch()"/>
+    <command id="addBreakpointCommand"
+             oncommand="DebuggerView.Breakpoints._onCmdAddBreakpoint()"/>
+    <command id="addConditionalBreakpointCommand"
+             oncommand="DebuggerView.Breakpoints._onCmdAddConditionalBreakpoint()"/>
     <command id="togglePauseOnExceptions"
              oncommand="DebuggerView.Options._togglePauseOnExceptions()"/>
     <command id="toggleShowPanesOnStartup"
              oncommand="DebuggerView.Options._toggleShowPanesOnStartup()"/>
     <command id="toggleShowNonEnum"
              oncommand="DebuggerView.Options._toggleShowVariablesNonEnum()"/>
     <command id="toggleShowVariablesSearchbox"
              oncommand="DebuggerView.Options._toggleShowVariablesSearchbox()"/>
   </commandset>
 
   <popupset id="debuggerPopupset">
     <menupopup id="sourceEditorContextMenu"
                onpopupshowing="goUpdateSourceEditorMenuItems()">
+      <menuitem id="se-dbg-cMenu-addBreakpoint"
+                label="&debuggerUI.seMenuBreak;"
+                key="addBreakpointKey"
+                command="addBreakpointCommand"/>
+      <menuitem id="se-dbg-cMenu-addConditionalBreakpoint"
+                label="&debuggerUI.seMenuCondBreak;"
+                key="addConditionalBreakpointKey"
+                command="addConditionalBreakpointCommand"/>
+      <menuseparator/>
       <menuitem id="se-cMenu-copy"/>
       <menuseparator/>
       <menuitem id="se-cMenu-selectAll"/>
       <menuseparator/>
       <menuitem id="se-cMenu-find"/>
       <menuitem id="se-cMenu-findAgain"/>
       <menuseparator/>
       <menuitem id="se-cMenu-gotoLine"/>
@@ -123,16 +136,24 @@
     <key id="globalSearchKey"
          key="F"
          modifiers="control shift"
          command="globalSearchCommand"/>
     <key id="variableSearchKey"
          key="V"
          modifiers="control shift"
          command="variableSearchCommand"/>
+    <key id="addBreakpointKey"
+         key="B"
+         modifiers="accel"
+         command="addBreakpointCommand"/>
+    <key id="addConditionalBreakpointKey"
+         key="B"
+         modifiers="accel shift"
+         command="addConditionalBreakpointCommand"/>
   </keyset>
 
   <vbox id="body" flex="1">
     <toolbar id="dbg-toolbar" class="devtools-toolbar">
 #ifdef XP_MACOSX
       <toolbarbutton id="close"
                      class="devtools-closebutton"
                      tooltiptext="&debuggerUI.closeButton.tooltip;"/>
@@ -196,27 +217,41 @@
         <hbox align="center">
           <button id="variable-operator-button" class="operator"
                   command="variableSearchCommand"/>
           <label id="variable-operator-label" class="plain operator"/>
         </hbox>
       </vbox>
     </panel>
 
+    <panel id="conditional-breakpoint-panel"
+           type="arrow"
+           noautofocus="true"
+           position="after_start">
+      <vbox>
+        <label class="description" value="&debuggerUI.condBreakPanelTitle;"/>
+        <textbox id="conditional-breakpoint-textbox"/>
+      </vbox>
+    </panel>
+
     <vbox id="dbg-content" flex="1">
       <vbox id="globalsearch" hidden="true"/>
       <splitter id="globalsearch-splitter"
                 class="devtools-horizontal-splitter" hidden="true"/>
       <hbox flex="1">
         <vbox id="stackframes+breakpoints">
           <vbox id="stackframes" flex="1"/>
           <splitter class="devtools-horizontal-splitter"/>
           <vbox id="breakpoints"/>
         </vbox>
         <splitter class="devtools-side-splitter"/>
         <vbox id="editor" flex="1"/>
         <splitter class="devtools-side-splitter"/>
-        <vbox id="variables"/>
+        <vbox id="variables+expressions">
+          <vbox id="expressions"/>
+          <splitter class="devtools-horizontal-splitter"/>
+          <vbox id="variables" flex="1"/>
+        </vbox>
       </hbox>
     </vbox>
 
   </vbox>
 </window>
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -34,16 +34,19 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_propertyview-10.js \
 	browser_dbg_propertyview-edit.js \
 	browser_dbg_propertyview-data.js \
 	browser_dbg_propertyview-filter-01.js \
 	browser_dbg_propertyview-filter-02.js \
 	browser_dbg_propertyview-filter-03.js \
 	browser_dbg_propertyview-filter-04.js \
 	browser_dbg_propertyview-filter-05.js \
+	browser_dbg_propertyview-filter-06.js \
+	browser_dbg_propertyview-filter-07.js \
+	browser_dbg_propertyview-filter-08.js \
 	browser_dbg_propertyview-reexpand.js \
 	browser_dbg_reload-same-script.js \
 	browser_dbg_pane-collapse.js \
 	browser_dbg_panesize.js \
 	browser_dbg_panesize-inner.js \
 	browser_dbg_stack-01.js \
 	browser_dbg_stack-02.js \
 	browser_dbg_stack-03.js \
@@ -64,16 +67,20 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_scripts-searching-08.js \
 	browser_dbg_scripts-searching-popup.js \
 	browser_dbg_pause-resume.js \
 	browser_dbg_update-editor-mode.js \
 	$(warning browser_dbg_select-line.js temporarily disabled due to oranges, see bug 726609) \
 	browser_dbg_clean-exit.js \
 	browser_dbg_bug723069_editor-breakpoints.js \
 	browser_dbg_bug723071_editor-breakpoints-pane.js \
+	browser_dbg_bug740825_conditional-breakpoints-01.js \
+	browser_dbg_bug740825_conditional-breakpoints-02.js \
+	browser_dbg_bug727429_watch-expressions-01.js \
+	browser_dbg_bug727429_watch-expressions-02.js \
 	browser_dbg_bug731394_editor-contextmenu.js \
 	browser_dbg_bug786070_hide_nonenums.js \
 	browser_dbg_displayName.js \
 	browser_dbg_iframes.js \
 	browser_dbg_pause-exceptions.js \
 	browser_dbg_multiple-windows.js \
 	browser_dbg_menustatus.js \
 	browser_dbg_bfcache.js \
@@ -97,13 +104,15 @@ MOCHITEST_BROWSER_PAGES = \
 	browser_dbg_frame-parameters.html \
 	browser_dbg_update-editor-mode.html \
 	test-editor-mode \
 	browser_dbg_displayName.html \
 	browser_dbg_iframes.html \
 	browser_dbg_with-frame.html \
 	browser_dbg_pause-exceptions.html \
 	browser_dbg_breakpoint-new-script.html \
+	browser_dbg_conditional-breakpoints.html \
+	browser_dbg_watch-expressions.html \
 	$(NULL)
 
 MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/debugger/test/browser_dbg_bug723071_editor-breakpoints-pane.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug723071_editor-breakpoints-pane.js
@@ -168,17 +168,17 @@ function test()
           function(cl, err) {
           onBreakpointAdd.call({ increment: increment, line: line }, cl, err);
 
           line = 7;
           gPane.addBreakpoint({url: gScripts.selectedValue, line: line},
             function(cl, err) {
             onBreakpointAdd.call({ increment: increment, line: line }, cl, err);
 
-            line = 8;
+            line = 9;
             gPane.addBreakpoint({url: gScripts.selectedValue, line: line},
               function(cl, err) {
               onBreakpointAdd.call({ increment: increment, line: line }, cl, err);
 
               executeSoon(function() {
                 callback();
               });
             });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug727429_watch-expressions-01.js
@@ -0,0 +1,238 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 727429: test the debugger watch expressions.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_watch-expressions.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gWatch = null;
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.contentWindow;
+    gWatch = gDebugger.DebuggerView.WatchExpressions;
+
+    gDebugger.DebuggerView.togglePanes({ visible: true, animated: false });
+
+    executeSoon(function() {
+      performTest();
+    });
+  });
+
+  function performTest()
+  {
+    is(gWatch.getExpressions().length, 0,
+      "There should initially be no watch expressions");
+
+    addAndCheckExpressions(1, 0, "a");
+    addAndCheckExpressions(2, 0, "b");
+    addAndCheckExpressions(3, 0, "c");
+
+    removeAndCheckExpression(2, 1, "a");
+    removeAndCheckExpression(1, 0, "a");
+
+
+    addAndCheckExpressions(2, 0, "", true);
+    gDebugger.editor.focus();
+    is(gWatch.getExpressions().length, 1,
+      "Empty watch expressions are automatically removed");
+
+    addAndCheckExpressions(2, 0, "a", true);
+    gDebugger.editor.focus();
+    is(gWatch.getExpressions().length, 1,
+      "Duplicate watch expressions are automatically removed");
+
+
+    addAndCheckCustomExpression(2, 0, "bazΩΩka");
+    addAndCheckCustomExpression(3, 0, "bambøøcha");
+
+
+    EventUtils.sendMouseEvent({ type: "click" },
+      gWatch.getItemAtIndex(0).target.closeNode,
+      gDebugger);
+
+    is(gWatch.getExpressions().length, 2,
+      "Watch expressions are removed when the close button is pressed");
+    is(gWatch.getExpressions()[0], "bazΩΩka",
+      "The expression at index " + 0 + " should be correct (1)");
+    is(gWatch.getExpressions()[1], "a",
+      "The expression at index " + 1 + " should be correct (2)");
+
+
+    EventUtils.sendMouseEvent({ type: "click" },
+      gWatch.getItemAtIndex(0).target.closeNode,
+      gDebugger);
+
+    is(gWatch.getExpressions().length, 1,
+      "Watch expressions are removed when the close button is pressed");
+    is(gWatch.getExpressions()[0], "a",
+      "The expression at index " + 0 + " should be correct (3)");
+
+
+    EventUtils.sendMouseEvent({ type: "click" },
+      gWatch.getItemAtIndex(0).target.closeNode,
+      gDebugger);
+
+    is(gWatch.getExpressions().length, 0,
+      "Watch expressions are removed when the close button is pressed");
+
+
+    EventUtils.sendMouseEvent({ type: "click" },
+      gWatch._container._parent,
+      gDebugger);
+
+    is(gWatch.getExpressions().length, 1,
+      "Watch expressions are added when the view container is pressed");
+
+
+    closeDebuggerAndFinish();
+  }
+
+  function addAndCheckCustomExpression(total, index, string, noBlur) {
+    addAndCheckExpressions(total, index, "", true);
+
+    for (let i = 0; i < string.length; i++) {
+      EventUtils.sendChar(string[i]);
+    }
+
+    gDebugger.editor.focus();
+
+    let id = gWatch.getItemAtIndex(index).attachment.id;
+    let element = gDebugger.document.getElementById("expression-" + id);
+
+    is(gWatch.getItemAtIndex(index).attachment.initialExpression, "",
+      "The initial expression at index " + index + " should be correct (1)");
+    is(gWatch.getItemForElement(element).attachment.initialExpression, "",
+      "The initial expression at index " + index + " should be correct (2)");
+
+    is(gWatch.getItemAtIndex(index).attachment.expression, string,
+      "The expression at index " + index + " should be correct (1)");
+    is(gWatch.getItemForElement(element).attachment.expression, string,
+      "The expression at index " + index + " should be correct (2)");
+
+    is(gWatch.getExpression(index), string,
+      "The expression at index " + index + " should be correct (3)");
+    is(gWatch.getExpressions()[index], string,
+      "The expression at index " + index + " should be correct (4)");
+  }
+
+  function addAndCheckExpressions(total, index, string, noBlur) {
+    gWatch.addExpression(string);
+
+    is(gWatch.getExpressions().length, total,
+      "There should be " + total + " watch expressions available (1)");
+    is(gWatch.totalItems, total,
+      "There should be " + total + " watch expressions available (2)");
+
+    ok(gWatch.getItemAtIndex(index),
+      "The expression at index " + index + " should be available");
+    ok(gWatch.getItemAtIndex(index).attachment.id,
+      "The expression at index " + index + " should have an id");
+    is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
+      "The expression at index " + index + " should have an initial expression");
+
+    let id = gWatch.getItemAtIndex(index).attachment.id;
+    let element = gDebugger.document.getElementById("expression-" + id);
+
+    ok(element,
+      "Three should be a new expression item in the view");
+    ok(gWatch.getItemForElement(element),
+      "The watch expression item should be accessible");
+    is(gWatch.getItemForElement(element), gWatch.getItemAtIndex(index),
+      "The correct watch expression item was accessed");
+
+    ok(gWatch.getItemAtIndex(index) instanceof gDebugger.MenuItem,
+      "The correct watch expression element was accessed (1)");
+    ok(gWatch._container.getItemAtIndex(index) instanceof XULElement,
+      "The correct watch expression element was accessed (2)");
+    is(element, gWatch._container.getItemAtIndex(index),
+      "The correct watch expression element was accessed (3)");
+
+    is(element.arrowNode.hidden, false,
+      "The arrow node should be visible");
+    is(element.closeNode.hidden, false,
+      "The close button should be visible");
+    is(element.inputNode.getAttribute("focused"), "true",
+      "The textbox input should be focused");
+
+    is(gWatch._variables.scrollTop, 0,
+      "The variables view should be scrolled to top");
+
+    is(gWatch._cache[0], gWatch.getItemAtIndex(index),
+      "The correct watch expression was added to the cache (1)");
+    is(gWatch._cache[0], gWatch.getItemForElement(element),
+      "The correct watch expression was added to the cache (2)");
+
+    if (!noBlur) {
+      gDebugger.editor.focus();
+
+      is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
+        "The initial expression at index " + index + " should be correct (1)");
+      is(gWatch.getItemForElement(element).attachment.initialExpression, string,
+        "The initial expression at index " + index + " should be correct (2)");
+
+      is(gWatch.getItemAtIndex(index).attachment.expression, string,
+        "The expression at index " + index + " should be correct (1)");
+      is(gWatch.getItemForElement(element).attachment.expression, string,
+        "The expression at index " + index + " should be correct (2)");
+
+      is(gWatch.getExpression(index), string,
+        "The expression at index " + index + " should be correct (3)");
+      is(gWatch.getExpressions()[index], string,
+        "The expression at index " + index + " should be correct (4)");
+    }
+  }
+
+  function removeAndCheckExpression(total, index, string) {
+    gWatch.removeExpression(index);
+
+    is(gWatch.getExpressions().length, total,
+      "There should be " + total + " watch expressions available (1)");
+    is(gWatch.totalItems, total,
+      "There should be " + total + " watch expressions available (2)");
+
+    ok(gWatch.getItemAtIndex(index),
+      "The expression at index " + index + " should still be available");
+    ok(gWatch.getItemAtIndex(index).attachment.id,
+      "The expression at index " + index + " should still have an id");
+    is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
+      "The expression at index " + index + " should still have an initial expression");
+
+    let id = gWatch.getItemAtIndex(index).attachment.id;
+    let element = gDebugger.document.getElementById("expression-" + id);
+
+    is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
+      "The initial expression at index " + index + " should be correct (1)");
+    is(gWatch.getItemForElement(element).attachment.initialExpression, string,
+      "The initial expression at index " + index + " should be correct (2)");
+
+    is(gWatch.getItemAtIndex(index).attachment.expression, string,
+      "The expression at index " + index + " should be correct (1)");
+    is(gWatch.getItemForElement(element).attachment.expression, string,
+      "The expression at index " + index + " should be correct (2)");
+
+    is(gWatch.getExpression(index), string,
+      "The expression at index " + index + " should be correct (3)");
+    is(gWatch.getExpressions()[index], string,
+      "The expression at index " + index + " should be correct (4)");
+  }
+
+  registerCleanupFunction(function() {
+    removeTab(gTab);
+    gPane = null;
+    gTab = null;
+    gDebuggee = null;
+    gDebugger = null;
+    gWatch = null;
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug727429_watch-expressions-02.js
@@ -0,0 +1,275 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 727429: test the debugger watch expressions.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_watch-expressions.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gWatch = null;
+let gVars = null;
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.contentWindow;
+    gWatch = gDebugger.DebuggerView.WatchExpressions;
+    gVars = gDebugger.DebuggerView.Variables;
+
+    gDebugger.DebuggerView.togglePanes({ visible: true, animated: false });
+    addExpressions();
+    performTest();
+  });
+
+  function addExpressions()
+  {
+    gWatch.addExpression("'a'");
+    gWatch.addExpression("\"a\"");
+    gWatch.addExpression("'a\"\"'");
+    gWatch.addExpression("\"a''\"");
+    gWatch.addExpression("?");
+    gWatch.addExpression("a");
+    gWatch.addExpression("[1, 2, 3]");
+    gWatch.addExpression("x = [1, 2, 3]");
+    gWatch.addExpression("y = [1, 2, 3]; y.test = 4");
+    gWatch.addExpression("z = [1, 2, 3]; z.test = 4; z");
+    gWatch.addExpression("t = [1, 2, 3]; t.test = 4; !t");
+    gWatch.addExpression("encodeURI(\"\\\")");
+    gWatch.addExpression("decodeURI(\"\\\")");
+  }
+
+  function performTest()
+  {
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 0,
+      "There should be 0 hidden nodes in the watch expressions container");
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 13,
+      "There should be 13 visible nodes in the watch expressions container");
+
+    test1(function() {
+      test2(function() {
+        test3(function() {
+          test4(function() {
+            test5(function() {
+              test6(function() {
+                test7(function() {
+                  test8(function() {
+                    test9(function() {
+                      finishTest();
+                    });
+                  });
+                });
+              });
+            });
+          });
+        });
+      });
+    });
+  }
+
+  function finishTest()
+  {
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 0,
+      "There should be 0 hidden nodes in the watch expressions container");
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 12,
+      "There should be 12 visible nodes in the watch expressions container");
+
+    closeDebuggerAndFinish();
+  }
+
+  function test1(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test1");
+      checkWatchExpressions("ReferenceError: a is not defined");
+      callback();
+    });
+    executeSoon(function() {
+      gDebuggee.ermahgerd(); // ermahgerd!!
+    });
+  }
+
+  function test2(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test2");
+      checkWatchExpressions(undefined);
+      callback();
+    });
+    EventUtils.sendMouseEvent({ type: "mousedown" },
+      gDebugger.document.getElementById("resume"),
+      gDebugger);
+  }
+
+  function test3(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test3");
+      checkWatchExpressions({ type: "object", class: "Object" });
+      callback();
+    });
+    EventUtils.sendMouseEvent({ type: "mousedown" },
+      gDebugger.document.getElementById("resume"),
+      gDebugger);
+  }
+
+  function test4(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test4");
+      checkWatchExpressions(5, 12);
+      callback();
+    });
+    executeSoon(function() {
+      gWatch.addExpression("a = 5");
+      EventUtils.sendKey("RETURN");
+    });
+  }
+
+  function test5(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test5");
+      checkWatchExpressions(5, 12);
+      callback();
+    });
+    executeSoon(function() {
+      gWatch.addExpression("encodeURI(\"\\\")");
+      EventUtils.sendKey("RETURN");
+    });
+  }
+
+  function test6(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test6");
+      checkWatchExpressions(5, 12);
+      callback();
+    })
+    executeSoon(function() {
+      gWatch.addExpression("decodeURI(\"\\\")");
+      EventUtils.sendKey("RETURN");
+    });
+  }
+
+  function test7(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test7");
+      checkWatchExpressions(5, 12);
+      callback();
+    });
+    executeSoon(function() {
+      gWatch.addExpression("?");
+      EventUtils.sendKey("RETURN");
+    });
+  }
+
+  function test8(callback) {
+    waitForWatchExpressions(function() {
+      info("Performing test8");
+      checkWatchExpressions(5, 12);
+      callback();
+    });
+    executeSoon(function() {
+      gWatch.addExpression("a");
+      EventUtils.sendKey("RETURN");
+    });
+  }
+
+  function test9(callback) {
+    waitForAfterFramesCleared(function() {
+      info("Performing test9");
+      callback();
+    });
+    EventUtils.sendMouseEvent({ type: "mousedown" },
+      gDebugger.document.getElementById("resume"),
+      gDebugger);
+  }
+
+  function waitForAfterFramesCleared(callback) {
+    gDebugger.addEventListener("Debugger:AfterFramesCleared", function onClear() {
+      gDebugger.removeEventListener("Debugger:AfterFramesCleared", onClear, false);
+      executeSoon(callback);
+    }, false);
+  }
+
+  function waitForWatchExpressions(callback) {
+    gDebugger.addEventListener("Debugger:FetchedWatchExpressions", function onFetch() {
+      gDebugger.removeEventListener("Debugger:FetchedWatchExpressions", onFetch, false);
+      executeSoon(callback);
+    }, false);
+  }
+
+  function checkWatchExpressions(expected, total = 11) {
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, total,
+      "There should be " + total + " hidden nodes in the watch expressions container");
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 0,
+      "There should be 0 visible nodes in the watch expressions container");
+
+    let label = gDebugger.L10N.getStr("watchExpressionsScopeLabel");
+    let scope = gVars._currHierarchy.get(label);
+
+    ok(scope, "There should be a wach expressions scope in the variables view");
+    is(scope._store.size, total, "There should be " + total + " evaluations availalble");
+
+    let w1 = scope.get("'a'");
+    let w2 = scope.get("\"a\"");
+    let w3 = scope.get("'a\"\"'");
+    let w4 = scope.get("\"a''\"");
+    let w5 = scope.get("?");
+    let w6 = scope.get("a");
+    let w7 = scope.get("x = [1, 2, 3]");
+    let w8 = scope.get("y = [1, 2, 3]; y.test = 4");
+    let w9 = scope.get("z = [1, 2, 3]; z.test = 4; z");
+    let w10 = scope.get("t = [1, 2, 3]; t.test = 4; !t");
+    let w11 = scope.get("encodeURI(\"\\\")");
+    let w12 = scope.get("decodeURI(\"\\\")");
+
+    ok(w1, "The first watch expression should be present in the scope");
+    ok(w2, "The second watch expression should be present in the scope");
+    ok(w3, "The third watch expression should be present in the scope");
+    ok(w4, "The fourth watch expression should be present in the scope");
+    ok(w5, "The fifth watch expression should be present in the scope");
+    ok(w6, "The sixth watch expression should be present in the scope");
+    ok(w7, "The seventh watch expression should be present in the scope");
+    ok(w8, "The eight watch expression should be present in the scope");
+    ok(w9, "The ninth watch expression should be present in the scope");
+    ok(w10, "The tenth watch expression should be present in the scope");
+    ok(!w11, "The eleventh watch expression should not be present in the scope");
+    ok(!w12, "The twelveth watch expression should not be present in the scope");
+
+    is(w1.value, "a", "The first value is correct");
+    is(w2.value, "a", "The second value is correct");
+    is(w3.value, "a\"\"", "The third value is correct");
+    is(w4.value, "a''", "The fourth value is correct");
+    is(w5.value, "SyntaxError: syntax error", "The fifth value is correct");
+
+    if (typeof expected == "object") {
+      is(w6.value.type, expected.type, "The sixth value type is correct");
+      is(w6.value.class, expected.class, "The sixth value class is correct");
+    } else {
+      is(w6.value, expected, "The sixth value is correct");
+    }
+
+    is(w7.value.type, "object", "The seventh value type is correct");
+    is(w7.value.class, "Array", "The seventh value class is correct");
+
+    is(w8.value, "4", "The eight value is correct");
+
+    is(w9.value.type, "object", "The ninth value type is correct");
+    is(w9.value.class, "Array", "The ninth value class is correct");
+
+    is(w10.value, false, "The tenth value is correct");
+  }
+
+  registerCleanupFunction(function() {
+    removeTab(gTab);
+    gPane = null;
+    gTab = null;
+    gDebuggee = null;
+    gDebugger = null;
+    gWatch = null;
+    gVars = null;
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug740825_conditional-breakpoints-01.js
@@ -0,0 +1,384 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 740825: test the debugger conditional breakpoints.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_conditional-breakpoints.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gScripts = null;
+let gEditor = null;
+let gBreakpoints = null;
+let gBreakpointsPane = null;
+
+requestLongerTimeout(2);
+
+function test()
+{
+  let scriptShown = false;
+  let framesAdded = false;
+  let resumed = false;
+  let testStarted = false;
+
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.contentWindow;
+    gBreakpoints = gDebugger.DebuggerController.Breakpoints;
+    gBreakpointsPane = gDebugger.DebuggerView.Breakpoints;
+
+    gDebugger.DebuggerView.togglePanes({ visible: true, animated: false });
+    resumed = true;
+
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+      framesAdded = true;
+      executeSoon(startTest);
+    });
+
+    executeSoon(function() {
+      gDebuggee.ermahgerd(); // ermahgerd!!
+    });
+  });
+
+  function onScriptShown(aEvent)
+  {
+    scriptShown = aEvent.detail.url.indexOf("conditional-breakpoints") != -1;
+    executeSoon(startTest);
+  }
+
+  window.addEventListener("Debugger:SourceShown", onScriptShown);
+
+  function startTest()
+  {
+    if (scriptShown && framesAdded && resumed && !testStarted) {
+      window.removeEventListener("Debugger:SourceShown", onScriptShown);
+      testStarted = true;
+      Services.tm.currentThread.dispatch({ run: addBreakpoints }, 0);
+    }
+  }
+
+  function performTest()
+  {
+    gScripts = gDebugger.DebuggerView.Sources;
+
+    is(gDebugger.DebuggerController.activeThread.state, "paused",
+      "Should only be getting stack frames while paused.");
+
+    is(gScripts._container.itemCount, 1, "Found the expected number of scripts.");
+
+    gEditor = gDebugger.editor;
+
+    isnot(gEditor.getText().indexOf("ermahgerd"), -1,
+          "The correct script was loaded initially.");
+    is(gScripts.selectedValue, gScripts.values[0],
+          "The correct script is selected");
+
+    gBreakpoints = gPane.breakpoints;
+    is(Object.keys(gBreakpoints).length, 13, "thirteen breakpoints");
+    ok(!gPane.getBreakpoint("foo", 3), "getBreakpoint('foo', 3) returns falsey");
+
+    is(gEditor.getBreakpoints().length, 13, "thirteen breakpoints in the editor");
+
+    executeSoon(test1);
+  }
+
+  function test1(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 14, test2);
+  }
+
+  function test2(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 15, test3);
+  }
+
+  function test3(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 16, test4);
+  }
+
+  function test4(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 17, test5);
+  }
+
+  function test5(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 18, test6);
+  }
+
+  function test6(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 19, test7);
+  }
+
+  function test7(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 21, test8);
+  }
+
+  function test8(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 22, test9);
+  }
+
+  function test9(callback)
+  {
+    resumeAndTestBreakpoint(gScripts.selectedValue, 23, test10);
+  }
+
+  function test10(callback)
+  {
+    gDebugger.addEventListener("Debugger:AfterFramesCleared", function listener() {
+      gDebugger.removeEventListener("Debugger:AfterFramesCleared", listener, true);
+
+      is(gBreakpointsPane.selectedItem, null,
+        "There should be no selected breakpoint in the breakpoints pane.")
+      is(gBreakpointsPane._popupShown, false,
+        "The breakpoint conditional expression popup should not be shown.");
+
+      is(gDebugger.DebuggerView.StackFrames.visibleItems, 0,
+        "There should be no visible stackframes.");
+      is(gDebugger.DebuggerView.Breakpoints.visibleItems, 13,
+        "There should be thirteen visible breakpoints.");
+
+      testReload();
+    }, true);
+
+    gDebugger.DebuggerController.activeThread.resume();
+  }
+
+  function resumeAndTestBreakpoint(url, line, callback)
+  {
+    resume(line, function() {
+      waitForCaretPos(line - 1, function() {
+        testBreakpoint(gBreakpointsPane.selectedItem, gBreakpointsPane.selectedClient, url, line, true);
+        callback();
+      });
+    });
+  }
+
+  function testBreakpoint(aBreakpointItem, aBreakpointClient, url, line, editor)
+  {
+    is(aBreakpointItem.attachment.sourceLocation, gScripts.selectedValue,
+      "The breakpoint on line " + line + " wasn't added on the correct source.");
+    is(aBreakpointItem.attachment.lineNumber, line,
+      "The breakpoint on line " + line + " wasn't found.");
+    is(aBreakpointItem.attachment.enabled, true,
+      "The breakpoint on line " + line + " should be enabled.");
+    is(aBreakpointItem.attachment.isConditional, true,
+      "The breakpoint on line " + line + " should be conditional.");
+    is(gBreakpointsPane._popupShown, false,
+      "The breakpoint conditional expression popup should not be shown.");
+
+    is(aBreakpointClient.location.url, url,
+       "The breakpoint's client url is correct");
+    is(aBreakpointClient.location.line, line,
+       "The breakpoint's client line is correct");
+    isnot(aBreakpointClient.conditionalExpression, undefined,
+      "The breakpoint on line " + line + " should have a conditional expression.");
+
+    if (editor) {
+      is(gEditor.getCaretPosition().line + 1, line,
+        "The editor caret position is not situated on the proper line.");
+      is(gEditor.getCaretPosition().col, 0,
+        "The editor caret position is not situated on the proper column.");
+    }
+  }
+
+  function addBreakpoints(callback)
+  {
+    let currentUrl = gDebugger.DebuggerView.Sources.selectedValue;
+
+    gPane.addBreakpoint({ url: currentUrl, line: 12 }, function() {
+      gPane.addBreakpoint({ url: currentUrl, line: 13 }, function() {
+        gPane.addBreakpoint({ url: currentUrl, line: 14 }, function() {
+          gPane.addBreakpoint({ url: currentUrl, line: 15 }, function() {
+            gPane.addBreakpoint({ url: currentUrl, line: 16 }, function() {
+              gPane.addBreakpoint({ url: currentUrl, line: 17 }, function() {
+                gPane.addBreakpoint({ url: currentUrl, line: 18 }, function() {
+                  gPane.addBreakpoint({ url: currentUrl, line: 19 }, function() {
+                    gPane.addBreakpoint({ url: currentUrl, line: 20 }, function() {
+                      gPane.addBreakpoint({ url: currentUrl, line: 21 }, function() {
+                        gPane.addBreakpoint({ url: currentUrl, line: 22 }, function() {
+                          gPane.addBreakpoint({ url: currentUrl, line: 23 }, function() {
+                            gPane.addBreakpoint({ url: currentUrl, line: 24 }, function() {
+                              performTest();
+                            }, {
+                              conditionalExpression: "b"
+                            });
+                          }, {
+                            conditionalExpression: "a !== null"
+                          });
+                        }, {
+                          conditionalExpression: "a !== undefined"
+                        });
+                      }, {
+                        conditionalExpression: "a"
+                      });
+                    }, {
+                      conditionalExpression: "(function() { return false; })()"
+                    });
+                  }, {
+                    conditionalExpression: "(function() {})"
+                  });
+                }, {
+                  conditionalExpression: "({})"
+                });
+              }, {
+                conditionalExpression: "/regexp/"
+              });
+            }, {
+              conditionalExpression: "'nasu'"
+            });
+          }, {
+            conditionalExpression: "true"
+          });
+        }, {
+          conditionalExpression: "42"
+        });
+      }, {
+        conditionalExpression: "null"
+      });
+    }, {
+      conditionalExpression: "undefined"
+    });
+  }
+
+  function testReload()
+  {
+    function _get(url, line) {
+      return [
+        gDebugger.DebuggerView.Breakpoints.getBreakpoint(url, line),
+        gDebugger.DebuggerController.Breakpoints.getBreakpoint(url, line),
+        url, line, false
+      ];
+    }
+
+    waitForBreakpoints(13, function() {
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 14));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 15));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 16));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 17));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 18));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 19));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 21));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 22));
+      testBreakpoint.apply(this, _get(gScripts.selectedValue, 23));
+
+      is(gBreakpointsPane.selectedItem, null,
+        "There should be no selected item in the breakpoints pane.");
+      is(gBreakpointsPane.selectedClient, null,
+        "There should be no selected client in the breakpoints pane.");
+
+      closeDebuggerAndFinish();
+    });
+
+    finalCheck();
+    gDebuggee.location.reload();
+  }
+
+  function finalCheck() {
+    isnot(gEditor.getText().indexOf("ermahgerd"), -1,
+          "The correct script is still loaded.");
+    is(gScripts.selectedValue, gScripts.values[0],
+          "The correct script is still selected");
+  }
+
+  function resume(expected, callback) {
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+      Services.tm.currentThread.dispatch({ run: function() {
+        waitForBreakpoint(expected, callback);
+      }}, 0);
+    });
+
+    EventUtils.sendMouseEvent({ type: "mousedown" },
+      gDebugger.document.getElementById("resume"),
+      gDebugger);
+  }
+
+  let bogusClient = {
+    location: {
+      url: null,
+      line: null
+    }
+  };
+
+  function waitForBreakpoint(expected, callback) {
+    // Poll every few milliseconds until expected breakpoint is hit.
+    let count = 0;
+    let intervalID = window.setInterval(function() {
+      info("count: " + count + " ");
+      if (++count > 50) {
+        ok(false, "Timed out while polling for the breakpoint.");
+        window.clearInterval(intervalID);
+        return closeDebuggerAndFinish();
+      }
+      if ((gBreakpointsPane.selectedClient !== expected) &&
+          (gBreakpointsPane.selectedClient || bogusClient).location.line !== expected) {
+        return;
+      }
+      // We arrived at the expected line, it's safe to callback.
+      window.clearInterval(intervalID);
+      callback();
+    }, 100);
+  }
+
+  function waitForBreakpoints(total, callback)
+  {
+    // Poll every few milliseconds until the breakpoints are retrieved.
+    let count = 0;
+    let intervalID = window.setInterval(function() {
+      info("count: " + count + " ");
+      if (++count > 50) {
+        ok(false, "Timed out while polling for the breakpoints.");
+        window.clearInterval(intervalID);
+        return closeDebuggerAndFinish();
+      }
+      if (gBreakpointsPane.visibleItems != total) {
+        return;
+      }
+      // We got all the breakpoints, it's safe to callback.
+      window.clearInterval(intervalID);
+      callback();
+    }, 100);
+  }
+
+  function waitForCaretPos(number, callback)
+  {
+    // Poll every few milliseconds until the source editor line is active.
+    let count = 0;
+    let intervalID = window.setInterval(function() {
+      info("count: " + count + " ");
+      if (++count > 50) {
+        ok(false, "Timed out while polling for the line.");
+        window.clearInterval(intervalID);
+        return closeDebuggerAndFinish();
+      }
+      if (gEditor.getCaretPosition().line != number) {
+        return;
+      }
+      // We got the source editor at the expected line, it's safe to callback.
+      window.clearInterval(intervalID);
+      callback();
+    }, 100);
+  }
+
+  registerCleanupFunction(function() {
+    removeTab(gTab);
+    gPane = null;
+    gTab = null;
+    gDebuggee = null;
+    gDebugger = null;
+    gScripts = null;
+    gEditor = null;
+    gBreakpoints = null;
+    gBreakpointsPane = null;
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug740825_conditional-breakpoints-02.js
@@ -0,0 +1,511 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 740825: test the debugger conditional breakpoints.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_conditional-breakpoints.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gScripts = null;
+let gEditor = null;
+let gBreakpoints = null;
+let gBreakpointsPane = null;
+
+requestLongerTimeout(2);
+
+function test()
+{
+  let tempScope = {};
+  Cu.import("resource:///modules/source-editor.jsm", tempScope);
+  let SourceEditor = tempScope.SourceEditor;
+
+  let scriptShown = false;
+  let framesAdded = false;
+  let resumed = false;
+  let testStarted = false;
+
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.contentWindow;
+    gBreakpoints = gDebugger.DebuggerController.Breakpoints;
+    gBreakpointsPane = gDebugger.DebuggerView.Breakpoints;
+
+    gDebugger.DebuggerView.togglePanes({ visible: true, animated: false });
+    resumed = true;
+
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+      framesAdded = true;
+      executeSoon(startTest);
+    });
+
+    executeSoon(function() {
+      gDebuggee.ermahgerd(); // ermahgerd!!
+    });
+  });
+
+  function onScriptShown(aEvent)
+  {
+    scriptShown = aEvent.detail.url.indexOf("conditional-breakpoints") != -1;
+    executeSoon(startTest);
+  }
+
+  window.addEventListener("Debugger:SourceShown", onScriptShown);
+
+  function startTest()
+  {
+    if (scriptShown && framesAdded && resumed && !testStarted) {
+      window.removeEventListener("Debugger:SourceShown", onScriptShown);
+      testStarted = true;
+      Services.tm.currentThread.dispatch({ run: performTest }, 0);
+    }
+  }
+
+  function performTest()
+  {
+    gScripts = gDebugger.DebuggerView.Sources;
+
+    is(gDebugger.DebuggerController.activeThread.state, "paused",
+      "Should only be getting stack frames while paused.");
+
+    is(gScripts._container.itemCount, 1, "Found the expected number of scripts.");
+
+    gEditor = gDebugger.editor;
+