Merge from mozilla-central.
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 01 Apr 2013 21:48:56 +0200
changeset 138403 5fd27c1b3943e759973ec3cd13b5c35f8035745a
parent 138402 61f7ebb9f3d903556516bd6cbe8b84ae14c0fa33 (current diff)
parent 137804 aae004a3c5d9771fa4c8ee9c70ea274cc7623ee3 (diff)
child 138404 9baa31b30db4a246cce4882045cfb50450c6eb54
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.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 from mozilla-central.
browser/metro/base/tests/addons/browser_install1_1/bootstrap.js
browser/metro/base/tests/addons/browser_install1_1/install.rdf
browser/metro/base/tests/addons/browser_install1_2/install.rdf
browser/metro/base/tests/addons/browser_install1_3/install.rdf
browser/metro/base/tests/addons/browser_locale1/boostrap.js
browser/metro/base/tests/addons/browser_locale1/chrome.manifest
browser/metro/base/tests/addons/browser_locale1/install.rdf
browser/metro/base/tests/browser_canonizeURL.js
browser/metro/base/tests/browser_context_menu_tests.js
browser/metro/base/tests/browser_context_menu_tests_01.html
browser/metro/base/tests/browser_context_menu_tests_02.html
browser/metro/base/tests/browser_context_menu_tests_03.html
browser/metro/base/tests/browser_context_ui.js
browser/metro/base/tests/browser_downloads.js
browser/metro/base/tests/browser_onscreen_keyboard.html
browser/metro/base/tests/browser_onscreen_keyboard.js
browser/metro/base/tests/browser_plugin_input.html
browser/metro/base/tests/browser_plugin_input_keyboard.js
browser/metro/base/tests/browser_plugin_input_mouse.js
browser/metro/base/tests/browser_remotetabs.js
browser/metro/base/tests/browser_sanitize_ui.js
browser/metro/base/tests/browser_test.js
browser/metro/base/tests/browser_tilegrid.xul
browser/metro/base/tests/browser_tiles.js
browser/metro/base/tests/browser_topsites.js
browser/metro/base/tests/head.js
browser/metro/base/tests/res/image01.png
browser/metro/base/tests/text-block.html
content/svg/content/src/nsSVGFilters.cpp
dom/indexedDB/nsIIDBFactory.idl
dom/workers/RuntimeService.cpp
ipc/chromium/src/third_party/libevent/README.chromium
ipc/chromium/src/third_party/libevent/compat/sys/_time.h
ipc/chromium/src/third_party/libevent/evbuffer.c
ipc/chromium/src/third_party/libevent/evdns.3
ipc/chromium/src/third_party/libevent/event-config.h
ipc/chromium/src/third_party/libevent/event.3
ipc/chromium/src/third_party/libevent/evsignal.h
ipc/chromium/src/third_party/libevent/libevent.gyp
ipc/chromium/src/third_party/libevent/linux/config.h
ipc/chromium/src/third_party/libevent/log.h
ipc/chromium/src/third_party/libevent/mac/config.h
ipc/chromium/src/third_party/libevent/min_heap.h
ipc/chromium/src/third_party/libevent/mkinstalldirs
ipc/chromium/src/third_party/libevent/quotes.patch
ipc/chromium/src/third_party/libevent/sys-queue-macros.patch
js/src/ion/CodeGenerator.cpp
js/src/jsapi-tests/testIsAboutToBeFinalized.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsobj.cpp
js/src/vm/Debugger.cpp
js/src/vm/ObjectImpl-inl.h
js/src/vm/ObjectImpl.h
js/src/vm/ParallelDo.cpp
js/src/vm/ScopeObject.cpp
modules/libpref/src/init/all.js
--- a/.hgtags
+++ b/.hgtags
@@ -86,8 +86,9 @@ 0000000000000000000000000000000000000000
 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
 5bb309998e7050c9ee80b0147de1e473f008e221 FIREFOX_AURORA_20_BASE
 cc37417e2c284aed960f98ffa479de4ccdd5c7c3 FIREFOX_AURORA_21_BASE
+1c070ab0f9db59f13423b9c1db60419f7a9098f9 FIREFOX_AURORA_22_BASE
--- a/Makefile.in
+++ b/Makefile.in
@@ -30,24 +30,30 @@ include $(topsrcdir)/config/config.mk
 
 GARBAGE_DIRS += dist _javagen _profile _tests staticlib
 DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
    config/autoconf.mk \
    unallmakefiles mozilla-config.h \
    netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
    $(topsrcdir)/.mozconfig.mk $(topsrcdir)/.mozconfig.out
 
-default alldep all:: $(topsrcdir)/configure config.status
+default alldep all:: CLOBBER $(topsrcdir)/configure config.status
 	$(RM) -r $(DIST)/sdk
 	$(RM) -r $(DIST)/include
 	$(RM) -r $(DIST)/private
 	$(RM) -r $(DIST)/public
 	$(RM) $(DIST)/bin/chrome.manifest $(DIST)/bin/components/components.manifest
 	$(RM) -r _tests
 
+CLOBBER: $(topsrcdir)/CLOBBER
+	@echo "STOP!  The CLOBBER file has changed."
+	@echo "Please run the build through a sanctioned build wrapper, such as"
+	@echo "'mach build' or client.mk."
+	@exit 1
+
 $(topsrcdir)/configure: $(topsrcdir)/configure.in
 	@echo "STOP!  configure.in has changed, and your configure is out of date."
 	@echo "Please rerun autoconf and re-configure your build directory."
 	@echo "To ignore this message, touch 'configure' in the source directory,"
 	@echo "but your build might not succeed."
 	@exit 1
 
 config.status: $(topsrcdir)/configure
--- a/accessible/src/base/TextAttrs.cpp
+++ b/accessible/src/base/TextAttrs.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "TextAttrs.h"
 
+#include "Accessible-inl.h"
 #include "HyperTextAccessibleWrap.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "StyleInfo.h"
 
 #include "gfxFont.h"
 #include "gfxUserFontSet.h"
 #include "nsFontMetrics.h"
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -234,17 +234,17 @@ nsAccUtils::GetSelectableContainer(Acces
   if (!aAccessible)
     return nullptr;
 
   if (!(aState & states::SELECTABLE))
     return nullptr;
 
   Accessible* parent = aAccessible;
   while ((parent = parent->Parent()) && !parent->IsSelect()) {
-    if (Role(parent) == nsIAccessibleRole::ROLE_PANE)
+    if (parent->Role() == roles::PANE)
       return nullptr;
   }
   return parent;
 }
 
 bool
 nsAccUtils::IsARIASelected(Accessible* aAccessible)
 {
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -1,18 +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/. */
 
 #ifndef nsAccUtils_h_
 #define nsAccUtils_h_
 
-#include "nsIAccessible.h"
-#include "nsIAccessibleRole.h"
+#include "mozilla/a11y/Accessible.h"
 #include "nsIAccessibleText.h"
 
 #include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 
 #include "mozilla/dom/Element.h"
 #include "nsIDocShell.h"
 #include "nsIPersistentProperties2.h"
@@ -188,28 +187,16 @@ public:
   /**
    * Returns coordinates relative screen for the parent of the given accessible.
    *
    * @param [in] aAccessible  the accessible
    */
   static nsIntPoint GetScreenCoordsForParent(Accessible* aAccessible);
 
   /**
-   * Return the role of the given accessible.
-   */
-  static uint32_t Role(nsIAccessible *aAcc)
-  {
-    uint32_t role = nsIAccessibleRole::ROLE_NOTHING;
-    if (aAcc)
-      aAcc->GetRole(&role);
-
-    return role;
-  }
-
-  /**
    * Get the ARIA attribute characteristics for a given ARIA attribute.
    * 
    * @param aAtom  ARIA attribute
    * @return       A bitflag representing the attribute characteristics
    *               (see nsARIAMap.h for possible bit masks, prefixed "ARIA_")
    */
   static uint8_t GetAttributeCharacteristics(nsIAtom* aAtom);
 
@@ -235,22 +222,22 @@ public:
   /**
    * Return text length of the given accessible, return 0 on failure.
    */
   static uint32_t TextLength(Accessible* aAccessible);
 
   /**
    * Return true if the given accessible is embedded object.
    */
-  static bool IsEmbeddedObject(nsIAccessible *aAcc)
+  static bool IsEmbeddedObject(Accessible* aAcc)
   {
-    uint32_t role = Role(aAcc);
-    return role != nsIAccessibleRole::ROLE_TEXT_LEAF &&
-           role != nsIAccessibleRole::ROLE_WHITESPACE &&
-           role != nsIAccessibleRole::ROLE_STATICTEXT;
+    uint32_t role = aAcc->Role();
+    return role != roles::TEXT_LEAF &&
+           role != roles::WHITESPACE &&
+           role != roles::STATICTEXT;
   }
 
   /**
    * Transform nsIAccessibleStates constants to internal state constant.
    */
   static inline uint64_t To64State(uint32_t aState1, uint32_t aState2)
   {
     return static_cast<uint64_t>(aState1) +
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -32,16 +32,17 @@
 #include "Statistics.h"
 #include "TextLeafAccessibleWrap.h"
 
 #ifdef MOZ_ACCESSIBILITY_ATK
 #include "AtkSocketAccessible.h"
 #endif
 
 #ifdef XP_WIN
+#include "mozilla/a11y/Compatibility.h"
 #include "HTMLWin32ObjectAccessible.h"
 #endif
 
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
 #ifdef MOZ_CRASHREPORTER
@@ -58,16 +59,17 @@
 #include "nsObjectFrame.h"
 #include "nsSVGPathGeometryFrame.h"
 #include "nsTreeBodyFrame.h"
 #include "nsTreeColumns.h"
 #include "nsTreeUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/Util.h"
 #include "nsDeckFrame.h"
 
 #ifdef MOZ_XUL
 #include "XULAlertAccessible.h"
 #include "XULColorPickerAccessible.h"
 #include "XULComboboxAccessible.h"
 #include "XULElementAccessibles.h"
@@ -205,31 +207,87 @@ nsAccessibilityService::GetRootDocumentA
       }
 
       return aCanCreate ? GetDocAccessible(ps) : ps->GetDocAccessible();
     }
   }
   return nullptr;
 }
 
+#ifdef XP_WIN
+static StaticAutoPtr<nsTArray<nsCOMPtr<nsIContent> > > sPendingPlugins;
+static StaticAutoPtr<nsTArray<nsCOMPtr<nsITimer> > > sPluginTimers;
+
+class PluginTimerCallBack MOZ_FINAL : public nsITimerCallback
+{
+public:
+  PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {}
+
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL
+  {
+    nsIPresShell* ps = mContent->OwnerDoc()->GetShell();
+    if (ps) {
+      DocAccessible* doc = ps->GetDocAccessible();
+      if (doc) {
+        // Make sure that if we created an accessible for the plugin that wasn't
+        // a plugin accessible we remove it before creating the right accessible.
+        doc->RecreateAccessible(mContent);
+        sPluginTimers->RemoveElement(aTimer);
+        return NS_OK;
+      }
+    }
+
+    // We couldn't get a doc accessible so presumably the document went away.
+    // In this case don't leak our ref to the content or timer.
+    sPendingPlugins->RemoveElement(mContent);
+    sPluginTimers->RemoveElement(aTimer);
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIContent> mContent;
+};
+
+NS_IMPL_ISUPPORTS1(PluginTimerCallBack, nsITimerCallback)
+#endif
+
 already_AddRefed<Accessible>
 nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame,
                                                nsIContent* aContent,
                                                Accessible* aContext)
 {
   // nsObjectFrame means a plugin, so we need to use the accessibility support
   // of the plugin.
   if (aFrame->GetRect().IsEmpty())
     return nullptr;
 
 #if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
   nsRefPtr<nsNPAPIPluginInstance> pluginInstance;
   if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) &&
       pluginInstance) {
 #ifdef XP_WIN
+    if (!sPendingPlugins->Contains(aContent) &&
+        (Preferences::GetBool("accessibility.delay_plugins") ||
+         Compatibility::IsJAWS() || Compatibility::IsWE())) {
+      nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+      nsRefPtr<PluginTimerCallBack> cb = new PluginTimerCallBack(aContent);
+      timer->InitWithCallback(cb, Preferences::GetUint("accessibility.delay_plugin_time"),
+                              nsITimer::TYPE_ONE_SHOT);
+      sPluginTimers->AppendElement(timer);
+      sPendingPlugins->AppendElement(aContent);
+      return nullptr;
+    }
+
+    // We need to remove aContent from the pending plugins here to avoid
+    // reentrancy.  When the timer fires it calls
+    // DocAccessible::ContentInserted() which does the work async.
+    sPendingPlugins->RemoveElement(aContent);
+
     // Note: pluginPort will be null if windowless.
     HWND pluginPort = nullptr;
     aFrame->GetPluginPort(&pluginPort);
 
     Accessible* accessible =
       new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(),
                                          pluginPort);
     NS_ADDREF(accessible);
@@ -999,16 +1057,21 @@ nsAccessibilityService::Init()
   NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
 
 #ifdef MOZ_CRASHREPORTER
   CrashReporter::
     AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"),
                         NS_LITERAL_CSTRING("Active"));
 #endif
 
+#ifdef XP_WIN
+  sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >;
+  sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >;
+#endif
+
   gIsShutdown = false;
 
   // Now its safe to start platform accessibility.
   PlatformInit();
 
   return true;
 }
 
@@ -1025,16 +1088,26 @@ nsAccessibilityService::Shutdown()
     observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator);
   }
 
   // Stop accessible document loader.
   DocManager::Shutdown();
 
   SelectionManager::Shutdown();
 
+#ifdef XP_WIN
+  sPendingPlugins = nullptr;
+
+  uint32_t timerCount = sPluginTimers->Length();
+  for (uint32_t i = 0; i < timerCount; i++)
+    sPluginTimers->ElementAt(i)->Cancel();
+
+  sPluginTimers = nullptr;
+#endif
+
   // Application is going to be closed, shutdown accessibility and mark
   // accessibility service as shutdown to prevent calls of its methods.
   // Don't null accessibility service static member at this point to be safe
   // if someone will try to operate with it.
 
   NS_ASSERTION(!gIsShutdown, "Accessibility was shutdown already");
 
   gIsShutdown = true;
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -9,16 +9,17 @@
 
 #include "AccCollector.h"
 #include "AccGroupInfo.h"
 #include "AccIterator.h"
 #include "nsAccUtils.h"
 #include "nsAccessibleRelation.h"
 #include "nsAccessibilityService.h"
 #include "nsIAccessibleRelation.h"
+#include "nsIAccessibleRole.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "States.h"
 #include "StyleInfo.h"
 #include "TableAccessible.h"
@@ -2649,17 +2650,18 @@ Accessible::InsertChildAt(uint32_t aInde
     if (!mChildren.AppendElement(aChild))
       return false;
 
   } else {
     if (!mChildren.InsertElementAt(aIndex, aChild))
       return false;
 
     for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
-      NS_ASSERTION(mChildren[idx]->mIndexInParent == idx - 1, "Accessible child index doesn't match");
+      NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx - 1,
+                   "Accessible child index doesn't match");
       mChildren[idx]->mIndexInParent = idx;
     }
 
     mEmbeddedObjCollector = nullptr;
   }
 
   if (!nsAccUtils::IsEmbeddedObject(aChild))
     SetChildrenFlag(eMixedChildren);
@@ -2680,17 +2682,18 @@ Accessible::RemoveChild(Accessible* aChi
   uint32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
   if (index >= mChildren.Length() || mChildren[index] != aChild) {
     NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
     aChild->UnbindFromParent();
     return false;
   }
 
   for (uint32_t idx = index + 1; idx < mChildren.Length(); idx++) {
-    NS_ASSERTION(mChildren[idx]->mIndexInParent == idx, "Accessible child index doesn't match");
+    NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx,
+                 "Accessible child index doesn't match");
     mChildren[idx]->mIndexInParent = idx - 1;
   }
 
   aChild->UnbindFromParent();
   mChildren.RemoveElementAt(index);
   mEmbeddedObjCollector = nullptr;
 
   return true;
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
 /* 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 "HyperTextAccessible.h"
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
@@ -775,16 +776,47 @@ HyperTextAccessible::GetRelativeOffset(n
     if (!aNeedsStart) {
       -- hyperTextOffset;
     }
   }
 
   return hyperTextOffset;
 }
 
+int32_t
+HyperTextAccessible::FindWordBoundary(int32_t aOffset, nsDirection aDirection,
+                                      EWordMovementType aWordMovementType)
+{
+  // Convert hypertext offset to frame-relative offset.
+  int32_t offsetInFrame = aOffset, notUsedOffset = aOffset;
+  nsRefPtr<Accessible> accAtOffset;
+  nsIFrame* frameAtOffset =
+    GetPosAndText(offsetInFrame, notUsedOffset, nullptr, nullptr,
+                  nullptr, getter_AddRefs(accAtOffset));
+  if (!frameAtOffset) {
+    if (aOffset == CharacterCount()) {
+      // Asking for start of line, while on last character.
+      if (accAtOffset)
+        frameAtOffset = accAtOffset->GetFrame();
+    }
+    NS_ASSERTION(frameAtOffset, "No start frame for text getting!");
+    if (!frameAtOffset)
+      return -1;
+
+    // We're on the last continuation since we're on the last character.
+    frameAtOffset = frameAtOffset->GetLastContinuation();
+  }
+
+  // Return hypertext offset of the boundary of the found word.
+  return GetRelativeOffset(mDoc->PresShell(), frameAtOffset, offsetInFrame,
+                           accAtOffset, eSelectWord, aDirection,
+                           (aWordMovementType == eStartWord),
+                           aWordMovementType);
+}
+
 /*
 Gets the specified text relative to aBoundaryType, which means:
 BOUNDARY_CHAR             The character before/at/after the offset is returned.
 BOUNDARY_WORD_START       From the word start before/at/after the offset to the next word start.
 BOUNDARY_WORD_END         From the word end before/at/after the offset to the next work end.
 BOUNDARY_LINE_START       From the line start before/at/after the offset to the next line start.
 BOUNDARY_LINE_END         From the line end before/at/after the offset to the next line start.
 */
@@ -967,145 +999,122 @@ NS_IMETHODIMP
 HyperTextAccessible::GetTextBeforeOffset(int32_t aOffset,
                                          AccessibleTextBoundary aBoundaryType,
                                          int32_t* aStartOffset,
                                          int32_t* aEndOffset, nsAString& aText)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  if (aBoundaryType == BOUNDARY_CHAR) {
-    GetCharAt(aOffset, eGetBefore, aText, aStartOffset, aEndOffset);
-    return NS_OK;
+  int32_t offset = ConvertMagicOffset(aOffset);
+  if (offset < 0)
+    return NS_ERROR_INVALID_ARG;
+
+  switch (aBoundaryType) {
+    case BOUNDARY_CHAR:
+      GetCharAt(offset, eGetBefore, aText, aStartOffset, aEndOffset);
+      return NS_OK;
+
+    case BOUNDARY_WORD_START: {
+      if (offset == 0) { // no word before 0 offset
+        *aStartOffset = *aEndOffset = 0;
+        return NS_OK;
+      }
+
+      // If the offset is a word start then move backward to find start offset
+      // (end offset is the given offset). Otherwise move backward twice to find
+      // both start and end offsets.
+      int32_t midOffset = FindWordBoundary(offset, eDirPrevious, eStartWord);
+      *aEndOffset = FindWordBoundary(midOffset, eDirNext, eStartWord);
+      if (*aEndOffset == offset) {
+        *aStartOffset = midOffset;
+        return GetText(*aStartOffset, *aEndOffset, aText);
+      }
+
+      *aStartOffset = FindWordBoundary(midOffset, eDirPrevious, eStartWord);
+      *aEndOffset = midOffset;
+      return GetText(*aStartOffset, *aEndOffset, aText);
+    }
+
+    case BOUNDARY_WORD_END: {
+      if (offset == 0) { // no word before 0 offset
+        *aStartOffset = *aEndOffset = 0;
+        return NS_OK;
+      }
+
+      // Move word backward twice to find start and end offsets.
+      *aEndOffset = FindWordBoundary(offset, eDirPrevious, eEndWord);
+      *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
+      return GetText(*aStartOffset, *aEndOffset, aText);
+    }
+
+    case BOUNDARY_LINE_START:
+    case BOUNDARY_LINE_END:
+    case BOUNDARY_ATTRIBUTE_RANGE:
+      return GetTextHelper(eGetBefore, aBoundaryType, aOffset,
+                           aStartOffset, aEndOffset, aText);
+
+    default:
+      return NS_ERROR_INVALID_ARG;
   }
-
-  return GetTextHelper(eGetBefore, aBoundaryType, aOffset,
-                       aStartOffset, aEndOffset, aText);
 }
 
 NS_IMETHODIMP
 HyperTextAccessible::GetTextAtOffset(int32_t aOffset,
                                      AccessibleTextBoundary aBoundaryType,
                                      int32_t* aStartOffset,
                                      int32_t* aEndOffset, nsAString& aText)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsSelectionAmount selectionAmount = eSelectWord;
+  int32_t offset = ConvertMagicOffset(aOffset);
+  if (offset < 0)
+    return NS_ERROR_INVALID_ARG;
+
   EWordMovementType wordMovementType = eDefaultBehavior;
-  bool forwardBack = true;
+  bool moveForwardThenBack = true;
 
   switch (aBoundaryType) {
     case BOUNDARY_CHAR:
       return GetCharAt(aOffset, eGetAt, aText, aStartOffset, aEndOffset) ?
         NS_OK : NS_ERROR_INVALID_ARG;
 
-    case BOUNDARY_WORD_START:
-      wordMovementType = eStartWord;
-      break;
+    case BOUNDARY_WORD_START: {
+      uint32_t textLen =  CharacterCount();
+      if (offset == textLen) {
+        *aStartOffset = *aEndOffset = textLen;
+        return NS_OK;
+      }
 
-    case BOUNDARY_WORD_END:
-      wordMovementType = eEndWord;
-      forwardBack = false;
-      break;
+      *aEndOffset = FindWordBoundary(offset, eDirNext, eStartWord);
+      *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
+      return GetText(*aStartOffset, *aEndOffset, aText);
+    }
+
+    case BOUNDARY_WORD_END: {
+      if (offset == 0) {
+        *aStartOffset = *aEndOffset = 0;
+        return NS_OK;
+      }
+
+      *aStartOffset = FindWordBoundary(offset, eDirPrevious, eEndWord);
+      *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
+      return GetText(*aStartOffset, *aEndOffset, aText);
+    }
 
     case BOUNDARY_LINE_START:
     case BOUNDARY_LINE_END:
+    case BOUNDARY_ATTRIBUTE_RANGE:
       return GetTextHelper(eGetAt, aBoundaryType, aOffset,
                            aStartOffset, aEndOffset, aText);
-      break;
-
-    case BOUNDARY_ATTRIBUTE_RANGE:
-    {
-      nsresult rv = GetTextAttributes(false, aOffset,
-                                      aStartOffset, aEndOffset, nullptr);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      return GetText(*aStartOffset, *aEndOffset, aText);
-    }
 
     default:
       return NS_ERROR_INVALID_ARG;
   }
-
-  int32_t offset = ConvertMagicOffset(aOffset);
-  if (offset < 0)
-    return NS_ERROR_INVALID_ARG;
-
-  uint32_t textLen =  CharacterCount();
-  if (forwardBack) {
-    if (offset == textLen) {
-      *aStartOffset = *aEndOffset = textLen;
-      return NS_OK;
-    }
-  } else {
-    if (offset == 0) {
-      *aStartOffset = *aEndOffset = 0;
-      return NS_OK;
-    }
-  }
-
-  // Convert offsets to frame-relative
-  int32_t startOffset = offset, endOffset = offset;
-  nsRefPtr<Accessible> startAcc;
-  nsIFrame* startFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
-                                       nullptr, getter_AddRefs(startAcc));
-  if (!startFrame) {
-    if (offset == textLen) {
-      // Asking for start of line, while on last character
-      if (startAcc)
-        startFrame = startAcc->GetFrame();
-    }
-    NS_ASSERTION(startFrame, "No start frame for text getting!");
-    if (!startFrame)
-      return NS_ERROR_FAILURE;
-
-    // We're on the last continuation since we're on the last character.
-    startFrame = startFrame->GetLastContinuation();
-  }
-
-  offset = GetRelativeOffset(mDoc->PresShell(), startFrame, startOffset,
-                             startAcc, selectionAmount,
-                             (forwardBack ? eDirNext : eDirPrevious),
-                             forwardBack, wordMovementType);
-
-  if (forwardBack)
-    *aEndOffset = offset;
-  else
-    *aStartOffset = offset;
-
-  startOffset = endOffset = offset;
-  startFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
-                             nullptr, getter_AddRefs(startAcc));
-  if (!startFrame) {
-    if (offset == textLen) {
-      // Asking for start of line, while on last character
-      if (startAcc)
-        startFrame = startAcc->GetFrame();
-    }
-    NS_ASSERTION(startFrame, "No start frame for text getting!");
-    if (!startFrame)
-      return NS_ERROR_FAILURE;
-
-    // We're on the last continuation since we're on the last character.
-    startFrame = startFrame->GetLastContinuation();
-  }
-
-  offset = GetRelativeOffset(mDoc->PresShell(), startFrame, startOffset,
-                             startAcc, selectionAmount,
-                             (forwardBack ? eDirPrevious : eDirNext),
-                             forwardBack, wordMovementType);
-
-  if (forwardBack)
-    *aStartOffset = offset;
-  else
-    *aEndOffset = offset;
-
-  return GetText(*aStartOffset, *aEndOffset, aText);
 }
 
 NS_IMETHODIMP
 HyperTextAccessible::GetTextAfterOffset(int32_t aOffset,
                                         AccessibleTextBoundary aBoundaryType,
                                         int32_t* aStartOffset,
                                         int32_t* aEndOffset, nsAString& aText)
 {
@@ -1267,37 +1276,58 @@ HyperTextAccessible::NativeAttributes()
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::lineNumber, strLineNumber);
     }
   }
 
   if (!HasOwnContent())
     return attributes.forget();
 
   // For the html landmark elements we expose them like we do aria landmarks to
-  // make AT navigation schemes "just work". Note html:header is redundant as
-  // a landmark since it usually contains headings. We're not yet sure how the
-  // web will use html:footer but our best bet right now is as contentinfo.
-  if (mContent->Tag() == nsGkAtoms::nav)
+  // make AT navigation schemes "just work".
+  nsIAtom* tag = mContent->Tag();
+  if (tag == nsGkAtoms::nav) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("navigation"));
-  else if (mContent->Tag() == nsGkAtoms::section) 
+  } else if (tag == nsGkAtoms::section)  {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("region"));
-  else if (mContent->Tag() == nsGkAtoms::footer) 
+  } else if (tag == nsGkAtoms::header || tag == nsGkAtoms::footer) {
+    // Only map header and footer if they are not descendants
+    // of an article or section tag.
+    nsIContent* parent = mContent->GetParent();
+    while (parent) {
+      if (parent->Tag() == nsGkAtoms::article ||
+          parent->Tag() == nsGkAtoms::section)
+        break;
+      parent = parent->GetParent();
+    }
+
+    // No article or section elements found.
+    if (!parent) {
+      if (tag == nsGkAtoms::header) {
+        nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
+                               NS_LITERAL_STRING("banner"));
+      } else if (tag == nsGkAtoms::footer) {
+        nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
+                               NS_LITERAL_STRING("contentinfo"));
+      }
+    }
+  } else if (tag == nsGkAtoms::footer) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("contentinfo"));
-  else if (mContent->Tag() == nsGkAtoms::aside) 
+  } else if (tag == nsGkAtoms::aside) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("complementary"));
-  else if (mContent->Tag() == nsGkAtoms::article)
+  } else if (tag == nsGkAtoms::article) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("article"));
-  else if (mContent->Tag() == nsGkAtoms::main)
+  } else if (tag == nsGkAtoms::main) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                            NS_LITERAL_STRING("main"));
+  }
 
   return attributes.forget();
 }
 
 /*
  * Given an offset, the x, y, width, and height values are filled appropriately.
  */
 NS_IMETHODIMP
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -258,16 +258,22 @@ protected:
       int32_t caretOffset = -1;
       GetCaretOffset(&caretOffset);
       return caretOffset;
     }
 
     return aOffset;
   }
 
+  /**
+   * Return an offset of the found word boundary.
+   */
+  int32_t FindWordBoundary(int32_t aOffset, nsDirection aDirection,
+                           EWordMovementType aWordMovementType);
+
   /*
    * This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
    * @param aType, eGetBefore, eGetAt, eGetAfter
    * @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute
    * @param aOffset, offset into the hypertext to start from
    * @param *aStartOffset, the resulting start offset for the returned substring
    * @param *aEndOffset, the resulting end offset for the returned substring
    * @param aText, the resulting substring
--- a/accessible/tests/mochitest/attributes/test_xml-roles.html
+++ b/accessible/tests/mochitest/attributes/test_xml-roles.html
@@ -16,17 +16,22 @@
           src="../attributes.js"></script>
 
   <script type="application/javascript">
 
     function doTest()
     {
       // Some AT may look for this
       testAttrs("nav", {"xml-roles" : "navigation"}, true);
+      testAttrs("header", {"xml-roles" : "banner"}, true);
+      testAbsentAttrs("article_header", {"xml-roles" : "banner"});
+      testAbsentAttrs("section_header", {"xml-roles" : "banner"});
       testAttrs("footer", {"xml-roles" : "contentinfo"}, true);
+      testAbsentAttrs("article_footer", {"xml-roles" : "contentinfo"});
+      testAbsentAttrs("section_footer", {"xml-roles" : "contentinfo"});
       testAttrs("aside", {"xml-roles" : "complementary"}, true);
       testAttrs("section", {"xml-roles" : "region"}, true);
       testAttrs("main", {"xml-roles" : "main"}, true); // // ARIA override
       testAttrs("form", {"xml-roles" : "form"}, true);
       testAttrs("article", {"xml-roles" : "article"}, true);
       testAttrs("main_element", {"xml-roles" : "main"}, true);
 
       SimpleTest.finish();
@@ -63,23 +68,37 @@
      title="Map ARIA role FORM">
     Bug 734982
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=761891"
      title="HTML5 article element should expose xml-roles:article object attribute">
     Bug 761891
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=849624"
+     title="modify HTML5 header and footer accessibility API mapping">
+    Bug 849624
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <nav id="nav">a nav</nav>
+  <header id="header">a header</header>
   <footer id="footer">a footer</footer>
+  <article id="article_with_header_and_footer">
+    <header id="article_header">a header within an article</header>
+    <footer id="article_footer">a footer within an article</footer>
+  </article>
+  <section id="section_with_header_and_footer">
+    <header id="section_header">a header within an section</header>
+    <footer id="section_footer">a footer within an section</footer>
+  </section>
   <aside id="aside">by the way I am an aside</aside>
   <section id="section">a section</section>
   <article id="main" role="main">a main area</article>
   <article id="form" role="form">a form area</article>
   <article id="article">article</article>
   <main id="main_element">another main area</main>
 
 </body>
--- a/accessible/tests/mochitest/states/test_tree.xul
+++ b/accessible/tests/mochitest/states/test_tree.xul
@@ -91,20 +91,18 @@
       gQueue.push(new statesChecker("tree", new nsTreeTreeView()));
       gQueue.push(new statesChecker("treesingle", new nsTreeTreeView()));
       gQueue.push(new statesChecker("tabletree", new nsTreeTreeView()));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     if (MAC && (navigator.userAgent.indexOf("Mac OS X 10.6") != -1)) {
-
       todo(false,
            "Re-enable on Mac OS 10.6 after fixing bug 845095 - intermittent orange");
-      SimpleTest.finish();
     } else {
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
     }
   ]]>
   </script>
 
   <hbox flex="1" style="overflow: auto;">
--- a/accessible/tests/mochitest/text/test_multiline.html
+++ b/accessible/tests/mochitest/text/test_multiline.html
@@ -11,17 +11,17 @@
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
 
     function doTest()
     {
-      SimpleTest.expectAssertions(54);
+      SimpleTest.expectAssertions(44);
 
       // __o__n__e__w__o__r__d__\n
       //  0  1  2  3  4  5  6  7
       // __\n
       //  8
       // __t__w__o__ __w__o__r__d__s__\n
       //  9 10 11 12 13 14 15 16 17 18
 
@@ -192,106 +192,46 @@
       testTextBeforeOffset(10, BOUNDARY_CHAR, "t", 9, 10,
                            "div", kOk, kOk, kOk,
                            "divbr", kOk, kOk, kOk,
                            "editable", kOk, kOk, kOk,
                            "editablebr", kOk, kOk, kOk,
                            "textarea", kOk, kOk, kOk);
 
       // BOUNDARY_WORD_START
-      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
-                           "div", kOk, kOk, kOk,
-                           "divbr", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "editablebr", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(7, BOUNDARY_WORD_START, "", 0, 0,
-                           "div", kTodo, kOk, kTodo,
-                           "divbr", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "editablebr", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(8, BOUNDARY_WORD_START, "", 0, 0,
-                           "div", kTodo, kOk, kTodo,
-                           "divbr", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "editablebr", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(9, BOUNDARY_WORD_START, "oneword\n\n", 0, 9,
-                           "div", kOk, kOk, kOk,
-                           "divbr", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "editablebr", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(13, BOUNDARY_WORD_START, "two ", 9, 13,
-                           "div", kOk, kOk, kOk,
-                           "divbr", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "editablebr", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(18, BOUNDARY_WORD_START, "two ", 9, 13,
-                           "div", kTodo, kTodo, kTodo,
-                           "divbr", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "editablebr", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(19, BOUNDARY_WORD_START, "two ", 9, 13,
-                           "div", kTodo, kTodo, kTodo,
-                           "divbr", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "editablebr", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(7, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(8, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(9, BOUNDARY_WORD_START, "oneword\n\n", 0, 9, IDs);
+      testTextBeforeOffset(13, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
+      testTextBeforeOffset(18, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
+      testTextBeforeOffset(19, BOUNDARY_WORD_START, "words\n", 13, 19, IDs);
 
       // BOUNDARY_WORD_END
-      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
+      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(7, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(8, BOUNDARY_WORD_END, "oneword", 0, 7,
                            "div", kOk, kOk, kOk,
-                           "divbr", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "editablebr", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(7, BOUNDARY_WORD_END, "", 0, 0,
-                           "div", kTodo, kOk, kTodo,
                            "divbr", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "editablebr", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(8, BOUNDARY_WORD_END, "oneword", 0, 7,
-                           "div", kTodo, kTodo, kTodo,
-                           "divbr", kTodo, kOk, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "editablebr", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(9, BOUNDARY_WORD_END, "oneword", 0, 7,
-                           "div", kTodo, kTodo, kTodo,
-                           "divbr", kTodo, kOk, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
+                           "editable", kOk, kOk, kOk,
                            "editablebr", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(12, BOUNDARY_WORD_END, "oneword", 0, 7,
-                           "div", kTodo, kTodo, kTodo,
-                           "divbr", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "editablebr", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(13, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
-                           "div", kTodo, kTodo, kTodo,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(9, BOUNDARY_WORD_END, "oneword", 0, 7,
+                           "div", kOk, kOk, kOk,
+                           "divbr", kTodo, kOk, kTodo,
+                           "editable", kOk, kOk, kOk,
+                           "editablebr", kTodo, kOk, kTodo,
+                           "textarea", kOk, kOk, kOk);
+      testTextBeforeOffset(12, BOUNDARY_WORD_END, "oneword", 0, 7, IDs);
+      testTextBeforeOffset(13, BOUNDARY_WORD_END, "\n\ntwo", 7, 12, IDs);
+      testTextBeforeOffset(18, BOUNDARY_WORD_END, "\n\ntwo", 7, 12, IDs);
+      testTextBeforeOffset(19, BOUNDARY_WORD_END, " words", 12, 18,
+                           "div", kOk, kOk, kOk,
                            "divbr", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "editablebr", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(18, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
-                           "div", kTodo, kTodo, kTodo,
-                           "divbr", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "editablebr", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(19, BOUNDARY_WORD_END, " words", 13, 18,
-                           "div", kTodo, kTodo, kTodo,
-                           "divbr", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
+                           "editable", kOk, kOk, kOk,
                            "editablebr", kTodo, kTodo, kTodo,
                            "textarea", kTodo, kTodo, kTodo);
 
       // BOUNDARY_LINE_START
       testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0,
                            "div", kOk, kOk, kOk,
                            "divbr", kOk, kOk, kOk,
                            "editable", kOk, kOk, kOk,
@@ -518,16 +458,26 @@
 </head>
 <body>
 
   <a target="_blank"
      title="nsIAccessibleText getText related functions test in multiline text"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=612331">
    Bug 612331
   </a>
+  <a target="_blank"
+     title="getTextAtOffset for word boundaries: beginning of a new life"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
+    Bug 853340
+  </a>
+  <a target="_blank"
+     title="getTextBeforeOffset for word boundaries: evolving"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
+    Bug 855732
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
 
   <div id="div">oneword
 
 two words
 </div>
--- a/accessible/tests/mochitest/text/test_singleline.html
+++ b/accessible/tests/mochitest/text/test_singleline.html
@@ -7,19 +7,19 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
     if (navigator.platform.startsWith("Mac")) {
-      SimpleTest.expectAssertions(0, 28);
+      SimpleTest.expectAssertions(0, 20);
     } else {
-      SimpleTest.expectAssertions(28);
+      SimpleTest.expectAssertions(20);
     }
 
     function doTest()
     {
       // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
       //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 
       ////////////////////////////////////////////////////////////////////////
@@ -213,118 +213,38 @@
 
       // BOUNDARY_CHAR
       testCharBeforeOffset(IDs, 0, "", 0, 0);
       testCharBeforeOffset(IDs, 1, "h", 0, 1);
       testCharBeforeOffset(IDs, 14, "n", 13, 14);
       testCharBeforeOffset(IDs, 15, "d", 14, 15);
 
       // BOUNDARY_WORD_START
-      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(6, BOUNDARY_WORD_START, "hello ", 0, 6,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(7, BOUNDARY_WORD_START, "hello ", 0, 6,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(8, BOUNDARY_WORD_START, "hello ", 0, 6,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(9, BOUNDARY_WORD_START, "my ", 6, 9,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(10, BOUNDARY_WORD_START, "my ", 6, 9,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(14, BOUNDARY_WORD_START, "my ", 6, 9,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(15, BOUNDARY_WORD_START, "my ", 6, 9,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(6, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
+      testTextBeforeOffset(7, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
+      testTextBeforeOffset(8, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
+      testTextBeforeOffset(9, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
+      testTextBeforeOffset(10, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
+      testTextBeforeOffset(14, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
+      testTextBeforeOffset(15, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
 
       // BOUNDARY_WORD_END
-      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(6, BOUNDARY_WORD_END, "hello ", 0, 6,
-                           "input", kTodo, kTodo, kOk,
-                           "div", kTodo, kTodo, kOk,
-                           "editable", kTodo, kTodo, kOk,
-                           "textarea", kTodo, kTodo, kOk);
-      testTextBeforeOffset(7, BOUNDARY_WORD_END, "hello ", 0, 6,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(8, BOUNDARY_WORD_END, "hello ", 0, 6,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(9, BOUNDARY_WORD_END, " my", 5, 8,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(10, BOUNDARY_WORD_END, " my", 5, 8,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(14, BOUNDARY_WORD_END, " my", 5, 8,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(15, BOUNDARY_WORD_END, " my", 5, 8,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(6, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
+      testTextBeforeOffset(7, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
+      testTextBeforeOffset(8, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
+      testTextBeforeOffset(9, BOUNDARY_WORD_END, " my", 5, 8, IDs);
+      testTextBeforeOffset(10, BOUNDARY_WORD_END, " my", 5, 8, IDs);
+      testTextBeforeOffset(14, BOUNDARY_WORD_END, " my", 5, 8, IDs);
+      testTextBeforeOffset(15, BOUNDARY_WORD_END, " my", 5, 8, IDs);
 
       // BOUNDARY_LINE_START
       testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0,
                            "input", kOk, kOk, kOk,
                            "div", kOk, kOk, kOk,
                            "editable", kOk, kOk, kOk,
                            "textarea", kOk, kOk, kOk);
       testTextBeforeOffset(1, BOUNDARY_LINE_START, "", 0, 0,
@@ -458,17 +378,29 @@
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      title="nsIAccessibleText getText related function tests for html:input,html:div and html:textarea"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">Mozilla Bug 452769</a>
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">
+    Bug 452769
+  </a>
+  <a target="_blank"
+     title="getTextAtOffset for word boundaries: beginning of a new life"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
+    Bug 853340
+  </a>
+  <a target="_blank"
+     title="getTextBeforeOffset for word boundaries: evolving"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
+    Bug 855732
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <input id="input" value="hello my friend"/>
   <div id="div">hello my friend</div>
   <div id="editable" contenteditable="true">hello my friend</div>
--- a/accessible/tests/mochitest/text/test_whitespaces.html
+++ b/accessible/tests/mochitest/text/test_whitespaces.html
@@ -9,19 +9,19 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
 
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
     if (navigator.platform.startsWith("Mac")) {
-      SimpleTest.expectAssertions(0, 11);
+      SimpleTest.expectAssertions(0, 3);
     } else {
-      SimpleTest.expectAssertions(11);
+      SimpleTest.expectAssertions(3);
     }
 
     function doTest()
     {
       // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n
       //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
 
       ////////////////////////////////////////////////////////////////////////
@@ -193,173 +193,49 @@
       testCharBeforeOffset(IDs, 1, "B", 0, 1);
       testCharBeforeOffset(IDs, 6, " ", 5, 6);
       testCharBeforeOffset(IDs, 10, " ", 9, 10);
       testCharBeforeOffset(IDs, 11, " ", 10, 11);
       testCharBeforeOffset(IDs, 17, " ", 16, 17);
       testCharBeforeOffset(IDs, 19, " ", 18, 19);
 
       // BOUNDARY_WORD_START
-      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(6, BOUNDARY_WORD_START, "Brave ", 0, 6,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(9, BOUNDARY_WORD_START, "Brave ", 0, 6,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(10, BOUNDARY_WORD_START, "Brave ", 0, 6,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(11, BOUNDARY_WORD_START, "Sir  ", 6, 11,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(15, BOUNDARY_WORD_START, "Sir  ", 6, 11,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(16, BOUNDARY_WORD_START, "Sir  ", 6, 11,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(17, BOUNDARY_WORD_START, "Sir  ", 6, 11,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(18, BOUNDARY_WORD_START, "Sir  ", 6, 11,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(19, BOUNDARY_WORD_START, "Robin   ", 11, 19,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(20, BOUNDARY_WORD_START, "Robin   ", 11, 19,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(21, BOUNDARY_WORD_START, "Robin   ", 11, 19,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0, IDs);
+      testTextBeforeOffset(6, BOUNDARY_WORD_START, "Brave ", 0, 6, IDs);
+      testTextBeforeOffset(9, BOUNDARY_WORD_START, "Brave ", 0, 6, IDs);
+      testTextBeforeOffset(10, BOUNDARY_WORD_START, "Brave ", 0, 6, IDs);
+      testTextBeforeOffset(11, BOUNDARY_WORD_START, "Sir  ", 6, 11, IDs);
+      testTextBeforeOffset(15, BOUNDARY_WORD_START, "Sir  ", 6, 11, IDs);
+      testTextBeforeOffset(16, BOUNDARY_WORD_START, "Sir  ", 6, 11, IDs);
+      testTextBeforeOffset(17, BOUNDARY_WORD_START, "Sir  ", 6, 11, IDs);
+      testTextBeforeOffset(18, BOUNDARY_WORD_START, "Sir  ", 6, 11, IDs);
+      testTextBeforeOffset(19, BOUNDARY_WORD_START, "Robin   ", 11, 19, IDs);
+      testTextBeforeOffset(20, BOUNDARY_WORD_START, "Robin   ", 11, 19, IDs);
+      testTextBeforeOffset(21, BOUNDARY_WORD_START, "Robin   ", 11, 19, IDs);
 
       // BOUNDARY_WORD_END
-      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
-                           "input", kOk, kOk, kOk,
-                           "div", kOk, kOk, kOk,
-                           "editable", kOk, kOk, kOk,
-                           "textarea", kOk, kOk, kOk);
-      testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(4, BOUNDARY_WORD_END, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(6, BOUNDARY_WORD_END, "Brave", 0, 5,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(7, BOUNDARY_WORD_END, "Brave", 0, 5,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(8, BOUNDARY_WORD_END, "Brave", 0, 5,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(9, BOUNDARY_WORD_END, "Brave", 0, 5,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(10, BOUNDARY_WORD_END, " Sir", 5, 9,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(11, BOUNDARY_WORD_END, " Sir", 5, 9,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(15, BOUNDARY_WORD_END, " Sir", 5, 9,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(16, BOUNDARY_WORD_END, " Sir", 5, 9,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(17, BOUNDARY_WORD_END, "  Robin", 9, 16,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(18, BOUNDARY_WORD_END, "  Robin", 9, 16,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(19, BOUNDARY_WORD_END, "  Robin", 9, 16,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(21, BOUNDARY_WORD_END, "  Robin", 9, 16,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
-      testTextBeforeOffset(22, BOUNDARY_WORD_END, "  Robin", 9, 16,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(4, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextBeforeOffset(6, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+      testTextBeforeOffset(7, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+      testTextBeforeOffset(8, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+      testTextBeforeOffset(9, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+      testTextBeforeOffset(10, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+      testTextBeforeOffset(11, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+      testTextBeforeOffset(15, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+      testTextBeforeOffset(16, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+      testTextBeforeOffset(17, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
+      testTextBeforeOffset(18, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
+      testTextBeforeOffset(19, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
+      testTextBeforeOffset(21, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
+      testTextBeforeOffset(22, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
 
       ////////////////////////////////////////////////////////////////////////
       // getTextAtOffset
 
       // BOUNDARY_CHAR
       testTextAtOffset(0, BOUNDARY_CHAR, "B", 0, 1,
                        "input", kOk, kOk, kOk,
                        "div", kOk, kOk, kOk,
@@ -487,17 +363,29 @@
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      title="getText... methods tests on string with whitespaces for plain text containers"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=610568">Mozilla Bug 610568</a>
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=610568">
+    Bug 610568
+  </a>
+  <a target="_blank"
+     title="getTextAtOffset for word boundaries: beginning of a new life"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
+    Bug 853340
+  </a>
+  <a target="_blank"
+     title="getTextBeforeOffset for word boundaries: evolving"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
+    Bug 855732
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   <input id="input" value="Brave Sir  Robin   ran"/>
   <div id="div">Brave Sir  Robin   ran</div>
   <div id="editable" contenteditable="true">Brave Sir  Robin   ran</div>
   <textarea id="textarea" cols="300">Brave Sir  Robin   ran</textarea>
   </pre>
--- a/addon-sdk/source/lib/sdk/indexed-db.js
+++ b/addon-sdk/source/lib/sdk/indexed-db.js
@@ -53,12 +53,11 @@ exports.indexedDB = Object.freeze({
 
 exports.IDBKeyRange = IDBKeyRange;
 exports.DOMException = Ci.nsIDOMDOMException;
 exports.IDBCursor = Ci.nsIIDBCursor;
 exports.IDBTransaction = Ci.nsIIDBTransaction;
 exports.IDBOpenDBRequest = Ci.nsIIDBOpenDBRequest;
 exports.IDBVersionChangeEvent = Ci.nsIIDBVersionChangeEvent;
 exports.IDBDatabase = Ci.nsIIDBDatabase;
-exports.IDBFactory = Ci.nsIIDBFactory;
 exports.IDBIndex = Ci.nsIIDBIndex;
 exports.IDBObjectStore = Ci.nsIIDBObjectStore;
 exports.IDBRequest = Ci.nsIIDBRequest;
--- a/addon-sdk/source/test/test-indexed-db.js
+++ b/addon-sdk/source/test/test-indexed-db.js
@@ -4,33 +4,33 @@
 
 "use strict";
 
 let xulApp = require("sdk/system/xul-app");
 if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) {
 new function tests() {
 
 const { indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction,
-        IDBOpenDBRequest, IDBVersionChangeEvent, IDBDatabase, IDBFactory,
-        IDBIndex, IDBObjectStore, IDBRequest
+        IDBOpenDBRequest, IDBVersionChangeEvent, IDBDatabase, IDBIndex,
+        IDBObjectStore, IDBRequest
       } = require("sdk/indexed-db");
 
 exports["test indexedDB is frozen"] = function(assert){
   let original = indexedDB.open;
   let f = function(){};
   assert.throws(function(){indexedDB.open = f});
   assert.equal(indexedDB.open,original);
   assert.notEqual(indexedDB.open,f);
 
 };
 
 exports["test db variables"] = function(assert) {
   [ indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction,
     IDBOpenDBRequest, IDBOpenDBRequest, IDBVersionChangeEvent,
-    IDBDatabase, IDBFactory, IDBIndex, IDBObjectStore, IDBRequest
+    IDBDatabase, IDBIndex, IDBObjectStore, IDBRequest
   ].forEach(function(value) {
     assert.notEqual(typeof(value), "undefined", "variable is defined");
   });
 }
 
 exports["test open"] = function(assert, done) {
   let request = indexedDB.open("MyTestDatabase");
   request.onerror = function(event) {
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -383,16 +383,31 @@ pref("dom.sms.enabled", true);
 pref("dom.sms.strict7BitEncoding", false); // Disabled by default.
 
 // Temporary permission hack for WebContacts
 pref("dom.mozContacts.enabled", true);
 
 // WebAlarms
 pref("dom.mozAlarms.enabled", true);
 
+// SimplePush
+// serverURL to be assigned by services team
+pref("services.push.serverURL", "");
+pref("services.push.userAgentID", "");
+// exponential back-off start is 5 seconds like in HTTP/1.1
+pref("services.push.retryBaseInterval", 5000);
+// exponential back-off end is 20 minutes
+pref("services.push.maxRetryInterval", 1200000);
+// How long before a DOMRequest errors as timeout
+pref("services.push.requestTimeout", 10000);
+// enable udp wakeup support
+pref("services.push.udp.wakeupEnabled", true);
+// port on which UDP server socket is bound
+pref("services.push.udp.port", 2442);
+
 // NetworkStats
 #ifdef MOZ_B2G_RIL
 pref("dom.mozNetworkStats.enabled", true);
 pref("ril.lastKnownMcc", "724");
 #endif
 
 // WebSettings
 pref("dom.mozSettings.enabled", true);
old mode 100755
new mode 100644
--- 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=22.0a1
+MOZ_APP_VERSION=23.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=2.0.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -189,16 +189,17 @@
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_media.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_payment.xpt
+@BINPATH@/components/dom_push.xpt
 @BINPATH@/components/dom_json.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
 @BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_messages.xpt
 @BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_quota.xpt
@@ -500,16 +501,20 @@
 @BINPATH@/components/captivedetect.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
+@BINPATH@/components/Push.js
+@BINPATH@/components/Push.manifest
+@BINPATH@/components/PushService.js
+@BINPATH@/components/PushService.manifest
 
 @BINPATH@/components/nsDOMIdentity.js
 @BINPATH@/components/nsIDService.js
 @BINPATH@/components/Identity.manifest
 
 @BINPATH@/components/SystemMessageInternal.js
 @BINPATH@/components/SystemMessageManager.js
 @BINPATH@/components/SystemMessageManager.manifest
--- a/b2g/test/emulator.manifest
+++ b/b2g/test/emulator.manifest
@@ -1,6 +1,6 @@
 [{
-"size": 622597962,
-"digest": "31e1e74c621b98ee0970ed656636b495c4c90e82060442e8f19d35bc1839e55030736b4aa689c8325f9b9b80aa1cc6b1981dd95b88c8c63ca8e5b0db9957ac0a",
+"size": 621007059,
+"digest": "7ab61ca7a6c25297bbe3ec8328b2a8b4298cb0656747292e86a8caeef08500f2c68c00b5be02f2d83afaa2ae2fb7240e4d8a321c786fd1b0d57aeaa6e257dad5",
 "algorithm": "sha512",
 "filename": "emulator.zip"
 }]
--- 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="1363988948000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1364316540000">
   <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>
@@ -367,16 +367,20 @@
       <emItem  blockID="i320" id="torntv@torntv.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i3" id="langpack-vi-VN@firefox.mozilla.org">
                         <versionRange  minVersion="2.0" maxVersion="2.0">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i326" id="/^((support2_en@adobe14\.com)|(XN4Xgjw7n4@yUWgc\.com)|(C7yFVpIP@WeolS3acxgS\.com)|(Kbeu4h0z@yNb7QAz7jrYKiiTQ3\.com)|(aWQzX@a6z4gWdPu8FF\.com)|(CBSoqAJLYpCbjTP90@JoV0VMywCjsm75Y0toAd\.com)|(zZ2jWZ1H22Jb5NdELHS@o0jQVWZkY1gx1\.com))$/">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i162" id="{EB7508CA-C7B2-46E0-8C04-3E94A035BD49}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i106" os="WINNT" id="{97E22097-9A2F-45b1-8DAF-36AD648C7EF4}">
                         <versionRange  minVersion="0" maxVersion="15.0.5" severity="1">
                     </versionRange>
                   </emItem>
@@ -390,20 +394,16 @@
       <emItem  blockID="i304" id="{f0e59437-6148-4a98-b0a6-60d557ef57f4}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i86" id="{45147e67-4020-47e2-8f7a-55464fb535aa}">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i326" id="/^((support2_en@adobe14\.com)|(XN4Xgjw7n4@yUWgc\.com)|(C7yFVpIP@WeolS3acxgS\.com)|(Kbeu4h0z@yNb7QAz7jrYKiiTQ3\.com)|(aWQzX@a6z4gWdPu8FF\.com)|(CBSoqAJLYpCbjTP90@JoV0VMywCjsm75Y0toAd\.com))$/">
-                        <versionRange  minVersion="0" maxVersion="*" severity="3">
-                    </versionRange>
-                  </emItem>
       <emItem  blockID="i312" id="extension21804@extension21804.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i324" id="/^((34qEOefiyYtRJT@IM5Munavn\.com)|(Mro5Fm1Qgrmq7B@ByrE69VQfZvZdeg\.com)|(KtoY3KGxrCe5ie@yITPUzbBtsHWeCdPmGe\.com)|(9NgIdLK5Dq4ZMwmRo6zk@FNt2GCCLGyUuOD\.com)|(NNux7bWWW@RBWyXdnl6VGls3WAwi\.com)|(E3wI2n@PEHTuuNVu\.com)|(2d3VuWrG6JHBXbQdbr@3BmSnQL\.com))$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                   </emItem>
@@ -798,17 +798,38 @@
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p316">
                   <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="11.2.202.275" maxVersion="11.4.402.286.9999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="20.0a1" maxVersion="*" />
+                              <versionRange  minVersion="21.0a1" maxVersion="*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
+      <pluginItem  blockID="p328">
+                  <match name="filename" exp="Silverlight\.plugin" />                                    <versionRange  minVersion="5.1" maxVersion="5.1.20124.9999" severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="19.0a1" maxVersion="*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
+      <pluginItem  blockID="p330">
+            <match name="description" exp="^Shockwave Flash (([1-9]\.[0-9]+)|(10\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="19.0a1" maxVersion="*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
+      <pluginItem  blockID="p332">
+            <match name="description" exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
     </pluginItems>
 
   <gfxItems>
     <gfxBlacklistEntry  blockID="g35">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
                       <device>0x0a6c</device>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -5,16 +5,18 @@
 
 #include "nsXULAppAPI.h"
 #include "mozilla/AppData.h"
 #include "application.ini.h"
 #include "nsXPCOMGlue.h"
 #if defined(XP_WIN)
 #include <windows.h>
 #include <stdlib.h>
+#include <io.h>
+#include <fcntl.h>
 #elif defined(XP_UNIX)
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
 #endif
 
 #ifdef XP_MACOSX
 #include "MacQuirks.h"
@@ -44,17 +46,20 @@
 
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 
 #define kDesktopFolder "browser"
 #define kMetroFolder "metro"
 #define kMetroAppIniFilename "metroapp.ini"
+#ifdef XP_WIN
 #define kMetroTestFile "tests.ini"
+const char* kMetroConsoleIdParam = "testconsoleid=";
+#endif
 
 static void Output(const char *fmt, ... )
 {
   va_list ap;
   va_start(ap, fmt);
 
 #ifndef XP_WIN
   vfprintf(stderr, fmt, ap);
@@ -96,16 +101,52 @@ static bool IsArg(const char* arg, const
 #if defined(XP_WIN) || defined(XP_OS2)
   if (*arg == '/')
     return !strcasecmp(++arg, s);
 #endif
 
   return false;
 }
 
+#ifdef XP_WIN
+/*
+ * AttachToTestsConsole - Windows helper for when we are running
+ * in the immersive environment. Firefox is launched by Windows in
+ * response to a request by metrotestharness, which is launched by
+ * runtests.py. As such stdout in fx doesn't point to the right
+ * stream. This helper touches up stdout such that test output gets
+ * routed to the console the tests are run in.
+ */
+static void AttachToTestsConsole(DWORD aProcessId)
+{
+  if (!AttachConsole(aProcessId)) {
+    OutputDebugStringW(L"Could not attach to console.\n");
+    return;
+  }
+
+  HANDLE winOut = CreateFileA("CONOUT$",
+                              GENERIC_READ | GENERIC_WRITE,
+                              FILE_SHARE_WRITE, 0,
+                              OPEN_EXISTING, 0, 0);
+  if (winOut == INVALID_HANDLE_VALUE) {
+    OutputDebugStringW(L"Could not attach to console.\n");
+    return;
+  }
+
+  // Set the c runtime handle
+  int stdOut = _open_osfhandle((intptr_t)winOut, _O_APPEND);
+  if (stdOut == -1) {
+    OutputDebugStringW(L"Could not open c-runtime handle.\n");
+    return;
+  }
+  FILE *fp = _fdopen(stdOut, "a");
+  *stdout = *fp;
+}
+#endif
+
 XRE_GetFileFromPathType XRE_GetFileFromPath;
 XRE_CreateAppDataType XRE_CreateAppData;
 XRE_FreeAppDataType XRE_FreeAppData;
 #ifdef XRE_HAS_DLL_BLOCKLIST
 XRE_SetupDllBlocklistType XRE_SetupDllBlocklist;
 #endif
 XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
 XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
@@ -309,16 +350,25 @@ static int do_main(int argc, char* argv[
       char* ptr = buffer;
       newArgv[0] = ptr;
       while (*ptr != NULL &&
              (ptr - buffer) < sizeof(buffer) &&
              newArgc < ARRAYSIZE(newArgv)) {
         if (isspace(*ptr)) {
           *ptr = '\0';
           ptr++;
+          // Check for the console id the metrotestharness passes in, we need
+          // to connect up to this so test output goes to the right place.
+          if (ptr && !strncmp(ptr, kMetroConsoleIdParam, strlen(kMetroConsoleIdParam))) {
+            DWORD processId = strtol(ptr + strlen(kMetroConsoleIdParam), nullptr, 10);
+            if (processId > 0) {
+              AttachToTestsConsole(processId);
+            }
+            continue;
+          }
           newArgv[newArgc] = ptr;
           newArgc++;
           continue;
         }
         ptr++;
       }
       if (ptr == newArgv[newArgc-1])
         newArgc--;
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1227,8 +1227,11 @@ pref("dom.identity.enabled", false);
 // Override the Gecko-default value of false for Firefox.
 pref("plain_text.wrap_long_lines", true);
 
 #ifndef RELEASE_BUILD
 // Enable Web Audio for Firefox Desktop in Nightly and Aurora
 pref("media.webaudio.enabled", true);
 #endif
 
+// If this turns true, Moz*Gesture events are not called stopPropagation()
+// before content.
+pref("dom.debug.propagate_gesture_events_through_content", false);
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -5,43 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #endif
  */
 
 // One of the possible values for the mousewheel.* preferences.
 // From nsEventStateManager.cpp.
 const MOUSE_SCROLL_ZOOM = 3;
 
-Cu.import('resource://gre/modules/ContentPrefInstance.jsm');
-
-function getContentPrefs(aWindow) {
-  let context = aWindow ? aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsILoadContext) : null;
-  return new ContentPrefInstance(context);
-}
-
 /**
  * Controls the "full zoom" setting and its site-specific preferences.
  */
 var FullZoom = {
   // Identifies the setting in the content prefs database.
   name: "browser.content.full-zoom",
 
-  // The global value (if any) for the setting.  Lazily loaded from the service
-  // when first requested, then updated by the pref change listener as it changes.
-  // If there is no global value, then this should be undefined.
-  get globalValue() {
-    var globalValue = getContentPrefs(gBrowser.contentDocument.defaultView).getPref(null, this.name);
-    if (typeof globalValue != "undefined")
-      globalValue = this._ensureValid(globalValue);
-    delete this.globalValue;
-    return this.globalValue = globalValue;
-  },
-
   // browser.zoom.siteSpecific preference cache
   _siteSpecificPref: undefined,
 
   // browser.zoom.updateBackgroundTabs preference cache
   updateBackgroundTabs: undefined,
 
   get siteSpecific() {
     return this._siteSpecificPref;
@@ -59,30 +39,32 @@ var FullZoom = {
   //**************************************************************************//
   // Initialization & Destruction
 
   init: function FullZoom_init() {
     // Listen for scrollwheel events so we can save scrollwheel-based changes.
     window.addEventListener("DOMMouseScroll", this, false);
 
     // Register ourselves with the service so we know when our pref changes.
-    getContentPrefs().addObserver(this.name, this);
+    this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
+                 getService(Ci.nsIContentPrefService2);
+    this._cps2.addObserverForName(this.name, this);
 
     this._siteSpecificPref =
       gPrefService.getBoolPref("browser.zoom.siteSpecific");
     this.updateBackgroundTabs =
       gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
     // Listen for changes to the browser.zoom branch so we can enable/disable
     // updating background tabs and per-site saving and restoring of zoom levels.
     gPrefService.addObserver("browser.zoom.", this, true);
   },
 
   destroy: function FullZoom_destroy() {
     gPrefService.removeObserver("browser.zoom.", this);
-    getContentPrefs().removeObserver(this.name, this);
+    this._cps2.removeObserverForName(this.name, this);
     window.removeEventListener("DOMMouseScroll", this, false);
   },
 
 
   //**************************************************************************//
   // Event Handlers
 
   // nsIDOMEventListener
@@ -128,17 +110,24 @@ var FullZoom = {
 
     // XXX Lazily cache all the possible action prefs so we don't have to get
     // them anew from the pref service for every scroll event?  We'd have to
     // make sure to observe them so we can update the cache when they change.
 
     // We have to call _applySettingToPref in a timeout because we handle
     // the event before the event state manager has a chance to apply the zoom
     // during nsEventStateManager::PostHandleEvent.
-    window.setTimeout(function (self) { self._applySettingToPref() }, 0, this);
+    //
+    // Since the zoom will have already been updated by the time
+    // _applySettingToPref is called, pass true to suppress updating the zoom
+    // again.  Note that this is not an optimization: due to asynchronicity of
+    // the content preference service, the zoom may have been updated again by
+    // the time that onContentPrefSet is called as a result of this call to
+    // _applySettingToPref.
+    window.setTimeout(function (self) self._applySettingToPref(true), 0, this);
   },
 
   // nsIObserver
 
   observe: function (aSubject, aTopic, aData) {
     switch (aTopic) {
       case "nsPref:changed":
         switch (aData) {
@@ -153,121 +142,173 @@ var FullZoom = {
         }
         break;
     }
   },
 
   // nsIContentPrefObserver
 
   onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue) {
-    let contentPrefs = getContentPrefs(gBrowser.contentDocument.defaultView);
-    if (aGroup == contentPrefs.grouper.group(gBrowser.currentURI))
-      this._applyPrefToSetting(aValue);
-    else if (aGroup == null) {
-      this.globalValue = this._ensureValid(aValue);
-
-      // If the current page doesn't have a site-specific preference,
-      // then its zoom should be set to the new global preference now that
-      // the global preference has changed.
-      if (!contentPrefs.hasPref(gBrowser.currentURI, this.name))
-        this._applyPrefToSetting();
+    if (this._ignoreNextOnContentPrefSet) {
+      delete this._ignoreNextOnContentPrefSet;
+      return;
     }
+    this._onContentPrefChanged(aGroup, aValue);
   },
 
   onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName) {
-    let contentPrefs = getContentPrefs(gBrowser.contentDocument.defaultView);
-    if (aGroup == contentPrefs.grouper.group(gBrowser.currentURI))
-      this._applyPrefToSetting();
-    else if (aGroup == null) {
-      this.globalValue = undefined;
+    this._onContentPrefChanged(aGroup, undefined);
+  },
+
+  /**
+   * Appropriately updates the zoom level after a content preference has
+   * changed.
+   *
+   * @param aGroup  The group of the changed preference.
+   * @param aValue  The new value of the changed preference.  Pass undefined to
+   *                indicate the preference's removal.
+   */
+  _onContentPrefChanged: function FullZoom__onContentPrefChanged(aGroup, aValue) {
+    if (!gBrowser.currentURI)
+      return;
 
-      // If the current page doesn't have a site-specific preference,
-      // then its zoom should be set to the default preference now that
-      // the global preference has changed.
-      if (!contentPrefs.hasPref(gBrowser.currentURI, this.name))
-        this._applyPrefToSetting();
+    let domain = this._cps2.extractDomain(gBrowser.currentURI.spec);
+    if (aGroup) {
+      if (aGroup == domain)
+        this._applyPrefToSetting(aValue);
+      return;
     }
+
+    this._globalValue = aValue === undefined ? aValue :
+                          this._ensureValid(aValue);
+
+    // If the current page doesn't have a site-specific preference, then its
+    // zoom should be set to the new global preference now that the global
+    // preference has changed.
+    let hasPref = false;
+    let ctxt = this._loadContextFromWindow(gBrowser.contentWindow);
+    this._cps2.getByDomainAndName(gBrowser.currentURI.spec, this.name, ctxt, {
+      handleResult: function () hasPref = true,
+      handleCompletion: function () {
+        if (!hasPref &&
+            gBrowser.currentURI &&
+            this._cps2.extractDomain(gBrowser.currentURI.spec) == domain)
+          this._applyPrefToSetting();
+      }.bind(this)
+    });
   },
 
   // location change observer
 
   /**
    * Called when the location of a tab changes.
    * When that happens, we need to update the current zoom level if appropriate.
    *
    * @param aURI
    *        A URI object representing the new location.
    * @param aIsTabSwitch
    *        Whether this location change has happened because of a tab switch.
    * @param aBrowser
    *        (optional) browser object displaying the document
    */
   onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
-    if (!aURI || (aIsTabSwitch && !this.siteSpecific))
+    if (!aURI || (aIsTabSwitch && !this.siteSpecific)) {
+      this._notifyOnLocationChange();
       return;
+    }
 
     // Avoid the cps roundtrip and apply the default/global pref.
     if (aURI.spec == "about:blank") {
-      this._applyPrefToSetting(undefined, aBrowser);
+      this._applyPrefToSetting(undefined, aBrowser, function () {
+        this._notifyOnLocationChange();
+      }.bind(this));
       return;
     }
 
     let browser = aBrowser || gBrowser.selectedBrowser;
 
     // Media documents should always start at 1, and are not affected by prefs.
     if (!aIsTabSwitch && browser.contentDocument.mozSyntheticDocument) {
       ZoomManager.setZoomForBrowser(browser, 1);
+      this._notifyOnLocationChange();
+      return;
+    }
+
+    let ctxt = this._loadContextFromWindow(browser.contentWindow);
+    let pref = this._cps2.getCachedByDomainAndName(aURI.spec, this.name, ctxt);
+    if (pref) {
+      this._applyPrefToSetting(pref.value, browser, function () {
+        this._notifyOnLocationChange();
+      }.bind(this));
       return;
     }
 
-    let contentPrefs = getContentPrefs(gBrowser.contentDocument.defaultView);
-    if (contentPrefs.hasCachedPref(aURI, this.name)) {
-      let zoomValue = contentPrefs.getPref(aURI, this.name);
-      this._applyPrefToSetting(zoomValue, browser);
-    } else {
-      var self = this;
-      contentPrefs.getPref(aURI, this.name, function (aResult) {
-        // Check that we're still where we expect to be in case this took a while.
-        // Null check currentURI, since the window may have been destroyed before
-        // we were called.
-        if (browser.currentURI && aURI.equals(browser.currentURI)) {
-          self._applyPrefToSetting(aResult, browser);
+    let value = undefined;
+    this._cps2.getByDomainAndName(aURI.spec, this.name, ctxt, {
+      handleResult: function (resultPref) value = resultPref.value,
+      handleCompletion: function () {
+        if (browser.currentURI &&
+            this._cps2.extractDomain(browser.currentURI.spec) ==
+              this._cps2.extractDomain(aURI.spec)) {
+          this._applyPrefToSetting(value, browser, function () {
+            this._notifyOnLocationChange();
+          }.bind(this));
         }
-      });
-    }
+      }.bind(this)
+    });
   },
 
   // update state of zoom type menu item
 
   updateMenu: function FullZoom_updateMenu() {
     var menuItem = document.getElementById("toggle_zoom");
 
     menuItem.setAttribute("checked", !ZoomManager.useFullZoom);
   },
 
   //**************************************************************************//
   // Setting & Pref Manipulation
 
-  reduce: function FullZoom_reduce() {
+  /**
+   * Reduces the zoom level of the page in the current browser.
+   *
+   * @param callback  Optional.  If given, it's asynchronously called when the
+   *                  zoom update completes.
+   */
+  reduce: function FullZoom_reduce(callback) {
     ZoomManager.reduce();
-    this._applySettingToPref();
+    this._applySettingToPref(false, callback);
   },
 
-  enlarge: function FullZoom_enlarge() {
+  /**
+   * Enlarges the zoom level of the page in the current browser.
+   *
+   * @param callback  Optional.  If given, it's asynchronously called when the
+   *                  zoom update completes.
+   */
+  enlarge: function FullZoom_enlarge(callback) {
     ZoomManager.enlarge();
-    this._applySettingToPref();
+    this._applySettingToPref(false, callback);
   },
 
-  reset: function FullZoom_reset() {
-    if (typeof this.globalValue != "undefined")
-      ZoomManager.zoom = this.globalValue;
-    else
-      ZoomManager.reset();
-
-    this._removePref();
+  /**
+   * Sets the zoom level of the page in the current browser to the global zoom
+   * level.
+   *
+   * @param callback  Optional.  If given, it's asynchronously called when the
+   *                  zoom update completes.
+   */
+  reset: function FullZoom_reset(callback) {
+    this._getGlobalValue(gBrowser.contentWindow, function (value) {
+      if (value === undefined)
+        ZoomManager.reset();
+      else
+        ZoomManager.zoom = value;
+      this._removePref(callback);
+    });
   },
 
   /**
    * Set the zoom level for the current tab.
    *
    * Per nsPresContext::setFullZoom, we can set the zoom to its current value
    * without significant impact on performance, as the setting is only applied
    * if it differs from the current setting.  In fact getting the zoom and then
@@ -278,59 +319,177 @@ var FullZoom = {
    * a different text zoom (although it would be unusual), and it implies that
    * those child text zooms should get updated when the parent zoom gets set,
    * and perhaps the same is true for full zoom
    * (although nsDocumentViewer::SetFullZoom doesn't mention it).
    *
    * So when we apply new zoom values to the browser, we simply set the zoom.
    * We don't check first to see if the new value is the same as the current
    * one.
-   **/
-  _applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser) {
-    if ((!this.siteSpecific) || gInPrintPreviewMode)
+   *
+   * @param aValue     The zoom level value.
+   * @param aBrowser   The browser containing the page whose zoom level is to be
+   *                   set.  If falsey, the currently selected browser is used.
+   * @param aCallback  Optional.  If given, it's asynchronously called when
+   *                   complete.
+   */
+  _applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser, aCallback) {
+    if (!this.siteSpecific || gInPrintPreviewMode) {
+      this._executeSoon(aCallback);
       return;
+    }
 
     var browser = aBrowser || (gBrowser && gBrowser.selectedBrowser);
-    try {
-      if (browser.contentDocument.mozSyntheticDocument)
-        return;
+    if (browser.contentDocument.mozSyntheticDocument) {
+      this._executeSoon(aCallback);
+      return;
+    }
 
-      if (typeof aValue != "undefined")
-        ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
-      else if (typeof this.globalValue != "undefined")
-        ZoomManager.setZoomForBrowser(browser, this.globalValue);
-      else
-        ZoomManager.setZoomForBrowser(browser, 1);
+    if (aValue !== undefined) {
+      ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
+      this._executeSoon(aCallback);
+      return;
     }
-    catch(ex) {}
+
+    this._getGlobalValue(browser.contentWindow, function (value) {
+      if (gBrowser.selectedBrowser == browser)
+        ZoomManager.setZoomForBrowser(browser, value === undefined ? 1 : value);
+      this._executeSoon(aCallback);
+    });
   },
 
-  _applySettingToPref: function FullZoom__applySettingToPref() {
-    if (!this.siteSpecific || gInPrintPreviewMode ||
-        content.document.mozSyntheticDocument)
+  /**
+   * Saves the zoom level of the page in the current browser to the content
+   * prefs store.
+   *
+   * @param suppressZoomChange  Because this method sets a content preference,
+   *                            our onContentPrefSet method is called as a
+   *                            result, which updates the current zoom level.
+   *                            If suppressZoomChange is true, then the next
+   *                            call to onContentPrefSet will not update the
+   *                            zoom level.
+   * @param callback            Optional.  If given, it's asynchronously called
+   *                            when done.
+   */
+  _applySettingToPref: function FullZoom__applySettingToPref(suppressZoomChange, callback) {
+    if (!this.siteSpecific ||
+        gInPrintPreviewMode ||
+        content.document.mozSyntheticDocument) {
+      this._executeSoon(callback);
       return;
+    }
 
-    var zoomLevel = ZoomManager.zoom;
-    getContentPrefs(gBrowser.contentDocument.defaultView).setPref(gBrowser.currentURI, this.name, zoomLevel);
+    this._cps2.set(gBrowser.currentURI.spec, this.name, ZoomManager.zoom,
+                   this._loadContextFromWindow(gBrowser.contentWindow), {
+      handleCompletion: function () {
+        if (suppressZoomChange)
+          // The content preference service calls observers after callbacks, and
+          // it calls both in the same turn of the event loop.  onContentPrefSet
+          // will therefore be called next, in the same turn of the event loop,
+          // and it will check this flag.
+          this._ignoreNextOnContentPrefSet = true;
+        if (callback)
+          callback();
+      }.bind(this)
+    });
   },
 
-  _removePref: function FullZoom__removePref() {
-    if (!(content.document.mozSyntheticDocument))
-      getContentPrefs(gBrowser.contentDocument.defaultView).removePref(gBrowser.currentURI, this.name);
+  /**
+   * Removes from the content prefs store the zoom level of the current browser.
+   *
+   * @param callback  Optional.  If given, it's asynchronously called when done.
+   */
+  _removePref: function FullZoom__removePref(callback) {
+    if (content.document.mozSyntheticDocument) {
+      this._executeSoon(callback);
+      return;
+    }
+    let ctxt = this._loadContextFromWindow(gBrowser.contentWindow);
+    this._cps2.removeByDomainAndName(gBrowser.currentURI.spec, this.name, ctxt, {
+      handleCompletion: function () {
+        if (callback)
+          callback();
+      }
+    });
   },
 
 
   //**************************************************************************//
   // Utilities
 
   _ensureValid: function FullZoom__ensureValid(aValue) {
+    // Note that undefined is a valid value for aValue that indicates a known-
+    // not-to-exist value.
     if (isNaN(aValue))
       return 1;
 
     if (aValue < ZoomManager.MIN)
       return ZoomManager.MIN;
 
     if (aValue > ZoomManager.MAX)
       return ZoomManager.MAX;
 
     return aValue;
-  }
+  },
+
+  /**
+   * Gets the global browser.content.full-zoom content preference.
+   *
+   * WARNING: callback may be called synchronously or asynchronously.  The
+   * reason is that it's usually desirable to avoid turns of the event loop
+   * where possible, since they can lead to visible, jarring jumps in zoom
+   * level.  It's not always possible to avoid them, though.  As a convenience,
+   * then, this method takes a callback and returns nothing.
+   *
+   * @param window    The content window pertaining to the zoom.
+   * @param callback  Synchronously or asynchronously called when done.  It's
+   *                  bound to this object (FullZoom) and passed the preference
+   *                  value.
+   */
+  _getGlobalValue: function FullZoom__getGlobalValue(window, callback) {
+    // * !("_globalValue" in this) => global value not yet cached.
+    // * this._globalValue === undefined => global value known not to exist.
+    // * Otherwise, this._globalValue is a number, the global value.
+    if ("_globalValue" in this) {
+      callback.call(this, this._globalValue);
+      return;
+    }
+    let value = undefined;
+    this._cps2.getGlobal(this.name, this._loadContextFromWindow(window), {
+      handleResult: function (pref) value = pref.value,
+      handleCompletion: function () {
+        this._globalValue = this._ensureValid(value);
+        callback.call(this, this._globalValue);
+      }.bind(this)
+    });
+  },
+
+  /**
+   * Gets the load context from the given window.
+   *
+   * @param window  The window whose load context will be returned.
+   * @return        The nsILoadContext of the given window.
+   */
+  _loadContextFromWindow: function FullZoom__loadContextFromWindow(window) {
+    return window.
+           QueryInterface(Ci.nsIInterfaceRequestor).
+           getInterface(Ci.nsIWebNavigation).
+           QueryInterface(Ci.nsILoadContext);
+  },
+
+  /**
+   * Asynchronously broadcasts a "browser-fullZoom:locationChange" notification
+   * so that tests can select tabs, load pages, etc. and be notified when the
+   * zoom levels on those pages change.  The notification is always asynchronous
+   * so that observers are guaranteed a consistent behavior.
+   */
+  _notifyOnLocationChange: function FullZoom__notifyOnLocationChange() {
+    this._executeSoon(function () {
+      Services.obs.notifyObservers(null, "browser-fullZoom:locationChange", "");
+    });
+  },
+
+  _executeSoon: function FullZoom__executeSoon(callback) {
+    if (!callback)
+      return;
+    Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
+  },
 };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -763,17 +763,20 @@ let gGestureSupport = {
    * Dispatch events based on the type of mouse gesture event. For now, make
    * sure to stop propagation of every gesture event so that web content cannot
    * receive gesture events.
    *
    * @param aEvent
    *        The gesture event to handle
    */
   handleEvent: function GS_handleEvent(aEvent) {
-    aEvent.stopPropagation();
+    if (!Services.prefs.getBoolPref(
+           "dom.debug.propagate_gesture_events_through_content")) {
+      aEvent.stopPropagation();
+    }
 
     // Create a preference object with some defaults
     let def = function(aThreshold, aLatched)
       ({ threshold: aThreshold, latched: !!aLatched });
 
     switch (aEvent.type) {
       case "MozSwipeGesture":
         aEvent.preventDefault();
@@ -4208,42 +4211,16 @@ var XULBrowserWindow = {
           if (tooltipWindow == aWebProgress.DOMWindow) {
             pageTooltip.hidePopup();
             break;
           }
         }
       }
     }
 
-    // This code here does not compare uris exactly when determining
-    // whether or not the message should be hidden since the message
-    // may be prematurely hidden when an install is invoked by a click
-    // on a link that looks like this:
-    //
-    // <a href="#" onclick="return install();">Install Foo</a>
-    //
-    // - which fires a onLocationChange message to uri + '#'...
-    var selectedBrowser = gBrowser.selectedBrowser;
-    if (selectedBrowser.lastURI) {
-      let oldSpec = selectedBrowser.lastURI.spec;
-      let oldIndexOfHash = oldSpec.indexOf("#");
-      if (oldIndexOfHash != -1)
-        oldSpec = oldSpec.substr(0, oldIndexOfHash);
-      let newSpec = location;
-      let newIndexOfHash = newSpec.indexOf("#");
-      if (newIndexOfHash != -1)
-        newSpec = newSpec.substr(0, newIndexOfHash);
-      if (newSpec != oldSpec) {
-        // Remove all the notifications, except for those which want to
-        // persist across the first location change.
-        let nBox = gBrowser.getNotificationBox(selectedBrowser);
-        nBox.removeTransientNotifications();
-      }
-    }
-
     // Disable menu entries for images, enable otherwise
     if (content.document && mimeTypeIsTextBased(content.document.contentType))
       this.isImage.removeAttribute('disabled');
     else
       this.isImage.setAttribute('disabled', 'true');
 
     this.hideOverLinkImmediately = true;
     this.setOverLink("", null);
@@ -4686,36 +4663,36 @@ var TabsProgressListener = {
 
       // We also want to make changes to page UI for unprivileged about pages.
       BrowserOnAboutPageLoad(doc);
     }
   },
 
   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
                               aFlags) {
-    // Filter out sub-frame loads and location changes caused by anchor
-    // navigation or history.push/pop/replaceState.
-    if (aBrowser.contentWindow == aWebProgress.DOMWindow &&
-        !(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
+    // Filter out location changes caused by anchor navigation
+    // or history.push/pop/replaceState.
+    if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
+      return;
+
+    // Only need to call locationChange if the PopupNotifications object
+    // for this window has already been initialized (i.e. its getter no
+    // longer exists)
+    if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
+      PopupNotifications.locationChange(aBrowser);
+
+    gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();
+
+    // Filter out location changes in sub documents.
+    if (aBrowser.contentWindow == aWebProgress.DOMWindow) {
       // Initialize the click-to-play state.
       aBrowser._clickToPlayPluginsActivated = new Map();
       aBrowser._clickToPlayAllPluginsActivated = false;
       aBrowser._pluginScriptedState = gPluginHandler.PLUGIN_SCRIPTED_STATE_NONE;
 
-      // Only need to call locationChange if the PopupNotifications object
-      // for this window has already been initialized (i.e. its getter no
-      // longer exists)
-      if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
-        PopupNotifications.locationChange(aBrowser);
-
-      // Only handle background browsers as long as the selected browser is
-      // handled in XULBrowserWindow.onLocationChange (bug 839516).
-      if (aBrowser != gBrowser.selectedBrowser)
-        gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();
-
       FullZoom.onLocationChange(aLocationURI, false, aBrowser);
     }
   },
 
   onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
     if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
       let brandBundle = document.getElementById("bundle_brand");
       let brandShortName = brandBundle.getString("brandShortName");
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -355,18 +355,18 @@ Sanitizer.prototype = {
       {
         // Clear site-specific permissions like "Allow this site to open popups"
         var pm = Components.classes["@mozilla.org/permissionmanager;1"]
                            .getService(Components.interfaces.nsIPermissionManager);
         pm.removeAll();
         
         // Clear site-specific settings like page-zoom level
         var cps = Components.classes["@mozilla.org/content-pref/service;1"]
-                            .getService(Components.interfaces.nsIContentPrefService);
-        cps.removeGroupedPrefs(null);
+                            .getService(Components.interfaces.nsIContentPrefService2);
+        cps.removeAllDomains(null);
         
         // Clear "Never remember passwords for this site", which is not handled by
         // the permission manager
         var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
                               .getService(Components.interfaces.nsILoginManager);
         var hosts = pwmgr.getAllDisabledHosts();
         for each (var host in hosts) {
           pwmgr.setLoginSavingEnabled(host, true);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2921,18 +2921,18 @@
 # right of the newtab button.
         <children includes="tab"/>
 # This is to ensure anything extensions put here will go before the newtab
 # button, necessary due to the previous hack.
         <children/>
         <xul:toolbarbutton class="tabs-newtab-button"
                            command="cmd_newNavigatorTab"
                            onclick="checkForMiddleClick(this, event);"
-                           onmouseenter="document.getBindingParent(this)._enterNewTab();"
-                           onmouseleave="document.getBindingParent(this)._leaveNewTab();"
+                           onmouseover="document.getBindingParent(this)._enterNewTab();"
+                           onmouseout="document.getBindingParent(this)._leaveNewTab();"
                            tooltiptext="&newTabButton.tooltip;"/>
         <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
                     style="width: 0;"/>
       </xul:arrowscrollbox>
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor>
@@ -4263,27 +4263,21 @@
 
       <field name="mOverCloseButton">false</field>
       <field name="mCorrespondingMenuitem">null</field>
       <field name="closing">false</field>
       <field name="lastAccessed">0</field>
     </implementation>
 
     <handlers>
-      <handler event="mouseover">
-        var anonid = event.originalTarget.getAttribute("anonid");
+      <handler event="mouseover"><![CDATA[
+        let anonid = event.originalTarget.getAttribute("anonid");
         if (anonid == "close-button")
           this.mOverCloseButton = true;
-      </handler>
-      <handler event="mouseout">
-        var anonid = event.originalTarget.getAttribute("anonid");
-        if (anonid == "close-button")
-          this.mOverCloseButton = false;
-      </handler>
-      <handler event="mouseenter" phase="target"><![CDATA[
+
         let tab = event.target;
         if (tab.selected)
           return;
 
         let tabContainer = this.parentNode;
         let visibleTabs = tabContainer.tabbrowser.visibleTabs;
         let tabIndex = visibleTabs.indexOf(tab);
         if (tabIndex == 0) {
@@ -4301,17 +4295,21 @@
         } else {
           let candidate = visibleTabs[tabIndex + 1];
           if (!candidate.selected) {
             tabContainer._afterHoveredTab = candidate;
             candidate.setAttribute("afterhovered", "true");
           }
         }
       ]]></handler>
-      <handler event="mouseleave" phase="target"><![CDATA[
+      <handler event="mouseout"><![CDATA[
+        let anonid = event.originalTarget.getAttribute("anonid");
+        if (anonid == "close-button")
+          this.mOverCloseButton = false;
+
         let tabContainer = this.parentNode;
         if (tabContainer._beforeHoveredTab) {
           tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
           tabContainer._beforeHoveredTab = null;
         }
         if (tabContainer._afterHoveredTab) {
           tabContainer._afterHoveredTab.removeAttribute("afterhovered");
           tabContainer._afterHoveredTab = null;
--- a/browser/base/content/test/browser_bug386835.js
+++ b/browser/base/content/test/browser_bug386835.js
@@ -3,141 +3,87 @@ var gTestImage = "http://example.org/bro
 var gTab1, gTab2, gTab3;
 var gLevel;
 const BACK = 0;
 const FORWARD = 1;
 
 function test() {
   waitForExplicitFinish();
 
-  gTab1 = gBrowser.addTab(gTestPage);
-  gTab2 = gBrowser.addTab();
-  gTab3 = gBrowser.addTab();
-  gBrowser.selectedTab = gTab1;
+  Task.spawn(function () {
+    gTab1 = gBrowser.addTab(gTestPage);
+    gTab2 = gBrowser.addTab();
+    gTab3 = gBrowser.addTab();
 
-  load(gTab1, gTestPage, function () {
-    load(gTab2, gTestPage, secondPageLoaded);
-  });
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
+    yield FullZoomHelper.load(gTab1, gTestPage);
+    yield FullZoomHelper.load(gTab2, gTestPage);
+  }).then(secondPageLoaded, FullZoomHelper.failAndContinue(finish));
 }
 
 function secondPageLoaded() {
-  zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
-  zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
-  zoomTest(gTab3, 1, "Initial zoom of tab 3 should be 1");
+  Task.spawn(function () {
+    FullZoomHelper.zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
+    FullZoomHelper.zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
+    FullZoomHelper.zoomTest(gTab3, 1, "Initial zoom of tab 3 should be 1");
 
-  // Now have three tabs, two with the test page, one blank. Tab 1 is selected
-  // Zoom tab 1
-  FullZoom.enlarge();
-  gLevel = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
+    // Now have three tabs, two with the test page, one blank. Tab 1 is selected
+    // Zoom tab 1
+    yield FullZoomHelper.enlarge();
+    gLevel = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
 
-  ok(gLevel > 1, "New zoom for tab 1 should be greater than 1");
-  zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
-  zoomTest(gTab3, 1, "Zooming tab 1 should not affect tab 3");
+    ok(gLevel > 1, "New zoom for tab 1 should be greater than 1");
+    FullZoomHelper.zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
+    FullZoomHelper.zoomTest(gTab3, 1, "Zooming tab 1 should not affect tab 3");
 
-  load(gTab3, gTestPage, thirdPageLoaded);
+    yield FullZoomHelper.load(gTab3, gTestPage);
+  }).then(thirdPageLoaded, FullZoomHelper.failAndContinue(finish));
 }
 
 function thirdPageLoaded() {
-  zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
-  zoomTest(gTab2, 1, "Tab 2 should still not be affected");
-  zoomTest(gTab3, gLevel, "Tab 3 should have zoomed as it was loading in the background");
+  Task.spawn(function () {
+    FullZoomHelper.zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
+    FullZoomHelper.zoomTest(gTab2, 1, "Tab 2 should still not be affected");
+    FullZoomHelper.zoomTest(gTab3, gLevel, "Tab 3 should have zoomed as it was loading in the background");
 
-  // Switching to tab 2 should update its zoom setting.
-  afterZoom(function() {
-    zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
-    zoomTest(gTab2, gLevel, "Tab 2 should be zoomed now");
-    zoomTest(gTab3, gLevel, "Tab 3 should still be zoomed");
+    // Switching to tab 2 should update its zoom setting.
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
+    FullZoomHelper.zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
+    FullZoomHelper.zoomTest(gTab2, gLevel, "Tab 2 should be zoomed now");
+    FullZoomHelper.zoomTest(gTab3, gLevel, "Tab 3 should still be zoomed");
 
-    load(gTab1, gTestImage, imageLoaded);
-  });
-
-  gBrowser.selectedTab = gTab2;
+    yield FullZoomHelper.load(gTab1, gTestImage);
+  }).then(imageLoaded, FullZoomHelper.failAndContinue(finish));
 }
 
 function imageLoaded() {
-  zoomTest(gTab1, 1, "Zoom should be 1 when image was loaded in the background");
-  gBrowser.selectedTab = gTab1;
-  zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
-
-  executeSoon(imageZoomSwitch);
+  Task.spawn(function () {
+    FullZoomHelper.zoomTest(gTab1, 1, "Zoom should be 1 when image was loaded in the background");
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
+    FullZoomHelper.zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
+  }).then(imageZoomSwitch, FullZoomHelper.failAndContinue(finish));
 }
 
 function imageZoomSwitch() {
-  navigate(BACK, function () {
-    navigate(FORWARD, function () {
-      zoomTest(gTab1, 1, "Tab 1 should not be zoomed when an image loads");
+  Task.spawn(function () {
+    yield FullZoomHelper.navigate(BACK);
+    yield FullZoomHelper.navigate(FORWARD);
+    FullZoomHelper.zoomTest(gTab1, 1, "Tab 1 should not be zoomed when an image loads");
 
-      afterZoom(function() {
-        zoomTest(gTab1, 1, "Tab 1 should still not be zoomed when deselected");
-        finishTest();
-      });
-      gBrowser.selectedTab = gTab2;
-    });
-  });
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
+    FullZoomHelper.zoomTest(gTab1, 1, "Tab 1 should still not be zoomed when deselected");
+  }).then(finishTest, FullZoomHelper.failAndContinue(finish));
 }
 
 var finishTestStarted  = false;
 function finishTest() {
-  ok(!finishTestStarted, "finishTest called more than once");
-  finishTestStarted = true;
-  gBrowser.selectedTab = gTab1;
-  FullZoom.reset();
-  gBrowser.removeTab(gTab1);
-  FullZoom.reset();
-  gBrowser.removeTab(gTab2);
-  FullZoom.reset();
-  gBrowser.removeTab(gTab3);
-  finish();
-}
-
-function zoomTest(tab, val, msg) {
-  is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
+  Task.spawn(function () {
+    ok(!finishTestStarted, "finishTest called more than once");
+    finishTestStarted = true;
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
+    yield FullZoomHelper.reset();
+    gBrowser.removeTab(gTab1);
+    yield FullZoomHelper.reset();
+    gBrowser.removeTab(gTab2);
+    yield FullZoomHelper.reset();
+    gBrowser.removeTab(gTab3);
+  }).then(finish, FullZoomHelper.failAndContinue(finish));
 }
-
-function load(tab, url, cb) {
-  let didLoad = false;
-  let didZoom = false;
-  tab.linkedBrowser.addEventListener("load", function (event) {
-    event.currentTarget.removeEventListener("load", arguments.callee, true);
-    didLoad = true;
-    if (didZoom)
-      executeSoon(cb);
-  }, true);
-
-  afterZoom(function() {
-    didZoom = true;
-    if (didLoad)
-      executeSoon(cb);
-  });
-
-  tab.linkedBrowser.loadURI(url);
-}
-
-function navigate(direction, cb) {
-  let didPs = false;
-  let didZoom = false;
-  gBrowser.addEventListener("pageshow", function (event) {
-    gBrowser.removeEventListener("pageshow", arguments.callee, true);
-    didPs = true;
-    if (didZoom)
-      executeSoon(cb);
-  }, true);
-
-  afterZoom(function() {
-    didZoom = true;
-    if (didPs)
-      executeSoon(cb);
-  });
-
-  if (direction == BACK)
-    gBrowser.goBack();
-  else if (direction == FORWARD)
-    gBrowser.goForward();
-}
-
-function afterZoom(cb) {
-  let oldSZFB = ZoomManager.setZoomForBrowser;
-  ZoomManager.setZoomForBrowser = function(browser, value) {
-    oldSZFB.call(ZoomManager, browser, value);
-    ZoomManager.setZoomForBrowser = oldSZFB;
-    executeSoon(cb);
-  };
-}
--- a/browser/base/content/test/browser_bug416661.js
+++ b/browser/base/content/test/browser_bug416661.js
@@ -1,62 +1,42 @@
 var tabElm, zoomLevel;
 function start_test_prefNotSet() {
-  is(ZoomManager.zoom, 1, "initial zoom level should be 1");
-  FullZoom.enlarge();
+  Task.spawn(function () {
+    is(ZoomManager.zoom, 1, "initial zoom level should be 1");
+    yield FullZoomHelper.enlarge();
 
-  //capture the zoom level to test later
-  zoomLevel = ZoomManager.zoom;
-  isnot(zoomLevel, 1, "zoom level should have changed");
+    //capture the zoom level to test later
+    zoomLevel = ZoomManager.zoom;
+    isnot(zoomLevel, 1, "zoom level should have changed");
 
-  afterZoomAndLoad(continue_test_prefNotSet);
-  content.location = 
-    "http://mochi.test:8888/browser/browser/base/content/test/moz.png";
+    yield FullZoomHelper.load(gBrowser.selectedTab, "http://mochi.test:8888/browser/browser/base/content/test/moz.png");
+  }).then(continue_test_prefNotSet, FullZoomHelper.failAndContinue(finish));
 }
 
 function continue_test_prefNotSet () {
-  is(ZoomManager.zoom, 1, "zoom level pref should not apply to an image");
-  FullZoom.reset();
+  Task.spawn(function () {
+    is(ZoomManager.zoom, 1, "zoom level pref should not apply to an image");
+    yield FullZoomHelper.reset();
 
-  afterZoomAndLoad(end_test_prefNotSet);
-  content.location = 
-    "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html";
+    yield FullZoomHelper.load(gBrowser.selectedTab, "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html");
+  }).then(end_test_prefNotSet, FullZoomHelper.failAndContinue(finish));
 }
 
 function end_test_prefNotSet() {
-  is(ZoomManager.zoom, zoomLevel, "the zoom level should have persisted");
+  Task.spawn(function () {
+    is(ZoomManager.zoom, zoomLevel, "the zoom level should have persisted");
 
-  // Reset the zoom so that other tests have a fresh zoom level
-  FullZoom.reset();
-  gBrowser.removeCurrentTab();
-  finish();
+    // Reset the zoom so that other tests have a fresh zoom level
+    yield FullZoomHelper.reset();
+    gBrowser.removeCurrentTab();
+  }).then(finish, FullZoomHelper.failAndContinue(finish));
 }
 
-
 function test() {
   waitForExplicitFinish();
 
-  tabElm = gBrowser.addTab();
-  gBrowser.selectedTab = tabElm;
-
-  afterZoomAndLoad(start_test_prefNotSet);
-  content.location = 
-    "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html";
+  Task.spawn(function () {
+    tabElm = gBrowser.addTab();
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tabElm);
+    yield FullZoomHelper.load(tabElm, "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html");
+  }).then(start_test_prefNotSet, FullZoomHelper.failAndContinue(finish));
 }
-
-function afterZoomAndLoad(cb) {
-  let didLoad = false;
-  let didZoom = false;
-  tabElm.linkedBrowser.addEventListener("load", function() {
-    tabElm.linkedBrowser.removeEventListener("load", arguments.callee, true);
-    didLoad = true;
-    if (didZoom)
-      executeSoon(cb);
-  }, true);
-  let oldSZFB = ZoomManager.setZoomForBrowser;
-  ZoomManager.setZoomForBrowser = function(browser, value) {
-    oldSZFB.call(ZoomManager, browser, value);
-    ZoomManager.setZoomForBrowser = oldSZFB;
-    didZoom = true;
-    if (didLoad)
-      executeSoon(cb);
-  };
-}
--- a/browser/base/content/test/browser_bug419612.js
+++ b/browser/base/content/test/browser_bug419612.js
@@ -1,49 +1,32 @@
 function test() {
   waitForExplicitFinish();
 
-  let testPage = "http://example.org/browser/browser/base/content/test/dummy_page.html";
-  let tab1 = gBrowser.selectedTab = gBrowser.addTab();
-  tab1.linkedBrowser.addEventListener("load", (function(event) {
-    event.currentTarget.removeEventListener("load", arguments.callee, true);
+  Task.spawn(function () {
+    let testPage = "http://example.org/browser/browser/base/content/test/dummy_page.html";
+    let tab1 = gBrowser.addTab();
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
+    yield FullZoomHelper.load(tab1, testPage);
 
     let tab2 = gBrowser.addTab();
-    tab2.linkedBrowser.addEventListener("load", (function(event) {
-      event.currentTarget.removeEventListener("load", arguments.callee, true);
-
-      FullZoom.enlarge();
-      let tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
+    yield FullZoomHelper.load(tab2, testPage);
 
-      afterZoom(function() {
-        let tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
-        is(tab2Zoom, tab1Zoom, "Zoom should affect background tabs");
+    yield FullZoomHelper.enlarge();
+    let tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
 
-        gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", false);
-        FullZoom.reset();
-        gBrowser.selectedTab = tab1;
-        tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
-        tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
-        isnot(tab1Zoom, tab2Zoom, "Zoom should not affect background tabs");
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
+    let tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
+    is(tab2Zoom, tab1Zoom, "Zoom should affect background tabs");
 
-        if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
-          gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
-        gBrowser.removeTab(tab1);
-        gBrowser.removeTab(tab2);
-        finish();
-      });
-      gBrowser.selectedTab = tab2;
-    }), true);
-    tab2.linkedBrowser.loadURI(testPage);
-  }), true);
-  content.location = testPage;
+    gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", false);
+    yield FullZoomHelper.reset();
+    gBrowser.selectedTab = tab1;
+    tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
+    tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
+    isnot(tab1Zoom, tab2Zoom, "Zoom should not affect background tabs");
+
+    if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
+      gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
+    gBrowser.removeTab(tab1);
+    gBrowser.removeTab(tab2);
+  }).then(finish, FullZoomHelper.failAndContinue(finish));
 }
-
-function afterZoom(cb) {
-  let oldAPTS = FullZoom._applyPrefToSetting;
-  FullZoom._applyPrefToSetting = function(value, browser) {
-    if (!value)
-      value = undefined;
-    oldAPTS.call(FullZoom, value, browser);
-    FullZoom._applyPrefToSetting = oldAPTS;
-    executeSoon(cb);
-  };
-}
--- a/browser/base/content/test/browser_bug441778.js
+++ b/browser/base/content/test/browser_bug441778.js
@@ -8,37 +8,39 @@
  */
 
 function test() {
   waitForExplicitFinish();
 
   const TEST_PAGE_URL = 'data:text/html,<body><iframe src=""></iframe></body>';
   const TEST_IFRAME_URL = "http://test2.example.org/";
 
-  // Prepare the test tab
-  gBrowser.selectedTab = gBrowser.addTab();
-  let testBrowser = gBrowser.selectedBrowser;
+  Task.spawn(function () {
+    // Prepare the test tab
+    let tab = gBrowser.addTab();
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tab);
 
-  testBrowser.addEventListener("load", function () {
-    testBrowser.removeEventListener("load", arguments.callee, true);
+    let testBrowser = tab.linkedBrowser;
+
+    yield FullZoomHelper.load(tab, TEST_PAGE_URL);
 
     // Change the zoom level and then save it so we can compare it to the level
     // after loading the sub-document.
-    FullZoom.enlarge();
+    yield FullZoomHelper.enlarge();
     var zoomLevel = ZoomManager.zoom;
 
     // Start the sub-document load.
+    let deferred = Promise.defer();
     executeSoon(function () {
       testBrowser.addEventListener("load", function (e) {
         testBrowser.removeEventListener("load", arguments.callee, true);
 
         is(e.target.defaultView.location, TEST_IFRAME_URL, "got the load event for the iframe");
         is(ZoomManager.zoom, zoomLevel, "zoom is retained after sub-document load");
 
         gBrowser.removeCurrentTab();
-        finish();
+        deferred.resolve();
       }, true);
       content.document.querySelector("iframe").src = TEST_IFRAME_URL;
     });
-  }, true);
-
-  content.location = TEST_PAGE_URL;
+    yield deferred.promise;
+  }).then(finish, FullZoomHelper.failAndContinue(finish));
 }
--- a/browser/base/content/test/browser_bug555224.js
+++ b/browser/base/content/test/browser_bug555224.js
@@ -1,62 +1,39 @@
 /* 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/. */
 const TEST_PAGE = "/browser/browser/base/content/test/dummy_page.html";
 var gTestTab, gBgTab, gTestZoom;
 
-function afterZoomAndLoad(aCallback, aTab) {
-  let didLoad = false;
-  let didZoom = false;
-  aTab.linkedBrowser.addEventListener("load", function() {
-    aTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
-    didLoad = true;
-    if (didZoom)
-      executeSoon(aCallback);
-  }, true);
-  let oldAPTS = FullZoom._applyPrefToSetting;
-  FullZoom._applyPrefToSetting = function(value, browser) {
-    if (!value)
-      value = undefined;
-    oldAPTS.call(FullZoom, value, browser);
-    // Don't reset _applyPrefToSetting until we've seen the about:blank load(s)
-    if (browser && browser.currentURI.spec.startsWith("http:")) {
-      FullZoom._applyPrefToSetting = oldAPTS;
-      didZoom = true;
-    }
-    if (didLoad && didZoom)
-      executeSoon(aCallback);
-  };
-}
+function testBackgroundLoad() {
+  Task.spawn(function () {
+    is(ZoomManager.zoom, gTestZoom, "opening a background tab should not change foreground zoom");
 
-function testBackgroundLoad() {
-  is(ZoomManager.zoom, gTestZoom, "opening a background tab should not change foreground zoom");
-
-  gBrowser.removeTab(gBgTab);
+    gBrowser.removeTab(gBgTab);
 
-  FullZoom.reset();
-  gBrowser.removeTab(gTestTab);
-
-  finish();
+    yield FullZoomHelper.reset();
+    gBrowser.removeTab(gTestTab);
+  }).then(finish, FullZoomHelper.failAndContinue(finish));
 }
 
 function testInitialZoom() {
-  is(ZoomManager.zoom, 1, "initial zoom level should be 1");
-  FullZoom.enlarge();
+  Task.spawn(function () {
+    is(ZoomManager.zoom, 1, "initial zoom level should be 1");
+    yield FullZoomHelper.enlarge();
 
-  gTestZoom = ZoomManager.zoom;
-  isnot(gTestZoom, 1, "zoom level should have changed");
+    gTestZoom = ZoomManager.zoom;
+    isnot(gTestZoom, 1, "zoom level should have changed");
 
-  afterZoomAndLoad(testBackgroundLoad,
-                   gBgTab = gBrowser.loadOneTab("http://mochi.test:8888" + TEST_PAGE,
-                                                {inBackground: true}));
+    gBgTab = gBrowser.addTab();
+    yield FullZoomHelper.load(gBgTab, "http://mochi.test:8888" + TEST_PAGE);
+  }).then(testBackgroundLoad, FullZoomHelper.failAndContinue(finish));
 }
 
 function test() {
   waitForExplicitFinish();
 
-  gTestTab = gBrowser.addTab();
-  gBrowser.selectedTab = gTestTab;
-
-  afterZoomAndLoad(testInitialZoom, gTestTab);
-  content.location = "http://example.org" + TEST_PAGE;
+  Task.spawn(function () {
+    gTestTab = gBrowser.addTab();
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTestTab);
+    yield FullZoomHelper.load(gTestTab, "http://example.org" + TEST_PAGE);
+  }).then(testInitialZoom, FullZoomHelper.failAndContinue(finish));
 }
--- a/browser/base/content/test/browser_bug559991.js
+++ b/browser/base/content/test/browser_bug559991.js
@@ -1,57 +1,40 @@
+var tab;
+
 function test() {
 
   // ----------
   // Test setup
 
   waitForExplicitFinish();
 
-  let oldOLC = FullZoom.onLocationChange;
-  FullZoom.onLocationChange = function(aURI, aIsTabSwitch, aBrowser) {
-    // Ignore calls that are not about tab switching on this test
-    if (aIsTabSwitch)
-      oldOLC.call(FullZoom, aURI, aIsTabSwitch, aBrowser);
-  };
-
   gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", true);
   gPrefService.setBoolPref("browser.zoom.siteSpecific", true);
 
-  let oldAPTS = FullZoom._applyPrefToSetting;
   let uri = "http://example.org/browser/browser/base/content/test/dummy_page.html";
 
-  let tab = gBrowser.addTab();
-  tab.linkedBrowser.addEventListener("load", function(event) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  Task.spawn(function () {
+    tab = gBrowser.addTab();
+    yield FullZoomHelper.load(tab, uri);
 
     // -------------------------------------------------------------------
     // Test - Trigger a tab switch that should update the zoom level
-    FullZoom._applyPrefToSetting = function() {
-      ok(true, "applyPrefToSetting was called");
-      endTest();
-    }
-    gBrowser.selectedTab = tab;
-
-  }, true); 
-  tab.linkedBrowser.loadURI(uri);
-
-  // -------------
-  // Test clean-up
-  function endTest() {
-    FullZoom._applyPrefToSetting = oldAPTS;
-    FullZoom.onLocationChange = oldOLC;
-    gBrowser.removeTab(tab);
-
-    oldAPTS = null;
-    oldOLC = null;
-    tab = null;
-
-    if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
-      gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
-
-    if (gPrefService.prefHasUserValue("browser.zoom.siteSpecific"))
-      gPrefService.clearUserPref("browser.zoom.siteSpecific");
-
-    finish();
-  }
-
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tab);
+    ok(true, "applyPrefToSetting was called");
+  }).then(endTest, FullZoomHelper.failAndContinue(endTest));
 }
 
+// -------------
+// Test clean-up
+function endTest() {
+  gBrowser.removeTab(tab);
+
+  tab = null;
+
+  if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
+    gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
+
+  if (gPrefService.prefHasUserValue("browser.zoom.siteSpecific"))
+    gPrefService.clearUserPref("browser.zoom.siteSpecific");
+
+  finish();
+}
--- a/browser/base/content/test/browser_bug575830.js
+++ b/browser/base/content/test/browser_bug575830.js
@@ -8,30 +8,27 @@ function test() {
   const TEST_IMAGE = "http://example.org/browser/browser/base/content/test/moz.png";
 
   waitForExplicitFinish();
   registerCleanupFunction(function cleanup() {
     gBrowser.removeTab(tab1);
     gBrowser.removeTab(tab2);
   });
 
-  tab1 = gBrowser.addTab(TEST_IMAGE);
-  tab2 = gBrowser.addTab();
-  gBrowser.selectedTab = tab1;
+  Task.spawn(function () {
+    tab1 = gBrowser.addTab();
+    tab2 = gBrowser.addTab();
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
+    yield FullZoomHelper.load(tab1, TEST_IMAGE);
 
-  tab1.linkedBrowser.addEventListener("load", function onload() {
-    tab1.linkedBrowser.removeEventListener("load", onload, true);
     is(ZoomManager.zoom, 1, "initial zoom level for first should be 1");
 
-    FullZoom.enlarge();
+    yield FullZoomHelper.enlarge();
     let zoom = ZoomManager.zoom;
     isnot(zoom, 1, "zoom level should have changed");
 
-    gBrowser.selectedTab = tab2;
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
     is(ZoomManager.zoom, 1, "initial zoom level for second tab should be 1");
 
-    gBrowser.selectedTab = tab1;
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
     is(ZoomManager.zoom, zoom, "zoom level for first tab should not have changed");
-
-    finish();
-  }, true);
+  }).then(finish, FullZoomHelper.failAndContinue(finish));
 }
-
--- a/browser/base/content/test/browser_bug719271.js
+++ b/browser/base/content/test/browser_bug719271.js
@@ -6,136 +6,76 @@
 const TEST_PAGE = "http://example.org/browser/browser/base/content/test/zoom_test.html";
 const TEST_VIDEO = "http://example.org/browser/browser/base/content/test/video.ogg";
 
 var gTab1, gTab2, gLevel1, gLevel2;
 
 function test() {
   waitForExplicitFinish();
 
-  gTab1 = gBrowser.addTab();
-  gTab2 = gBrowser.addTab();
-  gBrowser.selectedTab = gTab1;
+  Task.spawn(function () {
+    gTab1 = gBrowser.addTab();
+    gTab2 = gBrowser.addTab();
 
-  load(gTab1, TEST_PAGE, function() {
-    load(gTab2, TEST_VIDEO, zoomTab1);
-  });
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
+    yield FullZoomHelper.load(gTab1, TEST_PAGE);
+    yield FullZoomHelper.load(gTab2, TEST_VIDEO);
+  }).then(zoomTab1, FullZoomHelper.failAndContinue(finish));
 }
 
 function zoomTab1() {
-  is(gBrowser.selectedTab, gTab1, "Tab 1 is selected");
-  zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
-  zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
+  Task.spawn(function () {
+    is(gBrowser.selectedTab, gTab1, "Tab 1 is selected");
+    FullZoomHelper.zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
+    FullZoomHelper.zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
 
-  FullZoom.enlarge();
-  gLevel1 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
+    yield FullZoomHelper.enlarge();
+    gLevel1 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
 
-  ok(gLevel1 > 1, "New zoom for tab 1 should be greater than 1");
-  zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
+    ok(gLevel1 > 1, "New zoom for tab 1 should be greater than 1");
+    FullZoomHelper.zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
 
-  gBrowser.selectedTab = gTab2;
-  zoomTest(gTab2, 1, "Tab 2 is still unzoomed after it is selected");
-  zoomTest(gTab1, gLevel1, "Tab 1 is still zoomed");
-
-  zoomTab2();
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
+    FullZoomHelper.zoomTest(gTab2, 1, "Tab 2 is still unzoomed after it is selected");
+    FullZoomHelper.zoomTest(gTab1, gLevel1, "Tab 1 is still zoomed");
+  }).then(zoomTab2, FullZoomHelper.failAndContinue(finish));
 }
 
 function zoomTab2() {
-  is(gBrowser.selectedTab, gTab2, "Tab 2 is selected");
+  Task.spawn(function () {
+    is(gBrowser.selectedTab, gTab2, "Tab 2 is selected");
 
-  FullZoom.reduce();
-  let gLevel2 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab2));
+    yield FullZoomHelper.reduce();
+    let gLevel2 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab2));
 
-  ok(gLevel2 < 1, "New zoom for tab 2 should be less than 1");
-  zoomTest(gTab1, gLevel1, "Zooming tab 2 should not affect tab 1");
+    ok(gLevel2 < 1, "New zoom for tab 2 should be less than 1");
+    FullZoomHelper.zoomTest(gTab1, gLevel1, "Zooming tab 2 should not affect tab 1");
 
-  afterZoom(function() {
-    zoomTest(gTab1, gLevel1, "Tab 1 should have the same zoom after it's selected");
-
-    testNavigation();
-  });
-  gBrowser.selectedTab = gTab1;
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
+    FullZoomHelper.zoomTest(gTab1, gLevel1, "Tab 1 should have the same zoom after it's selected");
+  }).then(testNavigation, FullZoomHelper.failAndContinue(finish));
 }
 
 function testNavigation() {
-  load(gTab1, TEST_VIDEO, function() {
-    zoomTest(gTab1, 1, "Zoom should be 1 when a video was loaded");
-    navigate(BACK, function() {
-      zoomTest(gTab1, gLevel1, "Zoom should be restored when a page is loaded");
-      navigate(FORWARD, function() {
-        zoomTest(gTab1, 1, "Zoom should be 1 again when navigating back to a video");
-        finishTest();
-      });
-    });
-  });
+  Task.spawn(function () {
+    yield FullZoomHelper.load(gTab1, TEST_VIDEO);
+    FullZoomHelper.zoomTest(gTab1, 1, "Zoom should be 1 when a video was loaded");
+    yield FullZoomHelper.navigate(FullZoomHelper.BACK);
+    FullZoomHelper.zoomTest(gTab1, gLevel1, "Zoom should be restored when a page is loaded");
+    yield FullZoomHelper.navigate(FullZoomHelper.FORWARD);
+    FullZoomHelper.zoomTest(gTab1, 1, "Zoom should be 1 again when navigating back to a video");
+  }).then(finishTest, FullZoomHelper.failAndContinue(finish));
 }
 
 var finishTestStarted  = false;
 function finishTest() {
-  ok(!finishTestStarted, "finishTest called more than once");
-  finishTestStarted = true;
-
-  gBrowser.selectedTab = gTab1;
-  FullZoom.reset();
-  gBrowser.removeTab(gTab1);
-
-  gBrowser.selectedTab = gTab2;
-  FullZoom.reset();
-  gBrowser.removeTab(gTab2);
-
-  finish();
-}
-
-function zoomTest(tab, val, msg) {
-  is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
-}
-
-function load(tab, url, cb) {
-  let didLoad = false;
-  let didZoom = false;
-  tab.linkedBrowser.addEventListener("load", function onload(event) {
-    event.currentTarget.removeEventListener("load", onload, true);
-    didLoad = true;
-    if (didZoom)
-      executeSoon(cb);
-  }, true);
-
-  afterZoom(function() {
-    didZoom = true;
-    if (didLoad)
-      executeSoon(cb);
-  });
+  Task.spawn(function () {
+    ok(!finishTestStarted, "finishTest called more than once");
+    finishTestStarted = true;
 
-  tab.linkedBrowser.loadURI(url);
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
+    yield FullZoomHelper.reset();
+    gBrowser.removeTab(gTab1);
+    yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
+    yield FullZoomHelper.reset();
+    gBrowser.removeTab(gTab2);
+  }).then(finish, FullZoomHelper.failAndContinue(finish));
 }
-
-const BACK = 0;
-const FORWARD = 1;
-function navigate(direction, cb) {
-  let didPs = false;
-  let didZoom = false;
-  gBrowser.addEventListener("pageshow", function onpageshow(event) {
-    gBrowser.removeEventListener("pageshow", onpageshow, true);
-    didPs = true;
-    if (didZoom)
-      executeSoon(cb);
-  }, true);
-
-  afterZoom(function() {
-    didZoom = true;
-    if (didPs)
-      executeSoon(cb);
-  });
-
-  if (direction == BACK)
-    gBrowser.goBack();
-  else if (direction == FORWARD)
-    gBrowser.goForward();
-}
-
-function afterZoom(cb) {
-  let oldSZFB = ZoomManager.setZoomForBrowser;
-  ZoomManager.setZoomForBrowser = function(browser, value) {
-    oldSZFB.call(ZoomManager, browser, value);
-    ZoomManager.setZoomForBrowser = oldSZFB;
-    executeSoon(cb);
-  };
-}
--- a/browser/base/content/test/browser_popupNotification.js
+++ b/browser/base/content/test/browser_popupNotification.js
@@ -720,17 +720,36 @@ var tests = [
 
     },
     onHidden: function (popup) {
       ok(this.notifyObj.mainActionClicked, "mainAction was clicked after the delay");
       ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback was not triggered");
       PopupNotifications.buttonDelay = PREF_SECURITY_DELAY_INITIAL;
     },
   },
-  { // Test #25 - location change in background tab removes notification
+  { // Test #25 - reload removes notification
+    run: function () {
+      loadURI("http://example.com/", function() {
+        let notifyObj = new basicNotification();
+        notifyObj.options.eventCallback = function (eventName) {
+          if (eventName == "removed") {
+            ok(true, "Notification removed in background tab after reloading");
+            executeSoon(function () {
+              goNext();
+            });
+          }
+        };
+        showNotification(notifyObj);
+        executeSoon(function () {
+          gBrowser.selectedBrowser.reload();
+        });
+      });
+    }
+  },
+  { // Test #26 - location change in background tab removes notification
     run: function () {
       let oldSelectedTab = gBrowser.selectedTab;
       let newTab = gBrowser.addTab("about:blank");
       gBrowser.selectedTab = newTab;
 
       loadURI("http://example.com/", function() {
         gBrowser.selectedTab = oldSelectedTab;
         let browser = gBrowser.getBrowserForTab(newTab);
@@ -748,17 +767,17 @@ var tests = [
         };
         showNotification(notifyObj);
         executeSoon(function () {
           browser.reload();
         });
       });
     }
   },
-  { // Test #26 -  Popup notification anchor shouldn't disappear when a notification with the same ID is re-added in a background tab
+  { // Test #27 -  Popup notification anchor shouldn't disappear when a notification with the same ID is re-added in a background tab
     run: function () {
       loadURI("http://example.com/", function () {
         let originalTab = gBrowser.selectedTab;
         let bgTab = gBrowser.addTab("about:blank");
         gBrowser.selectedTab = bgTab;
         loadURI("http://example.com/", function () {
           let anchor = document.createElement("box");
           anchor.id = "test26-anchor";
@@ -785,16 +804,34 @@ var tests = [
           is(anchor.getAttribute("showing"), "true", "anchor still showing");
 
           fgNotification.remove();
           gBrowser.removeTab(bgTab);
           goNext();
         });
       });
     }
+  },
+  { // Test #28 - location change in embedded frame removes notification
+    run: function () {
+      loadURI("data:text/html,<iframe id='iframe' src='http://example.com/'>", function () {
+        let notifyObj = new basicNotification();
+        notifyObj.options.eventCallback = function (eventName) {
+          if (eventName == "removed") {
+            ok(true, "Notification removed in background tab after reloading");
+            executeSoon(goNext);
+          }
+        };
+        showNotification(notifyObj);
+        executeSoon(function () {
+          content.document.getElementById("iframe")
+                          .setAttribute("src", "http://example.org/");
+        });
+      });
+    }
   }
 ];
 
 function showNotification(notifyObj) {
   return PopupNotifications.show(notifyObj.browser,
                                  notifyObj.id,
                                  notifyObj.message,
                                  notifyObj.anchorID,
@@ -872,17 +909,17 @@ function triggerSecondaryCommand(popup, 
   // One down event to open the popup
   EventUtils.synthesizeKey("VK_DOWN", { altKey: !navigator.platform.contains("Mac") });
 }
 
 function loadURI(uri, callback) {
   if (callback) {
     gBrowser.addEventListener("load", function() {
       // Ignore the about:blank load
-      if (gBrowser.currentURI.spec != uri)
+      if (gBrowser.currentURI.spec == "about:blank")
         return;
 
       gBrowser.removeEventListener("load", arguments.callee, true);
 
       callback();
     }, true);
   }
   gBrowser.loadURI(uri);
--- a/browser/base/content/test/head.js
+++ b/browser/base/content/test/head.js
@@ -256,8 +256,112 @@ function promiseHistoryClearedState(aURI
       is(aIsVisited, !aShouldBeCleared,
          "history visit " + aURI.spec + " should " + niceStr + " exist");
       callbackDone();
     });
   });
 
   return deferred.promise;
 }
+
+let FullZoomHelper = {
+
+  selectTabAndWaitForLocationChange: function selectTabAndWaitForLocationChange(tab) {
+    let deferred = Promise.defer();
+    if (tab && gBrowser.selectedTab == tab) {
+      deferred.resolve();
+      return deferred.promise;
+    }
+    if (tab)
+      gBrowser.selectedTab = tab;
+    Services.obs.addObserver(function obs() {
+      Services.obs.removeObserver(obs, "browser-fullZoom:locationChange");
+      deferred.resolve();
+    }, "browser-fullZoom:locationChange", false);
+    return deferred.promise;
+  },
+
+  load: function load(tab, url) {
+    let deferred = Promise.defer();
+    let didLoad = false;
+    let didZoom = false;
+
+    tab.linkedBrowser.addEventListener("load", function (event) {
+      event.currentTarget.removeEventListener("load", arguments.callee, true);
+      didLoad = true;
+      if (didZoom)
+        deferred.resolve();
+    }, true);
+
+    // Don't select background tabs.  That way tests can use this method on
+    // background tabs without having them automatically be selected.  Just wait
+    // for the zoom to change on the current tab if it's `tab`.
+    if (tab == gBrowser.selectedTab) {
+      this.selectTabAndWaitForLocationChange(null).then(function () {
+        didZoom = true;
+        if (didLoad)
+          deferred.resolve();
+      });
+    }
+    else
+      didZoom = true;
+
+    tab.linkedBrowser.loadURI(url);
+
+    return deferred.promise;
+  },
+
+  zoomTest: function zoomTest(tab, val, msg) {
+    is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
+  },
+
+  enlarge: function enlarge() {
+    let deferred = Promise.defer();
+    FullZoom.enlarge(function () deferred.resolve());
+    return deferred.promise;
+  },
+
+  reduce: function reduce() {
+    let deferred = Promise.defer();
+    FullZoom.reduce(function () deferred.resolve());
+    return deferred.promise;
+  },
+
+  reset: function reset() {
+    let deferred = Promise.defer();
+    FullZoom.reset(function () deferred.resolve());
+    return deferred.promise;
+  },
+
+  BACK: 0,
+  FORWARD: 1,
+  navigate: function navigate(direction) {
+    let deferred = Promise.defer();
+    let didPs = false;
+    let didZoom = false;
+
+    gBrowser.addEventListener("pageshow", function (event) {
+      gBrowser.removeEventListener("pageshow", arguments.callee, true);
+      didPs = true;
+      if (didZoom)
+        deferred.resolve();
+    }, true);
+
+    if (direction == this.BACK)
+      gBrowser.goBack();
+    else if (direction == this.FORWARD)
+      gBrowser.goForward();
+
+    this.selectTabAndWaitForLocationChange(null).then(function () {
+      didZoom = true;
+      if (didPs)
+        deferred.resolve();
+    });
+    return deferred.promise;
+  },
+
+  failAndContinue: function failAndContinue(func) {
+    return function (err) {
+      ok(false, err);
+      func();
+    };
+  },
+};
--- a/browser/components/preferences/tests/browser_permissions.js
+++ b/browser/components/preferences/tests/browser_permissions.js
@@ -269,39 +269,40 @@ var tests = [
        "are allowed");
 
     runNextTest();
   },
 
   function test_forget_site() {
     // click "Forget About This Site" button
     gBrowser.contentDocument.getElementById("forget-site-button").doCommand();
-
-    is(gSiteLabel.value, "", "site label cleared");
+    waitForClearHistory(function() {
+      is(gSiteLabel.value, "", "site label cleared");
 
-    let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
-    is(gSitesList.selectedItem, allSitesItem,
-       "all sites item selected after forgetting selected site");
+      let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
+      is(gSitesList.selectedItem, allSitesItem,
+         "all sites item selected after forgetting selected site");
 
-    // check to make sure site is gone from sites list
-    let testSiteItem = getSiteItem(TEST_URI_2.host);
-    ok(!testSiteItem, "site removed from sites list");
+      // check to make sure site is gone from sites list
+      let testSiteItem = getSiteItem(TEST_URI_2.host);
+      ok(!testSiteItem, "site removed from sites list");
 
-    // check to make sure we forgot all permissions corresponding to site
-    for (let type in TEST_PERMS) {
-      if (type == "password") {
-        ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
-           "password saving should be enabled by default");
-      } else {
-        is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, type), PERM_UNKNOWN,
-           type + " permission should not be set for test site 2");
+      // check to make sure we forgot all permissions corresponding to site
+      for (let type in TEST_PERMS) {
+        if (type == "password") {
+          ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
+             "password saving should be enabled by default");
+        } else {
+          is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, type), PERM_UNKNOWN,
+             type + " permission should not be set for test site 2");
+        }
       }
-    }
 
-    runNextTest();
+      runNextTest();
+    });
   }
 ];
 
 function getPermissionMenulist(aType) {
   return gBrowser.contentDocument.getElementById(aType + "-menulist");
 }
 
 function getSiteItem(aHost) {
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js
@@ -20,37 +20,39 @@ function test() {
         aWindow.gBrowser.selectedTab = tabMozilla;
 
         let mozillaBrowser = aWindow.gBrowser.getBrowserForTab(tabMozilla);
         mozillaBrowser.addEventListener("load", function onMozillaBrowserLoad() {
           mozillaBrowser.removeEventListener("load", onMozillaBrowserLoad, true);
           let mozillaZoom = aWindow.ZoomManager.zoom;
 
           // change the zoom on the mozilla page
-          aWindow.FullZoom.enlarge();
-          // make sure the zoom level has been changed
-          isnot(aWindow.ZoomManager.zoom, mozillaZoom, "Zoom level can be changed");
-          mozillaZoom = aWindow.ZoomManager.zoom;
+          aWindow.FullZoom.enlarge(function () {
+            // make sure the zoom level has been changed
+            isnot(aWindow.ZoomManager.zoom, mozillaZoom, "Zoom level can be changed");
+            mozillaZoom = aWindow.ZoomManager.zoom;
 
-          // switch to about: tab
-          aWindow.gBrowser.selectedTab = tabAbout;
+            // switch to about: tab
+            aWindow.gBrowser.selectedTab = tabAbout;
 
-          // switch back to mozilla tab
-          aWindow.gBrowser.selectedTab = tabMozilla;
+            // switch back to mozilla tab
+            aWindow.gBrowser.selectedTab = tabMozilla;
 
-          // make sure the zoom level has not changed
-          is(aWindow.ZoomManager.zoom, mozillaZoom,
-            "Entering private browsing should not reset the zoom on a tab");
+            // make sure the zoom level has not changed
+            is(aWindow.ZoomManager.zoom, mozillaZoom,
+              "Entering private browsing should not reset the zoom on a tab");
 
-          // cleanup
-          aWindow.FullZoom.reset();
-          aWindow.gBrowser.removeTab(tabMozilla);
-          aWindow.gBrowser.removeTab(tabAbout);
-          aWindow.close();
-          aCallback();
+            // cleanup
+            aWindow.FullZoom.reset(function () {
+              aWindow.gBrowser.removeTab(tabMozilla);
+              aWindow.gBrowser.removeTab(tabAbout);
+              aWindow.close();
+              aCallback();
+            });
+          });
         }, true);
         mozillaBrowser.contentWindow.location = "about:mozilla";
       }, true);
       aboutBrowser.contentWindow.location = "about:";
     });
   }
 
   whenNewWindowLoaded({private: true}, function(privateWindow) {
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
@@ -11,35 +11,45 @@ function test() {
   let windowsToClose = [];
   let windowsToReset = [];
 
   function doTest(aIsZoomedWindow, aWindow, aCallback) {
     aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
       aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
       if (aIsZoomedWindow) {
         // change the zoom on the blank page
-        aWindow.FullZoom.enlarge();
-        isnot(aWindow.ZoomManager.zoom, 1, "Zoom level for about:blank should be changed");
-      } else {
-        // make sure the zoom level is set to 1
-        is(aWindow.ZoomManager.zoom, 1, "Zoom level for about:privatebrowsing should be reset");
+        aWindow.FullZoom.enlarge(function () {
+          isnot(aWindow.ZoomManager.zoom, 1, "Zoom level for about:blank should be changed");
+          aCallback();
+        });
+        return;
       }
+      // make sure the zoom level is set to 1
+      is(aWindow.ZoomManager.zoom, 1, "Zoom level for about:privatebrowsing should be reset");
 
       aCallback();
     }, true);
 
     aWindow.gBrowser.selectedBrowser.loadURI("about:blank");
   }
 
   function finishTest() {
     // cleanup
+    let numWindows = windowsToReset.length;
+    if (!numWindows) {
+      finish();
+      return;
+    }
     windowsToReset.forEach(function(win) {
-      win.FullZoom.reset();
+      win.FullZoom.reset(function onReset() {
+        numWindows--;
+        if (!numWindows)
+          finish();
+      });
     });
-    finish();
   }
 
   function testOnWindow(options, callback) {
     let win = OpenBrowserWindow(options);
     win.addEventListener("load", function onLoad() {
       win.removeEventListener("load", onLoad, false);
       windowsToClose.push(win);
       windowsToReset.push(win);
--- a/browser/components/sessionstore/test/browser_394759_purge.js
+++ b/browser/components/sessionstore/test/browser_394759_purge.js
@@ -1,15 +1,26 @@
 /* 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/. */
 
 Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
 
+function waitForClearHistory(aCallback) {
+  let observer = {
+    observe: function(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(this, "browser:purge-domain-data");
+      setTimeout(aCallback, 0);
+    }
+  };
+  Services.obs.addObserver(observer, "browser:purge-domain-data", false);
+}
+
 function test() {
+  waitForExplicitFinish();
   // utility functions
   function countClosedTabsByTitle(aClosedTabList, aTitle)
     aClosedTabList.filter(function (aData) aData.title == aTitle).length;
 
   function countOpenTabsByTitle(aOpenTabList, aTitle)
     aOpenTabList.filter(function (aData) aData.entries.some(function (aEntry) aEntry.title == aTitle)).length
 
   // backup old state
@@ -75,41 +86,43 @@ function test() {
     ]
   };
 
   // set browser to test state
   ss.setBrowserState(JSON.stringify(testState));
 
   // purge domain & check that we purged correctly for closed windows
   ForgetAboutSite.removeDataFromDomain("mozilla.org");
-
-  let closedWindowData = JSON.parse(ss.getClosedWindowData());
+  waitForClearHistory(function() {
+    let closedWindowData = JSON.parse(ss.getClosedWindowData());
 
-  // First set of tests for _closedWindows[0] - tests basics
-  let win = closedWindowData[0];
-  is(win.tabs.length, 1, "1 tab was removed");
-  is(countOpenTabsByTitle(win.tabs, FORGET), 0,
-     "The correct tab was removed");
-  is(countOpenTabsByTitle(win.tabs, REMEMBER), 1,
-     "The correct tab was remembered");
-  is(win.selected, 1, "Selected tab has changed");
-  is(win.title, REMEMBER, "The window title was correctly updated");
+    // First set of tests for _closedWindows[0] - tests basics
+    let win = closedWindowData[0];
+    is(win.tabs.length, 1, "1 tab was removed");
+    is(countOpenTabsByTitle(win.tabs, FORGET), 0,
+       "The correct tab was removed");
+    is(countOpenTabsByTitle(win.tabs, REMEMBER), 1,
+       "The correct tab was remembered");
+    is(win.selected, 1, "Selected tab has changed");
+    is(win.title, REMEMBER, "The window title was correctly updated");
 
-  // Test more complicated case
-  win = closedWindowData[1];
-  is(win.tabs.length, 3, "2 tabs were removed");
-  is(countOpenTabsByTitle(win.tabs, FORGET), 0,
-     "The correct tabs were removed");
-  is(countOpenTabsByTitle(win.tabs, REMEMBER), 3,
-     "The correct tabs were remembered");
-  is(win.selected, 3, "Selected tab has changed");
-  is(win.title, REMEMBER, "The window title was correctly updated");
+    // Test more complicated case
+    win = closedWindowData[1];
+    is(win.tabs.length, 3, "2 tabs were removed");
+    is(countOpenTabsByTitle(win.tabs, FORGET), 0,
+       "The correct tabs were removed");
+    is(countOpenTabsByTitle(win.tabs, REMEMBER), 3,
+       "The correct tabs were remembered");
+    is(win.selected, 3, "Selected tab has changed");
+    is(win.title, REMEMBER, "The window title was correctly updated");
 
-  // Tests handling of _closedTabs
-  win = closedWindowData[2];
-  is(countClosedTabsByTitle(win._closedTabs, REMEMBER), 1,
-     "The correct number of tabs were removed, and the correct ones");
-  is(countClosedTabsByTitle(win._closedTabs, FORGET), 0,
-     "All tabs to be forgotten were indeed removed");
+    // Tests handling of _closedTabs
+    win = closedWindowData[2];
+    is(countClosedTabsByTitle(win._closedTabs, REMEMBER), 1,
+       "The correct number of tabs were removed, and the correct ones");
+    is(countClosedTabsByTitle(win._closedTabs, FORGET), 0,
+       "All tabs to be forgotten were indeed removed");
 
-  // restore pre-test state
-  ss.setBrowserState(oldState);
+    // restore pre-test state
+    ss.setBrowserState(oldState);
+    finish();
+  });
 }
--- a/browser/components/sessionstore/test/browser_464199.js
+++ b/browser/components/sessionstore/test/browser_464199.js
@@ -1,14 +1,24 @@
 /* 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/. */
 
 Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
 
+function waitForClearHistory(aCallback) {
+  let observer = {
+    observe: function(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(this, "browser:purge-domain-data");
+      setTimeout(aCallback, 0);
+    }
+  };
+  Services.obs.addObserver(observer, "browser:purge-domain-data", false);
+}
+
 function test() {
   /** Test for Bug 464199 **/
 
   waitForExplicitFinish();
 
   const REMEMBER = Date.now(), FORGET = Math.random();
   let test_state = { windows: [{ "tabs": [{ "entries": [] }], _closedTabs: [
     { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
@@ -54,23 +64,24 @@ function test() {
        "Closed tab list has the expected length");
     is(countByTitle(closedTabs, FORGET),
        test_state.windows[0]._closedTabs.length - remember_count,
        "The correct amout of tabs are to be forgotten");
     is(countByTitle(closedTabs, REMEMBER), remember_count,
        "Everything is set up.");
 
     ForgetAboutSite.removeDataFromDomain("example.net");
+    waitForClearHistory(function() {
+      closedTabs = JSON.parse(ss.getClosedTabData(newWin));
+      is(closedTabs.length, remember_count,
+         "The correct amout of tabs was removed");
+      is(countByTitle(closedTabs, FORGET), 0,
+         "All tabs to be forgotten were indeed removed");
+      is(countByTitle(closedTabs, REMEMBER), remember_count,
+         "... and tabs to be remembered weren't.");
 
-    closedTabs = JSON.parse(ss.getClosedTabData(newWin));
-    is(closedTabs.length, remember_count,
-       "The correct amout of tabs was removed");
-    is(countByTitle(closedTabs, FORGET), 0,
-       "All tabs to be forgotten were indeed removed");
-    is(countByTitle(closedTabs, REMEMBER), remember_count,
-       "... and tabs to be remembered weren't.");
-
-    // clean up
-    newWin.close();
-    gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
-    finish();
+      // clean up
+      newWin.close();
+      gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
+     finish();
+    });
   }, false);
 }
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-22.0a1
+23.0a1
--- a/browser/devtools/commandline/Commands.jsm
+++ b/browser/devtools/commandline/Commands.jsm
@@ -9,8 +9,9 @@ const Cu = Components.utils;
 
 Cu.import("resource:///modules/devtools/BuiltinCommands.jsm");
 Cu.import("resource:///modules/devtools/CmdDebugger.jsm");
 Cu.import("resource:///modules/devtools/CmdEdit.jsm");
 Cu.import("resource:///modules/devtools/CmdInspect.jsm");
 Cu.import("resource:///modules/devtools/CmdResize.jsm");
 Cu.import("resource:///modules/devtools/CmdTilt.jsm");
 Cu.import("resource:///modules/devtools/CmdScratchpad.jsm");
+Cu.import("resource:///modules/devtools/cmd-profiler.jsm");
--- a/browser/devtools/debugger/DebuggerPanel.jsm
+++ b/browser/devtools/debugger/DebuggerPanel.jsm
@@ -17,68 +17,58 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
   "resource://gre/modules/devtools/dbg-server.jsm");
 
 function DebuggerPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
 
+  this._view = this.panelWin.DebuggerView;
   this._controller = this.panelWin.DebuggerController;
-  this._view = this.panelWin.DebuggerView;
   this._controller._target = this.target;
   this._bkp = this._controller.Breakpoints;
 
   EventEmitter.decorate(this);
 }
 
 DebuggerPanel.prototype = {
   /**
-   * open is effectively an asynchronous constructor
+   * Open is effectively an asynchronous constructor.
+   *
+   * @return object
+   *         A Promise that is resolved when the Debugger completes opening.
    */
   open: function DebuggerPanel_open() {
-    let deferred = Promise.defer();
-
-    this._ensureOnlyOneRunningDebugger();
-
-    let onDebuggerLoaded = function () {
-      this.panelWin.removeEventListener("Debugger:Loaded",
-                                        onDebuggerLoaded, true);
-      this._isReady = true;
-      this.emit("ready");
-      deferred.resolve(this);
-    }.bind(this);
+    let promise;
 
-    let onDebuggerConnected = function () {
-      this.panelWin.removeEventListener("Debugger:Connected",
-                                        onDebuggerConnected, true);
-      this.emit("connected");
-    }.bind(this);
-
-    this.panelWin.addEventListener("Debugger:Loaded", onDebuggerLoaded, true);
-    this.panelWin.addEventListener("Debugger:Connected",
-                                   onDebuggerConnected, true);
-
-    // Remote debugging gets the debuggee from a RemoteTarget object.
-    if (this.target.isRemote) {
-      this.panelWin._remoteFlag = true;
-      return deferred.promise;
+    // Local debugging needs to make the target remote.
+    if (!this.target.isRemote) {
+      promise = this.target.makeRemote();
+    } else {
+      promise = Promise.resolve(this.target);
     }
 
-    // Local debugging needs to convert the TabTarget to a RemoteTarget.
-    return this.target.makeRemote().then(function success() {
-      return deferred.promise;
-    });
+    return promise
+      .then(() => this._controller.startupDebugger())
+      .then(() => this._controller.connect())
+      .then(() => {
+        this.isReady = true;
+        this.emit("ready");
+        return this;
+      })
+      .then(null, function onError(aReason) {
+        Cu.reportError("DebuggerPanel open failed. " +
+                       reason.error + ": " + reason.message);
+      });
   },
 
   // DevToolPanel API
   get target() this._toolbox.target,
 
-  get isReady() this._isReady,
-
   destroy: function() {
     this.emit("destroyed");
     return Promise.resolve(null);
   },
 
   // DebuggerPanel API
 
   addBreakpoint: function() {
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -33,181 +33,152 @@ Cu.import("resource://gre/modules/devtoo
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 Cu.import("resource:///modules/source-editor.jsm");
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+  "resource://gre/modules/commonjs/sdk/core/promise.js");
+
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource:///modules/devtools/Parser.jsm");
 
 /**
  * Object defining the debugger controller components.
  */
 let DebuggerController = {
   /**
    * Initializes the debugger controller.
    */
   initialize: function DC_initialize() {
     dumpn("Initializing the DebuggerController");
 
-    this._startupDebugger = this._startupDebugger.bind(this);
-    this._shutdownDebugger = this._shutdownDebugger.bind(this);
+    this.startupDebugger = this.startupDebugger.bind(this);
+    this.shutdownDebugger = this.shutdownDebugger.bind(this);
     this._onTabNavigated = this._onTabNavigated.bind(this);
     this._onTabDetached = this._onTabDetached.bind(this);
 
-    window.addEventListener("DOMContentLoaded", this._startupDebugger, true);
-    window.addEventListener("unload", this._shutdownDebugger, true);
+    // Chrome debugging lives in a different process and needs to handle
+    // debugger startup and shutdown by itself.
+    if (window._isChromeDebugger) {
+      window.addEventListener("DOMContentLoaded", this.startupDebugger, true);
+      window.addEventListener("unload", this.shutdownDebugger, true);
+    }
   },
 
   /**
-   * Initializes the view and connects a debugger client to the server.
+   * Initializes the view.
+   *
+   * @return object
+   *         A promise that is resolved when the debugger finishes startup.
    */
-  _startupDebugger: function DC__startupDebugger() {
+  startupDebugger: function DC_startupDebugger() {
     if (this._isInitialized) {
       return;
     }
     this._isInitialized = true;
-    window.removeEventListener("DOMContentLoaded", this._startupDebugger, true);
+    window.removeEventListener("DOMContentLoaded", this.startupDebugger, true);
 
-    DebuggerView.initialize(function() {
+    let deferred = Promise.defer();
+
+    DebuggerView.initialize(() => {
       DebuggerView._isInitialized = true;
 
-      window.dispatchEvent(document, "Debugger:Loaded");
-      this._connect();
-    }.bind(this));
+      // Chrome debugging needs to initiate the connection by itself.
+      if (window._isChromeDebugger) {
+        this.connect().then(deferred.resolve);
+      } else {
+        deferred.resolve();
+      }
+    });
+
+    return deferred.promise;
   },
 
   /**
    * Destroys the view and disconnects the debugger client from the server.
+   *
+   * @return object
+   *         A promise that is resolved when the debugger finishes shutdown.
    */
-  _shutdownDebugger: function DC__shutdownDebugger() {
+  shutdownDebugger: function DC__shutdownDebugger() {
     if (this._isDestroyed || !DebuggerView._isInitialized) {
       return;
     }
     this._isDestroyed = true;
-    window.removeEventListener("unload", this._shutdownDebugger, true);
+    window.removeEventListener("unload", this.shutdownDebugger, true);
 
-    DebuggerView.destroy(function() {
+    let deferred = Promise.defer();
+
+    DebuggerView.destroy(() => {
       DebuggerView._isDestroyed = true;
       this.SourceScripts.disconnect();
       this.StackFrames.disconnect();
       this.ThreadState.disconnect();
 
-      this._disconnect();
-      window.dispatchEvent(document, "Debugger:Unloaded");
-      window._isChromeDebugger && this._quitApp();
-    }.bind(this));
-  },
-
-  /**
-   * Prepares the hostname and port number for a remote debugger connection
-   * and handles connection retries and timeouts.
-   * XXX: remove all this (bug 823577)
-   * @return boolean
-   *         True if connection should proceed normally, false otherwise.
-   */
-  _prepareConnection: function DC__prepareConnection() {
-    // If we exceeded the total number of connection retries, bail.
-    if (this._remoteConnectionTry === Prefs.remoteConnectionRetries) {
-      Services.prompt.alert(null,
-        L10N.getStr("remoteDebuggerPromptTitle"),
-        L10N.getStr("remoteDebuggerConnectionFailedMessage"));
-
-      // If the connection was not established before a certain number of
-      // retries, close the remote debugger.
-      this._shutdownDebugger();
-      return false;
-    }
-
-    // TODO: This is ugly, need to rethink the design for the UI in #751677.
-    if (!Prefs.remoteAutoConnect) {
-      let prompt = new RemoteDebuggerPrompt();
-      let result = prompt.show(!!this._remoteConnectionTimeout);
+      this.disconnect();
+      deferred.resolve();
 
-      // If the connection was not established before the user canceled the
-      // prompt, close the remote debugger.
-      if (!result) {
-        this._shutdownDebugger();
-        return false;
-      }
-
-      Prefs.remoteHost = prompt.remote.host;
-      Prefs.remotePort = prompt.remote.port;
-      Prefs.remoteAutoConnect = prompt.remote.auto;
-    }
+      // Chrome debugging needs to close its parent process on shutdown.
+      window._isChromeDebugger && this._quitApp();
+    });
 
-    // If this debugger is connecting remotely to a server, we need to check
-    // after a while if the connection actually succeeded.
-    this._remoteConnectionTry = ++this._remoteConnectionTry || 1;
-    this._remoteConnectionTimeout = window.setTimeout(function() {
-      // If we couldn't connect to any server yet, try again...
-      if (!this.activeThread) {
-        this._onRemoteConnectionTimeout();
-        this._connect();
-      }
-    }.bind(this), Prefs.remoteTimeout);
-
-    // Proceed with the connection normally.
-    return true;
-  },
-
-  /**
-   * Called when a remote connection timeout occurs.
-   */
-  _onRemoteConnectionTimeout: function DC__onRemoteConnectionTimeout() {
-    Cu.reportError("Couldn't connect to " +
-      Prefs.remoteHost + ":" + Prefs.remotePort);
+    return deferred.promise;
   },
 
   /**
    * Initializes a debugger client and connects it to the debugger server,
    * wiring event handlers as necessary.
+   *
+   * @return object
+   *         A promise that is resolved when the debugger finishes connecting.
    */
-  _connect: function DC__connect() {
-    function callback() {
-      window.dispatchEvent(document, "Debugger:Connected");
-    }
+  connect: function DC_connect() {
+    let deferred = Promise.defer();
 
     if (!window._isChromeDebugger) {
-      let client = this.client = this._target.client;
-      this._target.on("close", this._onTabDetached);
-      this._target.on("navigate", this._onTabNavigated);
-      this._target.on("will-navigate", this._onTabNavigated);
+      let target = this._target;
+      let { client, form } = target;
+      target.on("close", this._onTabDetached);
+      target.on("navigate", this._onTabNavigated);
+      target.on("will-navigate", this._onTabNavigated);
 
-      if (this._target.chrome) {
-        let dbg = this._target.form.chromeDebugger;
-        this._startChromeDebugging(client, dbg, callback);
+      if (target.chrome) {
+        this._startChromeDebugging(client, form.chromeDebugger, deferred.resolve);
       } else {
-        this._startDebuggingTab(client, this._target.form, callback);
+        this._startDebuggingTab(client, form, deferred.resolve);
       }
-      return;
+
+      return deferred.promise;
     }
 
     // Chrome debugging needs to make the connection to the debuggee.
     let transport = debuggerSocketConnect(Prefs.chromeDebuggingHost,
                                           Prefs.chromeDebuggingPort);
 
-    let client = this.client = new DebuggerClient(transport);
+    let client = new DebuggerClient(transport);
     client.addListener("tabNavigated", this._onTabNavigated);
     client.addListener("tabDetached", this._onTabDetached);
 
-    client.connect(function(aType, aTraits) {
-      client.listTabs(function(aResponse) {
-        this._startChromeDebugging(client, aResponse.chromeDebugger, callback);
-      }.bind(this));
-    }.bind(this));
+    client.connect((aType, aTraits) => {
+      client.listTabs((aResponse) => {
+        this._startChromeDebugging(client, aResponse.chromeDebugger, deferred.resolve);
+      });
+    });
+
+    return deferred.promise;
   },
 
   /**
    * Disconnects the debugger client and removes event handlers as necessary.
    */
-  _disconnect: function DC__disconnect() {
+  disconnect: function DC_disconnect() {
     // Return early if the client didn't even have a chance to instantiate.
     if (!this.client) {
       return;
     }
     this.client.removeListener("tabNavigated", this._onTabNavigated);
     this.client.removeListener("tabDetached", this._onTabDetached);
 
     // When debugging content or a remote instance, the connection is closed by
@@ -244,91 +215,95 @@ let DebuggerController = {
     this.StackFrames._handleTabNavigation();
     this.SourceScripts._handleTabNavigation();
   },
 
   /**
    * Called when the debugged tab is closed.
    */
   _onTabDetached: function DC__onTabDetached() {
-    this._shutdownDebugger();
+    this.shutdownDebugger();
   },
 
   /**
    * Sets up a debugging session.
    *
    * @param DebuggerClient aClient
    *        The debugger client.
    * @param object aTabGrip
    *        The remote protocol grip of the tab.
+   * @param function aCallback
+   *        A function to invoke once the client attached to the active thread.
    */
   _startDebuggingTab: function DC__startDebuggingTab(aClient, aTabGrip, aCallback) {
     if (!aClient) {
       Cu.reportError("No client found!");
       return;
     }
     this.client = aClient;
 
-    aClient.attachTab(aTabGrip.actor, function(aResponse, aTabClient) {
+    aClient.attachTab(aTabGrip.actor, (aResponse, aTabClient) => {
       if (!aTabClient) {
         Cu.reportError("No tab client found!");
         return;
       }
       this.tabClient = aTabClient;
 
-      aClient.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) {
+      aClient.attachThread(aResponse.threadActor, (aResponse, aThreadClient) => {
         if (!aThreadClient) {
           Cu.reportError("Couldn't attach to thread: " + aResponse.error);
           return;
         }
         this.activeThread = aThreadClient;
 
         this.ThreadState.connect();
         this.StackFrames.connect();
         this.SourceScripts.connect();
         aThreadClient.resume();
 
         if (aCallback) {
           aCallback();
         }
-      }.bind(this));
-    }.bind(this));
+      });
+    });
   },
 
   /**
    * Sets up a chrome debugging session.
    *
    * @param DebuggerClient aClient
    *        The debugger client.
    * @param object aChromeDebugger
    *        The remote protocol grip of the chrome debugger.
+   * @param function aCallback
+   *        A function to invoke once the client attached to the active thread.
    */
   _startChromeDebugging: function DC__startChromeDebugging(aClient, aChromeDebugger, aCallback) {
     if (!aClient) {
       Cu.reportError("No client found!");
       return;
     }
     this.client = aClient;
 
-    aClient.attachThread(aChromeDebugger, function(aResponse, aThreadClient) {
+    aClient.attachThread(aChromeDebugger, (aResponse, aThreadClient) => {
       if (!aThreadClient) {
         Cu.reportError("Couldn't attach to thread: " + aResponse.error);
         return;
       }
       this.activeThread = aThreadClient;
 
       this.ThreadState.connect();
       this.StackFrames.connect();
       this.SourceScripts.connect();
       aThreadClient.resume();
 
       if (aCallback) {
         aCallback();
       }
-    }.bind(this));
+    });
   },
 
   /**
    * Attempts to quit the current process if allowed.
    */
   _quitApp: function DC__quitApp() {
     let canceled = Cc["@mozilla.org/supports-PRBool;1"]
       .createInstance(Ci.nsISupportsPRBool);
--- a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
@@ -15,63 +15,49 @@ let gDebugger = null;
 let gEditor = null;
 let gSources = null;
 let gBreakpoints = null;
 
 function test()
 {
   let scriptShown = false;
   let framesAdded = false;
+  let resumed = false;
   let testStarted = false;
 
-  gTab = addTab(TAB_URL, function onAddTab() {
-    info("tab added");
-    gDebuggee = gTab.linkedBrowser.contentWindow.wrappedJSObject;
-
-    let target = TargetFactory.forTab(gTab);
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.panelWin;
+    resumed = true;
 
-    gDevTools.showToolbox(target, "jsdebugger").then(function(toolbox) {
-      info("jsdebugger panel opened");
-      gPane = toolbox.getCurrentPanel();
-      gDebugger = gPane.panelWin;
-      gDebugger.addEventListener("Debugger:AfterSourcesAdded", onAfterSourcesAdded);
-    });
-  });
-
-  function onAfterSourcesAdded()
-  {
-    info("scripts added");
-    gDebugger.removeEventListener("Debugger:AfterSourcesAdded",onAfterSourcesAdded);
     gDebugger.addEventListener("Debugger:SourceShown", onSourceShown);
 
-    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded",
-      function onFramesAdded() {
-        info("frames added");
-        framesAdded = true;
-        executeSoon(startTest);
-      });
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+      framesAdded = true;
+      executeSoon(startTest);
+    });
 
     executeSoon(function() {
       gDebuggee.firstCall();
     });
-  }
+  });
 
   function onSourceShown(aEvent)
   {
     scriptShown = aEvent.detail.url.indexOf("-02.js") != -1;
-    info("script shown " + aEvent.detail.url);
     executeSoon(startTest);
   }
 
   function startTest()
   {
     if (scriptShown && framesAdded && !testStarted) {
       gDebugger.removeEventListener("Debugger:SourceShown", onSourceShown);
       testStarted = true;
-      info("test started");
       Services.tm.currentThread.dispatch({ run: performTest }, 0);
     }
   }
 
   function performTest()
   {
     gSources = gDebugger.DebuggerView.Sources;
     gEditor = gDebugger.editor;
@@ -166,17 +152,18 @@ function test()
     is(gEditor.getBreakpoints().length, 0,
       "editor.getBreakpoints().length is correct");
   }
 
   function testBreakpointAddBackground()
   {
     info("add a breakpoint to the second script which is not selected");
 
-    is(Object.keys(gBreakpoints).length, 0, "no breakpoints in the debugger");
+    is(Object.keys(gBreakpoints).length, 0,
+      "no breakpoints in the debugger");
     ok(!gPane.getBreakpoint(gSources.selectedValue, 6),
       "getBreakpoint(selectedScript, 6) returns no breakpoint");
     isnot(gSources.values[0], gSources.selectedValue,
       "first script location is not the currently selected script");
 
     gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap);
     gPane.addBreakpoint({url: gSources.values[0], line: 5}, onBreakpointAddBackground);
   }
@@ -199,17 +186,19 @@ function test()
     is(aBreakpointClient.location.url, gSources.values[0],
       "breakpoint2 client url is correct");
     is(aBreakpointClient.location.line, 5,
       "breakpoint2 client line is correct");
 
     executeSoon(function() {
       ok(aBreakpointClient.actor in gBreakpoints,
         "breakpoint2 client found in the list of debugger breakpoints");
-      is(Object.keys(gBreakpoints).length, 1, "one breakpoint in the debugger");
+
+      is(Object.keys(gBreakpoints).length, 1,
+        "one breakpoint in the debugger");
       is(gPane.getBreakpoint(gSources.values[0], 5), aBreakpointClient,
         "getBreakpoint(locations[0], 5) returns the correct breakpoint");
 
       // remove the trap listener
       gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap);
 
       info("switch to the second script");
       gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddSwitch);
@@ -224,17 +213,17 @@ function test()
     editorBreakpointChanges++;
 
     ok(aEvent, "breakpoint2 added to the editor");
     is(aEvent.added.length, 1, "one breakpoint added to the editor");
     is(aEvent.removed.length, 0, "no breakpoint was removed from the editor");
     is(aEvent.added[0].line, 4, "editor breakpoint line is correct");
 
     is(gEditor.getBreakpoints().length, 1,
-       "editor.getBreakpoints().length is correct");
+      "editor.getBreakpoints().length is correct");
   }
 
   function onEditorTextChanged()
   {
     // Wait for the actual text to be shown.
     if (gDebugger.editor.getText() == gDebugger.L10N.getStr("loadingText")) {
       return;
     }
--- a/browser/devtools/debugger/test/browser_dbg_cmd.js
+++ b/browser/devtools/debugger/test/browser_dbg_cmd.js
@@ -22,85 +22,77 @@ function test() {
         function cmd(typed, callback) {
           dbg._controller.activeThread.addOneTimeListener("paused", callback);
           helpers.audit(options, [{
             setup: typed,
             exec: { output: "" }
           }]);
         }
 
-        let testCommands = function(dbg, cmd) {
-          // Wait for the initial resume...
-          dbg._controller.activeThread.addOneTimeListener("resumed", function() {
-            info("Starting tests");
+        // Wait for the initial resume...
+        dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
+          info("Starting tests");
 
-            let contentDoc = content.window.document;
-            let output = contentDoc.querySelector("input[type=text]");
-            let btnDoit = contentDoc.querySelector("input[type=button]");
+          let contentDoc = content.window.document;
+          let output = contentDoc.querySelector("input[type=text]");
+          let btnDoit = contentDoc.querySelector("input[type=button]");
 
-            helpers.audit(options, [{
-              setup: "dbg list",
-              exec: { output: /browser_dbg_cmd.html/ }
-            }]);
+          helpers.audit(options, [{
+            setup: "dbg list",
+            exec: { output: /browser_dbg_cmd.html/ }
+          }]);
 
-            cmd("dbg interrupt", function() {
-              ok(true, "debugger is paused");
-              dbg._controller.activeThread.addOneTimeListener("resumed", function() {
-                ok(true, "debugger continued");
-                dbg._controller.activeThread.addOneTimeListener("paused", function() {
+          cmd("dbg interrupt", function() {
+            ok(true, "debugger is paused");
+            dbg._controller.activeThread.addOneTimeListener("resumed", function() {
+              ok(true, "debugger continued");
+              dbg._controller.activeThread.addOneTimeListener("paused", function() {
+                cmd("dbg step in", function() {
                   cmd("dbg step in", function() {
                     cmd("dbg step in", function() {
-                      cmd("dbg step in", function() {
-                        is(output.value, "step in", "debugger stepped in");
-                        cmd("dbg step over", function() {
-                          is(output.value, "step over", "debugger stepped over");
-                          cmd("dbg step out", function() {
-                            is(output.value, "step out", "debugger stepped out");
+                      is(output.value, "step in", "debugger stepped in");
+                      cmd("dbg step over", function() {
+                        is(output.value, "step over", "debugger stepped over");
+                        cmd("dbg step out", function() {
+                          is(output.value, "step out", "debugger stepped out");
+                          cmd("dbg continue", function() {
                             cmd("dbg continue", function() {
-                              cmd("dbg continue", function() {
-                                is(output.value, "dbg continue", "debugger continued");
+                              is(output.value, "dbg continue", "debugger continued");
 
-                                helpers.audit(options, [{
-                                  setup: "dbg close",
-                                  completed: false,
-                                  exec: { output: "" }
-                                }]);
+                              helpers.audit(options, [{
+                                setup: "dbg close",
+                                completed: false,
+                                exec: { output: "" }
+                              }]);
 
-                                let toolbox = gDevTools.getToolbox(options.target);
-                                if (!toolbox) {
+                              let toolbox = gDevTools.getToolbox(options.target);
+                              if (!toolbox) {
+                                ok(true, "Debugger was closed.");
+                                deferred.resolve();
+                              } else {
+                                toolbox.on("destroyed", function () {
                                   ok(true, "Debugger was closed.");
                                   deferred.resolve();
-                                } else {
-                                  toolbox.on("destroyed", function () {
-                                    ok(true, "Debugger was closed.");
-                                    deferred.resolve();
-                                  });
-                                }
-                              });
+                                });
+                              }
                             });
                           });
                         });
                       });
                     });
                   });
                 });
-                EventUtils.sendMouseEvent({type:"click"}, btnDoit);
               });
-
-              helpers.audit(options, [{
-                setup: "dbg continue",
-                exec: { output: "" }
-              }]);
+              EventUtils.sendMouseEvent({type:"click"}, btnDoit);
             });
+
+            helpers.audit(options, [{
+              setup: "dbg continue",
+              exec: { output: "" }
+            }]);
           });
-        };
-
-        if (dbg._controller.activeThread) {
-          testCommands(dbg, cmd);
-        } else {
-          dbg.once("connected", testCommands.bind(null, dbg, cmd));
-        }
+        });
       });
     });
 
     return deferred.promise;
   }).then(finish);
 }
--- a/browser/devtools/debugger/test/browser_dbg_cmd_break.js
+++ b/browser/devtools/debugger/test/browser_dbg_cmd_break.js
@@ -43,30 +43,29 @@ function test() {
         name: 'open toolbox',
         setup: function() {
           var deferred = Promise.defer();
 
           var openDone = gDevTools.showToolbox(options.target, "jsdebugger");
           openDone.then(function(toolbox) {
             let dbg = toolbox.getCurrentPanel();
             ok(dbg, "DebuggerPanel exists");
-            dbg.once("connected", function() {
-              // Wait for the initial resume...
-              dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
-                dbg._view.Variables.lazyEmpty = false;
+
+            // Wait for the initial resume...
+            dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
+              info("Starting tests");
 
-                client = dbg.panelWin.gClient;
-                client.activeThread.addOneTimeListener("framesadded", function() {
-                  line0 = '' + options.window.wrappedJSObject.line0;
-                  deferred.resolve();
-                });
+              client = dbg.panelWin.gClient;
+              client.activeThread.addOneTimeListener("framesadded", function() {
+                line0 = '' + options.window.wrappedJSObject.line0;
+                deferred.resolve();
+              });
 
-                // Trigger newScript notifications using eval.
-                content.wrappedJSObject.firstCall();
-              });
+              // Trigger newScript notifications using eval.
+              content.wrappedJSObject.firstCall();
             });
           });
 
           return deferred.promise;
         },
         post: function() {
           ok(client, "Debugger client exists");
           is(line0, 10, "line0 is 10");
--- a/browser/devtools/debugger/test/browser_dbg_function-search-02.html
+++ b/browser/devtools/debugger/test/browser_dbg_function-search-02.html
@@ -8,16 +8,22 @@
     <script type="text/javascript" src="test-function-search-01.js"></script>
     <script type="text/javascript" src="test-function-search-02.js"></script>
     <script type="text/javascript" src="test-function-search-03.js"></script>
   </head>
 
   <body>
     <p>Peanut butter jelly time!</p>
 
-    <script type="text/javascript">
-      function inline() {
-      }
+    <script type="text/javascript;version=1.8">
+      function inline() {}
+      let arrow = () => {}
+
+      let foo = bar => {}
+      let foo2 = bar2 = baz2 => 42;
+
+      setTimeout((foo, bar, baz) => {});
+      setTimeout((foo, bar, baz) => 42);
     </script>
 
   </body>
 
 </html>
--- a/browser/devtools/debugger/test/browser_dbg_function-search.js
+++ b/browser/devtools/debugger/test/browser_dbg_function-search.js
@@ -78,17 +78,21 @@ function htmlSearch(callback) {
       "An item should be selected in the filtered functions view");
 
     let url = gSources.selectedValue;
     if (url.indexOf("-02.html") != -1) {
 
       executeSoon(function() {
         let expectedResults = [
           ["inline", "-02.html", "", 16, 15],
-        ]
+          ["arrow", "-02.html", "", 17, 10],
+          ["foo", "-02.html", "", 19, 10],
+          ["foo2", "-02.html", "", 20, 10],
+          ["bar2", "-02.html", "", 20, 17]
+        ];
 
         for (let [label, value, description, line, col] of expectedResults) {
           is(gFilteredFunctions.selectedItem.label,
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
             "The corect label (" + label + ") is currently selected.");
           ok(gFilteredFunctions.selectedItem.value.contains(value),
             "The corect value (" + value + ") is attached.");
           is(gFilteredFunctions.selectedItem.description, description,
@@ -113,17 +117,17 @@ function htmlSearch(callback) {
 
         executeSoon(callback);
       });
     } else {
       ok(false, "How did you get here? Go away, you.");
     }
   });
 
-  write("@inline");
+  write("@");
 }
 
 function firstSearch(callback) {
   gDebugger.addEventListener("popupshown", function _onEvent(aEvent) {
     gDebugger.removeEventListener(aEvent.type, _onEvent);
     info("Current script url:\n" + gSources.selectedValue + "\n");
     info("Debugger editor text:\n" + gEditor.getText() + "\n");
 
@@ -143,17 +147,17 @@ function firstSearch(callback) {
           ["namedExpression" + s + "NAME", "-01.js", "test.prototype", 10, 2],
           ["a_test", "-01.js", "foo", 21, 2],
           ["n_test" + s + "x", "-01.js", "foo", 23, 2],
           ["a_test", "-01.js", "foo.sub", 26, 4],
           ["n_test" + s + "y", "-01.js", "foo.sub", 28, 4],
           ["a_test", "-01.js", "foo.sub.sub", 31, 6],
           ["n_test" + s + "z", "-01.js", "foo.sub.sub", 33, 6],
           ["test_SAME_NAME", "-01.js", "foo.sub.sub.sub", 36, 8]
-        ]
+        ];
 
         for (let [label, value, description, line, col] of expectedResults) {
           is(gFilteredFunctions.selectedItem.label,
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
             "The corect label (" + label + ") is currently selected.");
           ok(gFilteredFunctions.selectedItem.value.contains(value),
             "The corect value (" + value + ") is attached.");
           is(gFilteredFunctions.selectedItem.description, description,
@@ -208,17 +212,17 @@ function secondSearch(callback) {
           ["test4_SAME_NAME", "-02.js", "", 10, 4],
           ["x" + s + "X", "-02.js", "test.prototype", 13, 0],
           ["y" + s + "Y", "-02.js", "test.prototype.sub", 15, 0],
           ["z" + s + "Z", "-02.js", "test.prototype.sub.sub", 17, 0],
           ["t", "-02.js", "test.prototype.sub.sub.sub", 19, 0],
           ["x", "-02.js", "", 19, 31],
           ["y", "-02.js", "", 19, 40],
           ["z", "-02.js", "", 19, 49]
-        ]
+        ];
 
         for (let [label, value, description, line, col] of expectedResults) {
           is(gFilteredFunctions.selectedItem.label,
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
             "The corect label (" + label + ") is currently selected.");
           ok(gFilteredFunctions.selectedItem.value.contains(value),
             "The corect value (" + value + ") is attached.");
           is(gFilteredFunctions.selectedItem.description, description,
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -151,79 +151,76 @@ function attach_thread_actor_for_url(aCl
   });
 }
 
 function wait_for_connect_and_resume(aOnDebugging, aTab) {
   let target = TargetFactory.forTab(aTab);
 
   gDevTools.showToolbox(target, "jsdebugger").then(function(toolbox) {
     let dbg = toolbox.getCurrentPanel();
-    dbg.once("connected", function dbgConnected() {
 
-      // Wait for the initial resume...
-      dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
-        aOnDebugging();
-      });
+    // Wait for the initial resume...
+    dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
+      aOnDebugging();
     });
   });
 }
 
 function debug_tab_pane(aURL, aOnDebugging, aBeforeTabAdded) {
   // Make any necessary preparations (start the debugger server etc.)
   if (aBeforeTabAdded) {
     aBeforeTabAdded();
   }
 
   let tab = addTab(aURL, function() {
     let debuggee = gBrowser.selectedTab.linkedBrowser.contentWindow.wrappedJSObject;
     let target = TargetFactory.forTab(gBrowser.selectedTab);
 
+    info("Opening Debugger");
     gDevTools.showToolbox(target, "jsdebugger").then(function(toolbox) {
       let dbg = toolbox.getCurrentPanel();
-      dbg.once("connected", function() {
 
-        // Wait for the initial resume...
-        dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
-          dbg._view.Variables.lazyEmpty = false;
-          dbg._view.Variables.lazyAppend = false;
-          aOnDebugging(tab, debuggee, dbg);
-        });
+      // Wait for the initial resume...
+      dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
+        info("Debugger has started");
+        dbg._view.Variables.lazyEmpty = false;
+        dbg._view.Variables.lazyAppend = false;
+        aOnDebugging(tab, debuggee, dbg);
       });
     });
   });
 }
 
 function debug_remote(aURL, aOnDebugging, aBeforeTabAdded) {
   // Make any necessary preparations (start the debugger server etc.)
   if (aBeforeTabAdded) {
     aBeforeTabAdded();
   }
 
   let tab = addTab(aURL, function() {
     let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
 
+    info("Opening Remote Debugger");
     let win = DebuggerUI.toggleRemoteDebugger();
-    win._dbgwin.addEventListener("Debugger:Connected", function dbgConnected() {
-      win._dbgwin.removeEventListener("Debugger:Connected", dbgConnected, true);
 
-      // Wait for the initial resume...
-      win.panelWin.gClient.addOneTimeListener("resumed", function() {
-        win._dbgwin.DebuggerView.Variables.lazyEmpty = false;
-        win._dbgwin.DebuggerView.Variables.lazyAppend = false;
-        aOnDebugging(tab, debuggee, win);
-      });
-    }, true);
+    // Wait for the initial resume...
+    win.panelWin.gClient.addOneTimeListener("resumed", function() {
+      info("Remote Debugger has started");
+      win._dbgwin.DebuggerView.Variables.lazyEmpty = false;
+      win._dbgwin.DebuggerView.Variables.lazyAppend = false;
+      aOnDebugging(tab, debuggee, win);
+    });
   });
 }
 
 function debug_chrome(aURL, aOnClosing, aOnDebugging) {
   let tab = addTab(aURL, function() {
     let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
 
     info("Opening Browser Debugger");
-    DebuggerUI.toggleChromeDebugger(aOnClosing, function dbgRan(process) {
+    let win = DebuggerUI.toggleChromeDebugger(aOnClosing, function(process) {
+
+      // The remote debugging process has started...
       info("Browser Debugger has started");
-
-      // Wait for the remote debugging process to start...
       aOnDebugging(tab, debuggee, process);
     });
   });
 }
--- a/browser/devtools/profiler/ProfilerController.jsm
+++ b/browser/devtools/profiler/ProfilerController.jsm
@@ -137,17 +137,17 @@ ProfilerConnection.prototype = {
   }
 };
 
 /**
  * Object defining the profiler controller components.
  */
 function ProfilerController(target) {
   this.profiler = new ProfilerConnection(target.client);
-  this.pool = {};
+  this.profiles = new Map();
 
   // Chrome debugging targets have already obtained a reference to the
   // profiler actor.
   this._connected = !!target.chrome;
 
   if (target.chrome) {
     this.profiler.actor = target.form.profilerActor;
   }
@@ -205,22 +205,24 @@ ProfilerController.prototype = {
    * @param string name
    *        Name of the profile.
    * @param function cb
    *        Function to be called once the profiler is started
    *        or we get an error. It will be called with a single
    *        argument: an error object (may be null).
    */
   start: function PC_start(name, cb) {
-    if (this.pool[name]) {
+    if (this.profiles.has(name)) {
       return;
     }
 
-    let profile = this.pool[name] = makeProfile(name);
     let profiler = this.profiler;
+    let profile = makeProfile(name);
+    this.profiles.set(name, profile);
+
 
     // If profile is already running, no need to do anything.
     if (this.isProfileRecording(profile)) {
       return void cb();
     }
 
     this.isActive(function (err, isActive) {
       if (isActive) {
@@ -246,25 +248,25 @@ ProfilerController.prototype = {
    *        Name of the profile that needs to be stopped.
    * @param function cb
    *        Function to be called once the profiler is stopped
    *        or we get an error. It will be called with a single
    *        argument: an error object (may be null).
    */
   stop: function PC_stop(name, cb) {
     let profiler = this.profiler;
-    let profile = this.pool[name];
+    let profile = this.profiles.get(name);
 
     if (!profile || !this.isProfileRecording(profile)) {
       return;
     }
 
     let isRecording = function () {
-      for (let name in this.pool) {
-        if (this.isProfileRecording(this.pool[name])) {
+      for (let [ name, profile ] of this.profiles) {
+        if (this.isProfileRecording(profile)) {
           return true;
         }
       }
 
       return false;
     }.bind(this);
 
     let onStop = function (data) {
--- a/browser/devtools/profiler/ProfilerPanel.jsm
+++ b/browser/devtools/profiler/ProfilerPanel.jsm
@@ -39,25 +39,29 @@ XPCOMUtils.defineLazyModuleGetter(this, 
  *  - enabled, when Cleopatra gets enabled. Happens when another ProfileUI
  *    instance stops the profiler.
  *
  * @param number uid
  *   Unique ID for this profile.
  * @param ProfilerPanel panel
  *   A reference to the container panel.
  */
-function ProfileUI(uid, panel) {
+function ProfileUI(uid, name, panel) {
   let doc = panel.document;
   let win = panel.window;
 
   EventEmitter.decorate(this);
 
   this.isReady = false;
+  this.isStarted = false;
+  this.isFinished = false;
+
   this.panel = panel;
   this.uid = uid;
+  this.name = name;
 
   this.iframe = doc.createElement("iframe");
   this.iframe.setAttribute("flex", "1");
   this.iframe.setAttribute("id", "profiler-cleo-" + uid);
   this.iframe.setAttribute("src", "devtools/cleopatra.html?" + uid);
   this.iframe.setAttribute("hidden", "true");
 
   // Append our iframe and subscribe to postMessage events.
@@ -65,53 +69,34 @@ function ProfileUI(uid, panel) {
   // or when user clicks on start/stop buttons.
 
   doc.getElementById("profiler-report").appendChild(this.iframe);
   win.addEventListener("message", function (event) {
     if (parseInt(event.data.uid, 10) !== parseInt(this.uid, 10)) {
       return;
     }
 
-    let label = doc.querySelector("li#profile-" + this.uid + " > h1");
-    let name = label.textContent.replace(/\s\*$/, "");
-
     switch (event.data.status) {
       case "loaded":
         if (this.panel._runningUid !== null) {
           this.iframe.contentWindow.postMessage(JSON.stringify({
             uid: this._runningUid,
             isCurrent: this._runningUid === uid,
             task: "onStarted"
           }), "*");
         }
 
         this.isReady = true;
         this.emit("ready");
         break;
       case "start":
-        // Start profiling and, once started, notify the underlying page
-        // so that it could update the UI. Also, once started, we add a
-        // star to the profile name to indicate which profile is currently
-        // running.
-        this.panel.startProfiling(name, function onStart() {
-          label.textContent = name + " *";
-          this.panel.broadcast(this.uid, {task: "onStarted"});
-          this.emit("started");
-        }.bind(this));
-
+        this.start();
         break;
       case "stop":
-        // Stop profiling and, once stopped, notify the underlying page so
-        // that it could update the UI and remove a star from the profile
-        // name.
-        this.panel.stopProfiling(name, function onStop() {
-          label.textContent = name;
-          this.panel.broadcast(this.uid, {task: "onStopped"});
-          this.emit("stopped");
-        }.bind(this));
+        this.stop();
         break;
       case "disabled":
         this.emit("disabled");
         break;
       case "enabled":
         this.emit("enabled");
         break;
       case "displaysource":
@@ -165,16 +150,66 @@ ProfileUI.prototype = {
 
       onParsed();
     }.bind(this);
 
     poll();
   },
 
   /**
+   * Update profile's label in the sidebar.
+   *
+   * @param string text
+   *   New text for the label.
+   */
+  updateLabel: function PUI_udpateLabel(text) {
+    let doc = this.panel.document;
+    let label = doc.querySelector("li#profile-" + this.uid + "> h1");
+    label.textContent = text;
+  },
+
+  /**
+   * Start profiling and, once started, notify the underlying page
+   * so that it could update the UI. Also, once started, we add a
+   * star to the profile name to indicate which profile is currently
+   * running.
+   */
+  start: function PUI_start() {
+    if (this.isStarted || this.isFinished) {
+      return;
+    }
+
+    this.panel.startProfiling(this.name, function onStart() {
+      this.isStarted = true;
+      this.updateLabel(this.name + " *");
+      this.panel.broadcast(this.uid, {task: "onStarted"});
+      this.emit("started");
+    }.bind(this));
+  },
+
+  /**
+   * Stop profiling and, once stopped, notify the underlying page so
+   * that it could update the UI and remove a star from the profile
+   * name.
+   */
+  stop: function PUI_stop() {
+    if (!this.isStarted || this.isFinished) {
+      return;
+    }
+
+    this.panel.stopProfiling(this.name, function onStop() {
+      this.isStarted = false;
+      this.isFinished = true;
+      this.updateLabel(this.name);
+      this.panel.broadcast(this.uid, {task: "onStopped"});
+      this.emit("stopped");
+    }.bind(this));
+  },
+
+  /**
    * Destroys the ProfileUI instance.
    */
   destroy: function PUI_destroy() {
     this.isReady = null
     this.panel = null;
     this.uid = null;
     this.iframe = null;
   }
@@ -271,17 +306,19 @@ ProfilerPanel.prototype = {
 
     return promise
       .then(function(target) {
         let deferred = Promise.defer();
         this.controller = new ProfilerController(this.target);
 
         this.controller.connect(function onConnect() {
           let create = this.document.getElementById("profiler-create");
-          create.addEventListener("click", this.createProfile.bind(this), false);
+          create.addEventListener("click", function (ev) {
+            this.createProfile()
+          }.bind(this), false);
           create.removeAttribute("disabled");
 
           let profile = this.createProfile();
           this.switchToProfile(profile, function () {
             this.isReady = true;
             this.emit("ready");
 
             deferred.resolve(this);
@@ -298,37 +335,45 @@ ProfilerPanel.prototype = {
 
   /**
    * Creates a new profile instance (see ProfileUI) and
    * adds an appropriate item to the sidebar. Note that
    * this method doesn't automatically switch user to
    * the newly created profile, they have do to switch
    * explicitly.
    *
+   * @param string name
+   *        (optional) name of the new profile
+   *
    * @return ProfilerPanel
    */
-  createProfile: function PP_addProfile() {
+  createProfile: function PP_createProfile(name) {
+    if (name && this.getProfileByName(name)) {
+      return this.getProfileByName(name);
+    }
+
     let uid  = ++this._uid;
     let list = this.document.getElementById("profiles-list");
     let item = this.document.createElement("li");
     let wrap = this.document.createElement("h1");
+    name = name || L10N.getFormatStr("profiler.profileName", [uid]);
 
     item.setAttribute("id", "profile-" + uid);
     item.setAttribute("data-uid", uid);
     item.addEventListener("click", function (ev) {
       this.switchToProfile(this.profiles.get(uid));
     }.bind(this), false);
 
     wrap.className = "profile-name";
-    wrap.textContent = L10N.getFormatStr("profiler.profileName", [uid]);
+    wrap.textContent = name;
 
     item.appendChild(wrap);
     list.appendChild(item);
 
-    let profile = new ProfileUI(uid, this);
+    let profile = new ProfileUI(uid, name, this);
     this.profiles.set(uid, profile);
 
     this.emit("profileCreated", uid);
     return profile;
   },
 
   /**
    * Switches to a different profile by making its instance an
@@ -420,16 +465,50 @@ ProfilerPanel.prototype = {
 
         onStop();
         this.emit("stopped", data);
       }.bind(this));
     }.bind(this));
   },
 
   /**
+   * Lookup an individual profile by its name.
+   *
+   * @param string name name of the profile
+   * @return profile object or null
+   */
+  getProfileByName: function PP_getProfileByName(name) {
+    if (!this.profiles) {
+      return null;
+    }
+
+    for (let [ uid, profile ] of this.profiles) {
+      if (profile.name === name) {
+        return profile;
+      }
+    }
+
+    return null;
+  },
+
+  /**
+   * Lookup an individual profile by its UID.
+   *
+   * @param number uid UID of the profile
+   * @return profile object or null
+   */
+  getProfileByUID: function PP_getProfileByUID(uid) {
+    if (!this.profiles) {
+      return null;
+    }
+
+    return this.profiles.get(uid) || null;
+  },
+
+  /**
    * Broadcast messages to all Cleopatra instances.
    *
    * @param number target
    *   UID of the recepient profile. All profiles will receive the message
    *   but the profile specified by 'target' will have a special property,
    *   isCurrent, set to true.
    * @param object data
    *   An object with a property 'task' that will be sent over to Cleopatra.
new file mode 100644
--- /dev/null
+++ b/browser/devtools/profiler/cmd-profiler.jsm
@@ -0,0 +1,211 @@
+/* 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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+this.EXPORTED_SYMBOLS = [];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/devtools/Require.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
+  "resource:///modules/devtools/gDevTools.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+  "resource://gre/modules/devtools/Console.jsm");
+
+var Promise = require('util/promise');
+
+/*
+ * 'profiler' command. Doesn't do anything.
+ */
+gcli.addCommand({
+  name: "profiler",
+  description: gcli.lookup("profilerDesc"),
+  manual: gcli.lookup("profilerManual")
+});
+
+/*
+ * 'profiler open' command
+ */
+gcli.addCommand({
+  name: "profiler open",
+  description: gcli.lookup("profilerOpenDesc"),
+  params: [],
+
+  exec: function (args, context) {
+    return gDevTools.showToolbox(context.environment.target, "jsprofiler")
+      .then(function () null);
+  }
+});
+
+/*
+ * 'profiler close' command
+ */
+gcli.addCommand({
+  name: "profiler close",
+  description: gcli.lookup("profilerCloseDesc"),
+  params: [],
+
+  exec: function (args, context) {
+    return gDevTools.closeToolbox(context.environment.target)
+      .then(function () null);
+  }
+});
+
+/*
+ * 'profiler start' command
+ */
+gcli.addCommand({
+  name: "profiler start",
+  description: gcli.lookup("profilerStartDesc"),
+  returnType: "string",
+
+  params: [
+    {
+      name: "name",
+      type: "string"
+    }
+  ],
+
+  exec: function (args, context) {
+    function start() {
+      let name = args.name;
+      let panel = getPanel(context, "jsprofiler");
+      let profile = panel.getProfileByName(name) || panel.createProfile(name);
+
+      if (profile.isStarted) {
+        throw gcli.lookup("profilerAlreadyStarted");
+      }
+
+      if (profile.isFinished) {
+        throw gcli.lookup("profilerAlradyFinished");
+      }
+
+      panel.switchToProfile(profile, function () profile.start());
+      return gcli.lookup("profilerStarting");
+    }
+
+    return gDevTools.showToolbox(context.environment.target, "jsprofiler")
+      .then(start);
+  }
+});
+
+/*
+ * 'profiler stop' command
+ */
+gcli.addCommand({
+  name: "profiler stop",
+  description: gcli.lookup("profilerStopDesc"),
+  returnType: "string",
+
+  params: [
+    {
+      name: "name",
+      type: "string"
+    }
+  ],
+
+  exec: function (args, context) {
+    function stop() {
+      let panel = getPanel(context, "jsprofiler");
+      let profile = panel.getProfileByName(args.name);
+
+      if (!profile) {
+        throw gcli.lookup("profilerNotFound");
+      }
+
+      if (profile.isFinished) {
+        throw gcli.lookup("profilerAlreadyFinished");
+      }
+
+      if (!profile.isStarted) {
+        throw gcli.lookup("profilerNotStarted");
+      }
+
+      panel.switchToProfile(profile, function () profile.stop());
+      return gcli.lookup("profilerStopping");
+    }
+
+    return gDevTools.showToolbox(context.environment.target, "jsprofiler")
+      .then(stop);
+  }
+});
+
+/*
+ * 'profiler list' command
+ */
+gcli.addCommand({
+  name: "profiler list",
+  description: gcli.lookup("profilerListDesc"),
+  returnType: "dom",
+  params: [],
+
+  exec: function (args, context) {
+    let panel = getPanel(context, "jsprofiler");
+
+    if (!panel) {
+      throw gcli.lookup("profilerNotReady");
+    }
+
+    let doc = panel.document;
+    let div = createXHTMLElement(doc, "div");
+    let ol = createXHTMLElement(doc, "ol");
+
+    for ([ uid, profile] of panel.profiles) {
+      let li = createXHTMLElement(doc, "li");
+      li.textContent = profile.name;
+      if (profile.isStarted) {
+        li.textContent += " *";
+      }
+      ol.appendChild(li);
+    }
+
+    div.appendChild(ol);
+    return div;
+  }
+});
+
+/*
+ * 'profiler show' command
+ */
+gcli.addCommand({
+  name: "profiler show",
+  description: gcli.lookup("profilerShowDesc"),
+
+  params: [
+    {
+      name: "name",
+      type: "string"
+    }
+  ],
+
+  exec: function (args, context) {
+    let panel = getPanel(context, "jsprofiler");
+
+    if (!panel) {
+      throw gcli.lookup("profilerNotReady");
+    }
+
+    let profile = panel.getProfileByName(args.name);
+    if (!profile) {
+      throw gcli.lookup("profilerNotFound");
+    }
+
+    panel.switchToProfile(profile);
+  }
+});
+
+function getPanel(context, id) {
+  if (context == null) {
+    return undefined;
+  }
+
+  let toolbox = gDevTools.getToolbox(context.environment.target);
+  return toolbox == null ? undefined : toolbox.getPanel(id);
+}
+
+function createXHTMLElement(document, tagname) {
+  return document.createElementNS("http://www.w3.org/1999/xhtml", tagname);
+}
\ No newline at end of file
--- a/browser/devtools/profiler/test/Makefile.in
+++ b/browser/devtools/profiler/test/Makefile.in
@@ -9,16 +9,17 @@ VPATH          = @srcdir@
 relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_TESTS = \
 		browser_profiler_profiles.js \
 		browser_profiler_remote.js \
 		browser_profiler_bug_834878_source_buttons.js \
+		browser_profiler_cmd.js \
 		head.js \
 		$(NULL)
 
 # Naughty corner (bug 822041 & bug 822287)
 #		browser_profiler_run.js \
 #		browser_profiler_controller.js \
 #		browser_profiler_bug_830664_multiple_profiles.js \
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/profiler/test/browser_profiler_cmd.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
+
+let gTarget, gPanel, gOptions;
+
+function cmd(typed, expected="") {
+  helpers.audit(gOptions, [{
+    setup: typed,
+    exec: { output: expected }
+  }]);
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  helpers.addTabWithToolbar(URL, function (options) {
+    gOptions = options;
+    gTarget = options.target;
+
+    return gDevTools.showToolbox(options.target, "jsprofiler")
+      .then(setupGlobals)
+      .then(testProfilerStart)
+      .then(testProfilerList)
+      .then(testProfilerStop)
+      .then(testProfilerClose)
+  }).then(finishUp);
+}
+
+function setupGlobals() {
+  let deferred = Promise.defer();
+  gPanel = gDevTools.getToolbox(gTarget).getPanel("jsprofiler");
+  deferred.resolve();
+  return deferred.promise;
+}
+
+function testProfilerStart() {
+  let deferred = Promise.defer();
+
+  gPanel.once("started", function () {
+    is(gPanel.profiles.size, 2, "There are two profiles");
+    ok(!gPanel.getProfileByName("Profile 1").isStarted, "Profile 1 wasn't started");
+    ok(gPanel.getProfileByName("Profile 2").isStarted, "Profile 2 was started");
+    cmd('profiler start "Profile 2"', "This profile has already been started");
+    deferred.resolve();
+  });
+
+  cmd("profiler start", "Starting...");
+  return deferred.promise;
+}
+
+function testProfilerList() {
+  let deferred = Promise.defer();
+
+  cmd("profiler list", /^.*Profile\s1.*Profile\s2\s\*.*$/);
+  deferred.resolve();
+
+  return deferred.promise;
+}
+
+function testProfilerStop() {
+  let deferred = Promise.defer();
+
+  gPanel.once("stopped", function () {
+    ok(!gPanel.getProfileByName("Profile 2").isStarted, "Profile 2 was stopped");
+    ok(gPanel.getProfileByName("Profile 2").isFinished, "Profile 2 was stopped");
+    cmd('profiler stop "Profile 2"', "This profile has already been completed. " +
+      "Use 'profile show' command to see its results");
+    cmd('profiler stop "Profile 1"', "This profile has not been started yet. " +
+      "Use 'profile start' to start profliling");
+    cmd('profiler stop "invalid"', "Profile not found")
+    deferred.resolve();
+  });
+
+  cmd('profiler stop "Profile 2"', "Stopping...");
+  return deferred.promise;
+}
+
+function testProfilerShow() {
+  let deferred = Promise.defer();
+
+  is(gPanel.getProfileByName("Profile 2").uid, gPanel.activeProfile.uid,
+    "Profile 2 is active");
+
+  gPanel.once("profileSwitched", function () {
+    is(gPanel.getProfileByName("Profile 1").uid, gPanel.activeProfile.uid,
+      "Profile 1 is active");
+    cmd('profile show "invalid"', "Profile not found");
+    deferred.resolve();
+  });
+
+  cmd('profile show "Profile 1"');
+  return deferred.promise;
+}
+
+function testProfilerClose() {
+  let deferred = Promise.defer();
+
+  helpers.audit(gOptions, [{
+    setup: "profiler close",
+    completed: false,
+    exec: { output: "" }
+  }]);
+
+  let toolbox = gDevTools.getToolbox(gOptions.target);
+  if (!toolbox) {
+    ok(true, "Profiler was closed.");
+    deferred.resolve();
+  } else {
+    toolbox.on("destroyed", function () {
+      ok(true, "Profiler was closed.");
+      deferred.resolve();
+    });
+  }
+
+  return deferred.promise;
+}
+
+function finishUp() {
+  gTarget = null;
+  gPanel = null;
+  gOptions = null;
+  finish();
+}
\ No newline at end of file
--- a/browser/devtools/profiler/test/browser_profiler_profiles.js
+++ b/browser/devtools/profiler/test/browser_profiler_profiles.js
@@ -32,15 +32,35 @@ function onProfileCreated(name, uid) {
   ok(gPanel.activeProfile.uid !== uid, "New profile is not yet active");
 
   let btn = gPanel.document.getElementById("profile-" + uid);
   ok(btn, "Profile item has been added to the sidebar");
   btn.click();
 }
 
 function onProfileSwitched(name, uid) {
+  gPanel.once("profileCreated", onNamedProfileCreated);
+  gPanel.once("profileSwitched", onNamedProfileSwitched);
+
+  ok(gPanel.activeProfile.uid === uid, "Switched to a new profile");
+  gPanel.createProfile("Custom Profile");
+}
+
+function onNamedProfileCreated(name, uid) {
+  is(gPanel.profiles.size, 3, "There are three profiles now");
+  is(gPanel.getProfileByUID(uid).name, "Custom Profile", "Name is correct");
+
+  let label = gPanel.document.querySelector("li#profile-" + uid + "> h1");
+  is(label.textContent, "Custom Profile", "Name is correct on the label");
+
+  let btn = gPanel.document.getElementById("profile-" + uid);
+  ok(btn, "Profile item has been added to the sidebar");
+  btn.click();
+}
+
+function onNamedProfileSwitched(name, uid) {
   ok(gPanel.activeProfile.uid === uid, "Switched to a new profile");
 
   tearDown(gTab, function onTearDown() {
     gPanel = null;
     gTab = null;
   });
 }
\ No newline at end of file
--- a/browser/devtools/profiler/test/head.js
+++ b/browser/devtools/profiler/test/head.js
@@ -9,17 +9,22 @@ Cu.import("resource:///modules/devtools/
 let TargetFactory = temp.TargetFactory;
 
 Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
 let gDevTools = temp.gDevTools;
 
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm", temp);
 let DebuggerServer = temp.DebuggerServer;
 
+// Import the GCLI test helper
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
+
 registerCleanupFunction(function () {
+  helpers = null;
   Services.prefs.clearUserPref(PROFILER_ENABLED);
   Services.prefs.clearUserPref(REMOTE_ENABLED);
   DebuggerServer.destroy();
 });
 
 function getProfileInternals(uid) {
   let profile = (uid != null) ? gPanel.profiles.get(uid) : gPanel.activeProfile;
   let win = profile.iframe.contentWindow;
--- a/browser/devtools/shared/Parser.jsm
+++ b/browser/devtools/shared/Parser.jsm
@@ -259,16 +259,44 @@ SyntaxTree.prototype = {
           store.push({
             functionName: functionName,
             functionLocation: aNode.loc,
             inferredName: inferredName,
             inferredChain: inferredChain,
             inferredLocation: inferredLocation
           });
         }
+      },
+
+      /**
+       * Callback invoked for each arrow expression node.
+       * @param Node aNode
+       */
+      onArrowExpression: function STW_onArrowExpression(aNode) {
+        let parent = aNode._parent;
+        let inferredName, inferredChain, inferredLocation;
+
+        // Infer the function's name from an enclosing syntax tree node.
+        let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(aNode);
+        inferredName = inferredInfo.name;
+        inferredChain = inferredInfo.chain;
+        inferredLocation = inferredInfo.loc;
+
+        // Current node may be part of a larger assignment expression stack.
+        if (parent.type == "AssignmentExpression") {
+          this.onFunctionExpression(parent);
+        }
+
+        if (inferredName && inferredName.toLowerCase().contains(lowerCaseToken)) {
+          store.push({
+            inferredName: inferredName,
+            inferredChain: inferredChain,
+            inferredLocation: inferredLocation
+          });
+        }
       }
     });
 
     return store;
   },
 
   /**
    * Gets the "new" or "call" expression at the specified location.
@@ -1524,16 +1552,55 @@ let SyntaxTreeVisitor = {
     }
     if (aNode.rest) {
       this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
     }
     this[aNode.body.type](aNode.body, aNode, aCallbacks);
   },
 
   /**
+   * An arrow expression.
+   *
+   * interface ArrowExpression <: Function, Expression {
+   *   type: "ArrowExpression";
+   *   params: [ Pattern ];
+   *   defaults: [ Expression ];
+   *   rest: Identifier | null;
+   *   body: BlockStatement | Expression;
+   *   generator: boolean;
+   *   expression: boolean;
+   * }
+   */
+  ArrowExpression: function STV_ArrowExpression(aNode, aParent, aCallbacks) {
+    aNode._parent = aParent;
+
+    if (this.break) {
+      return;
+    }
+    if (aCallbacks.onNode) {
+      if (aCallbacks.onNode(aNode, aParent) === false) {
+        return;
+      }
+    }
+    if (aCallbacks.onArrowExpression) {
+      aCallbacks.onArrowExpression(aNode);
+    }
+    for (let param of aNode.params) {
+      this[param.type](param, aNode, aCallbacks);
+    }
+    for (let _default of aNode.defaults) {
+      this[_default.type](_default, aNode, aCallbacks);
+    }
+    if (aNode.rest) {
+      this[aNode.rest.type](aNode.rest, aNode, aCallbacks);
+    }
+    this[aNode.body.type](aNode.body, aNode, aCallbacks);
+  },
+
+  /**
    * A sequence expression, i.e., a comma-separated sequence of expressions.
    *
    * interface SequenceExpression <: Expression {
    *   type: "SequenceExpression";
    *   expressions: [ Expression ];
    * }
    */
   SequenceExpression: function STV_SequenceExpression(aNode, aParent, aCallbacks) {
--- a/browser/devtools/styleeditor/StyleEditorChrome.jsm
+++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm
@@ -550,16 +550,26 @@ StyleEditorChrome.prototype = {
 
         wire(aSummary, ".stylesheet-enabled", function onToggleEnabled(aEvent) {
           aEvent.stopPropagation();
           aEvent.target.blur();
 
           editor.enableStyleSheet(editor.styleSheet.disabled);
         });
 
+        wire(aSummary, ".stylesheet-name", {
+          events: {
+            "keypress": function onStylesheetNameActivate(aEvent) {
+              if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
+                this._view.activeSummary = aSummary;
+              }
+            }.bind(this)
+          }
+        });
+
         wire(aSummary, ".stylesheet-saveButton", function onSaveButton(aEvent) {
           aEvent.stopPropagation();
           aEvent.target.blur();
 
           editor.saveToFile(editor.savedFile);
         });
 
         this._updateSummaryForEditor(editor, aSummary);
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -75,23 +75,23 @@
     <xul:box class="splitview-side-details"/>
 
     <div id="splitview-templates" hidden="true">
       <li id="splitview-tpl-summary-stylesheet" tabindex="0">
         <a class="stylesheet-enabled" tabindex="0" href="#"
           title="&visibilityToggle.tooltip;"
           accesskey="&saveButton.accesskey;"></a>
         <hgroup class="stylesheet-info">
-          <h1><a class="stylesheet-name" href="#"><xul:label crop="start"/></a></h1>
+          <h1><a class="stylesheet-name" tabindex="0"><xul:label crop="start"/></a></h1>
           <div class="stylesheet-more">
             <h3 class="stylesheet-title"></h3>
             <h3 class="stylesheet-rule-count"></h3>
             <h3 class="stylesheet-error-message"></h3>
             <xul:spacer/>
-            <h3><a class="stylesheet-saveButton" href="#"
+            <h3><a class="stylesheet-saveButton"
                   title="&saveButton.tooltip;"
                   accesskey="&saveButton.accesskey;">&saveButton.label;</a></h3>
           </div>
         </hgroup>
       </li>
 
       <xul:box id="splitview-tpl-details-stylesheet" class="splitview-details">
         <xul:resizer class="splitview-portrait-resizer"
--- a/browser/devtools/styleeditor/test/Makefile.in
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -24,16 +24,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_styleeditor_passedinsheet.js \
                  browser_styleeditor_pretty.js \
                  browser_styleeditor_private_perwindowpb.js \
                  browser_styleeditor_readonly.js \
                  browser_styleeditor_reopen.js \
                  browser_styleeditor_sv_keynav.js \
                  browser_styleeditor_sv_resize.js \
                  browser_styleeditor_bug_826982_location_changed.js \
+                 browser_styleeditor_bug_851132_middle_click.js \
                  head.js \
                  helpers.js \
                  four.html \
                  head.js \
                  helpers.js \
                  import.css \
                  import.html \
                  import2.css \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "four.html";
+
+function test() {
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    run(aChrome);
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+let gSEChrome, timeoutID;
+
+function run(aChrome) {
+  gSEChrome = aChrome;
+  gBrowser.tabContainer.addEventListener("TabOpen", onTabAdded, false);
+  aChrome.editors[0].addActionListener({onAttach: onEditor0Attach});
+  aChrome.editors[1].addActionListener({onAttach: onEditor1Attach});
+}
+
+function getStylesheetNameLinkFor(aEditor) {
+  return gSEChrome.getSummaryElementForEditor(aEditor).querySelector(".stylesheet-name");
+}
+
+function onEditor0Attach(aEditor) {
+  waitForFocus(function () {
+    // left mouse click should focus editor 1
+    EventUtils.synthesizeMouseAtCenter(
+      getStylesheetNameLinkFor(gSEChrome.editors[1]),
+      {button: 0},
+      gChromeWindow);
+  }, gChromeWindow);
+}
+
+function onEditor1Attach(aEditor) {
+  ok(aEditor.sourceEditor.hasFocus(),
+     "left mouse click has given editor 1 focus");
+
+  // right mouse click should not open a new tab
+  EventUtils.synthesizeMouseAtCenter(
+    getStylesheetNameLinkFor(gSEChrome.editors[2]),
+    {button: 1},
+    gChromeWindow);
+
+  setTimeout(finish, 0);
+}
+
+function onTabAdded() {
+  ok(false, "middle mouse click has opened a new tab");
+  finish();
+}
+
+registerCleanupFunction(function () {
+  gBrowser.tabContainer.removeEventListener("TabOpen", onTabAdded, false);
+  gSEChrome = null;
+});
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.7.390
+Current extension version is: 0.7.423
 
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -371,18 +371,19 @@ ChromeActions.prototype = {
   supportsIntegratedFind: function() {
     // Integrated find is only supported when we're not in a frame and when the
     // new find events code exists.
     return this.domWindow.frameElement === null &&
            getChromeWindow(this.domWindow).gFindBar &&
            'updateControlState' in getChromeWindow(this.domWindow).gFindBar;
   },
   supportsDocumentFonts: function() {
-    var pref = getIntPref('browser.display.use_document_fonts', 1);
-    return !!pref;
+    var prefBrowser = getIntPref('browser.display.use_document_fonts', 1);
+    var prefGfx = getBoolPref('gfx.downloadable_fonts.enabled', true);
+    return (!!prefBrowser && prefGfx);
   },
   fallback: function(url, sendResponse) {
     var self = this;
     var domWindow = this.domWindow;
     var strings = getLocalizedStrings('chrome.properties');
     var message = getLocalizedString(strings, 'unsupported_feature');
 
     var notificationBox = null;
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -187,16 +187,17 @@ let PdfJs = {
   get enabled() {
     var disabled = getBoolPref(PREF_DISABLED, true);
     if (disabled)
       return false;
 
     var handlerInfo = Svc.mime.
                         getFromTypeAndExtension('application/pdf', 'pdf');
     return handlerInfo.alwaysAskBeforeHandling == false &&
+           handlerInfo.plugin == null &&
            handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally;
   },
 
   _ensureRegistered: function _ensureRegistered() {
     if (this._registered)
       return;
 
     this._pdfStreamConverterFactory = new Factory();
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -11,18 +11,18 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 var PDFJS = {};
-PDFJS.version = '0.7.390';
-PDFJS.build = '921f321';
+PDFJS.version = '0.7.423';
+PDFJS.build = 'aa22bef';
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
@@ -952,23 +952,29 @@ var Util = PDFJS.Util = (function UtilCl
   Util.sign = function Util_sign(num) {
     return num < 0 ? -1 : 1;
   };
 
   return Util;
 })();
 
 var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
-  function PageViewport(viewBox, scale, rotate, offsetX, offsetY) {
+  function PageViewport(viewBox, scale, rotation, offsetX, offsetY) {
+    this.viewBox = viewBox;
+    this.scale = scale;
+    this.rotation = rotation;
+    this.offsetX = offsetX;
+    this.offsetY = offsetY;
+
     // creating transform to convert pdf coordinate system to the normal
     // canvas like coordinates taking in account scale and rotation
     var centerX = (viewBox[2] + viewBox[0]) / 2;
     var centerY = (viewBox[3] + viewBox[1]) / 2;
     var rotateA, rotateB, rotateC, rotateD;
-    switch (rotate % 360) {
+    switch (rotation % 360) {
       case -180:
       case 180:
         rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
         break;
       case -270:
       case 90:
         rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
         break;
@@ -1002,23 +1008,28 @@ var PageViewport = PDFJS.PageViewport = 
       rotateA * scale,
       rotateB * scale,
       rotateC * scale,
       rotateD * scale,
       offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
       offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
     ];
 
-    this.offsetX = offsetX;
-    this.offsetY = offsetY;
     this.width = width;
     this.height = height;
     this.fontScale = scale;
   }
   PageViewport.prototype = {
+    clone: function PageViewPort_clone(args) {
+      args = args || {};
+      var scale = 'scale' in args ? args.scale : this.scale;
+      var rotation = 'rotation' in args ? args.rotation : this.rotation;
+      return new PageViewport(this.viewBox.slice(), scale, rotation,
+                              this.offsetX, this.offsetY);
+    },
     convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
       return Util.applyTransform([x, y], this.transform);
     },
     convertToViewportRectangle:
       function PageViewport_convertToViewportRectangle(rect) {
       var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
       var br = Util.applyTransform([rect[2], rect[3]], this.transform);
       return [tl[0], tl[1], br[0], br[1]];
@@ -1720,21 +1731,21 @@ var PDFPageProxy = (function PDFPageProx
     /**
      * For internal use only.
      */
     display: function PDFPageProxy_display(gfx, viewport, callback,
                                            continueCallback) {
       var stats = this.stats;
       stats.time('Rendering');
 
-      gfx.beginDrawing(viewport);
+      var operatorList = this.operatorList;
+      gfx.beginDrawing(viewport, operatorList.transparency);
 
       var startIdx = 0;
-      var length = this.operatorList.fnArray.length;
-      var operatorList = this.operatorList;
+      var length = operatorList.fnArray.length;
       var stepper = null;
       if (PDFJS.pdfBug && 'StepperManager' in globalScope &&
           globalScope['StepperManager'].enabled) {
         stepper = globalScope['StepperManager'].create(this.pageNumber - 1);
         stepper.init(operatorList);
         stepper.nextBreakPoint = stepper.getNextBreakPoint();
       }
 
@@ -2462,17 +2473,34 @@ var CanvasGraphics = (function CanvasGra
       'paintImageXObject': true,
       'paintInlineImageXObject': true,
       'paintInlineImageXObjectGroup': true,
       'paintImageMaskXObject': true,
       'paintImageMaskXObjectGroup': true,
       'shadingFill': true
     },
 
-    beginDrawing: function CanvasGraphics_beginDrawing(viewport) {
+    beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) {
+      // For pdfs that use blend modes we have to clear the canvas else certain
+      // blend modes can look wrong since we'd be blending with a white
+      // backdrop. The problem with a transparent backdrop though is we then
+      // don't get sub pixel anti aliasing on text, so we fill with white if
+      // we can.
+      var width = this.ctx.canvas.width;
+      var height = this.ctx.canvas.height;
+      if (transparency) {
+        this.ctx.clearRect(0, 0, width, height);
+      } else {
+        this.ctx.mozOpaque = true;
+        this.ctx.save();
+        this.ctx.fillStyle = 'rgb(255, 255, 255)';
+        this.ctx.fillRect(0, 0, width, height);
+        this.ctx.restore();
+      }
+
       var transform = viewport.transform;
       this.ctx.save();
       this.ctx.transform.apply(this.ctx, transform);
 
       if (this.textLayer) {
         this.textLayer.beginLayout();
       }
       if (this.imageLayer) {
@@ -12625,18 +12653,45 @@ var ColorSpace = (function ColorSpaceClo
      * Creates the output buffer and converts the specified number of the color
      * values to the RGB colors, similar to the getRgbBuffer.
      */
     createRgbBuffer: function ColorSpace_createRgbBuffer(src, srcOffset,
                                                          count, bits) {
       if (this.isPassthrough(bits)) {
         return src.subarray(srcOffset);
       }
-      var destLength = this.getOutputLength(count * this.numComps);
-      var dest = new Uint8Array(destLength);
+      var dest = new Uint8Array(count * 3);
+      var numComponentColors = 1 << bits;
+      // Optimization: create a color map when there is just one component and
+      // we are converting more colors than the size of the color map. We
+      // don't build the map if the colorspace is gray or rgb since those
+      // methods are faster than building a map. This mainly offers big speed
+      // ups for indexed and alternate colorspaces.
+      if (this.numComps === 1 && count > numComponentColors &&
+          this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
+        // TODO it may be worth while to cache the color map. While running
+        // testing I never hit a cache so I will leave that out for now (perhaps
+        // we are reparsing colorspaces too much?).
+        var allColors = bits <= 8 ? new Uint8Array(numComponentColors) :
+                                    new Uint16Array(numComponentColors);
+        for (var i = 0; i < numComponentColors; i++) {
+          allColors[i] = i;
+        }
+        var colorMap = new Uint8Array(numComponentColors * 3);
+        this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bits);
+
+        var destOffset = 0;
+        for (var i = 0; i < count; ++i) {
+          var key = src[srcOffset++] * 3;
+          dest[destOffset++] = colorMap[key];
+          dest[destOffset++] = colorMap[key + 1];
+          dest[destOffset++] = colorMap[key + 2];
+        }
+        return dest;
+      }
       this.getRgbBuffer(src, srcOffset, count, dest, 0, bits);
       return dest;
     }
   };
 
   ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
     var IR = ColorSpace.parseToIR(cs, xref, res);
     if (IR instanceof AlternateCS)
@@ -13029,17 +13084,17 @@ var DeviceRgbCS = (function DeviceRgbCSC
       var length = count * 3;
       if (bits == 8) {
         dest.set(src.subarray(srcOffset, srcOffset + length), destOffset);
         return;
       }
       var scale = 255 / ((1 << bits) - 1);
       var j = srcOffset, q = destOffset;
       for (var i = 0; i < length; ++i) {
-        dest[q++] = (scale * input[j++]) | 0;
+        dest[q++] = (scale * src[j++]) | 0;
       }
     },
     getOutputLength: function DeviceRgbCS_getOutputLength(inputLength) {
       return inputLength;
     },
     isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
       return bits == 8;
     },
@@ -14904,18 +14959,21 @@ var PartialEvaluator = (function Partial
         fn = 'paintImageXObject';
 
         PDFImage.buildImage(function(imageObj) {
             var imgData = imageObj.getImageData();
             handler.send('obj', [objId, pageIndex, 'Image', imgData]);
           }, handler, xref, resources, image, inline);
       }
 
-      if (!queue)
-        queue = {};
+      if (!queue) {
+        queue = {
+          transparency: false
+        };
+      }
 
       if (!queue.argsArray) {
         queue.argsArray = [];
       }
       if (!queue.fnArray) {
         queue.fnArray = [];
       }
 
@@ -15075,26 +15133,31 @@ var PartialEvaluator = (function Partial
                     case 'LC':
                     case 'LJ':
                     case 'ML':
                     case 'D':
                     case 'RI':
                     case 'FL':
                     case 'CA':
                     case 'ca':
-                    case 'BM':
                       gsStateObj.push([key, value]);
                       break;
                     case 'Font':
                       gsStateObj.push([
                         'Font',
                         handleSetFont(null, value[0]),
                         value[1]
                       ]);
                       break;
+                    case 'BM':
+                      if (!isName(value) || value.name !== 'Normal') {
+                        queue.transparency = true;
+                      }
+                      gsStateObj.push([key, value]);
+                      break;
                     case 'SMask':
                       // We support the default so don't trigger the TODO.
                       if (!isName(value) || value.name != 'None')
                         TODO('graphic state operator ' + key);
                       break;
                     // Only generate info log messages for the following since
                     // they are unlikey to have a big impact on the rendering.
                     case 'OP':
@@ -18168,16 +18231,20 @@ var Font = (function FontClosure() {
 
       case 'TrueType':
       case 'CIDFontType2':
         this.mimetype = 'font/opentype';
 
         // Repair the TrueType file. It is can be damaged in the point of
         // view of the sanitizer
         data = this.checkAndRepair(name, file, properties);
+        if (!data) {
+          // TrueType data is not found, e.g. when the font is an OpenType font
+          warn('Font is not a TrueType font');
+        }
         break;
 
       default:
         warn('Font ' + type + ' is not supported');
         break;
     }
 
     this.data = data;
@@ -19514,16 +19581,18 @@ var Font = (function FontClosure() {
           else if (table.tag == 'glyf')
             glyf = table;
           else if (table.tag == 'fpgm')
             fpgm = table;
           else if (table.tag == 'prep')
             prep = table;
           else if (table.tag == 'cvt ')
             cvt = table;
+          else if (table.tag == 'CFF ')
+            return null; // XXX: OpenType font is found, stopping
           else // skipping table if it's not a required or optional table
             continue;
         }
         tables.push(table);
       }
 
       var numTables = tables.length + requiredTables.length;
 
@@ -21147,23 +21216,28 @@ var Type1Font = function Type1Font(name,
                         subrs, properties);
   this.seacs = this.getSeacs(data.charstrings);
 };
 
 Type1Font.prototype = {
   getOrderedCharStrings: function Type1Font_getOrderedCharStrings(glyphs,
                                                             properties) {
     var charstrings = [];
+    var usedUnicodes = [];
     var i, length, glyphName;
     var unusedUnicode = CMAP_GLYPH_OFFSET;
     for (i = 0, length = glyphs.length; i < length; i++) {
       var item = glyphs[i];
       var glyphName = item.glyph;
       var unicode = glyphName in GlyphsUnicode ?
         GlyphsUnicode[glyphName] : unusedUnicode++;
+      while (usedUnicodes[unicode]) {
+        unicode = unusedUnicode++;
+      }
+      usedUnicodes[unicode] = true;
       charstrings.push({
         glyph: glyphName,
         unicode: unicode,
         gid: i,
         charstring: item.data,
         width: item.width,
         lsb: item.lsb
       });
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -767,16 +767,20 @@ var PDFView = {
     PDFFindController.initialize();
 
     this.initialized = true;
     container.addEventListener('scroll', function() {
       self.lastScroll = Date.now();
     }, false);
   },
 
+  getPage: function pdfViewGetPage(n) {
+    return this.pdfDocument.getPage(n);
+  },
+
   // Helper function to keep track whether a div was scrolled up or down and
   // then call a callback.
   watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
     state.down = true;
     state.lastY = viewAreaElement.scrollTop;
     viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
       var currentY = viewAreaElement.scrollTop;
       var lastY = state.lastY;
@@ -1251,47 +1255,94 @@ var PDFView = {
     var pagesCount = pdfDocument.numPages;
     var id = pdfDocument.fingerprint;
     document.getElementById('numPages').textContent =
       mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
     document.getElementById('pageNumber').max = pagesCount;
 
     PDFView.documentFingerprint = id;
     var store = PDFView.store = new Settings(id);
-    var storePromise = store.initializedPromise;
 
     this.pageRotation = 0;
 
     var pages = this.pages = [];
     this.pageText = [];
     this.startedTextExtraction = false;
-    var pagesRefMap = {};
+    var pagesRefMap = this.pagesRefMap = {};
     var thumbnails = this.thumbnails = [];
-    var pagePromises = [];
-    for (var i = 1; i <= pagesCount; i++)
-      pagePromises.push(pdfDocument.getPage(i));
+
+    var pagesPromise = new PDFJS.Promise();
     var self = this;
-    var pagesPromise = PDFJS.Promise.all(pagePromises);
-    pagesPromise.then(function(promisedPages) {
-      for (var i = 1; i <= pagesCount; i++) {
-        var page = promisedPages[i - 1];
-        var pageView = new PageView(container, page, i, scale,
-                                    page.stats, self.navigateTo.bind(self));
-        var thumbnailView = new ThumbnailView(thumbsView, page, i);
+
+    var firstPagePromise = pdfDocument.getPage(1);
+
+    // Fetch a single page so we can get a viewport that will be the default
+    // viewport for all pages
+    firstPagePromise.then(function(pdfPage) {
+      var viewport = pdfPage.getViewport(scale || 1.0);
+      var pagePromises = [];
+      for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
+        var viewportClone = viewport.clone();
+        var pageView = new PageView(container, pageNum, scale,
+                                    self.navigateTo.bind(self),
+                                    viewportClone);
+        var thumbnailView = new ThumbnailView(thumbsView, pageNum,
+                                              viewportClone);
         bindOnAfterDraw(pageView, thumbnailView);
-
         pages.push(pageView);
         thumbnails.push(thumbnailView);
-        var pageRef = page.ref;
-        pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
       }
 
-      self.pagesRefMap = pagesRefMap;
-
-      // Wait to do this here so all the canvases are setup.
+      var event = document.createEvent('CustomEvent');
+      event.initCustomEvent('documentload', true, true, {});
+      window.dispatchEvent(event);
+
+      for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
+        var pagePromise = pdfDocument.getPage(pageNum);
+        pagePromise.then(function(pdfPage) {
+          var pageNum = pdfPage.pageNumber;
+          var pageView = pages[pageNum - 1];
+          if (!pageView.pdfPage) {
+            // The pdfPage might already be set if we've already entered
+            // pageView.draw()
+            pageView.setPdfPage(pdfPage);
+          }
+          var thumbnailView = thumbnails[pageNum - 1];
+          if (!thumbnailView.pdfPage) {
+            thumbnailView.setPdfPage(pdfPage);
+          }
+
+          var pageRef = pdfPage.ref;
+          var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
+          pagesRefMap[refStr] = pdfPage.pageNumber;
+        });
+        pagePromises.push(pagePromise);
+      }
+
+      PDFJS.Promise.all(pagePromises).then(function(pages) {
+        pagesPromise.resolve(pages);
+      });
+    });
+
+    var storePromise = store.initializedPromise;
+    PDFJS.Promise.all([firstPagePromise, storePromise]).then(function() {
+      var storedHash = null;
+      if (store.get('exists', false)) {
+        var pageNum = store.get('page', '1');
+        var zoom = store.get('zoom', PDFView.currentScale);
+        var left = store.get('scrollLeft', '0');
+        var top = store.get('scrollTop', '0');
+
+        storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
+                    left + ',' + top;
+      }
+      self.setInitialView(storedHash, scale);
+    });
+
+    pagesPromise.then(function() {
       if (PDFView.supportsPrinting) {
         pdfDocument.getJavaScript().then(function(javaScript) {
           if (javaScript.length) {
             console.warn('Warning: JavaScript is not supported');
             PDFView.fallback();
           }
           // Hack to support auto printing.
           var regex = /\bprint\s*\(/g;
@@ -1308,36 +1359,24 @@ var PDFView = {
       }
     });
 
     var destinationsPromise = pdfDocument.getDestinations();
     destinationsPromise.then(function(destinations) {
       self.destinations = destinations;
     });
 
-    // outline and initial view depends on destinations and pagesRefMap
-    var promises = [pagesPromise, destinationsPromise, storePromise,
+    // outline depends on destinations and pagesRefMap
+    var promises = [pagesPromise, destinationsPromise,
                     PDFView.animationStartedPromise];
     PDFJS.Promise.all(promises).then(function() {
       pdfDocument.getOutline().then(function(outline) {
         self.outline = new DocumentOutlineView(outline);
       });
 
-      var storedHash = null;
-      if (store.get('exists', false)) {
-        var page = store.get('page', '1');
-        var zoom = store.get('zoom', PDFView.currentScale);
-        var left = store.get('scrollLeft', '0');
-        var top = store.get('scrollTop', '0');
-
-        storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
-      }
-
-      self.setInitialView(storedHash, scale);
-
       // Make all navigation keys work on document load,
       // unless the viewer is embedded in another page.
       if (window.parent.location === window.location) {
         PDFView.container.focus();
       }
     });
 
     pdfDocument.getMetadata().then(function(data) {
@@ -1644,16 +1683,35 @@ var PDFView = {
 
   beforePrint: function pdfViewSetupBeforePrint() {
     if (!this.supportsPrinting) {
       var printMessage = mozL10n.get('printing_not_supported', null,
           'Warning: Printing is not fully supported by this browser.');
       this.error(printMessage);
       return;
     }
+
+    var alertNotReady = false;
+    if (!this.pages.length) {
+      alertNotReady = true;
+    } else {
+      for (var i = 0, ii = this.pages.length; i < ii; ++i) {
+        if (!this.pages[i].pdfPage) {
+          alertNotReady = true;
+          break;
+        }
+      }
+    }
+    if (alertNotReady) {
+      var notReadyMessage = mozL10n.get('printing_not_ready', null,
+          'Warning: The PDF is not fully loaded for printing.');
+      window.alert(notReadyMessage);
+      return;
+    }
+
     var body = document.querySelector('body');
     body.setAttribute('data-mozPrintCallback', true);
     for (var i = 0, ii = this.pages.length; i < ii; ++i) {
       this.pages[i].beforePrint();
     }
   },
 
   afterPrint: function pdfViewSetupAfterPrint() {
@@ -1738,25 +1796,28 @@ var PDFView = {
 
     for (var i = 0, l = this.pages.length; i < l; i++) {
       var page = this.pages[i];
       page.update(page.scale, this.pageRotation);
     }
 
     for (var i = 0, l = this.thumbnails.length; i < l; i++) {
       var thumb = this.thumbnails[i];
-      thumb.updateRotation(this.pageRotation);
+      thumb.update(this.pageRotation);
     }
 
-    var currentPage = this.pages[this.page - 1];
-
     this.parseScale(this.currentScaleValue, true);
 
     this.renderHighestPriority();
 
+    var currentPage = this.pages[this.page - 1];
+    if (!currentPage) {
+      return;
+    }
+
     // Wait for fullscreen to take effect
     setTimeout(function() {
       currentPage.scrollIntoView();
     }, 0);
   },
 
   /**
    * This function flips the page in presentation mode if the user scrolls up
@@ -1819,24 +1880,24 @@ var PDFView = {
    * @this {PDFView}
    */
   clearMouseScrollState: function pdfViewClearMouseScrollState() {
     this.mouseScrollTimeStamp = 0;
     this.mouseScrollDelta = 0;
   }
 };
 
-var PageView = function pageView(container, pdfPage, id, scale,
-                                 stats, navigateTo) {
+var PageView = function pageView(container, id, scale,
+                                 navigateTo, defaultViewport) {
   this.id = id;
-  this.pdfPage = pdfPage;
 
   this.rotation = 0;
   this.scale = scale || 1.0;
-  this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate);
+  this.viewport = defaultViewport;
+  this.pdfPageRotate = defaultViewport.rotate;
 
   this.renderingState = RenderingStates.INITIAL;
   this.resume = null;
 
   this.textContent = null;
   this.textLayer = null;
 
   var anchor = document.createElement('a');
@@ -1846,37 +1907,49 @@ var PageView = function pageView(contain
   div.id = 'pageContainer' + this.id;
   div.className = 'page';
   div.style.width = Math.floor(this.viewport.width) + 'px';
   div.style.height = Math.floor(this.viewport.height) + 'px';
 
   container.appendChild(anchor);
   container.appendChild(div);
 
+  this.setPdfPage = function pageViewSetPdfPage(pdfPage) {
+    this.pdfPage = pdfPage;
+    this.pdfPageRotate = pdfPage.rotate;
+    this.viewport = pdfPage.getViewport(this.scale);
+    this.stats = pdfPage.stats;
+    this.update();
+  };
+
   this.destroy = function pageViewDestroy() {
     this.update();
-    this.pdfPage.destroy();
+    if (this.pdfPage) {
+      this.pdfPage.destroy();
+    }
   };
 
   this.update = function pageViewUpdate(scale, rotation) {
     this.renderingState = RenderingStates.INITIAL;
     this.resume = null;
 
     if (typeof rotation !== 'undefined') {
       this.rotation = rotation;
     }
 
     this.scale = scale || this.scale;
 
-    var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
-    var viewport = this.pdfPage.getViewport(this.scale, totalRotation);
-
-    this.viewport = viewport;
-    div.style.width = Math.floor(viewport.width) + 'px';
-    div.style.height = Math.floor(viewport.height) + 'px';
+    var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+    this.viewport = this.viewport.clone({
+      scale: this.scale,
+      rotation: totalRotation
+    });
+
+    div.style.width = Math.floor(this.viewport.width) + 'px';
+    div.style.height = Math.floor(this.viewport.height) + 'px';
 
     while (div.hasChildNodes())
       div.removeChild(div.lastChild);
     div.removeAttribute('data-loaded');
 
     delete this.canvas;
 
     this.loadingIconDiv = document.createElement('div');
@@ -2074,54 +2147,66 @@ var PageView = function pageView(contain
   this.getTextContent = function pageviewGetTextContent() {
     if (!this.textContent) {
       this.textContent = this.pdfPage.getTextContent();
     }
     return this.textContent;
   };
 
   this.draw = function pageviewDraw(callback) {
+    var pdfPage = this.pdfPage;
+
+    if (!pdfPage) {
+      var promise = PDFView.getPage(this.id);
+      promise.then(function(pdfPage) {
+        this.setPdfPage(pdfPage);
+        this.draw(callback);
+      }.bind(this));
+      return;
+    }
+
     if (this.renderingState !== RenderingStates.INITIAL) {
       console.error('Must be in new state before drawing');
     }
 
     this.renderingState = RenderingStates.RUNNING;
 
     var canvas = document.createElement('canvas');
     canvas.id = 'page' + this.id;
     div.appendChild(canvas);
     this.canvas = canvas;
 
+    var scale = this.scale, viewport = this.viewport;
+    var outputScale = PDFView.getOutputScale();
+    canvas.width = Math.floor(viewport.width) * outputScale.sx;
+    canvas.height = Math.floor(viewport.height) * outputScale.sy;
+
     var textLayerDiv = null;
     if (!PDFJS.disableTextLayer) {
       textLayerDiv = document.createElement('div');
       textLayerDiv.className = 'textLayer';
+      textLayerDiv.style.width = canvas.width + 'px';
+      textLayerDiv.style.height = canvas.height + 'px';
       div.appendChild(textLayerDiv);
     }
     var textLayer = this.textLayer =
           textLayerDiv ? new TextLayerBuilder(textLayerDiv, this.id - 1) : null;
 
-    var scale = this.scale, viewport = this.viewport;
-    var outputScale = PDFView.getOutputScale();
-    canvas.width = Math.floor(viewport.width) * outputScale.sx;
-    canvas.height = Math.floor(viewport.height) * outputScale.sy;
-
     if (outputScale.scaled) {
       var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' +
                                 (1 / outputScale.sy) + ')';
       CustomStyle.setProp('transform' , canvas, cssScale);
       CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
       if (textLayerDiv) {
         CustomStyle.setProp('transform' , textLayerDiv, cssScale);
         CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%');
       }
     }
 
     var ctx = canvas.getContext('2d');
-    ctx.clearRect(0, 0, canvas.width, canvas.height);
     // TODO(mack): use data attributes to store these
     ctx._scaleX = outputScale.sx;
     ctx._scaleY = outputScale.sy;
     if (outputScale.scaled) {
       ctx.scale(outputScale.sx, outputScale.sy);
     }
     // Checking if document fonts are used only once
     var checkIfDocumentFontsUsed = !PDFView.pdfDocument.embeddedFontsUsed;
@@ -2154,16 +2239,23 @@ var PageView = function pageView(contain
       }
 
       self.stats = pdfPage.stats;
       self.updateStats();
       if (self.onAfterDraw)
         self.onAfterDraw();
 
       cache.push(self);
+
+      var event = document.createEvent('CustomEvent');
+      event.initCustomEvent('pagerender', true, true, {
+        pageNumber: pdfPage.pageNumber
+      });
+      div.dispatchEvent(event);
+
       callback();
     }
 
     var renderContext = {
       canvasContext: ctx,
       viewport: this.viewport,
       textLayer: textLayer,
       continueCallback: function pdfViewcContinueCallback(cont) {
@@ -2202,16 +2294,17 @@ var PageView = function pageView(contain
     }
 
     setupAnnotations(this.pdfPage, this.viewport);
     div.setAttribute('data-loaded', true);
   };
 
   this.beforePrint = function pageViewBeforePrint() {
     var pdfPage = this.pdfPage;
+
     var viewport = pdfPage.getViewport(1);
     // Use the same hack we use for high dpi displays for printing to get better
     // output until bug 811002 is fixed in FF.
     var PRINT_OUTPUT_SCALE = 2;
     var canvas = this.canvas = document.createElement('canvas');
     canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
     canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
     canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt';
@@ -2252,163 +2345,193 @@ var PageView = function pageView(contain
         else
           obj.done();
         self.pdfPage.destroy();
       });
     };
   };
 
   this.updateStats = function pageViewUpdateStats() {
+    if (!this.stats) {
+      return;
+    }
+
     if (PDFJS.pdfBug && Stats.enabled) {
       var stats = this.stats;
       Stats.add(this.id, stats);
     }
   };
 };
 
-var ThumbnailView = function thumbnailView(container, pdfPage, id) {
+var ThumbnailView = function thumbnailView(container, id, defaultViewport) {
   var anchor = document.createElement('a');
   anchor.href = PDFView.getAnchorUrl('#page=' + id);
   anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}');
   anchor.onclick = function stopNavigation() {
     PDFView.page = id;
     return false;
   };
 
-  var rotation = 0;
-  var totalRotation = (rotation + pdfPage.rotate) % 360;
-  var viewport = pdfPage.getViewport(1, totalRotation);
-  var pageWidth = this.width = viewport.width;
-  var pageHeight = this.height = viewport.height;
-  var pageRatio = pageWidth / pageHeight;
+
+  this.pdfPage = undefined;
+  this.viewport = defaultViewport;
+  this.pdfPageRotate = defaultViewport.rotate;
+
+  this.rotation = 0;
+  this.pageWidth = this.viewport.width;
+  this.pageHeight = this.viewport.height;
+  this.pageRatio = this.pageWidth / this.pageHeight;
   this.id = id;
 
-  var canvasWidth = 98;
-  var canvasHeight = canvasWidth / this.width * this.height;
-  var scaleX = this.scaleX = (canvasWidth / pageWidth);
-  var scaleY = this.scaleY = (canvasHeight / pageHeight);
+  this.canvasWidth = 98;
+  this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
+  this.scale = (this.canvasWidth / this.pageWidth);
 
   var div = this.el = document.createElement('div');
   div.id = 'thumbnailContainer' + id;
   div.className = 'thumbnail';
 
   if (id === 1) {
     // Highlight the thumbnail of the first page when no page number is
     // specified (or exists in cache) when the document is loaded.
     div.classList.add('selected');
   }
 
   var ring = document.createElement('div');
   ring.className = 'thumbnailSelectionRing';
-  ring.style.width = canvasWidth + 'px';
-  ring.style.height = canvasHeight + 'px';
+  ring.style.width = this.canvasWidth + 'px';
+  ring.style.height = this.canvasHeight + 'px';
 
   div.appendChild(ring);
   anchor.appendChild(div);
   container.appendChild(anchor);
 
   this.hasImage = false;
   this.renderingState = RenderingStates.INITIAL;
 
-  this.updateRotation = function(rot) {
-
-    rotation = rot;
-    totalRotation = (rotation + pdfPage.rotate) % 360;
-    viewport = pdfPage.getViewport(1, totalRotation);
-    pageWidth = this.width = viewport.width;
-    pageHeight = this.height = viewport.height;
-    pageRatio = pageWidth / pageHeight;
-
-    canvasHeight = canvasWidth / this.width * this.height;
-    scaleX = this.scaleX = (canvasWidth / pageWidth);
-    scaleY = this.scaleY = (canvasHeight / pageHeight);
+  this.setPdfPage = function thumbnailViewSetPdfPage(pdfPage) {
+    this.pdfPage = pdfPage;
+    this.pdfPageRotate = pdfPage.rotate;
+    this.viewport = pdfPage.getViewport(1);
+    this.update();
+  };
+
+  this.update = function thumbnailViewUpdate(rot) {
+    if (!this.pdfPage) {
+      return;
+    }
+
+    if (rot !== undefined) {
+      this.rotation = rot;
+    }
+
+    var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
+    this.viewport = this.viewport.clone({
+      scale: 1,
+      rotation: totalRotation
+    });
+    this.pageWidth = this.viewport.width;
+    this.pageHeight = this.viewport.height;
+    this.pageRatio = this.pageWidth / this.pageHeight;
+
+    this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
+    this.scale = (this.canvasWidth / this.pageWidth);
 
     div.removeAttribute('data-loaded');
     ring.textContent = '';
-    ring.style.width = canvasWidth + 'px';
-    ring.style.height = canvasHeight + 'px';
+    ring.style.width = this.canvasWidth + 'px';
+    ring.style.height = this.canvasHeight + 'px';
 
     this.hasImage = false;
     this.renderingState = RenderingStates.INITIAL;
     this.resume = null;
   };
 
-  function getPageDrawContext() {
+  this.getPageDrawContext = function thumbnailViewGetPageDrawContext() {
     var canvas = document.createElement('canvas');
     canvas.id = 'thumbnail' + id;
 
-    canvas.width = canvasWidth;
-    canvas.height = canvasHeight;
+    canvas.width = this.canvasWidth;
+    canvas.height = this.canvasHeight;
     canvas.className = 'thumbnailImage';
     canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas',
       {page: id}, 'Thumbnail of Page {{page}}'));
 
     div.setAttribute('data-loaded', true);
 
     ring.appendChild(canvas);
 
     var ctx = canvas.getContext('2d');
     ctx.save();
     ctx.fillStyle = 'rgb(255, 255, 255)';
-    ctx.fillRect(0, 0, canvasWidth, canvasHeight);
+    ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
     ctx.restore();
     return ctx;
-  }
+  };
 
   this.drawingRequired = function thumbnailViewDrawingRequired() {
     return !this.hasImage;
   };
 
   this.draw = function thumbnailViewDraw(callback) {
+    if (!this.pdfPage) {
+      var promise = PDFView.getPage(this.id);
+      promise.then(function(pdfPage) {
+        this.setPdfPage(pdfPage);
+        this.draw(callback);
+      }.bind(this));
+      return;
+    }
+
     if (this.renderingState !== RenderingStates.INITIAL) {
       console.error('Must be in new state before drawing');
     }
 
     this.renderingState = RenderingStates.RUNNING;
     if (this.hasImage) {
       callback();
       return;
     }
 
     var self = this;
-    var ctx = getPageDrawContext();
-    var drawViewport = pdfPage.getViewport(scaleX, totalRotation);
+    var ctx = this.getPageDrawContext();
+    var drawViewport = this.viewport.clone({ scale: this.scale });
     var renderContext = {
       canvasContext: ctx,
       viewport: drawViewport,
       continueCallback: function(cont) {
         if (PDFView.highestPriorityPage !== 'thumbnail' + self.id) {
           self.renderingState = RenderingStates.PAUSED;
           self.resume = function() {
             self.renderingState = RenderingStates.RUNNING;
             cont();
           };
           return;
         }
         cont();
       }
     };
-    pdfPage.render(renderContext).then(
+    this.pdfPage.render(renderContext).then(
       function pdfPageRenderCallback() {
         self.renderingState = RenderingStates.FINISHED;
         callback();
       },
       function pdfPageRenderError(error) {
         self.renderingState = RenderingStates.FINISHED;
         callback();
       }
     );
     this.hasImage = true;
   };
 
   this.setImage = function thumbnailViewSetImage(img) {
     if (this.hasImage || !img)
       return;
     this.renderingState = RenderingStates.FINISHED;
-    var ctx = getPageDrawContext();
+    var ctx = this.getPageDrawContext();
     ctx.drawImage(img, 0, 0, img.width, img.height,
                   0, 0, ctx.canvas.width, ctx.canvas.height);
 
     this.hasImage = true;
   };
 };
 
 var DocumentOutlineView = function documentOutlineView(outline) {
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -192,16 +192,17 @@
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_media.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
+@BINPATH@/components/dom_push.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
 @BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_quota.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
@@ -499,32 +500,39 @@
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
+@BINPATH@/components/Push.js
+@BINPATH@/components/Push.manifest
+@BINPATH@/components/PushService.js
+@BINPATH@/components/PushService.manifest
 @BINPATH@/components/nsDOMIdentity.js
 @BINPATH@/components/nsIDService.js
 @BINPATH@/components/Identity.manifest
 @BINPATH@/components/recording-cmdline.js
 @BINPATH@/components/recording-cmdline.manifest
 
 @BINPATH@/components/PermissionSettings.js
 @BINPATH@/components/PermissionSettings.manifest
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
 @BINPATH@/components/AlarmsManager.js
 @BINPATH@/components/AlarmsManager.manifest
 @BINPATH@/components/TCPSocket.js
 @BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
+@BINPATH@/components/AppProtocolHandler.js
+@BINPATH@/components/AppProtocolHandler.manifest
+
 #ifdef MOZ_WEBRTC
 @BINPATH@/components/PeerConnection.js
 @BINPATH@/components/PeerConnection.manifest
 #endif
 
 #ifdef ENABLE_MARIONETTE
 @BINPATH@/chrome/marionette@JAREXT@
 @BINPATH@/chrome/marionette.manifest
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -1217,16 +1217,17 @@ xpicleanup@BIN_SUFFIX@
   components/dom_css.xpt
   components/dom_events.xpt
   components/dom_geolocation.xpt
   components/dom_devicestorage.xpt
   components/dom_html.xpt
   components/dom_json.xpt
   components/dom_loadsave.xpt
   components/dom_offline.xpt
+  components/dom_push.xpt
   components/dom_range.xpt
   components/dom_sidebar.xpt
   components/dom_smil.xpt
   components/dom_storage.xpt
   components/dom_stylesheets.xpt
   components/dom_svg.xpt
   components/dom_threads.xpt
   components/dom_traversal.xpt
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -1055,8 +1055,74 @@ paintflashingChromeDesc=chrome frames
 
 # LOCALIZATION NOTE (paintflashingManual) A longer description describing the
 # set of commands that control paint flashing.
 paintflashingManual=Draw repainted areas in different colors
 
 # LOCALIZATION NOTE (paintflashingTooltip) A string displayed as the
 # tooltip of button in devtools toolbox which toggles paint flashing.
 paintflashingTooltip=Highlight painted area
+
+# LOCALIZATION NOTE (profilerDesc) A very short string used to describe the
+# function of the profiler command.
+profilerDesc=Manage profiler
+
+# LOCALIZATION NOTE (profilerManual) A longer description describing the
+# set of commands that control the profiler.
+profilerManual=Commands to start or stop a JavaScript profiler
+
+# LOCALIZATION NOTE (profilerOpen) A very short string used to describe the function
+# of the profiler open command.
+profilerOpenDesc=Open the profiler
+
+# LOCALIZATION NOTE (profilerClose) A very short string used to describe the function
+# of the profiler close command.
+profilerCloseDesc=Close the profiler
+
+# LOCALIZATION NOTE (profilerStart) A very short string used to describe the function
+# of the profiler start command.
+profilerStartDesc=Start profiling
+
+# LOCALIZATION NOTE (profilerStop) A very short string used to describe the function
+# of the profiler stop command.
+profilerStopDesc=Stop profiling
+
+# LOCALIZATION NOTE (profilerList) A very short string used to describe the function
+# of the profiler list command.
+profilerListDesc=List all profiles
+
+# LOCALIZATION NOTE (profilerShow) A very short string used to describe the function
+# of the profiler show command.
+profilerShowDesc=Show individual profile
+
+# LOCALIZATION NOTE (profilerAlreadyStarted) A message that is displayed whenever
+# an operation cannot be completed because the profile in question has already
+# been started.
+profilerAlreadyStarted=This profile has already been started
+
+# LOCALIZATION NOTE (profilerAlreadyFinished) A message that is displayed whenever
+# an operation cannot be completed because the profile in question has already
+# been finished. It also contains a hint to use the 'profile show' command to see
+# the profiling results.
+profilerAlreadyFinished=This profile has already been completed. Use 'profile show' command to see its results
+
+# LOCALIZATION NOTE (profilerNotFound) A message that is displayed whenever
+# an operation cannot be completed because the profile in question could not be
+# found.
+profilerNotFound=Profile not found
+
+# LOCALIZATION NOTE (profilerNotStarted) A message that is displayed whenever
+# an operation cannot be completed because the profile in question has not been
+# started yet. It also contains a hint to use the 'profile start' command to
+# start the profiler.
+profilerNotStarted=This profile has not been started yet. Use 'profile start' to start profliling
+
+# LOCALIZATION NOTE (profilerStarting) A very short string that indicates that
+# we're starting the profiler.
+profilerStarting=Starting...
+
+# LOCALIZATION NOTE (profilerStopping) A very short string that indicates that
+# we're stopping the profiler.
+profilerStopping=Stopping...
+
+# LOCALIZATION NOTE (profilerNotReady) A message that is displayed whenever
+# an operation cannot be completed because the profiler has not been opened yet.
+profilerNotReady=For this command to work you need to open the profiler first
\ No newline at end of file
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -116,9 +116,10 @@ missing_file_error=Missing PDF file.
 # LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip.
 # "{{type}}" will be replaced with an annotation type from a list defined in
 # the PDF spec (32000-1:2008 Table 169 – Annotation types).
 # Some common types are e.g.: "Check", "Text", "Comment", "Note"
 text_annotation_type=[{{type}} Annotation]
 request_password=PDF is protected by a password:
 
 printing_not_supported=Warning: Printing is not fully supported by this browser.
+printing_not_ready=Warning: The PDF is not fully loaded for printing.
 web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts.
--- a/browser/metro/base/content/helperui/FindHelperUI.js
+++ b/browser/metro/base/content/helperui/FindHelperUI.js
@@ -9,16 +9,24 @@ var FindHelperUI = {
     next: "cmd_findNext",
     previous: "cmd_findPrevious",
     close: "cmd_findClose"
   },
 
   _open: false,
   _status: null,
 
+  /*
+   * Properties
+   */
+
+  get isActive() {
+    return this._open;
+  },
+
   get status() {
     return this._status;
   },
 
   set status(val) {
     if (val != this._status) {
       this._status = val;
       if (!val)
@@ -89,18 +97,21 @@ var FindHelperUI = {
       case "PanFinished":
         this._container.style.visibility = "visible";
         this._textbox.collapsed = false;
         break;
     }
   },
 
   show: function findHelperShow() {
+    // Hide any menus
+    ContextUI.dismiss();
 
-    ContextUI.dismiss();
+    // Shutdown selection related ui
+    SelectionHelperUI.closeEditSession();
 
     this._container.show(this);
     this.search(this._textbox.value);
     this._textbox.select();
     this._textbox.focus();
     this._open = true;
 
     // Prevent the view to scroll automatically while searching
--- a/browser/metro/base/content/input.js
+++ b/browser/metro/base/content/input.js
@@ -141,17 +141,17 @@ var TouchModule = {
           case "touchend":
             this._onTouchEnd(aEvent);
             break;
           case "dblclick":
             // XXX This will get picked up somewhere below us for "double tap to zoom"
             // once we get omtc and the apzc. Currently though dblclick is delivered to
             // content and triggers selection of text, so fire up the SelectionHelperUI
             // once selection is present.
-            if (!SelectionHelperUI.isActive) {
+            if (!SelectionHelperUI.isActive && !FindHelperUI.isActive) {
               setTimeout(function () {
                 SelectionHelperUI.attachEditSession(Browser.selectedTab.browser,
                                                     aEvent.clientX, aEvent.clientY);
               }, 50);
             }
             break;
         }
       }
--- a/browser/metro/base/content/sanitize.js
+++ b/browser/metro/base/content/sanitize.js
@@ -119,18 +119,18 @@ Sanitizer.prototype = {
 
     siteSettings: {
       clear: function ()
       {
         // Clear site-specific permissions like "Allow this site to open popups"
         Services.perms.removeAll();
 
         // Clear site-specific settings like page-zoom level
-        var cps = Cc["@mozilla.org/content-pref/service;1"].getService(Ci.nsIContentPrefService);
-        cps.removeGroupedPrefs();
+        var cps = Cc["@mozilla.org/content-pref/service;1"].getService(Ci.nsIContentPrefService2);
+        cps.removeAllDomains(null);
 
         // Clear "Never remember passwords for this site", which is not handled by
         // the permission manager
         var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
         var hosts = pwmgr.getAllDisabledHosts({})
         for each (var host in hosts) {
           pwmgr.setLoginSavingEnabled(host, true);
         }
--- a/browser/metro/base/tests/Makefile.in
+++ b/browser/metro/base/tests/Makefile.in
@@ -5,52 +5,20 @@
 DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-BROWSER_TESTS = \
-  head.js \
-  browser_test.js \
-  browser_canonizeURL.js \
-  browser_context_ui.js \
-  browser_tiles.js \
-  browser_tilegrid.xul \
-  browser_onscreen_keyboard.js \
-  browser_onscreen_keyboard.html \
-  browser_remotetabs.js \
-  browser_downloads.js \
-  browser_plugin_input.html \
-  browser_plugin_input_mouse.js \
-  browser_plugin_input_keyboard.js \
-  browser_context_menu_tests.js \
-  browser_sanitize_ui.js \
-  browser_context_menu_tests_01.html \
-  browser_context_menu_tests_02.html \
-  browser_context_menu_tests_03.html \
-  text-block.html \
-  browser_topsites.js \
-  $(NULL)
-BROWSER_TEST_RESOURCES = \
-  res/image01.png \
-  $(NULL)
-
 XPCSHELL_TESTS = unit
 
 # For now we're copying the actual Util code.
 # We should make this into a jsm module. See bug 848137
 XPCSHELL_RESOURCES = \
   $(DEPTH)/browser/metro/base/content/Util.js \
   $(NULL)
 
-libs:: $(BROWSER_TESTS)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)
-
-libs:: $(BROWSER_TEST_RESOURCES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)/res
-
 libs:: $(XPCSHELL_RESOURCES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit/
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_1/bootstrap.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-function install(data, reason) {}
-function startup(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
-
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_1/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>addon1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:updateURL>http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf</em:updateURL>
-    <em:bootstrap>true</em:bootstrap>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-        <em:minVersion>0</em:minVersion>
-        <em:maxVersion>*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Front End MetaData -->
-    <em:name>Install Tests</em:name>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_2/install.rdf
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>addon2@tests.mozilla.org</em:id>
-    <em:version>2.0</em:version>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-        <em:minVersion>0</em:minVersion>
-        <em:maxVersion>*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Front End MetaData -->
-    <em:name>Install Tests 2</em:name>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_3/install.rdf
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>addon1@tests.mozilla.org</em:id>
-    <em:version>3.0</em:version>
-    <em:updateURL>http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf</em:updateURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-        <em:minVersion>0</em:minVersion>
-        <em:maxVersion>*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Front End MetaData -->
-    <em:name>Install Tests</em:name>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_locale1/boostrap.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-function install(data, reason) {}
-function startup(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
-
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_locale1/chrome.manifest
+++ /dev/null
@@ -1,4 +0,0 @@
-locale mozapps te-st chrome # locale
-locale browser te-st chrome # duplicate locale
-locale browser te-st-a chrome # second locale
-locale branding te-st-3 chrome # wrong component
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_locale1/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>locale1@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:type>8</em:type>
-    <em:bootstrap>true</em:bootstrap>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-        <em:minVersion>0</em:minVersion>
-        <em:maxVersion>*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Locale</em:name>
-
-  </Description>
-</RDF>
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/Makefile.in
@@ -0,0 +1,46 @@
+# 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/.
+
+DEPTH     = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+relativesrcdir  = @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+BROWSER_TESTS = \
+  head.js \
+  browser_test.js \
+  browser_canonizeURL.js \
+  browser_context_ui.js \
+  browser_tiles.js \
+  browser_tilegrid.xul \
+  browser_onscreen_keyboard.js \
+  browser_onscreen_keyboard.html \
+  browser_remotetabs.js \
+  browser_downloads.js \
+  browser_plugin_input.html \
+  browser_plugin_input_mouse.js \
+  browser_plugin_input_keyboard.js \
+  browser_context_menu_tests.js \
+  browser_context_menu_tests_01.html \
+  browser_context_menu_tests_02.html \
+  browser_context_menu_tests_03.html \
+  text-block.html \
+  browser_sanitize_ui.js \
+  browser_topsites.js \
+  $(NULL)
+
+BROWSER_TEST_RESOURCES = \
+  res/image01.png \
+  $(NULL)
+
+libs:: $(BROWSER_TESTS)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)
+
+libs:: $(BROWSER_TEST_RESOURCES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)/res
+
+include $(topsrcdir)/config/rules.mk
rename from browser/metro/base/tests/browser_canonizeURL.js
rename to browser/metro/base/tests/mochitest/browser_canonizeURL.js
rename from browser/metro/base/tests/browser_context_menu_tests.js
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests.js
rename from browser/metro/base/tests/browser_context_menu_tests_01.html
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests_01.html
rename from browser/metro/base/tests/browser_context_menu_tests_02.html
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests_02.html
rename from browser/metro/base/tests/browser_context_menu_tests_03.html
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests_03.html
rename from browser/metro/base/tests/browser_context_ui.js
rename to browser/metro/base/tests/mochitest/browser_context_ui.js
rename from browser/metro/base/tests/browser_downloads.js
rename to browser/metro/base/tests/mochitest/browser_downloads.js
rename from browser/metro/base/tests/browser_onscreen_keyboard.html
rename to browser/metro/base/tests/mochitest/browser_onscreen_keyboard.html
rename from browser/metro/base/tests/browser_onscreen_keyboard.js
rename to browser/metro/base/tests/mochitest/browser_onscreen_keyboard.js
rename from browser/metro/base/tests/browser_plugin_input.html
rename to browser/metro/base/tests/mochitest/browser_plugin_input.html
rename from browser/metro/base/tests/browser_plugin_input_keyboard.js
rename to browser/metro/base/tests/mochitest/browser_plugin_input_keyboard.js
rename from browser/metro/base/tests/browser_plugin_input_mouse.js
rename to browser/metro/base/tests/mochitest/browser_plugin_input_mouse.js
rename from browser/metro/base/tests/browser_remotetabs.js
rename to browser/metro/base/tests/mochitest/browser_remotetabs.js
rename from browser/metro/base/tests/browser_sanitize_ui.js
rename to browser/metro/base/tests/mochitest/browser_sanitize_ui.js
rename from browser/metro/base/tests/browser_test.js
rename to browser/metro/base/tests/mochitest/browser_test.js
rename from browser/metro/base/tests/browser_tilegrid.xul
rename to browser/metro/base/tests/mochitest/browser_tilegrid.xul
rename from browser/metro/base/tests/browser_tiles.js
rename to browser/metro/base/tests/mochitest/browser_tiles.js
rename from browser/metro/base/tests/browser_topsites.js
rename to browser/metro/base/tests/mochitest/browser_topsites.js
rename from browser/metro/base/tests/head.js
rename to browser/metro/base/tests/mochitest/head.js
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/moz.build
@@ -0,0 +1,4 @@
+# vim: set filetype=python:
+# 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/.
rename from browser/metro/base/tests/res/image01.png
rename to browser/metro/base/tests/mochitest/res/image01.png
rename from browser/metro/base/tests/text-block.html
rename to browser/metro/base/tests/mochitest/text-block.html
--- a/browser/metro/base/tests/moz.build
+++ b/browser/metro/base/tests/moz.build
@@ -1,5 +1,6 @@
 # vim: set filetype=python:
 # 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/.
 
+TEST_DIRS += ['mochitest']
--- a/browser/metro/shell/testing/metrotestharness.cpp
+++ b/browser/metro/shell/testing/metrotestharness.cpp
@@ -151,16 +151,24 @@ public:
   ~DeleteTestFileHelper() {
     if (mTestFile.GetLength()) {
       Log(L"Deleting %s", CStringW(mTestFile));
       DeleteFileA(mTestFile);
     }
   }
 };
 
+static void AddConsoleIdToParams()
+{
+  DWORD dwId = GetCurrentProcessId();
+  CString tmp;
+  tmp.Format(L" testconsoleid=%d", dwId);
+  sAppParams += tmp;
+}
+
 static bool Launch()
 {
   Log(L"Launching browser...");
 
   DWORD processID;
 
   // The interface that allows us to activate the browser
   CComPtr<IApplicationActivationManager> activateMgr;
@@ -294,14 +302,15 @@ int wmain(int argc, WCHAR* argv[])
 
     sAppParams.Append(argv[idx]);
     sAppParams.Append(L" ");
   }
   sAppParams.Trim();
   if (sFirefoxPath.GetLength()) {
     Log(L"firefoxpath: '%s'", sFirefoxPath);
   }
+  AddConsoleIdToParams();
   Log(L"args: '%s'", sAppParams);
   Launch();
 
   CoUninitialize();
   return 0;
 }
--- a/build/valgrind/cross-architecture.sup
+++ b/build/valgrind/cross-architecture.sup
@@ -85,18 +85,17 @@
    fun:moz_xmalloc
    fun:_ZNSt11_Deque_baseIN11MessageLoop11PendingTaskESaIS1_EE17_M_initialize_mapEj
    ...
 }
 {
    Bug 793548
    Memcheck:Leak
    fun:malloc
-   fun:moz_xmalloc
-   fun:NS_Alloc_P
+   ...
    fun:_Z12ToNewCStringRK19nsACString_internal
    fun:_ZN13CrashReporter14SetupExtraDataEP7nsIFileRK19nsACString_internal
    ...
 }
 {
    Bug 793608
    Memcheck:Leak
    ...
@@ -289,15 +288,22 @@
    Bug 823782
    Memcheck:Leak
    fun:malloc
    ...
    fun:_ZN2js6ctypes10StructTypeL6CreateEP9JSContextjPN2JS5ValueE
    ...
 }
 {
+   Bug 823782
+   Memcheck:Leak
+   fun:malloc
+   fun:_ZN2js6ctypes9Int64Base9ConstructEP9JSContextN2JS6HandleIP8JSObjectEEmb
+   ...
+}
+{
    Bug 824323
    Memcheck:Leak
    fun:malloc
    ...
    fun:_ZN7mozilla3dom7workers13WorkerPrivate9DoRunLoopEP9JSContext
    ...
 }
--- a/client.mk
+++ b/client.mk
@@ -103,16 +103,19 @@ define CR
 
 endef
 
 # As $(shell) doesn't preserve newlines, use sed to replace them with an
 # unlikely sequence (||), which is then replaced back to newlines by make
 # before evaluation.
 $(eval $(subst ||,$(CR),$(shell _PYMAKE=$(.PYMAKE) $(TOPSRCDIR)/$(MOZCONFIG_LOADER) $(TOPSRCDIR) 2> $(TOPSRCDIR)/.mozconfig.out | sed 's/$$/||/')))
 
+ifdef NO_AUTOCLOBBER
+export NO_AUTOCLOBBER=1
+endif
 
 # Automatically add -jN to make flags if not defined. N defaults to number of cores.
 ifeq (,$(findstring -j,$(MOZ_MAKE_FLAGS)))
   cores=$(shell $(PYTHON) -c 'import multiprocessing; print(multiprocessing.cpu_count())')
   MOZ_MAKE_FLAGS += -j$(cores)
 endif
 
 
@@ -297,19 +300,23 @@ CONFIGURE_ENV_ARGS += \
 #   $(TOPSRCDIR) will set @srcdir@ to "."; otherwise, it is set to the full
 #   path of $(TOPSRCDIR).
 ifeq ($(TOPSRCDIR),$(OBJDIR))
   CONFIGURE = ./configure
 else
   CONFIGURE = $(TOPSRCDIR)/configure
 endif
 
+check-clobber:
+	$(PYTHON) $(TOPSRCDIR)/python/mozbuild/mozbuild/controller/clobber.py $(TOPSRCDIR) $(OBJDIR)
+
 configure-files: $(CONFIGURES)
 
 configure-preqs = \
+  check-clobber \
   configure-files \
   $(call mkdir_deps,$(OBJDIR)) \
   $(if $(MOZ_BUILD_PROJECTS),$(call mkdir_deps,$(MOZ_OBJDIR))) \
   save-mozconfig \
   $(NULL)
 
 save-mozconfig: $(FOUND_MOZCONFIG)
 	-cp $(FOUND_MOZCONFIG) $(OBJDIR)/.mozconfig
@@ -437,9 +444,28 @@ check-sync-dirs-%:
 echo-variable-%:
 	@echo $($*)
 
 # This makefile doesn't support parallel execution. It does pass
 # MOZ_MAKE_FLAGS to sub-make processes, so they will correctly execute
 # in parallel.
 .NOTPARALLEL:
 
-.PHONY: checkout real_checkout depend realbuild build profiledbuild cleansrcdir pull_all build_all clobber clobber_all pull_and_build_all everything configure preflight_all preflight postflight postflight_all $(OBJDIR_TARGETS)
+.PHONY: checkout \
+    real_checkout \
+    depend \
+    realbuild \
+    build \
+    profiledbuild \
+    cleansrcdir \
+    pull_all \
+    build_all \
+    check-clobber \
+    clobber \
+    clobber_all \
+    pull_and_build_all \
+    everything \
+    configure \
+    preflight_all \
+    preflight \
+    postflight \
+    postflight_all \
+    $(OBJDIR_TARGETS)
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-22.0a1
+23.0a1
--- a/configure.in
+++ b/configure.in
@@ -115,40 +115,16 @@ then
 	***
 	EOF
     exit 1
     break
   fi
 fi
 MOZ_BUILD_ROOT=`pwd`
 
-dnl Do not allow building if a clobber is required
-dnl ==============================================================
-dnl TODO Make this better, ideally this would clobber automaticially
-if test -e $_objdir/CLOBBER; then
-  if test $_topsrcdir/CLOBBER -nt $_objdir/CLOBBER; then
-    echo "	***"
-    echo "	*	The CLOBBER file has been updated, indicating that an incremental build"
-    echo "	*	since your last build will probably not work. A full build is required."
-    echo "	*	The change that caused this is:"
-    cat $_topsrcdir/CLOBBER | sed '/^#/d' | sed 's/^/	*	/'
-    echo "	*	"
-    echo "	*	The easiest way to fix this is to manually delete your objdir:"
-    echo "	*	rm -rf $_objdir"
-    echo "	*	"
-    echo "	*	Or, if you know this clobber doesn't apply to you, it can be ignored with:"
-    echo "	*	cp '$_topsrcdir/CLOBBER' $_objdir"
-    echo "	***"
-    exit 1
-    break;
-  fi
-else
-  touch $_objdir/CLOBBER
-fi
-
 MOZ_PYTHON
 
 MOZ_DEFAULT_COMPILER
 
 COMPILE_ENVIRONMENT=1
 MOZ_ARG_DISABLE_BOOL(compile-environment,
 [  --disable-compile-environment
                           Disable compiler/library checks.],
@@ -3985,27 +3961,16 @@ MOZ_ARG_WITH_BOOL(system-nss,
 if test -n "$_USE_SYSTEM_NSS"; then
     AM_PATH_NSS(3.14.3, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
 fi
 
 if test -n "$MOZ_NATIVE_NSS"; then
    NSS_LIBS="$NSS_LIBS -lcrmf"
 else
    NSS_CFLAGS='-I$(LIBXUL_DIST)/include/nss'
-   if test -z "$IMPORT_LIB_SUFFIX"; then
-       DLL_LINK_SUFFIX='$(DLL_SUFFIX)'
-   else
-       DLL_LINK_SUFFIX='.$(IMPORT_LIB_SUFFIX)'
-   fi
-   NSS_DEP_LIBS="\
-        \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)crmf.\$(LIB_SUFFIX) \
-        \$(LIBXUL_DIST)/lib/\$(DLL_PREFIX)smime$NSS_VERSION${DLL_LINK_SUFFIX} \
-        \$(LIBXUL_DIST)/lib/\$(DLL_PREFIX)ssl$NSS_VERSION${DLL_LINK_SUFFIX} \
-        \$(LIBXUL_DIST)/lib/\$(DLL_PREFIX)nss$NSS_VERSION${DLL_LINK_SUFFIX} \
-        \$(LIBXUL_DIST)/lib/\$(DLL_PREFIX)nssutil$NSS_VERSION${DLL_LINK_SUFFIX}"
 
    if test -z "$GNU_CC" -a "$OS_ARCH" = "WINNT" -o "$OS_ARCH" = "OS2"; then
        NSS_LIBS="\
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)crmf.\$(LIB_SUFFIX) \
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)smime$NSS_VERSION.\$(LIB_SUFFIX) \
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)ssl$NSS_VERSION.\$(LIB_SUFFIX) \
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)nss$NSS_VERSION.\$(LIB_SUFFIX) \
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)nssutil$NSS_VERSION.\$(LIB_SUFFIX)"
@@ -4534,16 +4499,19 @@ MOZ_WIDGET_GTK=
 
 case "$MOZ_WIDGET_TOOLKIT" in
 
 cairo-windows)
     MOZ_WIDGET_TOOLKIT=windows
     MOZ_WEBGL=1
     MOZ_PDF_PRINTING=1
     MOZ_INSTRUMENT_EVENT_LOOP=1
+    if test -n "$GNU_CC"; then
+        MOZ_FOLD_LIBS=
+    fi
     ;;
 
 cairo-gtk2|cairo-gtk2-x11)
     MOZ_WIDGET_TOOLKIT=gtk2
     MOZ_ENABLE_GTK2=1
     MOZ_ENABLE_XREMOTE=1
     MOZ_WEBGL=1
     MOZ_GL_DEFAULT_PROVIDER=GLX
@@ -8855,17 +8823,16 @@ AC_SUBST(MOZ_PNG_CFLAGS)
 AC_SUBST(MOZ_PNG_LIBS)
 
 AC_SUBST(NSPR_CFLAGS)
 AC_SUBST(NSPR_LIBS)
 AC_SUBST(MOZ_NATIVE_NSPR)
 
 AC_SUBST(NSS_CFLAGS)
 AC_SUBST(NSS_LIBS)
-AC_SUBST(NSS_DEP_LIBS)
 AC_SUBST(MOZ_NATIVE_NSS)
 AC_SUBST(NSS_DISABLE_DBM)
 
 OS_CFLAGS="$CFLAGS"
 OS_CXXFLAGS="$CXXFLAGS"
 OS_CPPFLAGS="$CPPFLAGS"
 OS_COMPILE_CFLAGS="$COMPILE_CFLAGS"
 OS_COMPILE_CXXFLAGS="$COMPILE_CXXFLAGS"
--- a/content/base/public/nsIDOMDataChannel.idl
+++ b/content/base/public/nsIDOMDataChannel.idl
@@ -1,24 +1,27 @@
 #include "domstubs.idl"
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIVariant;
 
-[scriptable, builtinclass, uuid(7363aa5c-f4b2-4b86-8d15-e0f714c9216b)]
+[scriptable, builtinclass, uuid(1aed816d-1156-414e-8fe2-f01daa6021f0)]
 interface nsIDOMDataChannel : nsIDOMEventTarget
 {
   readonly attribute DOMString label;
+  readonly attribute DOMString protocol;
   readonly attribute boolean reliable;
   readonly attribute boolean ordered;
 
   readonly attribute DOMString readyState;
   readonly attribute unsigned long bufferedAmount;
 
+  readonly attribute unsigned short stream;
+
   [implicit_jscontext] attribute jsval onopen;
   [implicit_jscontext] attribute jsval onerror;
   [implicit_jscontext] attribute jsval onclose;
   [implicit_jscontext] attribute jsval onmessage;
 
   attribute DOMString binaryType;
 
   void close();
--- a/content/base/public/nsIDOMFileReader.idl
+++ b/content/base/public/nsIDOMFileReader.idl
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMEventListener;
 interface nsIDOMBlob;
 interface nsIDOMDOMError;
 
-[scriptable, builtinclass, uuid(62a1628e-c856-4aee-8273-d0c62488e2ca)]
+[scriptable, builtinclass, uuid(81a8d00b-2982-44f6-aecf-faac0d0819d6)]
 interface nsIDOMFileReader : nsIDOMEventTarget
 {
   [implicit_jscontext]
   void readAsArrayBuffer(in nsIDOMBlob filedata);
   void readAsBinaryString(in nsIDOMBlob filedata);
   void readAsText(in nsIDOMBlob filedata, [optional] in DOMString encoding);
   void readAsDataURL(in nsIDOMBlob file);
 
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -16,29 +16,29 @@ interface nsPIDOMWindow;
 interface nsIInputStream;
 interface nsIDOMBlob;
 
 %{C++
 // for jsval
 #include "jsapi.h"
 %}
 
-[scriptable, builtinclass, uuid(a137d5e6-81e2-4fa3-a791-26459df723ff)]
+[scriptable, builtinclass, uuid(5bc978f2-41e5-4349-a12d-b018092271f7)]
 interface nsIXMLHttpRequestEventTarget : nsIDOMEventTarget {
   // event handler attributes
   [implicit_jscontext] attribute jsval onabort;
   [implicit_jscontext] attribute jsval onerror;
   [implicit_jscontext] attribute jsval onload;
   [implicit_jscontext] attribute jsval onloadstart;
   [implicit_jscontext] attribute jsval onprogress;
   [implicit_jscontext] attribute jsval ontimeout;
   [implicit_jscontext] attribute jsval onloadend;
 };
 
-[scriptable, builtinclass, uuid(8dbd2448-740a-412c-b314-434f24a1c510)]
+[scriptable, builtinclass, uuid(df3796fa-d98a-4185-9dda-d2f2b56a5d38)]
 interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget {
   // for future use
 };
 
 /**
  * Mozilla's XMLHttpRequest is modelled after Microsoft's IXMLHttpRequest
  * object. The goal has been to make Mozilla's version match Microsoft's
  * version as closely as possible, but there are bound to be some differences.
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5030,43 +5030,57 @@ nsContentUtils::AddScriptBlocker()
   if (!sScriptBlockerCount) {
     NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
                  "Should not already have a count");
     sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length();
   }
   ++sScriptBlockerCount;
 }
 
+#ifdef DEBUG
+static bool sRemovingScriptBlockers = false;
+#endif
+
 /* static */
 void
 nsContentUtils::RemoveScriptBlocker()
 {
+  MOZ_ASSERT(!sRemovingScriptBlockers);
   NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
   --sScriptBlockerCount;
   if (sScriptBlockerCount) {
     return;
   }
 
   uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
   uint32_t lastBlocker = sBlockedScriptRunners->Length();
   uint32_t originalFirstBlocker = firstBlocker;
   uint32_t blockersCount = lastBlocker - firstBlocker;
   sRunnersCountAtFirstBlocker = 0;
   NS_ASSERTION(firstBlocker <= lastBlocker,
                "bad sRunnersCountAtFirstBlocker");
 
   while (firstBlocker < lastBlocker) {
-    nsCOMPtr<nsIRunnable> runnable = (*sBlockedScriptRunners)[firstBlocker];
+    nsCOMPtr<nsIRunnable> runnable;
+    runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
     ++firstBlocker;
 
+    // Calling the runnable can reenter us
     runnable->Run();
+    // So can dropping the reference to the runnable
+    runnable = nullptr;
+
     NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
                  "Bad count");
     NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
   }
+#ifdef DEBUG
+  AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
+  sRemovingScriptBlockers = true;
+#endif
   sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
 }
 
 /* static */
 bool
 nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
 {
   if (!aRunnable) {
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -184,16 +184,30 @@ NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, 
 
 NS_IMETHODIMP
 nsDOMDataChannel::GetLabel(nsAString& aLabel)
 {
   mDataChannel->GetLabel(aLabel);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMDataChannel::GetProtocol(nsAString& aProtocol)
+{
+  mDataChannel->GetProtocol(aProtocol);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::GetStream(uint16_t *aStream)
+{
+  mDataChannel->GetStream(aStream);
+  return NS_OK;
+}
+
 // XXX should be GetType()?  Open question for the spec
 NS_IMETHODIMP
 nsDOMDataChannel::GetReliable(bool* aReliable)
 {
   *aReliable = (mDataChannel->GetType() == mozilla::DataChannelConnection::RELIABLE);
   return NS_OK;
 }
 
--- a/content/base/test/chrome/test_csp_bug768029.html
+++ b/content/base/test/chrome/test_csp_bug768029.html
@@ -24,17 +24,17 @@ Components.utils.import("resource://gre/
 
 // Note: we don't have to inspect all the different operations of CSP,
 // we're just looking for specific differences in behavior that indicate
 // a default CSP got applied.
 const DEFAULT_CSP_PRIV = "default-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'";
 const DEFAULT_CSP_CERT = "default-src *; script-src 'self'; style-src 'self'; object-src 'none'";
 
 if (navigator.platform.startsWith("Linux")) {
-  SimpleTest.expectAssertions(0, 1);
+  SimpleTest.expectAssertions(0, 1); // bug 846137, ASSERTION: wrong thread: 'PR_GetCurrentThread() == gSocketThread'
 }
 SimpleTest.waitForExplicitFinish();
 
 var gData = [
   {
     app: "https://example.com/manifest.webapp",
     appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_INSTALLED,
     origin: "https://example.com",
--- a/content/base/test/file_mixed_content_frameNavigation_innermost.html
+++ b/content/base/test/file_mixed_content_frameNavigation_innermost.html
@@ -42,17 +42,19 @@
 
     case "securePage_navigate_grandchild":
       document.getElementById("content").innerHTML=
         '<a href="http://example.com/tests/content/base/test/file_mixed_content_frameNavigation_innermost.html?securePage_navigate_grandchild_response" id="link">Testing\<\/a>';
       document.getElementById("link").click();
       break;
 
     case "securePage_navigate_grandchild_response":
+      dump("\nNavigated to grandchild iframe from secure location to insecure location.  About to post message to the top page.\n");
       parent.parent.parent.postMessage({"test": "securePage_navigate_grandchild", "msg": "navigated to insecure grandchild iframe on secure page"}, "http://mochi.test:8888");
+      dump("\npostMessage to parent attempted.\n");
       document.getElementById("content").innerHTML = "<p>Navigated from secure to insecure grandchild frame on a secure page</p>";
       break;
 
     case "blankTarget":
       window.close();
       break;
 
     default:
--- a/content/base/test/file_mixed_content_frameNavigation_secure_grandchild.html
+++ b/content/base/test/file_mixed_content_frameNavigation_secure_grandchild.html
@@ -37,17 +37,19 @@ https://bugzilla.mozilla.org/show_bug.cg
     }
     else {
       if(counter < MAX_COUNT) {
         counter++;
         setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
       }
       else {
         // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+        dump("\nWe have past the maximum timeout.  Navigating a grandchild iframe from an https location to an http location on a secure page failed.  We are about to post message to the top level page\n");
         parent.parent.postMessage({"test": "securePage_navigate_grandchild", "msg": "navigating to insecure grandchild iframe blocked on secure page"}, "http://mochi.test:8888");
+        dump("\nAttempted postMessage\n");
       }
     }
   }
 
   setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
 
 </script>
 </body>
--- a/content/base/test/test_XHR_anon.html
+++ b/content/base/test/test_XHR_anon.html
@@ -1,17 +1,17 @@
 <!DOCTYPE html>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for XMLHttpRequest with system privileges</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body onload="runTests();">
+<body onload="setup();">
 <p id="display">
 <iframe id="loader"></iframe>
 </p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
@@ -38,17 +38,16 @@ let am = {
   },
 }
 
 var tests = [ test1, test2, test2a, test3, test3, test3, test4, test4, test4, test5, test5, test5 ];
 
 function runTests() {
   if (!tests.length) {
     am.tearDown();
-    SpecialPowers.removePermission("systemXHR", document);
     SimpleTest.finish();
     return;
   }
 
   var test = tests.shift();
   test();
 }
 
@@ -162,15 +161,17 @@ function test5() {
   };
   xhr.onerror = function onerror() {
     ok(false, "Got an error event!");
     runTests();
   }
   xhr.send();
 }
 
-am.init();
-SpecialPowers.addPermission("systemXHR", true, document);
-SimpleTest.waitForExplicitFinish();
+function setup() {
+  am.init();
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runTests);
+}
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -255,16 +255,58 @@ NS_DEFINE_STATIC_IID_ACCESSOR(HTMLInputE
 HTMLInputElement::nsFilePickerShownCallback::nsFilePickerShownCallback(
   HTMLInputElement* aInput, nsIFilePicker* aFilePicker, bool aMulti)
   : mFilePicker(aFilePicker)
   , mInput(aInput)
   , mMulti(aMulti)
 {
 }
 
+NS_IMPL_ISUPPORTS1(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)
+
+NS_IMETHODIMP
+UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason)
+{
+  nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  NS_ENSURE_STATE(localFile);
+
+  if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR ||
+      !mResult) {
+    // Default to "desktop" directory for each platform
+    nsCOMPtr<nsIFile> homeDir;
+    NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir));
+    localFile = do_QueryInterface(homeDir);
+  } else {
+    nsAutoString prefStr;
+    nsCOMPtr<nsIVariant> pref;
+    mResult->GetValue(getter_AddRefs(pref));
+    pref->GetAsAString(prefStr);
+    localFile->InitWithPath(prefStr);
+  }
+
+  mFilePicker->SetDisplayDirectory(localFile);
+  mFilePicker->Open(mFpCallback);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref)
+{
+  mResult = pref;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UploadLastDir::ContentPrefCallback::HandleError(nsresult error)
+{
+  // HandleCompletion is always called (even with HandleError was called),
+  // so we don't need to do anything special here.
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
 {
   if (aResult == nsIFilePicker::returnCancel) {
     return NS_OK;
   }
 
   // Collect new selected filenames
@@ -384,16 +426,19 @@ HTMLInputElement::AsyncClickHandler::Run
     filePicker->AppendFilters(nsIFilePicker::filterAll);
   }
 
   // Set default directry and filename
   nsAutoString defaultName;
 
   const nsCOMArray<nsIDOMFile>& oldFiles = mInput->GetFilesInternal();
 
+  nsCOMPtr<nsIFilePickerShownCallback> callback =
+    new HTMLInputElement::nsFilePickerShownCallback(mInput, filePicker, multi);
+
   if (oldFiles.Count()) {
     nsString path;
 
     oldFiles[0]->GetMozFullPathInternal(path);
 
     nsCOMPtr<nsIFile> localFile;
     rv = NS_NewLocalFile(path, false, getter_AddRefs(localFile));
 
@@ -410,33 +455,23 @@ HTMLInputElement::AsyncClickHandler::Run
     // one file was selected before.
     if (oldFiles.Count() == 1) {
       nsAutoString leafName;
       oldFiles[0]->GetName(leafName);
       if (!leafName.IsEmpty()) {
         filePicker->SetDefaultString(leafName);
       }
     }
-  } else {
-    // Attempt to retrieve the last used directory from the content pref service
-    nsCOMPtr<nsIFile> localFile;
-    HTMLInputElement::gUploadLastDir->FetchLastUsedDirectory(doc,
-                                                             getter_AddRefs(localFile));
-    if (!localFile) {
-      // Default to "desktop" directory for each platform
-      nsCOMPtr<nsIFile> homeDir;
-      NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir));
-      localFile = do_QueryInterface(homeDir);
-    }
-    filePicker->SetDisplayDirectory(localFile);
-  }
-
-  nsCOMPtr<nsIFilePickerShownCallback> callback =
-    new HTMLInputElement::nsFilePickerShownCallback(mInput, filePicker, multi);
-  return filePicker->Open(callback);
+
+    return filePicker->Open(callback);
+  }
+
+  HTMLInputElement::gUploadLastDir->FetchDirectoryAndDisplayPicker(doc, filePicker, callback);
+  return NS_OK;
+
 }
 
 #define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir")
 
 NS_IMPL_ISUPPORTS2(UploadLastDir, nsIObserver, nsISupportsWeakReference)
 
 void
 HTMLInputElement::InitUploadLastDir() {
@@ -451,51 +486,46 @@ HTMLInputElement::InitUploadLastDir() {
 }
 
 void
 HTMLInputElement::DestroyUploadLastDir() {
   NS_IF_RELEASE(gUploadLastDir);
 }
 
 nsresult
-UploadLastDir::FetchLastUsedDirectory(nsIDocument* aDoc, nsIFile** aFile)
+UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc,
+                                              nsIFilePicker* aFilePicker,
+                                              nsIFilePickerShownCallback* aFpCallback)
 {
   NS_PRECONDITION(aDoc, "aDoc is null");
-  NS_PRECONDITION(aFile, "aFile is null");
+  NS_PRECONDITION(aFilePicker, "aFilePicker is null");
+  NS_PRECONDITION(aFpCallback, "aFpCallback is null");
 
   nsIURI* docURI = aDoc->GetDocumentURI();
   NS_PRECONDITION(docURI, "docURI is null");
 
   nsCOMPtr<nsISupports> container = aDoc->GetContainer();
   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(container);
 
-  // Attempt to get the CPS, if it's not present we'll just return
-  nsCOMPtr<nsIContentPrefService> contentPrefService =
+  nsCOMPtr<nsIContentPrefCallback2> prefCallback = 
+    new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);
+
+  // Attempt to get the CPS, if it's not present we'll fallback to use the Desktop folder
+  nsCOMPtr<nsIContentPrefService2> contentPrefService =
     do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
-  if (!contentPrefService)
-    return NS_ERROR_NOT_AVAILABLE;
-  nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
-  if (!uri)
-    return NS_ERROR_OUT_OF_MEMORY;
-  uri->SetAsISupports(docURI);
-
-  // Get the last used directory, if it is stored
-  bool hasPref;
-  if (NS_SUCCEEDED(contentPrefService->HasPref(uri, CPS_PREF_NAME, loadContext, &hasPref)) && hasPref) {
-    nsCOMPtr<nsIVariant> pref;
-    contentPrefService->GetPref(uri, CPS_PREF_NAME, loadContext, nullptr, getter_AddRefs(pref));
-    nsString prefStr;
-    pref->GetAsAString(prefStr);
-
-    nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
-    if (!localFile)
-      return NS_ERROR_OUT_OF_MEMORY;
-    localFile->InitWithPath(prefStr);
-    localFile.forget(aFile);
-  }
+  if (!contentPrefService) {
+    prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
+    return NS_OK;
+  }
+
+  nsAutoCString cstrSpec;
+  docURI->GetSpec(cstrSpec);
+  NS_ConvertUTF8toUTF16 spec(cstrSpec);
+
+  contentPrefService->GetByDomainAndName(spec, CPS_PREF_NAME, loadContext, prefCallback);
   return NS_OK;
 }
 
 nsresult
 UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIDOMFile* aDomFile)
 {
   NS_PRECONDITION(aDoc, "aDoc is null");
   NS_PRECONDITION(aDomFile, "aDomFile is null");
@@ -516,48 +546,48 @@ UploadLastDir::StoreLastUsedDirectory(ns
 
   nsCOMPtr<nsIFile> parentFile;
   localFile->GetParent(getter_AddRefs(parentFile));
   if (!parentFile) {
     return NS_OK;
   }
 
   // Attempt to get the CPS, if it's not present we'll just return
-  nsCOMPtr<nsIContentPrefService> contentPrefService =
+  nsCOMPtr<nsIContentPrefService2> contentPrefService =
     do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
   if (!contentPrefService)
     return NS_ERROR_NOT_AVAILABLE;
-  nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
-  if (!uri)
-    return NS_ERROR_OUT_OF_MEMORY;
-  uri->SetAsISupports(docURI);
+
+  nsAutoCString cstrSpec;
+  docURI->GetSpec(cstrSpec);
+  NS_ConvertUTF8toUTF16 spec(cstrSpec);
 
   // Find the parent of aFile, and store it
   nsString unicodePath;
   parentFile->GetPath(unicodePath);
   if (unicodePath.IsEmpty()) // nothing to do
     return NS_OK;
   nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID);
   if (!prefValue)
     return NS_ERROR_OUT_OF_MEMORY;
   prefValue->SetAsAString(unicodePath);
 
   nsCOMPtr<nsISupports> container = aDoc->GetContainer();
   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(container);
-  return contentPrefService->SetPref(uri, CPS_PREF_NAME, prefValue, loadContext);
+  return contentPrefService->Set(spec, CPS_PREF_NAME, prefValue, loadContext, nullptr);
 }
 
 NS_IMETHODIMP
 UploadLastDir::Observe(nsISupports* aSubject, char const* aTopic, PRUnichar const* aData)
 {
   if (strcmp(aTopic, "browser:purge-session-history") == 0) {
-    nsCOMPtr<nsIContentPrefService> contentPrefService =
+    nsCOMPtr<nsIContentPrefService2> contentPrefService =
       do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
     if (contentPrefService)
-      contentPrefService->RemovePrefsByName(CPS_PREF_NAME, nullptr);
+      contentPrefService->RemoveByName(CPS_PREF_NAME, nullptr, nullptr);
   }
   return NS_OK;
 }
 
 #ifdef ACCESSIBILITY
 //Helper method
 static nsresult FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
                                           nsPresContext* aPresContext,
@@ -2392,17 +2422,17 @@ HTMLInputElement::Focus(ErrorResult& aEr
   nsIFrame* frame = GetPrimaryFrame();
   if (frame) {
     for (nsIFrame* childFrame = frame->GetFirstPrincipalChild();
          childFrame;
          childFrame = childFrame->GetNextSibling()) {
       // See if the child is a button control.
       nsCOMPtr<nsIFormControl> formCtrl =
         do_QueryInterface(childFrame->GetContent());
-      if (formCtrl && formCtrl->GetType() == NS_FORM_INPUT_BUTTON) {
+      if (formCtrl && formCtrl->GetType() == NS_FORM_BUTTON_BUTTON) {
         nsCOMPtr<nsIDOMElement> element = do_QueryInterface(formCtrl);
         nsIFocusManager* fm = nsFocusManager::GetFocusManager();
         if (fm && element) {
           fm->SetFocus(element, 0);
         }
         break;
       }
     }
--- a/content/html/content/src/HTMLInputElement.h
+++ b/content/html/content/src/HTMLInputElement.h
@@ -14,16 +14,17 @@
 #include "nsIDOMNSEditableElement.h"
 #include "nsTextEditorState.h"
 #include "nsCOMPtr.h"
 #include "nsIConstraintValidation.h"
 #include "nsDOMFile.h"
 #include "nsHTMLFormElement.h" // for ShouldShowInvalidUI()
 #include "nsIFile.h"
 #include "nsIFilePicker.h"
+#include "nsIContentPrefService2.h"
 
 class nsDOMFileList;
 class nsIFilePicker;
 class nsIRadioGroupContainer;
 class nsIRadioGroupVisitor;
 class nsIRadioVisitor;
 
 namespace mozilla {
@@ -31,31 +32,53 @@ namespace dom {
 
 class UploadLastDir MOZ_FINAL : public nsIObserver, public nsSupportsWeakReference {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   /**
    * Fetch the last used directory for this location from the content
-   * pref service, if it is available.
+   * pref service, and display the file picker opened in that directory.
    *
-   * @param aDoc  current document
-   * @param aFile path to the last used directory
+   * @param aDoc          current document
+   * @param aFilePicker   the file picker to open
+   * @param aFpCallback   the callback object to be run when the file is shown.
    */
-  nsresult FetchLastUsedDirectory(nsIDocument* aDoc, nsIFile** aFile);
+  nsresult FetchDirectoryAndDisplayPicker(nsIDocument* aDoc,
+                                          nsIFilePicker* aFilePicker,
+                                          nsIFilePickerShownCallback* aFpCallback);
 
   /**
    * Store the last used directory for this location using the
    * content pref service, if it is available
    * @param aURI URI of the current page
    * @param aDomFile file chosen by the user - the path to the parent of this
    *        file will be stored
    */
   nsresult StoreLastUsedDirectory(nsIDocument* aDoc, nsIDOMFile* aDomFile);
+
+  class ContentPrefCallback MOZ_FINAL : public nsIContentPrefCallback2
+  {
+    public:
+    ContentPrefCallback(nsIFilePicker* aFilePicker, nsIFilePickerShownCallback* aFpCallback)
+    : mFilePicker(aFilePicker)
+    , mFpCallback(aFpCallback)
+    { }
+
+    virtual ~ContentPrefCallback()
+    { }
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICONTENTPREFCALLBACK2
+
+    nsCOMPtr<nsIFilePicker> mFilePicker;
+    nsCOMPtr<nsIFilePickerShownCallback> mFpCallback;
+    nsCOMPtr<nsIContentPref> mResult;
+  };
 };
 
 class HTMLInputElement : public nsGenericHTMLFormElement,
                          public nsImageLoadingContent,
                          public nsIDOMHTMLInputElement,
                          public nsITextControlElement,
                          public nsIPhonetic,
                          public nsIDOMNSEditableElement,
--- a/content/svg/content/src/SVGAnimationElement.h
+++ b/content/svg/content/src/SVGAnimationElement.h
@@ -31,16 +31,18 @@ protected:
 
 public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAnimationElement,
                                            SVGAnimationElementBase)
 
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
   // nsIContent specializations
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual void UnbindFromTree(bool aDeep, bool aNullParent);
 
   virtual nsresult UnsetAttr(int32_t aNamespaceID, nsIAtom* aAttribute,
                              bool aNotify);
--- a/content/svg/content/src/SVGGradientElement.h
+++ b/content/svg/content/src/SVGGradientElement.h
@@ -43,16 +43,18 @@ class SVGGradientElement : public SVGGra
   friend class ::nsSVGGradientFrame;
 
 protected:
   SVGGradientElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual JSObject*
   WrapNode(JSContext* aCx, JSObject* aScope) MOZ_OVERRIDE = 0;
 
 public:
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
   // nsIContent
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
   virtual SVGAnimatedTransformList*
     GetAnimatedTransformList(uint32_t aFlags = 0);
   virtual nsIAtom* GetTransformListAttrName() const {
     return nsGkAtoms::gradientTransform;
   }
--- a/content/svg/content/src/SVGTextContentElement.cpp
+++ b/content/svg/content/src/SVGTextContentElement.cpp
@@ -62,16 +62,30 @@ SVGTextContentElement::GetComputedTextLe
     nsSVGTextFrame2* textFrame = GetSVGTextFrame();
     return textFrame ? textFrame->GetComputedTextLength(this) : 0.0f;
   } else {
     nsSVGTextContainerFrame* metrics = GetTextContainerFrame();
     return metrics ? metrics->GetComputedTextLength() : 0.0f;
   }
 }
 
+void
+SVGTextContentElement::SelectSubString(uint32_t charnum, uint32_t nchars, ErrorResult& rv)
+{
+  if (FrameIsSVGText()) {
+    nsSVGTextFrame2* textFrame = GetSVGTextFrame();
+    if (!textFrame)
+      return;
+
+    rv = textFrame->SelectSubString(this, charnum, nchars);
+  } else {
+    rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  }
+}
+
 float
 SVGTextContentElement::GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv)
 {
   if (FrameIsSVGText()) {
     nsSVGTextFrame2* textFrame = GetSVGTextFrame();
     if (!textFrame)
       return 0.0f;
 
--- a/content/svg/content/src/SVGTextContentElement.h
+++ b/content/svg/content/src/SVGTextContentElement.h
@@ -23,16 +23,17 @@ typedef SVGGraphicsElement SVGTextConten
 class SVGTextContentElement : public SVGTextContentElementBase
 {
 public:
   using FragmentOrElement::TextLength;
 
   // WebIDL
   int32_t GetNumberOfChars();
   float GetComputedTextLength();
+  void SelectSubString(uint32_t charnum, uint32_t nchars, ErrorResult& rv);
   float GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv);
   already_AddRefed<nsISVGPoint> GetStartPositionOfChar(uint32_t charnum, ErrorResult& rv);
   already_AddRefed<nsISVGPoint> GetEndPositionOfChar(uint32_t charnum, ErrorResult& rv);
   already_AddRefed<SVGIRect> GetExtentOfChar(uint32_t charnum, ErrorResult& rv);
   float GetRotationOfChar(uint32_t charnum, ErrorResult& rv);
   int32_t GetCharNumAtPosition(nsISVGPoint& point);
 
 protected:
--- a/content/svg/content/src/SVGTransformableElement.h
+++ b/content/svg/content/src/SVGTransformableElement.h
@@ -21,16 +21,18 @@ class SVGIRect;
 
 class SVGTransformableElement : public nsSVGElement
 {
 public:
   SVGTransformableElement(already_AddRefed<nsINodeInfo> aNodeInfo)
     : nsSVGElement(aNodeInfo) {}
   virtual ~SVGTransformableElement() {}
 
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
   // WebIDL
   already_AddRefed<DOMSVGAnimatedTransformList> Transform();
   nsSVGElement* GetNearestViewportElement();
   nsSVGElement* GetFarthestViewportElement();
   already_AddRefed<SVGIRect> GetBBox(ErrorResult& rv);
   already_AddRefed<SVGMatrix> GetCTM();
   already_AddRefed<SVGMatrix> GetScreenCTM();
   already_AddRefed<SVGMatrix> GetTransformToElement(SVGGraphicsElement& aElement,
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -132,16 +132,18 @@ public:
 
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
   // nsSVGElement interface
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
   virtual bool HasValidDimensions() const;
 
   bool IsNodeOfType(uint32_t aFlags) const
     { return !(aFlags & ~(eCONTENT | eFILTER)); }
 
   virtual nsSVGString& GetResultImageName() = 0;
   // Return a list of all image names used as sources. Default is to
   // return no sources.
@@ -234,16 +236,18 @@ typedef nsSVGElement SVGFEUnstyledElemen
 
 class SVGFEUnstyledElement : public SVGFEUnstyledElementBase
 {
 protected:
   SVGFEUnstyledElement(already_AddRefed<nsINodeInfo> aNodeInfo)
     : SVGFEUnstyledElementBase(aNodeInfo) {}
 
 public:
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
   // returns true if changes to the attribute should cause us to
   // repaint the filter
   virtual bool AttributeAffectsRendering(
           int32_t aNameSpaceID, nsIAtom* aAttribute) const = 0;
 };
 
 //------------------------------------------------------------
 
copy from content/svg/content/test/getSubStringLength-helper.svg
copy to content/svg/content/test/selectSubString-helper.svg
copy from content/svg/content/test/test_getSubStringLength.xhtml
copy to content/svg/content/test/test_selectSubString.xhtml
--- a/content/svg/content/test/test_getSubStringLength.xhtml
+++ b/content/svg/content/test/test_selectSubString.xhtml
@@ -1,107 +1,72 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=420243
+https://bugzilla.mozilla.org/show_bug.cgi?id=398825
 -->
 <head>
-  <title>Test for Bug 420243</title>
+  <title>Test for Bug 398825</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=420243">Mozilla Bug 420243</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=398825">Mozilla Bug 398825</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
-<iframe id="svg" src="getSubStringLength-helper.svg"></iframe>
+<iframe id="svg" src="selectSubString-helper.svg"></iframe>
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 SimpleTest.waitForExplicitFinish();
 
-function runTests(text, charWidth)
+function runTests()
 {
-  function chars(n) { return charWidth * n; }
+  var document = $("svg").contentWindow.document;
+  var text = document.getElementById("text");
 
   function expectThrow(charnum, nchars)
   {
     try
     {
-      text.getSubStringLength(charnum, nchars);
+      text.selectSubString(charnum, nchars);
       ok(false,
-         "text.getSubStringLength(" + charnum + "," + nchars + ") " +
-         "should have thrown");
+          "text.selectSubString(" + charnum + "," + nchars + ") " +
+          "should have thrown");
     }
     catch (e)
     {
       is(e.name, "IndexSizeError",
-         "expected an index error for " +
-         "text.getSubStringLength(" + charnum + "," + nchars + ")");
+          "expected an index error for " +
+          "text.selectSubString(" + charnum + "," + nchars + ")");
       is(e.code, DOMException.INDEX_SIZE_ERR,
-         "expected an index error for " +
-         "text.getSubStringLength(" + charnum + "," + nchars + ")");
+          "expected an index error for " +
+          "text.selectSubString(" + charnum + "," + nchars + ")");
     }
   }
   
-  function expectValue(charnum, nchars, expected)
-  {
-    try
-    {
-      is(text.getSubStringLength(charnum, nchars), expected,
-         "text.getSubStringLength(" + charnum + "," + nchars + ") " +
-         "returned wrong value");
-    }
-    catch (e)
-    {
-      ok(false,
-         "unexpected exception for " +
-         "text.getSubStringLength(" + charnum + "," + nchars + ")");
-    }
-  }
-
   expectThrow(-1, 2);
   expectThrow(-1, 0);
   expectThrow(1, 3);
   expectThrow(0, 4);
   expectThrow(3, 0);
 
-  expectValue(0, 0, chars(0));
-  expectValue(1, 0, chars(0));
-  expectValue(2, 0, chars(0));
-  expectValue(0, 1, chars(1));
-  expectValue(1, 1, chars(1));
-  expectValue(2, 1, chars(1));
-  expectValue(0, 2, chars(2));
-  expectValue(1, 2, chars(2));
-  expectValue(0, 3, chars(3));
-
   expectThrow(1, -1);
   expectThrow(2, -1);
   expectThrow(3, -1);
   expectThrow(3, -3);
   expectThrow(-1, -1);
+
+  SimpleTest.finish();
 }
 
-
 function run()
 {
-  try
-  {
-    var document = $("svg").contentWindow.document;
-    var text = document.getElementById("text");
-
-    runTests(text, text.getSubStringLength(0, 1));
-  }
-  catch (e)
-  {
-    ok(false, "threw error: " + e);
-  }
-
-  SimpleTest.finish();
+  SpecialPowers.pushPrefEnv({ set: [['svg.text.css-frames.enabled', true]] },
+                            runTests);
 }
 
 window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
 </html>
--- a/db/sqlite3/src/Makefile.in
+++ b/db/sqlite3/src/Makefile.in
@@ -16,17 +16,16 @@ ifdef MOZ_FOLD_LIBS
 FORCE_STATIC_LIB = 1
 else
 FORCE_SHARED_LIB = 1
 endif
 VISIBILITY_FLAGS =
 LIB_IS_C_ONLY    = 1
 
 ifeq ($(OS_ARCH),WINNT)
-ifndef GNU_CC
 DEFFILE = $(CURDIR)/sqlite-processed.def
 RCFILE  = sqlite.rc
 RESFILE = sqlite.res
 
 GARBAGE += \
   sqlite-version.h \
   $(DEFFILE) \
   $(NULL)
@@ -38,17 +37,16 @@ sqlite-version.h: sqlite-version.py sqli
 # We have to preprocess our def file because we need different symbols in debug
 # builds exposed that are not built in non-debug builds.
 $(DEFFILE): sqlite.def
 	@$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
 	  $(srcdir)/sqlite.def > $(DEFFILE)
 
 export:: sqlite-version.h
 endif
-endif
 
 ifeq (Darwin,$(OS_TARGET))
 # On OSX, with jemalloc enabled, having sqlite linked against mozglue
 # causes crashes in NSS standalone tools.
 MOZ_GLUE_LDFLAGS =
 endif
 
 # XXX Force -O2 optimisation on Mac because using the default -O3 causes
--- a/dom/apps/src/PermissionsTable.jsm
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -119,16 +119,21 @@ this.PermissionsTable =  { geolocation: 
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            power: {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
+                           push: {
+                            app: ALLOW_ACTION,
+                            privileged: ALLOW_ACTION,
+                            certified: ALLOW_ACTION
+                           },
                            settings: {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION,
                              access: ["read", "write"],
                              additional: ["indexedDB-chrome-settings"]
                            },
                            permissions: {
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -303,17 +303,16 @@
 #include "nsIMessageManager.h"
 #include "mozilla/dom/Element.h"
 #include "nsHTMLSelectElement.h"
 #include "HTMLLegendElement.h"
 
 #include "DOMSVGStringList.h"
 
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
-#include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/indexedDB/IDBFileHandle.h"
 #include "mozilla/dom/indexedDB/IDBRequest.h"
 #include "mozilla/dom/indexedDB/IDBDatabase.h"
 #include "mozilla/dom/indexedDB/IDBEvents.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
 #include "mozilla/dom/indexedDB/IDBCursor.h"
 #include "mozilla/dom/indexedDB/IDBKeyRange.h"
@@ -519,34 +518,16 @@ DOMCI_DATA_NO_CLASS(DOMConstructor)
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(_class, _class, _helper, _flags)
 
 #define NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(_class, _helper, _flags)          \
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA_WITH_NAME(_class, _class, _helper, \
                                                  _flags)
 
 namespace {
 
-class IDBFactorySH : public nsDOMGenericSH
-{
-protected:
-  IDBFactorySH(nsDOMClassInfoData* aData) : nsDOMGenericSH(aData)
-  { }
-
-  virtual ~IDBFactorySH()
-  { }
-
-public:
-  NS_IMETHOD PostCreatePrototype(JSContext * cx, JSObject * proto);
-
-  static nsIClassInfo *doCreate(nsDOMClassInfoData *aData)
-  {
-    return new IDBFactorySH(aData);
-  }
-};
-
 class IDBEventTargetSH : public nsEventTargetSH
 {
 protected:
   IDBEventTargetSH(nsDOMClassInfoData* aData) : nsEventTargetSH(aData)
   { }
 
   virtual ~IDBEventTargetSH()
   { }
@@ -665,19 +646,19 @@ static nsDOMClassInfoData sClassInfoData
 #define MOZ_GENERATED_EVENT(_event_interface)             \
   NS_DEFINE_CLASSINFO_DATA(_event_interface, nsEventSH,   \
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENT_LIST
 
   NS_DEFINE_CLASSINFO_DATA(DeviceMotionEvent, nsEventSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(DeviceAcceleration, nsEventSH,
+  NS_DEFINE_CLASSINFO_DATA(DeviceAcceleration, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(DeviceRotationRate, nsEventSH,
+  NS_DEFINE_CLASSINFO_DATA(DeviceRotationRate, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // Misc HTML classes
   NS_DEFINE_CLASSINFO_DATA(HTMLDocument, nsHTMLDocumentSH,
                            DOCUMENT_SCRIPTABLE_FLAGS |
                            nsIXPCScriptable::WANT_GETPROPERTY)
   // HTML element classes
   NS_DEFINE_CLASSINFO_DATA(HTMLAppletElement, nsHTMLPluginObjElementSH,
@@ -1002,18 +983,16 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageSender, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DesktopNotification, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(DesktopNotificationCenter, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
-  NS_DEFINE_CLASSINFO_DATA(IDBFactory, IDBFactorySH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(IDBFileHandle, FileHandle, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBRequest, IDBEventTargetSH,
                            IDBEVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBDatabase, IDBEventTargetSH,
                            IDBEVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBObjectStore, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -2504,20 +2483,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(DesktopNotification, nsIDOMDesktopNotification)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDesktopNotification)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DesktopNotificationCenter, nsIDOMDesktopNotificationCenter)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDesktopNotificationCenter)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(IDBFactory, nsIIDBFactory)
-    DOM_CLASSINFO_MAP_ENTRY(nsIIDBFactory)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(IDBFileHandle, nsIDOMFileHandle)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
     DOM_CLASSINFO_MAP_ENTRY(nsIIDBFileHandle)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(IDBRequest, nsIIDBRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIIDBRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
@@ -6030,102 +6005,16 @@ nsEventSH::AddProperty(nsIXPConnectWrapp
 void
 nsEventSH::PreserveWrapper(nsISupports* aNative)
 {
   nsDOMEvent* event =
     nsDOMEvent::FromSupports(aNative);
   nsContentUtils::PreserveWrapper(aNative, event);
 }
 
-// IDBFactory helper
-
-/* static */
-template<nsresult (*func)(JSContext *, unsigned, jsval *, bool), bool aDelete>
-JSBool
-IDBFNativeShim(JSContext *cx, unsigned argc, jsval *vp)
-{
-  nsresult rv = (*func)(cx, argc, vp, aDelete);
-  if (NS_FAILED(rv)) {
-    xpc::Throw(cx, rv);
-    return false;
-  }
-
-  return true;
-}
-
-/* static */ nsresult
-IDBFOpenForPrincipal(JSContext *cx, unsigned argc, JS::Value *vp, bool aDelete)
-{
-  // Just to be on the extra-safe side
-  if (!nsContentUtils::IsCallerChrome()) {
-    MOZ_NOT_REACHED("Shouldn't be possible to get here");
-    return NS_ERROR_FAILURE;
-  }
-
-  JSObject* principalJS;
-  JSString* nameJS;
-  uint32_t version = 0;
-  if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "oS/u",
-                           &principalJS, &nameJS, &version)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (version < 1 && argc >= 3) {
-    return NS_ERROR_TYPE_ERR;
-  }
-
-  if (aDelete) {
-    version = 0;
-  }
-
-  nsDependentJSString name;
-  NS_ENSURE_TRUE(name.init(cx, nameJS), NS_ERROR_UNEXPECTED);
-
-  nsCOMPtr<nsIPrincipal> principal = do_QueryWrapper(cx, principalJS);
-  NS_ENSURE_TRUE(principal, NS_ERROR_NO_INTERFACE);
-
-  nsCString extendedOrigin;
-  nsresult rv = principal->GetExtendedOrigin(extendedOrigin);
-  NS_ENSURE_FALSE(extendedOrigin.IsEmpty(), NS_ERROR_UNEXPECTED);
-
-  nsCOMPtr<nsIIDBFactory> factory =
-    do_QueryWrapper(cx, JS_THIS_OBJECT(cx, vp));
-  NS_ENSURE_TRUE(factory, NS_ERROR_NO_INTERFACE);
-
-  nsRefPtr<indexedDB::IDBOpenDBRequest> request;
-  rv = static_cast<indexedDB::IDBFactory*>(factory.get())->
-    OpenCommon(name, version, extendedOrigin, aDelete, cx,
-               getter_AddRefs(request));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return WrapNative(cx, JS_GetGlobalForScopeChain(cx),
-                    static_cast<nsIIDBOpenDBRequest*>(request),
-                    &NS_GET_IID(nsIIDBOpenDBRequest), true, vp);
-}
-
-NS_IMETHODIMP
-IDBFactorySH::PostCreatePrototype(JSContext * cx, JSObject * proto)
-{
-  // set up our proto first
-  nsresult rv = nsDOMGenericSH::PostCreatePrototype(cx, proto);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (xpc::AccessCheck::isChrome(js::GetObjectCompartment(proto)) &&
-      (!JS_DefineFunction(cx, proto, "openForPrincipal",
-                          IDBFNativeShim<IDBFOpenForPrincipal, false>,
-                          3, JSPROP_ENUMERATE) ||
-       !JS_DefineFunction(cx, proto, "deleteForPrincipal",
-                          IDBFNativeShim<IDBFOpenForPrincipal, true>,
-                          2, JSPROP_ENUMERATE))) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  return rv;
-}
-
 // IDBEventTarget helper
 
 NS_IMETHODIMP
 IDBEventTargetSH::PreCreate(nsISupports *aNativeObj, JSContext *aCx,
                             JSObject *aGlobalObj, JSObject **aParentObj)
 {
   IDBWrapperCache *target = IDBWrapperCache::FromSupports(aNativeObj);
   JSObject *parent = target->GetParentObject();
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -231,17 +231,16 @@ DOMCI_CLASS(AnimationEvent)
 
 DOMCI_CLASS(ContentFrameMessageManager)
 DOMCI_CLASS(ChromeMessageBroadcaster)
 DOMCI_CLASS(ChromeMessageSender)
 
 DOMCI_CLASS(DesktopNotification)
 DOMCI_CLASS(DesktopNotificationCenter)
 
-DOMCI_CLASS(IDBFactory)
 DOMCI_CLASS(IDBFileHandle)
 DOMCI_CLASS(IDBRequest)
 DOMCI_CLASS(IDBDatabase)
 DOMCI_CLASS(IDBObjectStore)
 DOMCI_CLASS(IDBTransaction)
 DOMCI_CLASS(IDBCursor)
 DOMCI_CLASS(IDBCursorWithValue)
 DOMCI_CLASS(IDBKeyRange)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8839,17 +8839,17 @@ nsGlobalWindow::GetLocalStorage(nsIDOMSt
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIDOMStorageIndexedDB
 //*****************************************************************************
 
 NS_IMETHODIMP
-nsGlobalWindow::GetIndexedDB(nsIIDBFactory** _retval)
+nsGlobalWindow::GetIndexedDB(nsISupports** _retval)
 {
   if (!mIndexedDB) {
     nsresult rv;
 
     // If the document has the sandboxed origin flag set
     // don't allow access to indexedDB.
     if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
       return NS_ERROR_DOM_SECURITY_ERR;
@@ -8872,23 +8872,23 @@ nsGlobalWindow::GetIndexedDB(nsIIDBFacto
     }
 
     // This may be null if being created from a file.
     rv = indexedDB::IDBFactory::Create(this, nullptr,
                                        getter_AddRefs(mIndexedDB));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<nsIIDBFactory> request(mIndexedDB);
+  nsCOMPtr<nsISupports> request(mIndexedDB);
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsGlobalWindow::GetMozIndexedDB(nsIIDBFactory** _retval)
+nsGlobalWindow::GetMozIndexedDB(nsISupports** _retval)
 {
   return GetIndexedDB(_retval);
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIInterfaceRequestor
 //*****************************************************************************
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -46,17 +46,16 @@
 #include "nsRect.h"
 #include "mozFlushType.h"
 #include "prclist.h"
 #include "nsIDOMStorageObsolete.h"
 #include "nsIDOMStorageEvent.h"
 #include "nsIDOMStorageIndexedDB.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIArray.h"
-#include "nsIIDBFactory.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsIInlineEventHandlers.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsIIdleObserver.h"
 #include "nsIDOMWakeLock.h"
@@ -115,16 +114,19 @@ class nsDOMWindowUtils;
 class nsIIdleService;
 
 class nsWindowSizes;
 
 namespace mozilla {
 namespace dom {
 class Navigator;
 class URL;
+namespace indexedDB {
+class IDBFactory;
+} // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
 
 extern nsresult
 NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
                           bool *aIsInterval,
                           int32_t *aInterval,
                           nsIScriptTimeoutHandler **aRet);
@@ -1143,17 +1145,17 @@ protected:
   bool mCleanedUp, mCallCleanUpAfterModalDialogCloses;
 
   nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
 
   nsDataHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers;
 
   nsCOMPtr<nsIDocument> mSuspendedDoc;
 
-  nsCOMPtr<nsIIDBFactory> mIndexedDB;
+  nsRefPtr<mozilla::dom::indexedDB::IDBFactory> mIndexedDB;
 
   // This counts the number of windows that have been opened in rapid succession
   // (i.e. within dom.successive_dialog_time_limit of each other). It is reset
   // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
   // have elapsed without any other dialogs.
   uint32_t                      mDialogAbuseCount;
 
   // This holds the time when the last modal dialog was shown. If more than
--- a/dom/base/nsIDOMDOMRequest.idl
+++ b/dom/base/nsIDOMDOMRequest.idl
@@ -6,17 +6,17 @@
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMDOMError;
 interface nsIDOMWindow;
 interface nsIDOMDOMCursor;
 interface nsICursorContinueCallback;
 
-[scriptable, builtinclass, uuid(d88998b7-ee30-4ae5-bbed-58f5711929de)]
+[scriptable, builtinclass, uuid(e18fdde5-35b0-46df-8522-f88adf7698f3)]
 interface nsIDOMDOMRequest : nsIDOMEventTarget
 {
   readonly attribute DOMString readyState; // "pending" or "done"
 
   readonly attribute jsval result;
   readonly attribute nsIDOMDOMError error;
 
   [implicit_jscontext] attribute jsval onsuccess;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -488,16 +488,22 @@ DOMInterfaces = {
         'sheet'
     ]
 },
 
 'HTMLUListElement': {
     'nativeType' : 'mozilla::dom::HTMLSharedListElement'
 },
 
+'IDBFactory': {
+    'nativeType': 'mozilla::dom::indexedDB::IDBFactory',
+    'implicitJSContext': [ 'open', 'deleteDatabase', 'openForPrincipal',
+                           'deleteForPrincipal' ],
+},
+
 'IDBVersionChangeEvent': {
     'nativeType': 'mozilla::dom::indexedDB::IDBVersionChangeEvent',
     'headerFile': 'IDBEvents.h',
 },
 
 'IID': [
 {
     'nativeType': 'nsIJSID',
@@ -1384,16 +1390,17 @@ addExternalIface('CanvasPattern', header
 addExternalIface('Counter')
 addExternalIface('CSSRule')
 addExternalIface('DOMError')
 addExternalIface('CSSRuleList')
 addExternalIface('DOMStringList')
 addExternalIface('File')
 addExternalIface('HitRegionOptions', nativeType='nsISupports')
 addExternalIface('HTMLCanvasElement', nativeType='mozilla::dom::HTMLCanvasElement')
+addExternalIface('IDBOpenDBRequest', nativeType='nsIIDBOpenDBRequest')
 addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
 addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
 addExternalIface('LockedFile')
 addExternalIface('MediaList')
 addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
 addExternalIface('MozBoxObject', nativeType='nsIBoxObject')
 addExternalIface('MozControllers', nativeType='nsIControllers')
 addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -31,8 +31,9 @@ MSG_DEF(MSG_ENFORCE_RANGE_NON_FINITE, 1,
 MSG_DEF(MSG_ENFORCE_RANGE_OUT_OF_RANGE, 1, "Value is out of range for {0}.")
 MSG_DEF(MSG_NOT_SEQUENCE, 0, "object can not be converted to a sequence")
 MSG_DEF(MSG_NOT_DICTIONARY, 0, "value can not be converted to a dictionary")
 MSG_DEF(MSG_INVALID_ARG, 2, "argument {0} is not valid for any of the {1}-argument overloads")
 MSG_DEF(MSG_GLOBAL_NOT_NATIVE, 0, "global is not a native object")
 MSG_DEF(MSG_ENCODING_NOT_SUPPORTED, 1, "The given encoding '{0}' is not supported.")
 MSG_DEF(MSG_DOM_ENCODING_NOT_UTF, 0, "The encoding must be utf-8, utf-16, or utf-16be.")
 MSG_DEF(MSG_NOT_FINITE, 0, "Floating-point value is not finite.")
+MSG_DEF(MSG_INVALID_VERSION, 0, "0 (Zero) is not a valid database version.")
--- a/dom/bluetooth/nsIDOMBluetoothAdapter.idl
+++ b/dom/bluetooth/nsIDOMBluetoothAdapter.idl
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMDOMRequest;
 interface nsIDOMBlob;
 interface nsIDOMBluetoothDevice;
 
-[scriptable, builtinclass, uuid(b0e7117a-25e3-4ea0-9cf4-3f8cd85f9e7a)]
+[scriptable, builtinclass, uuid(4321647b-0d45-4231-920b-8d238b6d1700)]
 interface nsIDOMBluetoothAdapter : nsIDOMEventTarget
 {
   readonly attribute DOMString address;
   [binaryname(AdapterClass)] readonly attribute unsigned long class;
   readonly attribute bool discovering;
 
   [implicit_jscontext]
   readonly attribute jsval devices;
--- a/dom/bluetooth/nsIDOMBluetoothDevice.idl
+++ b/dom/bluetooth/nsIDOMBluetoothDevice.idl
@@ -1,17 +1,17 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* 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 "nsIDOMEventTarget.idl"
 
-[scriptable, builtinclass, uuid(4ca7697a-9d0c-492e-a6be-ccc5c3ec503c)]
+[scriptable, builtinclass, uuid(7297ef65-db38-45f1-a5dc-b7347aaa223d)]
 interface nsIDOMBluetoothDevice : nsIDOMEventTarget
 {
   readonly attribute DOMString address;
   readonly attribute DOMString name;
   readonly attribute DOMString icon;
   [binaryname(DeviceClass)] readonly attribute unsigned long class;
   [implicit_jscontext] readonly attribute jsval uuids;
   [implicit_jscontext] readonly attribute jsval services;
--- a/dom/bluetooth/nsIDOMBluetoothManager.idl
+++ b/dom/bluetooth/nsIDOMBluetoothManager.idl
@@ -4,17 +4,17 @@
  * 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 "nsIDOMEventTarget.idl"
 
 interface nsIDOMDOMRequest;
 interface nsIDOMBluetoothAdapter;
 
-[scriptable, builtinclass, uuid(3300693f-ae91-4a3f-b887-bf502c6a97ee)]
+[scriptable, builtinclass, uuid(303a262a-1dd1-486d-a108-d8c582e86765)]
 interface nsIDOMBluetoothManager : nsIDOMEventTarget
 {
   readonly attribute bool enabled;
 
   nsIDOMDOMRequest getDefaultAdapter();
 
   bool isConnected(in unsigned short aProfile);
 
--- a/dom/browser-element/mochitest/browserElement_KeyEvents.js
+++ b/dom/browser-element/mochitest/browserElement_KeyEvents.js
@@ -2,84 +2,96 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that an iframe with the |mozbrowser| attribute does bubble some
 // whitelisted key events.
 "use strict";
 
 let Ci = SpecialPowers.Ci;
 
-let whitelistedEvents = [
+let whitelistedKeyCodes = [
   Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE,   // Back button.
   Ci.nsIDOMKeyEvent.DOM_VK_SLEEP,    // Power button
   Ci.nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU,
   Ci.nsIDOMKeyEvent.DOM_VK_F5,       // Search button.
   Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP,  // Volume up.
   Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN // Volume down.
 ];
 
+let blacklistedKeyCodes = [
+  Ci.nsIDOMKeyEvent.DOM_VK_A,
+  Ci.nsIDOMKeyEvent.DOM_VK_B
+];
+
 SimpleTest.waitForExplicitFinish();
-
 browserElementTestHelpers.setEnabledPref(true);
 browserElementTestHelpers.addPermission();
 
 // Number of expected events at which point we will consider the test as done.
-var nbEvents = whitelistedEvents.length * 3;
+var nbEvents = whitelistedKeyCodes.length * 3;
 
 var iframe;
 var finished = false;
 function runTest() {
   iframe = document.createElement('iframe');
   SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.src = browserElementTestHelpers.focusPage;
   document.body.appendChild(iframe);
 
   SimpleTest.waitForFocus(function() {
     iframe.focus();
     SimpleTest.executeSoon(test2);
   });
 }
 
 function eventHandler(e) {
-  ok(((e.type == 'keydown' || e.type == 'keypress' || e.type == 'keyup') &&
-      !e.defaultPrevented &&
-      (whitelistedEvents.indexOf(e.keyCode) != -1)),
-      "[ " + e.type + "] Handled event should be a non prevented key event in the white list.");
+  if (whitelistedKeyCodes.indexOf(e.keyCode) == -1 &&
+      blacklistedKeyCodes.indexOf(e.keyCode) == -1) {
+    // See bug 856006: We sometimes get unexpected key presses, and we don't
+    // know why.  Don't turn the test orange over this.
+    ok(true, "Ignoring unexpected " + e.type +
+       " with keyCode " + e.keyCode + ".");
+    return;
+  }
+
+  ok(e.type == 'keydown' || e.type == 'keypress' || e.type == 'keyup',
+     "e.type was " + e.type + ", expected keydown, keypress, or keyup");
+  ok(!e.defaultPrevented, "expected !e.defaultPrevented");
+  ok(whitelistedKeyCodes.indexOf(e.keyCode) != -1,
+     "Expected a whitelited keycode, but got " + e.keyCode + "instead.");
 
   nbEvents--;
 
   if (nbEvents == 0) {
     SimpleTest.finish();
     return;
   }
 
   if (nbEvents < 0 && !finished) {
     ok(false, "got an unexpected event! " + e.type + " " + e.keyCode);
-    SimpleTest.finish();
-    return;
   }
 }
 
 function test2() {
   is(document.activeElement, iframe, "iframe should be focused");
 
   addEventListener('keydown', eventHandler);
   addEventListener('keypress', eventHandler);
   addEventListener('keyup', eventHandler);
 
-  // Those event should not be received because not whitelisted.
+  // These events should not be received because they are not whitelisted.
   synthesizeKey("VK_A", {});
   synthesizeKey("VK_B", {});
 
-  // Those events should not be received because prevent default is called.
+  // These events should not be received because preventDefault is called.
   synthesizeKey("VK_ESCAPE", {});
 
-  // Those events should be received.
-  synthesizeKey("VK_F5", {}); // F5 key is going to be canceled by ESC key.
+  // These events should be received.
+  synthesizeKey("VK_F5", {});
   synthesizeKey("VK_ESCAPE", {});
-  synthesizeKey("VK_PAGE_UP", {});   // keypress is ignored because .preventDefault() will be called.
-  synthesizeKey("VK_PAGE_DOWN", {}); // keypress is ignored because .preventDefault() will be called.
+  synthesizeKey("VK_PAGE_UP", {});
+  synthesizeKey("VK_PAGE_DOWN", {});
   synthesizeKey("VK_CONTEXT_MENU", {});
   synthesizeKey("VK_SLEEP", {});
   finished = true;
 }
 
 addEventListener('testready', runTest);
--- a/dom/cellbroadcast/interfaces/nsIDOMMozCellBroadcast.idl
+++ b/dom/cellbroadcast/interfaces/nsIDOMMozCellBroadcast.idl
@@ -4,14 +4,14 @@
 
 #include "nsIDOMEventTarget.idl"
 
 /**
  * Cell Broadcast short message service (CBS) permits a number of
  * unacknowledged general CBS messages to be broadcast to all receivers within
  * a particular region.
  */
-[scriptable, builtinclass, uuid(06bf2607-cd01-4307-9063-b6eac13b4613)]
+[scriptable, builtinclass, uuid(113bd5ff-8981-4e55-8c20-ea68fd722a48)]
 interface nsIDOMMozCellBroadcast : nsIDOMEventTarget
 {
   [implicit_jscontext] attribute jsval onreceived;
 };
 
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -6,17 +6,17 @@
 
 const DEBUG = false;
 function debug(s) { dump("-*- Fallback ContactService component: " + s + "\n"); }
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
-this.EXPORTED_SYMBOLS = ["DOMContactManager"];
+this.EXPORTED_SYMBOLS = [];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/ContactDB.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
@@ -31,17 +31,17 @@ XPCOMUtils.defineLazyGetter(this, "mRIL"
       }
     };
   }
   return telephony.getService(Ci.nsIRadioInterfaceLayer);
 });
 
 let myGlobal = this;
 
-this.DOMContactManager = {
+let ContactService = {
   init: function() {
     if (DEBUG) debug("Init");
     this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:Clear", "Contact:Save",
                       "Contact:Remove", "Contacts:GetSimContacts",
                       "Contacts:RegisterForMessages", "child-process-shutdown"];
     this._children = [];
     this._messages.forEach(function(msgName) {
       ppmm.addMessageListener(msgName, this);
@@ -197,9 +197,9 @@ this.DOMContactManager = {
         }
         break;
       default:
         if (DEBUG) debug("WRONG MESSAGE NAME: " + aMessage.name);
     }
   }
 }
 
-DOMContactManager.init();
+ContactService.init();
--- a/dom/contacts/tests/test_contacts_basics.html
+++ b/dom/contacts/tests/test_contacts_basics.html
@@ -242,17 +242,17 @@ var steps = [
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function () {
-      ok(req.result.length == 0, "Empty database is empty.");
+      is(req.result.length, 0, "Empty database.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Adding empty contact");
     createResult1 = new mozContact();
     createResult1.init({});
@@ -263,29 +263,29 @@ var steps = [
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function () {
-      ok(req.result.length == 1, "One contact.");
+      is(req.result.length, 1, "One contact.");
       findResult1 = req.result[0];
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Deleting empty contact");
     req = navigator.mozContacts.remove(findResult1);
     req.onsuccess = function () {
       var req2 = mozContacts.find({});
       req2.onsuccess = function () {
-        ok(req2.result.length == 0, "Empty Database.");
+        is(req2.result.length, 0, "Empty Database.");
         clearTemps();
         next();
       }
       req2.onerror = onFailure;
     }
     req.onerror = onFailure;
   },
   function () {
@@ -309,17 +309,17 @@ var steps = [
   },
   function () {
     ok(true, "Retrieving by substring");
     var options = {filterBy: ["givenName"],
                    filterOp: "contains",
                    filterValue: properties1.givenName[1].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       dump("findResult: " + JSON.stringify(findResult1) + "\n");
       // Some manual testing. Testint the testfunctions
       // tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+9-876-5432"}],
       is(findResult1.tel[0].carrier, "testCarrier", "Same Carrier");
       is(findResult1.tel[0].type, "work", "Same type");
@@ -338,17 +338,17 @@ var steps = [
   },
   function () {
     ok(true, "Searching for exact email");
     var options = {filterBy: ["email"],
                    filterOp: "equals",
                    filterValue: properties1.email[0].value};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(findResult1, createResult1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
@@ -357,17 +357,17 @@ var steps = [
        is(event.contactID, findResult1.id, "Same contactID");
        is(event.reason, "update", "Same reason");
      }
     var options = {filterBy: ["givenName"],
                    filterOp: "contains",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       findResult1.jobTitle = ["new Job"];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
@@ -388,17 +388,17 @@ var steps = [
   },
   function () {
     ok(true, "Retrieving by substring");
     var options = {filterBy: ["givenName"],
                    filterOp: "contains",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       checkContacts(createResult1, findResult1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Remove contact1");
@@ -414,17 +414,17 @@ var steps = [
   },
   function () {
     ok(true, "Retrieving by substring");
     var options = {filterBy: ["givenName"],
                    filterOp: "contains",
                    filterValue: properties1.givenName[1].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 0, "Found no contact.");
+      is(req.result.length, 0, "Found no contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Remove contact2");
     mozContacts.oncontactchange = function(event) {
       is(event.contactID, createResult2.id, "Same contactID");
@@ -438,17 +438,17 @@ var steps = [
   },
   function () {
     ok(true, "Retrieving by substring");
     var options = {filterBy: ["givenName"],
                    filterOp: "contains",
                    filterValue: properties1.givenName[1].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 0, "Found no contact.");
+      is(req.result.length, 0, "Found no contact.");
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Deleting database");
     mozContacts.oncontactchange = function(event) {
       is(event.contactID, "undefined", "Same contactID");
@@ -477,125 +477,125 @@ var steps = [
   },
   function () {
     ok(true, "Retrieving by substring tel1");
     var options = {filterBy: ["tel"],
                    filterOp: "contains",
                    filterValue: properties1.tel[1].value.substring(0,5)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring tel2");
     var options = {filterBy: ["tel"],
                    filterOp: "contains",
                    filterValue: "9876"};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring tel3");
     var options = {filterBy: ["tel"],
                    filterOp: "contains",
                    filterValue: "98765432"};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring2");
     var options = {filterBy: ["givenName"],
                    filterOp: "contains",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring3");
     var options = {filterBy: ["familyName", "givenName"],
                    filterOp: "contains",
                    filterValue: properties1.givenName[0].substring(0,3)};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring3, Testing multi entry");
     var options = {filterBy: ["givenName", "familyName"],
                    filterOp: "contains",
                    filterValue: properties1.familyName[1].substring(0,3).toLowerCase()};
     req = mozContacts.find(options);
     req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving all contacts");
     req = mozContacts.find({});
     req.onsuccess = function() {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
+      is(req.result.length, 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, findResult1);
       ok(findResult1.updated, "Has updated field");
       ok(findResult1.published, "Has published field");
       next();
     }
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Modifying contact1");
     findResult1.impp = properties1.impp = [{value:"phil impp"}];
     req = navigator.mozContacts.save(findResult1);
     req.onsuccess = function () {
       var req2 = mozContacts.find({});
       req2.onsuccess = function() {
-        ok(req2.result.length == 1, "Found exactly 1 contact.");
+        is(req2.result.length, 1, "Found exactly 1 contact.");
         findResult2 = req2.result[0];