Merge inbound to m-c. FIREFOX_AURORA_22_BASE
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 01 Apr 2013 13:36:59 -0400
changeset 126855 1c070ab0f9db59f13423b9c1db60419f7a9098f9
parent 126786 7bbe0b582f70fdf90afa8b5785d91baa0cda7e71 (current diff)
parent 126854 77578bde1c1ca318c4969b061330ebb6d1cd1180 (diff)
child 126856 beb8f161da3aedc6c29dbac017261beb5b9e36d3
push id24495
push userryanvm@gmail.com
push dateMon, 01 Apr 2013 17:37:08 +0000
treeherdermozilla-central@1c070ab0f9db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone22.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 inbound to m-c.
browser/app/profile/firefox.js
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
--- 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
@@ -776,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.
 */
@@ -968,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)
 {
--- 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/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/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.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/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_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/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/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/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/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(bb47d50e-48ab-464d-b665-5ea47382e8d6)]
+[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/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/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/dom/indexedDB/CheckPermissionsHelper.cpp
+++ b/dom/indexedDB/CheckPermissionsHelper.cpp
@@ -10,16 +10,17 @@
 #include "nsILoadContext.h"
 #include "nsIWebNavigation.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 
+#include "CheckQuotaHelper.h"
 #include "nsContentUtils.h"
 #include "nsDOMStorage.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 
@@ -33,19 +34,20 @@
 // This is a little confusing, but our default behavior (UNKNOWN_ACTION) is to
 // allow access without a prompt. If the "indexedDB" permission is set to
 // ALLOW_ACTION then we will issue a prompt before allowing access. Otherwise
 // (DENY_ACTION) we deny access.
 #define PERMISSION_ALLOWED nsIPermissionManager::UNKNOWN_ACTION
 #define PERMISSION_DENIED nsIPermissionManager::DENY_ACTION
 #define PERMISSION_PROMPT nsIPermissionManager::ALLOW_ACTION
 
-using namespace mozilla;
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::services;
+using mozilla::dom::quota::CheckQuotaHelper;
+using mozilla::Preferences;
 
 namespace {
 
 inline
 uint32_t
 GetIndexedDBPermissions(nsIDOMWindow* aWindow)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -106,24 +108,29 @@ CheckPermissionsHelper::Run()
   nsresult rv;
   if (mHasPrompted) {
     // Add permissions to the database, but only if we are in the parent
     // process (if we are in the child process, we have already
     // set the permission when the prompt was shown in the parent, as
     // we cannot set the permission from the child).
     if (permission != PERMISSION_PROMPT &&
         IndexedDatabaseManager::IsMainProcess()) {
+      NS_ASSERTION(mWindow, "Null window!");
+
+      nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
+      NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
+
+      nsIPrincipal* windowPrincipal = sop->GetPrincipal();
+      NS_ASSERTION(windowPrincipal, "Null principal!");
+
       nsCOMPtr<nsIPermissionManager> permissionManager =
         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
       NS_ENSURE_STATE(permissionManager);
 
-      nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
-      NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
-
-      rv = permissionManager->AddFromPrincipal(sop->GetPrincipal(),
+      rv = permissionManager->AddFromPrincipal(windowPrincipal,
                                                PERMISSION_INDEXEDDB, permission,
                                                nsIPermissionManager::EXPIRE_NEVER,
                                                0);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
   else if (permission == PERMISSION_PROMPT && mPromptAllowed) {
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
@@ -136,16 +143,34 @@ CheckPermissionsHelper::Run()
 
   nsRefPtr<OpenDatabaseHelper> helper;
   helper.swap(mHelper);
 
   nsCOMPtr<nsIDOMWindow> window;
   window.swap(mWindow);
 
   if (permission == PERMISSION_ALLOWED) {
+    // If we're running from a window then we should check the quota permission
+    // as well. If we don't have a window then we're opening a chrome database
+    // and the quota will be unlimited already.
+    if (window) {
+      nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
+      NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
+
+      nsIPrincipal* windowPrincipal = sop->GetPrincipal();
+      NS_ASSERTION(windowPrincipal, "Null principal!");
+
+      uint32_t quotaPermission =
+        CheckQuotaHelper::GetQuotaPermission(windowPrincipal);
+
+      if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) {
+        helper->SetUnlimitedQuotaAllowed();
+      }
+    }
+
     quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
     NS_ASSERTION(quotaManager, "This should never be null!");
 
     return helper->Dispatch(quotaManager->IOThread());
   }
 
   NS_ASSERTION(permission == PERMISSION_PROMPT ||
                permission == PERMISSION_DENIED,
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2686,32 +2686,35 @@ inline nsresult
 CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
 {
   nsresult rv;
 
   do {
     char copyBuffer[FILE_COPY_BUFFER_SIZE];
 
     uint32_t numRead;
-    rv = aInputStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (numRead <= 0) {
+    rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+    if (!numRead) {
       break;
     }
 
     uint32_t numWrite;
     rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+    if (numWrite < numRead) {
+      // Must have hit the quota limit.
+      return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+    }
   } while (true);
 
   rv = aOutputStream->Flush();
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 void
 ObjectStoreHelper::ReleaseMainThreadObjects()
 {
   mObjectStore = nullptr;
@@ -2909,17 +2912,17 @@ AddHelper::DoDatabaseWork(mozIStorageCon
         nativeFile = fileManager->GetFileForId(directory, id);
         NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
         nsRefPtr<FileOutputStream> outputStream = FileOutputStream::Create(
           mObjectStore->Transaction()->Database()->Origin(), nativeFile);
         NS_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
         rv = CopyData(inputStream, outputStream);
-        NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+        NS_ENSURE_SUCCESS(rv, rv);
 
         cloneFile.mFile->AddFileInfo(fileInfo);
       }
 
       if (index) {
         fileIds.Append(NS_LITERAL_STRING(" "));
       }
       fileIds.AppendInt(id);
--- a/dom/indexedDB/Makefile.in
+++ b/dom/indexedDB/Makefile.in
@@ -67,16 +67,17 @@ EXPORTS_mozilla/dom/indexedDB = \
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/caps/include \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/content/events/src \
   -I$(topsrcdir)/db/sqlite3/src \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/src/storage \
+  -I$(topsrcdir)/dom/quota \
   -I$(topsrcdir)/xpcom/build \
   $(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
 
 # Make sure to quickstub as much as possible here! See
 # js/xpconnect/src/dom_quickstubs.qsconf.
 include $(topsrcdir)/config/config.mk
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -1646,17 +1646,17 @@ OpenDatabaseHelper::DoDatabaseWork()
 
   nsCOMPtr<nsIFile> dbDirectory;
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never be null!");
 
   nsresult rv =
     quotaManager->EnsureOriginIsInitialized(mASCIIOrigin,
-                                            mPrivilege,
+                                            mTrackingQuota,
                                             getter_AddRefs(dbDirectory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   bool exists;
   rv = dbDirectory->Exists(&exists);
--- a/dom/indexedDB/OpenDatabaseHelper.h
+++ b/dom/indexedDB/OpenDatabaseHelper.h
@@ -1,50 +1,58 @@
 /* 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 mozilla_dom_indexeddb_opendatabasehelper_h__
 #define mozilla_dom_indexeddb_opendatabasehelper_h__
 
 #include "AsyncConnectionHelper.h"
+
+#include "nsIRunnable.h"
+
+#include "mozilla/dom/quota/StoragePrivilege.h"
+
 #include "DatabaseInfo.h"
 #include "IDBDatabase.h"
 #include "IDBRequest.h"
 
-#include "nsIRunnable.h"
-
 class mozIStorageConnection;
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
 }
 }
 
 BEGIN_INDEXEDDB_NAMESPACE
 
+class CheckPermissionsHelper;
+
 class OpenDatabaseHelper : public HelperBase
 {
+  friend class CheckPermissionsHelper;
+
   typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
 
 public:
   OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
                      const nsAString& aName,
                      const nsACString& aASCIIOrigin,
                      uint64_t aRequestedVersion,
                      bool aForDeletion,
                      mozilla::dom::ContentParent* aContentParent,
                      StoragePrivilege aPrivilege)
     : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
       mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
       mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
       mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
       mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
-      mLoadDBMetadata(false)
+      mLoadDBMetadata(false),
+      mTrackingQuota(aPrivilege != mozilla::dom::quota::Chrome)
   {
     NS_ASSERTION(!aForDeletion || !aRequestedVersion,
                  "Can't be for deletion and request a version!");
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
@@ -97,16 +105,22 @@ protected:
   nsresult StartSetVersion();
   nsresult StartDelete();
   virtual nsresult GetSuccessResult(JSContext* aCx,
                                     jsval* aVal) MOZ_OVERRIDE;
   void DispatchSuccessEvent();
   void DispatchErrorEvent();
   virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
 
+  // Called by CheckPermissionsHelper on the main thread before dispatch.
+  void SetUnlimitedQuotaAllowed()
+  {
+    mTrackingQuota = false;
+  }
+
   // Methods only called on the DB thread
   nsresult DoDatabaseWork();
 
   // In-params.
   nsRefPtr<IDBOpenDBRequest> mOpenDBRequest;
   nsString mName;
   nsCString mASCIIOrigin;
   uint64_t mRequestedVersion;
@@ -135,13 +149,14 @@ protected:
   };
   OpenDatabaseState mState;
   nsresult mResultCode;
 
   nsRefPtr<FileManager> mFileManager;
 
   nsRefPtr<DatabaseInfo> mDBInfo;
   bool mLoadDBMetadata;
+  bool mTrackingQuota;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_opendatabasehelper_h__
\ No newline at end of file
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1267,16 +1267,17 @@ MediaManager::Observe(nsISupports* aSubj
       prefs->RemoveObserver("media.navigator.video.default_minfps", this);
     }
 
     // Close off any remaining active windows.
     {
       MutexAutoLock lock(mMutex);
       GetActiveWindows()->Clear();
       mActiveCallbacks.Clear();
+      LOG(("Releasing MediaManager singleton and thread"));
       sSingleton = nullptr;
     }
 
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
     nsRefPtr<nsRunnable> runnable;
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -677,38 +677,45 @@ PeerConnection.prototype = {
         state = "closed";
         break;
     }
     return state;
   },
 
   createDataChannel: function(label, dict) {
     this._checkClosed();
-    if (dict &&
-        dict.maxRetransmitTime != undefined &&
+    if (dict == undefined) {
+	dict = {};
+    }
+    if (dict.maxRetransmitTime != undefined &&
         dict.maxRetransmitNum != undefined) {
       throw new Components.Exception("Both maxRetransmitTime and maxRetransmitNum cannot be provided");
     }
+    let protocol;
+    if (dict.protocol == undefined) {
+      protocol = "";
+    } else {
+      protocol = dict.protocol;
+    }
 
     // Must determine the type where we still know if entries are undefined.
     let type;
     if (dict.maxRetransmitTime != undefined) {
       type = Ci.IPeerConnection.kDataChannelPartialReliableTimed;
     } else if (dict.maxRetransmitNum != undefined) {
       type = Ci.IPeerConnection.kDataChannelPartialReliableRexmit;
     } else {
       type = Ci.IPeerConnection.kDataChannelReliable;
     }
 
     // Synchronous since it doesn't block.
-    // TODO -- this may need to be revisited, based on how the
-    // spec ends up defining data channel handling
     let channel = this._pc.createDataChannel(
-      label, type, dict.outOfOrderAllowed, dict.maxRetransmitTime,
-      dict.maxRetransmitNum
+      label, protocol, type, dict.outOfOrderAllowed, dict.maxRetransmitTime,
+      dict.maxRetransmitNum, dict.preset ? true : false,
+      dict.stream != undefined ? dict.stream : 0xFFFF
     );
     return channel;
   },
 
   connectDataConnection: function(localport, remoteport, numstreams) {
     if (numstreams == undefined || numstreams <= 0) {
       numstreams = 16;
     }
@@ -924,17 +931,20 @@ PeerConnectionObserver.prototype = {
         });
       } catch(e) {}
     }
   },
 
   notifyDataChannel: function(channel) {
     if (this._dompc.ondatachannel) {
       try {
-        this._dompc.ondatachannel.onCallback(channel);
+        this._dompc.ondatachannel.onCallback({
+          channel: channel,
+          __exposedProps__: { channel: "r" }
+        });
       } catch(e) {}
     }
   },
 
   notifyConnection: function() {
     if (this._dompc.onconnection) {
       try {
         this._dompc.onconnection.onCallback();
--- a/dom/media/bridge/IPeerConnection.idl
+++ b/dom/media/bridge/IPeerConnection.idl
@@ -62,17 +62,17 @@ interface IPeerConnectionObserver : nsIS
   /* When SDP is parsed and a candidate line is found this method is called.
    * It should hook back into the media transport to notify it of ICE candidates
    * listed in the SDP PeerConnectionImpl does not parse ICE candidates, just
    * pulls them out of the SDP.
    */
   void foundIceCandidate(in string candidate);
 };
 
-[scriptable, uuid(2bba7b2b-e152-4ae7-b7d4-f84e41a2211b)]
+[scriptable, uuid(121ff773-949b-48b9-83b2-9a4ef908833c)]
 interface IPeerConnection : nsISupports
 {
   const unsigned long kHintAudio = 0x00000001;
   const unsigned long kHintVideo = 0x00000002;
 
   const long kActionNone = -1;
   const long kActionOffer = 0;
   const long kActionAnswer = 1;
@@ -142,14 +142,15 @@ interface IPeerConnection : nsISupports
   readonly attribute string localDescription;
   readonly attribute string remoteDescription;
 
   readonly attribute unsigned long iceState;
   readonly attribute unsigned long readyState;
   readonly attribute unsigned long sipccState;
 
   /* Data channels */
-  nsIDOMDataChannel createDataChannel(in ACString label,
+  nsIDOMDataChannel createDataChannel(in ACString label, in ACString protocol,
     in unsigned short type, in boolean outOfOrderAllowed,
-    in unsigned short maxTime, in unsigned short maxNum);
+    in unsigned short maxTime, in unsigned short maxNum,
+    in boolean externalNegotiated, in unsigned short stream);
   void connectDataConnection(in unsigned short localport,
     in unsigned short remoteport, in unsigned short numstreams);
 };
--- a/dom/media/tests/mochitest/test_peerConnection_bug834153.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug834153.html
@@ -31,14 +31,14 @@
       pc2.createAnswer(function (d) {
         is(d.type,"answer","CreateAnswer created an answer");
         SimpleTest.finish();
       }, function (err) {
         croak("createAnswer failed: " + err);
       });
     }, function (err) {
         croak("createOffer failed: " + err);
-    });
+    },  { mandatory: { OfferToReceiveAudio: true} });
   }, true);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
@@ -1162,25 +1162,24 @@ MobileMessageDatabaseService.prototype =
         threadParticipants = threadParticipants.concat(slicedReceivers);
       }
     }
 
     let timestamp = aMessage.timestamp;
 
     // Adding needed indexes and extra attributes for internal use.
     // threadIdIndex & participantIdsIndex are filled in saveRecord().
-    aMessage.deliveryIndex = [DELIVERY_RECEIVED, timestamp];
     aMessage.readIndex = [FILTER_READ_UNREAD, timestamp];
+    aMessage.read = FILTER_READ_UNREAD;
 
     if (aMessage.type == "sms") {
+      aMessage.delivery = DELIVERY_RECEIVED;
       aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS;
-      aMessage.delivery = DELIVERY_RECEIVED;
     }
-
-    aMessage.read = FILTER_READ_UNREAD;
+    aMessage.deliveryIndex = [aMessage.delivery, timestamp];
 
     return this.saveRecord(aMessage, threadParticipants, aCallback);
   },
 
   saveSendingMessage: function saveSendingMessage(aMessage, aCallback) {
     if ((aMessage.type != "sms" && aMessage.type != "mms") ||
         (aMessage.type == "sms" && !aMessage.receiver) ||
         (aMessage.type == "mms" && !Array.isArray(aMessage.receivers)) ||
--- a/dom/push/src/PushService.js
+++ b/dom/push/src/PushService.js
@@ -13,17 +13,16 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/services-common/preferences.js");
-Cu.import("resource://gre/modules/services-common/utils.js");
 Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
 
 const kPUSHDB_DB_NAME = "push";
 const kPUSHDB_DB_VERSION = 1; // Change this if the IndexedDB format changes
 const kPUSHDB_STORE_NAME = "push";
 const kCONFLICT_RETRY_ATTEMPTS = 3; // If channelID registration says 409, how
                                     // many times to retry with a new channelID
 
--- a/dom/quota/CheckQuotaHelper.cpp
+++ b/dom/quota/CheckQuotaHelper.cpp
@@ -30,39 +30,24 @@
 USING_QUOTA_NAMESPACE
 using namespace mozilla::services;
 using mozilla::MutexAutoLock;
 
 namespace {
 
 inline
 uint32_t
-GetQuotaPermissions(nsIDOMWindow* aWindow)
+GetQuotaPermissionFromWindow(nsIDOMWindow* aWindow)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
   NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
 
-  if (nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) {
-    return nsIPermissionManager::ALLOW_ACTION;
-  }
-
-  nsCOMPtr<nsIPermissionManager> permissionManager =
-    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-  NS_ENSURE_TRUE(permissionManager, nsIPermissionManager::DENY_ACTION);
-
-  uint32_t permission;
-  nsresult rv =
-    permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(),
-                                                   PERMISSION_INDEXEDDB_UNLIMITED,
-                                                   &permission);
-  NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
-
-  return permission;
+  return CheckQuotaHelper::GetQuotaPermission(sop->GetPrincipal());
 }
 
 } // anonymous namespace
 
 CheckQuotaHelper::CheckQuotaHelper(nsPIDOMWindow* aWindow,
                                    mozilla::Mutex& aMutex)
 : mWindow(aWindow),
   mMutex(aMutex),
@@ -123,30 +108,54 @@ CheckQuotaHelper::Cancel()
       }
       else {
         NS_WARNING("Failed to notify!");
       }
     }
   }
 }
 
+// static
+uint32_t
+CheckQuotaHelper::GetQuotaPermission(nsIPrincipal* aPrincipal)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aPrincipal, "Null principal!");
+
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    return nsIPermissionManager::ALLOW_ACTION;
+  }
+
+  nsCOMPtr<nsIPermissionManager> pm =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+  NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
+
+  uint32_t permission;
+  nsresult rv = pm->TestPermissionFromPrincipal(aPrincipal,
+                                                PERMISSION_INDEXEDDB_UNLIMITED,
+                                                &permission);
+  NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
+
+  return permission;
+}
+
 NS_IMPL_THREADSAFE_ISUPPORTS3(CheckQuotaHelper, nsIRunnable,
                                                 nsIInterfaceRequestor,
                                                 nsIObserver)
 
 NS_IMETHODIMP
 CheckQuotaHelper::Run()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsresult rv = NS_OK;
 
   if (NS_SUCCEEDED(rv)) {
     if (!mHasPrompted) {
-      mPromptResult = GetQuotaPermissions(mWindow);
+      mPromptResult = GetQuotaPermissionFromWindow(mWindow);
     }
 
     if (mHasPrompted) {
       // Add permissions to the database, but only if we are in the parent
       // process (if we are in the child process, we have already
       // set the permission when the prompt was shown in the parent, as
       // we cannot set the permission from the child).
       if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION &&
--- a/dom/quota/CheckQuotaHelper.h
+++ b/dom/quota/CheckQuotaHelper.h
@@ -11,16 +11,17 @@
 
 #include "nsIInterfaceRequestor.h"
 #include "nsIObserver.h"
 #include "nsIRunnable.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/CondVar.h"
 
+class nsIPrincipal;
 class nsPIDOMWindow;
 
 BEGIN_QUOTA_NAMESPACE
 
 class CheckQuotaHelper MOZ_FINAL : public nsIRunnable,
                                    public nsIInterfaceRequestor,
                                    public nsIObserver
 {
@@ -32,16 +33,18 @@ public:
 
   CheckQuotaHelper(nsPIDOMWindow* aWindow,
                    mozilla::Mutex& aMutex);
 
   bool PromptAndReturnQuotaIsDisabled();
 
   void Cancel();
 
+  static uint32_t GetQuotaPermission(nsIPrincipal* aPrincipal);
+
 private:
   nsPIDOMWindow* mWindow;
 
   mozilla::Mutex& mMutex;
   mozilla::CondVar mCondVar;
   uint32_t mPromptResult;
   bool mWaiting;
   bool mHasPrompted;
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -476,20 +476,24 @@ QuotaManager::Init()
   // Register IndexedDB
   mClients.AppendElement(new indexedDB::Client());
 
   return NS_OK;
 }
 
 void
 QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin,
-                                 int64_t aLimit,
-                                 int64_t aUsage)
+                                 int64_t aLimitBytes,
+                                 int64_t aUsageBytes)
 {
-  OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage);
+  MOZ_ASSERT(aUsageBytes >= 0);
+  MOZ_ASSERT(aLimitBytes > 0);
+  MOZ_ASSERT(aUsageBytes <= aLimitBytes);
+
+  OriginInfo* info = new OriginInfo(aOrigin, aLimitBytes, aUsageBytes);
 
   MutexAutoLock lock(mQuotaMutex);
 
   NS_ASSERTION(!mOriginInfos.GetWeak(aOrigin), "Replacing an existing entry!");
   mOriginInfos.Put(aOrigin, info);
 }
 
 void
@@ -813,17 +817,17 @@ QuotaManager::GetDirectoryForOrigin(cons
   NS_ENSURE_SUCCESS(rv, rv);
 
   directory.forget(aDirectory);
   return NS_OK;
 }
 
 nsresult
 QuotaManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
-                                        StoragePrivilege aPrivilege,
+                                        bool aTrackQuota,
                                         nsIFile** aDirectory)
 {
 #ifdef DEBUG
   {
     bool correctThread;
     NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
                  correctThread,
                  "Running on the wrong thread!");
@@ -865,17 +869,17 @@ QuotaManager::EnsureOriginIsInitialized(
   }
 
   rv = MaybeUpgradeOriginDirectory(directory);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We need to initialize directories of all clients if they exists and also
   // get the total usage to initialize the quota.
   nsAutoPtr<UsageRunnable> runnable;
-  if (aPrivilege != Chrome) {
+  if (aTrackQuota) {
     runnable = new UsageRunnable();
   }
 
   nsCOMPtr<nsISimpleEnumerator> entries;
   rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasMore;
@@ -910,22 +914,35 @@ QuotaManager::EnsureOriginIsInitialized(
       NS_WARNING("Unknown directory found!");
       return NS_ERROR_UNEXPECTED;
     }
 
     rv = mClients[clientType]->InitOrigin(aOrigin, runnable);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (aPrivilege != Chrome) {
-    QuotaManager* quotaManager = QuotaManager::Get();
-    NS_ASSERTION(quotaManager, "Shouldn't be null!");
+  if (aTrackQuota) {
+    uint64_t quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
+    uint64_t totalUsageBytes = runnable->TotalUsage();
+
+    if (totalUsageBytes > quotaMaxBytes) {
+      NS_WARNING("Origin is already using more storage than allowed by quota!");
+      return NS_ERROR_UNEXPECTED;
+    }
 
-    quotaManager->InitQuotaForOrigin(aOrigin, GetStorageQuotaMB(),
-                                     runnable->TotalUsage());
+    // XXX This signed/unsigned mismatch must be fixed.
+    int64_t limit = quotaMaxBytes >= uint64_t(INT64_MAX) ?
+                    INT64_MAX :
+                    int64_t(quotaMaxBytes);
+
+    int64_t usage = totalUsageBytes >= uint64_t(INT64_MAX) ?
+                    INT64_MAX :
+                    int64_t(totalUsageBytes);
+
+    InitQuotaForOrigin(aOrigin, limit, usage);
   }
 
   mInitializedOrigins.AppendElement(aOrigin);
 
   NS_ADDREF(*aDirectory = directory);
   return NS_OK;
 }
 
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -77,18 +77,18 @@ public:
   static QuotaManager*
   FactoryCreate();
 
   // Returns true if we've begun the shutdown process.
   static bool IsShuttingDown();
 
   void
   InitQuotaForOrigin(const nsACString& aOrigin,
-                     int64_t aLimit,
-                     int64_t aUsage);
+                     int64_t aLimitBytes,
+                     int64_t aUsageBytes);
 
   void
   DecreaseUsageForOrigin(const nsACString& aOrigin,
                          int64_t aSize);
 
   void
   RemoveQuotaForPattern(const nsACString& aPattern);
 
@@ -187,17 +187,17 @@ public:
   }
 
   nsresult
   GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
                         nsIFile** aDirectory) const;
 
   nsresult
   EnsureOriginIsInitialized(const nsACString& aOrigin,
-                            StoragePrivilege aPrivilege,
+                            bool aTrackQuota,
                             nsIFile** aDirectory);
 
   void
   UninitializeOriginsByPattern(const nsACString& aPattern);
 
   nsIThread*
   IOThread()
   {
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -466,18 +466,18 @@ RILContentHelper.prototype = {
 
     if (isNaN(parseInt(network.mcc, 10))) {
       throw new Error("Invalid network MCC: " + network.mcc);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    if (this.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL
-        && this.rilContext.voiceConnectionInfo.network === network) {
+    if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL &&
+        this.rilContext.voiceConnectionInfo.network === network) {
 
       // Already manually selected this network, so schedule
       // onsuccess to be fired on the next tick
       this.dispatchFireRequestSuccess(requestId, null);
       return request;
     }
 
     this._selectingNetwork = network;
@@ -500,17 +500,17 @@ RILContentHelper.prototype = {
 
     if (this._selectingNetwork) {
       throw new Error("Already selecting a network: " + this._selectingNetwork);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    if (this.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
+    if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
       // Already using automatic selection mode, so schedule
       // onsuccess to be be fired on the next tick
       this.dispatchFireRequestSuccess(requestId, null);
       return request;
     }
 
     this._selectingNetwork = "automatic";
     cpmm.sendAsyncMessage("RIL:SelectNetworkAuto", {requestId: requestId});
@@ -1010,17 +1010,17 @@ RILContentHelper.prototype = {
         break;
       case "RIL:EnumerateCalls":
         this.handleEnumerateCalls(msg.json.calls);
         break;
       case "RIL:GetAvailableNetworks":
         this.handleGetAvailableNetworks(msg.json);
         break;
       case "RIL:NetworkSelectionModeChanged":
-        this.networkSelectionMode = msg.json.mode;
+        this.rilContext.networkSelectionMode = msg.json.mode;
         break;
       case "RIL:SelectNetwork":
         this.handleSelectNetwork(msg.json,
                                  RIL.GECKO_NETWORK_SELECTION_MANUAL);
         break;
       case "RIL:SelectNetworkAuto":
         this.handleSelectNetwork(msg.json,
                                  RIL.GECKO_NETWORK_SELECTION_AUTOMATIC);
@@ -1165,17 +1165,17 @@ RILContentHelper.prototype = {
       networks[i] = info;
     }
 
     Services.DOMRequest.fireSuccess(request, networks);
   },
 
   handleSelectNetwork: function handleSelectNetwork(message, mode) {
     this._selectingNetwork = null;
-    this.networkSelectionMode = mode;
+    this.rilContext.networkSelectionMode = mode;
 
     if (message.errorMsg) {
       this.fireRequestError(message.requestId, message.errorMsg);
     } else {
       this.fireRequestSuccess(message.requestId, null);
     }
   },
 
--- a/dom/webidl/SVGTextContentElement.webidl
+++ b/dom/webidl/SVGTextContentElement.webidl
@@ -30,11 +30,12 @@ interface SVGTextContentElement : SVGGra
   SVGPoint getStartPositionOfChar(unsigned long charnum);
   [Throws]
   SVGPoint getEndPositionOfChar(unsigned long charnum);
   [Creator, Throws]
   SVGRect getExtentOfChar(unsigned long charnum);
   [Throws]
   float getRotationOfChar(unsigned long charnum);
   long getCharNumAtPosition(SVGPoint point);
-  // void selectSubString(unsigned long charnum, unsigned long nchars);
+  [Throws]
+  void selectSubString(unsigned long charnum, unsigned long nchars);
 };
 
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -152,16 +152,25 @@ public:
     void VerifyD2DDevice(bool aAttemptForce);
 
 #ifdef CAIRO_HAS_D2D_SURFACE
     HRESULT CreateDevice(nsRefPtr<IDXGIAdapter1> &adapter1, int featureLevelIndex);
 #endif
 
     HDC GetScreenDC() { return mScreenDC; }
 
+    /**
+     * Return the resolution scaling factor to convert between "logical" or
+     * "screen" pixels as used by Windows (dependent on the DPI scaling option
+     * in the Display control panel) and actual device pixels.
+     */
+    double GetDPIScale() {
+        return GetDeviceCaps(mScreenDC, LOGPIXELSY) / 96.0;
+    }
+
     nsresult GetFontList(nsIAtom *aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray<nsString>& aListOfFonts);
 
     nsresult UpdateFontList();
 
     virtual void GetCommonFallbackFonts(const uint32_t aCh,
                                         int32_t aRunScript,
--- a/image/test/reftest/pngsuite-ancillary/reftest.list
+++ b/image/test/reftest/pngsuite-ancillary/reftest.list
@@ -1,16 +1,16 @@
 # PngSuite - Ancillary chunks
 
 # cHRM chunks
 #
 # ccwn2c08 - gamma 1.0000 chunk, chroma chunk w:0.3127,0.3290 r:0.64,0.33 g:0.30,0.60 b:0.15,0.06
-fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) == ccwn2c08.png ccwn2c08.html
+fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) fuzzy-if(winWidget,8,569) == ccwn2c08.png ccwn2c08.html
 # ccwn3p08 - gamma 1.0000 chunk, chroma chunk w:0.3127,0.3290 r:0.64,0.33 g:0.30,0.60 b:0.15,0.06
-fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) == ccwn3p08.png ccwn3p08.html
+fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) fuzzy-if(winWidget,8,577) == ccwn3p08.png ccwn3p08.html
 
 # pHYs chunks
 #
 # PngSuite implies these first 3 should end up as 32x32 bitmaps, but
 # per discussion in bug 408622 that's not actually true.
 #
 # cdfn2c08 - physical pixel dimensions, 8x32 flat pixels
 fails-if(Android) == cdfn2c08.png cdfn2c08.html
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/src/README.mozilla
@@ -0,0 +1,28 @@
+About the hyphenation code in this directory
+============================================
+
+The core hyphenation code (files "hyphen.c" and "hyphen.h") comes from the
+Hyphen library, part of the hunspell project. The various COPYING* and README*
+files (except this README.mozilla) are likewise from the hunspell distribution
+of Hyphen:
+
+  http://sourceforge.net/projects/hunspell/files/Hyphen/.
+
+This code is distributed under the GPL 2.0/LGPL 2.1/MPL 1.1 tri-license, as
+detailed in the associated README and COPYING files.
+
+Note that we do not include other tools and resources found in the complete
+Hyphen package from upstream, so the original README.* files may refer to
+additional files that are not present in the Mozilla source tree.
+
+
+The other source files here:
+
+  hnjalloc.h
+  hnjstdio.cpp
+  nsHyphenationManager.cpp
+  nsHyphenator.cpp
+
+as well as the build files (Makefile.in and moz.build) are Mozilla-authored
+code, and the standard MPL 2.0 applies to these, as noted in the comments
+within the files.
--- a/intl/icu/source/aclocal.m4
+++ b/intl/icu/source/aclocal.m4
@@ -33,17 +33,20 @@ powerpc*-*-linux*)
 *-*-linux*|*-*-gnu|*-*-k*bsd*-gnu|*-*-kopensolaris*-gnu) icu_cv_host_frag=mh-linux ;;
 *-*-cygwin|*-*-mingw32)
 	if test "$GCC" = yes; then
 		AC_TRY_COMPILE([
 #ifndef __MINGW32__
 #error This is not MinGW
 #endif], [], icu_cv_host_frag=mh-mingw, icu_cv_host_frag=mh-cygwin)
 	else
-		icu_cv_host_frag=mh-cygwin-msvc
+	        case "${host}" in
+		*-*-mingw32) icu_cv_host_frag=mh-msys-msvc ;;
+		*-*-cygwin) icu_cv_host_frag=mh-cygwin-msvc ;;
+		esac
 	fi ;;
 *-*-*bsd*|*-*-dragonfly*) 	icu_cv_host_frag=mh-bsd-gcc ;;
 *-*-aix*)
 	if test "$GCC" = yes; then
 		icu_cv_host_frag=mh-aix-gcc
 	else
 		icu_cv_host_frag=mh-aix-va
 	fi ;;
@@ -456,27 +459,31 @@ AC_DEFUN(AC_CHECK_STRICT_COMPILE,
             # We use -std=c99 to disable the gnu99 defaults and its associated warnings
             CFLAGS="$CFLAGS -Wall -std=c99 -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CFLAGS="$CFLAGS /W4"
-                fi
+                fi ;;
+            *-*-mingw32)
+                CFLAGS="$CFLAGS -W4" ;;
             esac
         fi
         if test "$GXX" = yes
         then
             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CXXFLAGS="$CXXFLAGS /W4"
-                fi
+                fi ;;
+            *-*-mingw32)
+                CXXFLAGS="$CXXFLAGS -W4" ;;
             esac
         fi
     fi
 ])
 
 
copy from intl/icu/source/config/mh-cygwin-msvc
copy to intl/icu/source/config/mh-msys-msvc
--- a/intl/icu/source/config/mh-cygwin-msvc
+++ b/intl/icu/source/config/mh-msys-msvc
@@ -1,22 +1,22 @@
-## Cygwin with Microsoft Visual C++ compiler specific setup
+## MSYS with Microsoft Visual C++ compiler specific setup
 ## Copyright (c) 2001-2012, International Business Machines Corporation and
 ## others. All Rights Reserved.
 
 # We install sbin tools into the same bin directory because
 # pkgdata needs some of the tools in sbin, and we can't always depend on
 # icu-config working on Windows.
 sbindir=$(bindir)
 
 ## Commands to generate dependency files
 GEN_DEPS.c=	:
 GEN_DEPS.cc=	:
-#GEN_DEPS.c=	$(COMPILE.c) /E
-#GEN_DEPS.cc=	$(COMPILE.cc) /E
+#GEN_DEPS.c=	$(COMPILE.c) -E
+#GEN_DEPS.cc=	$(COMPILE.cc) -E
 
 ## Flags to create/use a static library
 ifneq ($(ENABLE_SHARED),YES)
 ## Make sure that the static libraries can be built and used
 CPPFLAGS += -DU_STATIC_IMPLEMENTATION#M#
 else
 ## Make sure that the static libraries can be built
 STATICCPPFLAGS = -DU_STATIC_IMPLEMENTATION
@@ -36,63 +36,63 @@ CPPFLAGS+=-DU_RELEASE=1#M#
 endif
 
 ifeq ($(ENABLE_DEBUG),1)
 # Pass debugging flag through
 CPPFLAGS+=-D_DEBUG=1#M#
 ICULIBSUFFIX:=$(ICULIBSUFFIX)d#M#
 endif
 
-# /GF pools strings and places them into read-only memory
-# /EHsc enables exception handling
-# /Zc:wchar_t makes wchar_t a native type. Required for C++ ABI compatibility.
+# -GF pools strings and places them into read-only memory
+# -EHsc enables exception handling
+# -Zc:wchar_t makes wchar_t a native type. Required for C++ ABI compatibility.
 # -D_CRT_SECURE_NO_DEPRECATE is needed to quiet warnings about using standard C functions.
-CFLAGS+=/GF /nologo
-CXXFLAGS+=/GF /nologo /EHsc /Zc:wchar_t
+CFLAGS+=-GF -nologo
+CXXFLAGS+=-GF -nologo -EHsc -Zc:wchar_t
 CPPFLAGS+=-D_CRT_SECURE_NO_DEPRECATE
 DEFS+=-DWIN32 -DCYGWINMSVC
-LDFLAGS+=/nologo
+LDFLAGS+=-nologo
 
 # Commands to compile
-COMPILE.c=	$(CC) $(CPPFLAGS) $(DEFS) $(CFLAGS) /c
-COMPILE.cc=	$(CXX) $(CPPFLAGS) $(DEFS) $(CXXFLAGS) /c
+COMPILE.c=	$(CC) $(CPPFLAGS) $(DEFS) $(CFLAGS) -c
+COMPILE.cc=	$(CXX) $(CPPFLAGS) $(DEFS) $(CXXFLAGS) -c
 
 # Commands to link
-LINK.c=		LINK.EXE /subsystem:console $(LDFLAGS)
-LINK.cc=	LINK.EXE /subsystem:console $(LDFLAGS)
+LINK.c=		LINK.EXE -subsystem:console $(LDFLAGS)
+LINK.cc=	LINK.EXE -subsystem:console $(LDFLAGS)
 
 ## Commands to make a shared library
-SHLIB.c=	LINK.EXE /DLL $(LDFLAGS)
-SHLIB.cc=	LINK.EXE /DLL $(LDFLAGS)
+SHLIB.c=	LINK.EXE -DLL $(LDFLAGS)
+SHLIB.cc=	LINK.EXE -DLL $(LDFLAGS)
 
 ## Compiler switch to embed a runtime search path
 LD_RPATH=	
 LD_RPATH_PRE=
 
 ## Compiler switch to embed a library name
-LD_SONAME = /IMPLIB:$(SO_TARGET:.dll=.lib)
+LD_SONAME = -IMPLIB:$(SO_TARGET:.dll=.lib)
 
 ## Shared object suffix
 SO = dll
 ## Non-shared intermediate object suffix
 STATIC_O = ao
 # OUTOPT is for creating a specific output name
-OUTOPT = /out:
+OUTOPT = -out:
 
 # Static library prefix and file extension
 LIBSICU = $(STATIC_PREFIX)$(ICUPREFIX)
 A = lib
 
 # Cygwin's ar can't handle Win64 right now. So we use Microsoft's tool instead.
 AR = LIB.EXE#M#
-ARFLAGS := /nologo $(ARFLAGS:r=)#M#
+ARFLAGS := -nologo $(ARFLAGS:r=)#M#
 RANLIB = ls -s#M#
-AR_OUTOPT = /OUT:#M#
+AR_OUTOPT = -OUT:#M#
 
-## An import library is needed for z/OS, MSVC and Cygwin
+## An import library is needed for z-OS, MSVC and Cygwin
 IMPORT_LIB_EXT = .lib
 
 LIBPREFIX=
 DEFAULT_LIBS = advapi32.lib
 
 # Change the stubnames so that poorly working FAT disks and installation programs can work.
 # This is also for backwards compatibility.
 DATA_STUBNAME = dt
@@ -109,47 +109,35 @@ LIBICUUC=	$(LIBDIR)/$(LIBICU)$(COMMON_ST
 LIBICUI18N=	$(LIBDIR)/$(LIBICU)$(I18N_STUBNAME)$(ICULIBSUFFIX).lib
 LIBICULE=	$(LIBDIR)/$(LIBICU)$(LAYOUT_STUBNAME)$(ICULIBSUFFIX).lib
 LIBICULX=	$(LIBDIR)/$(LIBICU)$(LAYOUTEX_STUBNAME)$(ICULIBSUFFIX).lib
 LIBICUIO=	$(LIBDIR)/$(LIBICU)$(IO_STUBNAME)$(ICULIBSUFFIX).lib
 LIBCTESTFW=	$(top_builddir)/tools/ctestfw/$(LIBICU)$(CTESTFW_STUBNAME)$(ICULIBSUFFIX).lib
 LIBICUTOOLUTIL=	$(LIBDIR)/$(LIBICU)$(TOOLUTIL_STUBNAME)$(ICULIBSUFFIX).lib
 
 ## These are the library specific LDFLAGS
-LDFLAGSICUDT+=	/base:"0x4ad00000" /NOENTRY# The NOENTRY option is required for creating a resource-only DLL.
-LDFLAGSICUUC=	/base:"0x4a800000"# in-uc = 1MB
-LDFLAGSICUI18N=	/base:"0x4a900000"# io-in = 2MB
-LDFLAGSICUIO=	/base:"0x4ab00000"# le-io = 1MB
-LDFLAGSICULE=	/base:"0x4ac00000"# lx-le = 512KB
-LDFLAGSICULX=	/base:"0x4ac80000"
+LDFLAGSICUDT+=	-base:"0x4ad00000" -NOENTRY# The NOENTRY option is required for creating a resource-only DLL.
+LDFLAGSICUUC=	-base:"0x4a800000"# in-uc = 1MB
+LDFLAGSICUI18N=	-base:"0x4a900000"# io-in = 2MB
+LDFLAGSICUIO=	-base:"0x4ab00000"# le-io = 1MB
+LDFLAGSICULE=	-base:"0x4ac00000"# lx-le = 512KB
+LDFLAGSICULX=	-base:"0x4ac80000"
 LDFLAGSCTESTFW=# Unused for now.
-LDFLAGSICUTOOLUTIL=	/base:"0x4ac00000"# Same as layout. Layout and tools probably won't mix.
-
-# The #M# is used to delete lines for icu-config
-# Current full path directory.
-CURR_FULL_DIR=$(subst \,/,$(shell cygpath -da .))#M# -m isn't used because it doesn't work on Win98
-# Current full path directory for use in source code in a -D compiler option.
-CURR_SRCCODE_FULL_DIR=$(subst \,\\,$(shell cygpath -da .))#M#
-
-ifeq ($(srcdir),.)
-SOURCE_FILE=$<
-else
-SOURCE_FILE=$(shell cygpath -dma $<)#M#
-endif
+LDFLAGSICUTOOLUTIL=	-base:"0x4ac00000"# Same as layout. Layout and tools probably won't mix.
 
 ## Compilation rules
 %.$(STATIC_O): $(srcdir)/%.c
-	$(COMPILE.c) $(STATICCPPFLAGS) $(STATICCFLAGS) /Fo$@ $(SOURCE_FILE)
+	$(COMPILE.c) $(STATICCPPFLAGS) $(STATICCFLAGS) -Fo$@ $<
 %.o: $(srcdir)/%.c
-	$(COMPILE.c) $(DYNAMICCPPFLAGS) $(DYNAMICCFLAGS) /Fo$@ $(SOURCE_FILE)
+	$(COMPILE.c) $(DYNAMICCPPFLAGS) $(DYNAMICCFLAGS) -Fo$@ $<
 
 %.$(STATIC_O): $(srcdir)/%.cpp
-	$(COMPILE.cc) $(STATICCPPFLAGS) $(STATICCXXFLAGS) /Fo$@ $(SOURCE_FILE)
+	$(COMPILE.cc) $(STATICCPPFLAGS) $(STATICCXXFLAGS) -Fo$@ $<
 %.o: $(srcdir)/%.cpp
-	$(COMPILE.cc) $(DYNAMICCPPFLAGS) $(DYNAMICCXXFLAGS) /Fo$@ $(SOURCE_FILE)
+	$(COMPILE.cc) $(DYNAMICCPPFLAGS) $(DYNAMICCXXFLAGS) -Fo$@ $<
 
 
 ## Dependency rules
 ## This is a start to how depdendencies could work
 # The commented out rules may not properly delete the file when ^C is pressed
 #   or the compiler fails.
 # make currently doesn't like rules with C:\\PROGRA~1\\.. in the depedency.
 #   So system headers are ignored by ignoring \\
@@ -170,34 +158,34 @@ endif
 #	@echo -n "$@ $(basename $<).o : " > $@
 #	@$(SHELL) -ec '$(GEN_DEPS.cc) $< \
 #		| grep "#line 1 " | grep -v \\\\ | cut -d " " -f 3 \
 #		| /usr/bin/sort -u | sed s/\"$$/\\\\/ | sed s/^\"/\	/ >> $@ \
 #		|| (rm -f $@ && echo $@ && false)'
 
 ## Compile a Windows resource file
 %.res : $(srcdir)/%.rc
-	rc.exe /fo$@ $(CPPFLAGS) $(SOURCE_FILE)
+	rc.exe -fo$@ $(CPPFLAGS) $<
 
 ## Versioned target for a shared library.
 FINAL_SO_TARGET= $(basename $(SO_TARGET))$(SO_TARGET_VERSION_MAJOR).$(SO)
 MIDDLE_SO_TARGET=$(FINAL_SO_TARGET)
 
 ## Starting in MSVC 2005, manifest files are required. This reduces the obnoxiousness of this feature.
 	POST_SO_BUILD_STEP = @([ -e $<.manifest ] && \
 	( echo Embedding manifest into $< && mt.exe -nologo -manifest $<.manifest -outputresource:"$<;2" && rm -rf $<.manifest )) \
 	|| true
 	POST_BUILD_STEP = @([ -e $@.manifest ] && \
 	( echo Embedding manifest into $@ && mt.exe -nologo -manifest $@.manifest -outputresource:"$@;1" && rm -rf $@.manifest )) \
 	|| true
 
 ## Special pkgdata information that is needed
 PKGDATA_VERSIONING = -r $(SO_TARGET_VERSION_MAJOR)
-ICUPKGDATA_INSTALL_DIR = $(shell mkdir -p  $(DESTDIR)$(ICUPKGDATA_DIR) ;  cygpath -dma $(DESTDIR)$(ICUPKGDATA_DIR))#M#
-ICUPKGDATA_INSTALL_LIBDIR = $(shell mkdir -p  $(DESTDIR)$(libdir) ; cygpath -dma $(DESTDIR)$(libdir))#M#
+ICUPKGDATA_INSTALL_DIR = $(shell mkdir -p  $(DESTDIR)$(ICUPKGDATA_DIR) ;  echo $(DESTDIR)$(ICUPKGDATA_DIR))#M#
+ICUPKGDATA_INSTALL_LIBDIR = $(shell mkdir -p  $(DESTDIR)$(libdir) ; echo $(DESTDIR)$(libdir))#M#
 
 ## Versioned import library names. The library names are versioned,
 ## but the import libraries do not need versioning.
 IMPORT_LIB = $(basename $(SO_TARGET))$(IMPORT_LIB_EXT)#M#
 MIDDLE_IMPORT_LIB = $(IMPORT_LIB)#M#
 FINAL_IMPORT_LIB = $(MIDDLE_IMPORT_LIB)#M#
 
 # The following is for Makefile.inc's use.
@@ -220,10 +208,10 @@ INSTALL-L=$(INSTALL_PROGRAM)
 LDLIBRARYPATH_ENVVAR = PATH
 
 # These are needed to allow the pkgdata nmake files to work
 PKGDATA_INVOKE_OPTS = MAKEFLAGS=
 
 # Include the version information in the shared library
 ENABLE_SO_VERSION_DATA=1
 
-## End Cygwin-specific setup
+## End MSYS-specific setup
 
--- a/intl/icu/source/configure
+++ b/intl/icu/source/configure
@@ -4068,29 +4068,33 @@ fi
             # We use -std=c99 to disable the gnu99 defaults and its associated warnings
             CFLAGS="$CFLAGS -Wall -std=c99 -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CFLAGS="$CFLAGS /W4"
-                fi
+                fi ;;
+            *-*-mingw32)
+                CFLAGS="$CFLAGS -W4" ;;
             esac
         fi
         if test "$GXX" = yes
         then
             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CXXFLAGS="$CXXFLAGS /W4"
-                fi
+                fi ;;
+            *-*-mingw32)
+                CXXFLAGS="$CXXFLAGS -W4" ;;
             esac
         fi
     fi
 
 
 # Check if we can build and use 64-bit libraries
 
 
@@ -4864,17 +4868,20 @@ main ()
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
   icu_cv_host_frag=mh-mingw
 else
   icu_cv_host_frag=mh-cygwin
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 	else
-		icu_cv_host_frag=mh-cygwin-msvc
+	        case "${host}" in
+		*-*-mingw32) icu_cv_host_frag=mh-msys-msvc ;;
+		*-*-cygwin) icu_cv_host_frag=mh-cygwin-msvc ;;
+		esac
 	fi ;;
 *-*-*bsd*|*-*-dragonfly*) 	icu_cv_host_frag=mh-bsd-gcc ;;
 *-*-aix*)
 	if test "$GCC" = yes; then
 		icu_cv_host_frag=mh-aix-gcc
 	else
 		icu_cv_host_frag=mh-aix-va
 	fi ;;
@@ -5106,17 +5113,17 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enabled" >&5
 $as_echo "$enabled" >&6; }
 
 
 # MSVC floating-point option
 MSVC_RELEASE_FLAG=""
 if test $enabled = yes
 then
-    if test $icu_cv_host_frag = mh-cygwin-msvc
+    if test $icu_cv_host_frag = mh-cygwin-msvc -o $icu_cv_host_frag = mh-msys-msvc
     then
         cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
     #if defined _MSC_VER && _MSC_VER >= 1400
     #else
     Microsoft Visual C++ < 2005
     #endif
--- a/intl/icu/source/configure.in
+++ b/intl/icu/source/configure.in
@@ -313,18 +313,18 @@ AC_ARG_ENABLE(auto-cleanup,
     esac],
 )
 AC_MSG_RESULT($enabled)
 AC_SUBST(UCLN_NO_AUTO_CLEANUP)
 
 # MSVC floating-point option
 MSVC_RELEASE_FLAG=""
 if test $enabled = yes
-then  
-    if test $icu_cv_host_frag = mh-cygwin-msvc
+then
+    if test $icu_cv_host_frag = mh-cygwin-msvc -o $icu_cv_host_frag = mh-msys-msvc
     then
         AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
     #if defined _MSC_VER && _MSC_VER >= 1400
     #else
     Microsoft Visual C++ < 2005
     #endif
         ]], [[]])],[MSVC_RELEASE_FLAG="/fp:precise"],[MSVC_RELEASE_FLAG="/Op"])
         
--- a/intl/icu/source/data/Makefile.in
+++ b/intl/icu/source/data/Makefile.in
@@ -345,17 +345,18 @@ GENRBOPTS=-k
 -include $(MISCSRCDIR)/misclocal.mk
 MSC_SOURCE= $(MISC_SOURCE) $(MISC_SOURCE_LOCAL)
 MSC_SRC_FILES=$(MSC_SOURCE:%=$(MISCSRCDIR)/%)
 
 ifeq ($(ENABLE_SO_VERSION_DATA),1)
 ifeq ($(PKGDATA_MODE),dll)
 SO_VERSION_DATA = $(OUTTMPDIR)/icudata.res
 $(SO_VERSION_DATA) : $(MISCSRCDIR)/icudata.rc
-	rc.exe /i$(srcdir)/../common /i$(top_builddir)/common /fo$@ $(CPPFLAGS) $(SOURCE_FILE)
+        # fixme: need to tell whether to use - or /, $(SOURCEFILE) or $<
+	rc.exe -i$(srcdir)/../common -i$(top_builddir)/common -fo$@ $(CPPFLAGS) $<
 endif
 endif
 
 INDEX_NAME=res_index
 INDEX_FILE=$(OUTTMPDIR)/$(INDEX_NAME).txt
 
 ALL_RES_SRC= $(RES_SRC) $(TRNS_SOURCE) $(MSC_SOURCE)
 RES_FILES = $(ALL_RES_SRC:%.txt=$(BUILDDIR)/%.res) $(BUILDDIR)/$(INDEX_NAME).res $(BUILDDIR)/pool.res
--- a/intl/icu/source/runConfigureICU
+++ b/intl/icu/source/runConfigureICU
@@ -279,16 +279,27 @@ case $platform in
         DEBUG_CXXFLAGS='-g -O0'
         ;;
     MinGW)
         THE_OS="MinGW"
         THE_COMP="the GNU C++"
         RELEASE_CFLAGS='-O3'
         RELEASE_CXXFLAGS='-O3'
         ;;
+    MSYS/MSVC)
+        THE_OS="MSYS"
+        THE_COMP="Microsoft Visual C++"
+        CC=cl; export CC
+        CXX=cl; export CXX
+        RELEASE_CFLAGS='-Gy -MD'
+        RELEASE_CXXFLAGS='-Gy -MD'
+        DEBUG_CFLAGS='-Zi -MDd'
+        DEBUG_CXXFLAGS='-Zi -MDd'
+        DEBUG_LDFLAGS='-DEBUG'
+        ;;
     *BSD)
         THE_OS="BSD"
         THE_COMP="the GNU C++"
         CC=gcc; export CC
         CXX=g++; export CXX
         DEBUG_CFLAGS='-g -O0'
         DEBUG_CXFLAGS='-g -O0'
         ;;
--- a/intl/update-icu.sh
+++ b/intl/update-icu.sh
@@ -1,13 +1,24 @@
 #!/bin/sh
 # 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/.
 
+# Warning
+# =======
+# As of ICU 51.1, ICU as obtained from the ICU repository does not
+# build with the Mozilla build tools for Windows. Check
+# http://bugs.icu-project.org/trac/ticket/9985
+# whether this has been addressed in the version you're updating to.
+# If not, obtain the patch "Make ICU build with Mozilla build for Windows" from
+# https://bugzilla.mozilla.org/show_bug.cgi?id=724533
+# and reapply it after running update-icu.sh (additional updates may be needed).
+# If the bug has been addressed, please delete this warning.
+
 # Usage: update-icu.sh <URL of ICU SVN with release>
 # E.g., for ICU 50.1.1: update-icu.sh http://source.icu-project.org/repos/icu/icu/tags/release-50-1-1/
 
 if [ $# -lt 1 ]; then
   echo "Usage: update-icu.sh <URL of ICU SVN with release>"
   exit 1
 fi
 
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -194,162 +194,171 @@ JS_DumpProfile(const char *outfile, cons
     return ok;
 }
 
 #ifdef MOZ_PROFILING
 
 struct RequiredStringArg {
     JSContext *mCx;
     char *mBytes;
-    RequiredStringArg(JSContext *cx, unsigned argc, jsval *vp, size_t argi, const char *caller)
+    RequiredStringArg(JSContext *cx, const CallArgs &args, size_t argi, const char *caller)
         : mCx(cx), mBytes(NULL)
     {
-        if (argc <= argi) {
+        if (args.length() <= argi) {
             JS_ReportError(cx, "%s: not enough arguments", caller);
-        } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
+        } else if (!args[argi].isString()) {
             JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
         } else {
-            mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
+            mBytes = JS_EncodeString(cx, args[argi].toString());
         }
     }
     operator void*() {
         return (void*) mBytes;
     }
     ~RequiredStringArg() {
         if (mBytes)
             js_free(mBytes);
     }
 };
 
 static JSBool
 StartProfiling(JSContext *cx, unsigned argc, jsval *vp)
 {
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() == 0) {
+        args.rval().setBoolean(JS_StartProfiling(NULL));
         return JS_TRUE;
     }
 
-    RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
+    RequiredStringArg profileName(cx, args, 0, "startProfiling");
     if (!profileName)
         return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
+    args.rval().setBoolean(JS_StartProfiling(profileName.mBytes));
     return JS_TRUE;
 }
 
 static JSBool
 StopProfiling(JSContext *cx, unsigned argc, jsval *vp)
 {
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() == 0) {
+        args.rval().setBoolean(JS_StopProfiling(NULL));
         return JS_TRUE;
     }
 
-    RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
+    RequiredStringArg profileName(cx, args, 0, "stopProfiling");
     if (!profileName)
         return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
+    args.rval().setBoolean(JS_StopProfiling(profileName.mBytes));
     return JS_TRUE;
 }
 
 static JSBool
 PauseProfilers(JSContext *cx, unsigned argc, jsval *vp)
 {
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() == 0) {
+        args.rval().setBoolean(JS_PauseProfilers(NULL));
         return JS_TRUE;
     }
 
-    RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
+    RequiredStringArg profileName(cx, args, 0, "pauseProfiling");
     if (!profileName)
         return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
+    args.rval().setBoolean(JS_PauseProfilers(profileName.mBytes));
     return JS_TRUE;
 }
 
 static JSBool
 ResumeProfilers(JSContext *cx, unsigned argc, jsval *vp)
 {
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() == 0) {
+        args.rval().setBoolean(JS_ResumeProfilers(NULL));
         return JS_TRUE;
     }
 
-    RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
+    RequiredStringArg profileName(cx, args, 0, "resumeProfiling");
     if (!profileName)
         return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
+    args.rval().setBoolean(JS_ResumeProfilers(profileName.mBytes));
     return JS_TRUE;
 }
 
 /* Usage: DumpProfile([filename[, profileName]]) */
 static JSBool
 DumpProfile(JSContext *cx, unsigned argc, jsval *vp)
 {
     bool ret;
-    if (argc == 0) {
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() == 0) {
         ret = JS_DumpProfile(NULL, NULL);
     } else {
-        RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
+        RequiredStringArg filename(cx, args, 0, "dumpProfile");
         if (!filename)
             return JS_FALSE;
 
-        if (argc == 1) {
+        if (args.length() == 1) {
             ret = JS_DumpProfile(filename.mBytes, NULL);
         } else {
-            RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
+            RequiredStringArg profileName(cx, args, 1, "dumpProfile");
             if (!profileName)
                 return JS_FALSE;
 
             ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
         }
     }
 
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
+    args.rval().setBoolean(ret);
     return true;
 }
 
 #if defined(MOZ_SHARK) || defined(MOZ_INSTRUMENTS)
 
 static JSBool
 IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JS_SET_RVAL(cx, vp, JSVAL_TRUE);
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(true);
     return true;
 }
 
 #endif
 
 #ifdef MOZ_CALLGRIND
 static JSBool
 StartCallgrind(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(js_StartCallgrind());
     return JS_TRUE;
 }
 
 static JSBool
 StopCallgrind(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(js_StopCallgrind());
     return JS_TRUE;
 }
 
 static JSBool
 DumpCallgrind(JSContext *cx, unsigned argc, jsval *vp)
 {
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() == 0) {
+        args.rval().setBoolean(js_DumpCallgrind(NULL));
         return JS_TRUE;
     }
 
-    RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
+    RequiredStringArg outFile(cx, args, 0, "dumpCallgrind");
     if (!outFile)
         return JS_FALSE;
 
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
+    args.rval().setBoolean(js_DumpCallgrind(outFile.mBytes));
     return JS_TRUE;
 }
 #endif
 
 static JSFunctionSpec profiling_functions[] = {
     JS_FN("startProfiling",  StartProfiling,      1,0),
     JS_FN("stopProfiling",   StopProfiling,       1,0),
     JS_FN("pauseProfilers",  PauseProfilers,      1,0),
--- a/layout/base/nsPresArena.h
+++ b/layout/base/nsPresArena.h
@@ -40,16 +40,17 @@ public:
   // Every aID must always be used with the same object size, aSize.
   NS_HIDDEN_(void*) AllocateByFrameID(nsQueryFrame::FrameIID aID, size_t aSize);
   NS_HIDDEN_(void)  FreeByFrameID(nsQueryFrame::FrameIID aID, void* aPtr);
 
   enum ObjectID {
     nsLineBox_id = nsQueryFrame::NON_FRAME_MARKER,
     nsRuleNode_id,
     nsStyleContext_id,
+    nsFrameList_id,
 
     // The PresArena implementation uses this bit to distinguish objects
     // allocated by size from objects allocated by type ID (that is, frames
     // using AllocateByFrameID and other objects using AllocateByObjectID).
     // It should not collide with any Object ID (above) or frame ID (in
     // nsQueryFrame.h).  It is not 0x80000000 to avoid the question of
     // whether enumeration constants are signed.
     NON_OBJECT_MARKER = 0x40000000
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -47,17 +47,16 @@
 #include "nsTextFrameTextRunCache.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsTextFragment.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsHTMLDNSPrefetch.h"
 #include "nsHtml5Module.h"
 #include "nsFocusManager.h"
-#include "nsFrameList.h"
 #include "nsListControlFrame.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "SVGElementFactory.h"
 #include "nsSVGUtils.h"
 #include "nsMathMLAtoms.h"
 #include "nsMathMLOperators.h"
 #include "Navigator.h"
 #include "nsDOMStorageBaseDB.h"
@@ -250,18 +249,16 @@ nsLayoutStatics::Initialize()
   nsContentSink::InitializeStatics();
   nsHtml5Module::InitializeStatics();
   nsLayoutUtils::Initialize();
   nsIPresShell::InitializeStatics();
   nsRefreshDriver::InitializeStatics();
 
   nsCORSListenerProxy::Startup();
 
-  nsFrameList::Init();
-
   NS_SealStaticAtomTable();
 
   nsWindowMemoryReporter::Init();
 
   SVGElementFactory::Init();
   nsSVGUtils::Init();
 
   InitProcessPriorityManager();
@@ -368,18 +365,16 @@ nsLayoutStatics::Shutdown()
   nsTreeSanitizer::ReleaseStatics();
 
   nsHtml5Module::ReleaseStatics();
 
   nsRegion::ShutdownStatic();
 
   NS_ShutdownEventTargetChainItemRecyclePool();
 
-  nsFrameList::Shutdown();
-
   HTMLInputElement::DestroyUploadLastDir();
 
   nsLayoutUtils::Shutdown();
 
   nsHyphenationManager::Shutdown();
   nsEditorSpellCheck::ShutDown();
   nsDOMMutationObserver::Shutdown();
 
--- a/layout/generic/nsAbsoluteContainingBlock.cpp
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -3,48 +3,63 @@
  * 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/. */
 
 /*
  * code for managing absolutely positioned children of a rendering
  * object that is a containing block for them
  */
 
-#include "nsCOMPtr.h"
 #include "nsAbsoluteContainingBlock.h"
+
 #include "nsContainerFrame.h"
+#include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsHTMLParts.h"
+#include "nsHTMLReflowState.h"
 #include "nsPresContext.h"
 #include "nsFrameManager.h"
 #include "nsCSSFrameConstructor.h"
 
 #ifdef DEBUG
 #include "nsBlockFrame.h"
+
+static void PrettyUC(nscoord aSize, char* aBuf)
+{
+  if (NS_UNCONSTRAINEDSIZE == aSize) {
+    strcpy(aBuf, "UC");
+  } else {
+    if((int32_t)0xdeadbeef == aSize) {
+      strcpy(aBuf, "deadbeef");
+    } else {
+      sprintf(aBuf, "%d", aSize);
+    }
+  }
+}
 #endif
 
 nsresult
 nsAbsoluteContainingBlock::SetInitialChildList(nsIFrame*       aDelegatingFrame,
                                                ChildListID     aListID,
                                                nsFrameList&    aChildList)
 {
-  NS_PRECONDITION(GetChildListID() == aListID, "unexpected child list name");
+  NS_PRECONDITION(mChildListID == aListID, "unexpected child list name");
 #ifdef DEBUG
   nsFrame::VerifyDirtyBitSet(aChildList);
 #endif
   mAbsoluteFrames.SetFrames(aChildList);
   return NS_OK;
 }
 
 nsresult
 nsAbsoluteContainingBlock::AppendFrames(nsIFrame*      aDelegatingFrame,
                                         ChildListID    aListID,
                                         nsFrameList&   aFrameList)
 {
-  NS_ASSERTION(GetChildListID() == aListID, "unexpected child list");
+  NS_ASSERTION(mChildListID == aListID, "unexpected child list");
 
   // Append the frames to our list of absolutely positioned frames
 #ifdef DEBUG
   nsFrame::VerifyDirtyBitSet(aFrameList);
 #endif
   mAbsoluteFrames.AppendFrames(nullptr, aFrameList);
 
   // no damage to intrinsic widths, since absolutely positioned frames can't
@@ -57,17 +72,17 @@ nsAbsoluteContainingBlock::AppendFrames(
 }
 
 nsresult
 nsAbsoluteContainingBlock::InsertFrames(nsIFrame*      aDelegatingFrame,
                                         ChildListID    aListID,
                                         nsIFrame*      aPrevFrame,
                                         nsFrameList&   aFrameList)
 {
-  NS_ASSERTION(GetChildListID() == aListID, "unexpected child list");
+  NS_ASSERTION(mChildListID == aListID, "unexpected child list");
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == aDelegatingFrame,
                "inserting after sibling frame with different parent");
 
 #ifdef DEBUG
   nsFrame::VerifyDirtyBitSet(aFrameList);
 #endif
   mAbsoluteFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
 
@@ -80,17 +95,17 @@ nsAbsoluteContainingBlock::InsertFrames(
   return NS_OK;
 }
 
 void
 nsAbsoluteContainingBlock::RemoveFrame(nsIFrame*       aDelegatingFrame,
                                        ChildListID     aListID,
                                        nsIFrame*       aOldFrame)
 {
-  NS_ASSERTION(GetChildListID() == aListID, "unexpected child list");
+  NS_ASSERTION(mChildListID == aListID, "unexpected child list");
   nsIFrame* nif = aOldFrame->GetNextInFlow();
   if (nif) {
     static_cast<nsContainerFrame*>(nif->GetParent())
       ->DeleteNextInFlowChild(aOldFrame->PresContext(), nif, false);
   }
 
   mAbsoluteFrames.DestroyFrame(aOldFrame);
 }
@@ -323,17 +338,17 @@ nsAbsoluteContainingBlock::DoMarkFramesD
   }
 }
 
 // XXX Optimize the case where it's a resize reflow and the absolutely
 // positioned child has the exact same size and position and skip the
 // reflow...
 
 // When bug 154892 is checked in, make sure that when 
-// GetChildListID() == kFixedList, the height is unconstrained.
+// mChildListID == kFixedList, the height is unconstrained.
 // since we don't allow replicated frames to split.
 
 nsresult
 nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame*                aDelegatingFrame,
                                                nsPresContext*          aPresContext,
                                                const nsHTMLReflowState& aReflowState,
                                                nscoord                  aContainingBlockWidth,
                                                nscoord                  aContainingBlockHeight,
@@ -477,27 +492,8 @@ nsAbsoluteContainingBlock::ReflowAbsolut
 #endif
 
   if (aOverflowAreas) {
     aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + rect.TopLeft());
   }
 
   return rv;
 }
-
-#ifdef DEBUG
- void nsAbsoluteContainingBlock::PrettyUC(nscoord aSize,
-                        char*   aBuf)
-{
-  if (NS_UNCONSTRAINEDSIZE == aSize) {
-    strcpy(aBuf, "UC");
-  }
-  else {
-    if((int32_t)0xdeadbeef == aSize)
-    {
-      strcpy(aBuf, "deadbeef");
-    }
-    else {
-      sprintf(aBuf, "%d", aSize);
-    }
-  }
-}
-#endif
--- a/layout/generic/nsAbsoluteContainingBlock.h
+++ b/layout/generic/nsAbsoluteContainingBlock.h
@@ -7,62 +7,55 @@
  * code for managing absolutely positioned children of a rendering
  * object that is a containing block for them
  */
 
 #ifndef nsAbsoluteContainingBlock_h___
 #define nsAbsoluteContainingBlock_h___
 
 #include "nsFrameList.h"
-#include "nsHTMLReflowState.h"
-#include "nsGkAtoms.h"
-#include "nsContainerFrame.h"
+#include "nsIFrame.h"
 
-class nsIAtom;
-class nsIFrame;
+class nsContainerFrame;
+class nsHTMLReflowState;
 class nsPresContext;
 
 /**
  * This class contains the logic for being an absolute containing block.  This
  * class is used within viewport frames (for frames representing content with
  * fixed position) and blocks (for frames representing absolutely positioned
  * content), since each set of frames is absolutely positioned with respect to
  * its parent.
  *
  * There is no principal child list, just a named child list which contains
  * the absolutely positioned frames (kAbsoluteList or kFixedList).
  *
  * All functions include as the first argument the frame that is delegating
  * the request.
- *
  */
 class nsAbsoluteContainingBlock
 {
 public:
   typedef nsIFrame::ChildListID ChildListID;
 
   nsAbsoluteContainingBlock(ChildListID aChildListID)
 #ifdef DEBUG
     : mChildListID(aChildListID)
 #endif
   {
-    NS_ASSERTION(mChildListID == nsIFrame::kAbsoluteList ||
-                 mChildListID == nsIFrame::kFixedList,
-                 "should either represent position:fixed or absolute content");
+    MOZ_ASSERT(mChildListID == nsIFrame::kAbsoluteList ||
+               mChildListID == nsIFrame::kFixedList,
+               "should either represent position:fixed or absolute content");
   }
 
-#ifdef DEBUG
-  ChildListID GetChildListID() const { return mChildListID; }
-#endif
-
   const nsFrameList& GetChildList() const { return mAbsoluteFrames; }
   void AppendChildList(nsTArray<nsIFrame::ChildList>* aLists,
                        ChildListID aListID) const
   {
-    NS_ASSERTION(aListID == GetChildListID(), "wrong list ID");
+    NS_ASSERTION(aListID == mChildListID, "wrong list ID");
     GetChildList().AppendIfNonempty(aLists, aListID);
   }
 
   nsresult SetInitialChildList(nsIFrame*       aDelegatingFrame,
                                ChildListID     aListID,
                                nsFrameList&    aChildList);
   nsresult AppendFrames(nsIFrame*      aDelegatingFrame,
                         ChildListID    aListID,
@@ -70,80 +63,86 @@ public:
   nsresult InsertFrames(nsIFrame*      aDelegatingFrame,
                         ChildListID    aListID,
                         nsIFrame*      aPrevFrame,
                         nsFrameList&   aFrameList);
   void RemoveFrame(nsIFrame*      aDelegatingFrame,
                    ChildListID    aListID,
                    nsIFrame*      aOldFrame);
 
-  // Called by the delegating frame after it has done its reflow first. This
-  // function will reflow any absolutely positioned child frames that need to
-  // be reflowed, e.g., because the absolutely positioned child frame has
-  // 'auto' for an offset, or a percentage based width or height.
-  //
-  // aOverflowAreas, if non-null, is unioned with (in the local
-  // coordinate space) the overflow areas of the absolutely positioned
-  // children.
-  //
-  // aReflowStatus is assumed to be already-initialized, e.g. with the status
-  // of the delegating frame's main reflow. This function merges in the
-  // statuses of the absolutely positioned children's reflows.
+  /**
+   * Called by the delegating frame after it has done its reflow first. This
+   * function will reflow any absolutely positioned child frames that need to
+   * be reflowed, e.g., because the absolutely positioned child frame has
+   * 'auto' for an offset, or a percentage based width or height.
+   *
+   * @param aOverflowAreas, if non-null, is unioned with (in the local
+   * coordinate space) the overflow areas of the absolutely positioned
+   * children.
+   *
+   * @param aReflowStatus is assumed to be already-initialized, e.g. with the
+   * status of the delegating frame's main reflow. This function merges in the
+   * statuses of the absolutely positioned children's reflows.
+   */
   nsresult Reflow(nsContainerFrame*        aDelegatingFrame,
                   nsPresContext*           aPresContext,
                   const nsHTMLReflowState& aReflowState,
                   nsReflowStatus&          aReflowStatus,
                   nscoord                  aContainingBlockWidth,
                   nscoord                  aContainingBlockHeight,
                   bool                     aConstrainHeight,
                   bool                     aCBWidthChanged,
                   bool                     aCBHeightChanged,
                   nsOverflowAreas*         aOverflowAreas);
 
 
   void DestroyFrames(nsIFrame* aDelegatingFrame,
                      nsIFrame* aDestructRoot);
 
-  bool    HasAbsoluteFrames() {return mAbsoluteFrames.NotEmpty();}
+  bool HasAbsoluteFrames() const { return mAbsoluteFrames.NotEmpty(); }
 
-  // Mark our size-dependent absolute frames with NS_FRAME_HAS_DIRTY_CHILDREN
-  // so that we'll make sure to reflow them.
+  /**
+   * Mark our size-dependent absolute frames with NS_FRAME_HAS_DIRTY_CHILDREN
+   * so that we'll make sure to reflow them.
+   */
   void MarkSizeDependentFramesDirty();
 
-  // Mark all our absolute frames with NS_FRAME_IS_DIRTY
+  /**
+   * Mark all our absolute frames with NS_FRAME_IS_DIRTY.
+   */
   void MarkAllFramesDirty();
 
 protected:
-  // Returns true if the position of f depends on the position of
-  // its placeholder or if the position or size of f depends on a
-  // containing block dimension that changed.
-  bool FrameDependsOnContainer(nsIFrame* f, bool aCBWidthChanged,
-                                 bool aCBHeightChanged);
+  /**
+   * Returns true if the position of aFrame depends on the position of
+   * its placeholder or if the position or size of aFrame depends on a
+   * containing block dimension that changed.
+   */
+  bool FrameDependsOnContainer(nsIFrame* aFrame, bool aCBWidthChanged,
+                               bool aCBHeightChanged);
 
   nsresult ReflowAbsoluteFrame(nsIFrame*                aDelegatingFrame,
-                               nsPresContext*          aPresContext,
+                               nsPresContext*           aPresContext,
                                const nsHTMLReflowState& aReflowState,
                                nscoord                  aContainingBlockWidth,
                                nscoord                  aContainingBlockHeight,
                                bool                     aConstrainHeight,
                                nsIFrame*                aKidFrame,
                                nsReflowStatus&          aStatus,
                                nsOverflowAreas*         aOverflowAreas);
 
-  // Mark our absolute frames dirty.  If aMarkAllDirty is true, all will be
-  // marked with NS_FRAME_IS_DIRTY.  Otherwise, the size-dependant ones will be
-  // marked with NS_FRAME_HAS_DIRTY_CHILDREN.
+  /**
+   * Mark our absolute frames dirty.
+   * @param aMarkAllDirty if true, all will be marked with NS_FRAME_IS_DIRTY.
+   * Otherwise, the size-dependant ones will be marked with
+   * NS_FRAME_HAS_DIRTY_CHILDREN.
+   */
   void DoMarkFramesDirty(bool aMarkAllDirty);
 
 protected:
   nsFrameList mAbsoluteFrames;  // additional named child list
 
 #ifdef DEBUG
   ChildListID const mChildListID; // kFixedList or kAbsoluteList
-
-  // helper routine for debug printout
-  void PrettyUC(nscoord aSize,
-                char*   aBuf);
 #endif
 };
 
 #endif /* nsnsAbsoluteContainingBlock_h___ */
-
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -70,16 +70,17 @@ static const int MIN_LINES_NEEDING_CURSO
 static const PRUnichar kDiscCharacter = 0x2022;
 static const PRUnichar kCircleCharacter = 0x25e6;
 static const PRUnichar kSquareCharacter = 0x25aa;
 
 #define DISABLE_FLOAT_BREAKING_IN_COLUMNS
 
 using namespace mozilla;
 using namespace mozilla::css;
+using namespace mozilla::layout;
 
 #ifdef DEBUG
 #include "nsBlockDebugFlags.h"
 
 bool nsBlockFrame::gLamePaintMetrics;
 bool nsBlockFrame::gLameReflowMetrics;
 bool nsBlockFrame::gNoisy;
 bool nsBlockFrame::gNoisyDamageRepair;
@@ -243,22 +244,19 @@ RecordReflowStatus(bool aChildIsBlock, n
 // Destructor function for the overflowLines frame property
 static void
 DestroyOverflowLines(void* aPropertyValue)
 {
   NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable");
 }
 
 NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines)
-NS_DECLARE_FRAME_PROPERTY(OverflowOutOfFlowsProperty,
-                          nsContainerFrame::DestroyFrameList)
-NS_DECLARE_FRAME_PROPERTY(PushedFloatProperty,
-                          nsContainerFrame::DestroyFrameList)
-NS_DECLARE_FRAME_PROPERTY(OutsideBulletProperty,
-                          nsContainerFrame::DestroyFrameList)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty)
 NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nullptr)
 
 //----------------------------------------------------------------------
 
 nsIFrame*
 NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, uint32_t aFlags)
 {
   nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext);
@@ -274,36 +272,46 @@ nsBlockFrame::~nsBlockFrame()
 
 void
 nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   ClearLineCursor();
   DestroyAbsoluteFrames(aDestructRoot);
   mFloats.DestroyFramesFrom(aDestructRoot);
   nsPresContext* presContext = PresContext();
+  nsIPresShell* shell = presContext->PresShell();
   nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
                             &mFrames);
 
-  nsFrameList* pushedFloats = RemovePushedFloats();
-  if (pushedFloats) {
-    pushedFloats->DestroyFrom(aDestructRoot);
+  FramePropertyTable* props = presContext->PropertyTable();
+
+  if (HasPushedFloats()) {
+    SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+                               PushedFloatProperty());
+    RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
   }
 
   // destroy overflow lines now
   FrameLines* overflowLines = RemoveOverflowLines();
   if (overflowLines) {
     nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
                               aDestructRoot, &overflowLines->mFrames);
     delete overflowLines;
   }
 
-  {
-    nsAutoOOFFrameList oofs(this);
-    oofs.mList.DestroyFramesFrom(aDestructRoot);
-    // oofs is now empty and will remove the frame list property
+  if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
+    SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+                               OverflowOutOfFlowsProperty());
+    RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
+  }
+
+  if (HasOutsideBullet()) {
+    SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+                               OutsideBulletProperty());
+    RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
   }
 
   nsBlockFrameSuper::DestroyFrom(aDestructRoot);
 }
 
 /* virtual */ nsILineIterator*
 nsBlockFrame::GetLineIterator()
 {
@@ -4468,30 +4476,30 @@ nsBlockFrame::DrainPushedFloats(nsBlockR
   // placeholders were in earlier blocks (since first-in-flows whose
   // placeholders are in this block will get pulled appropriately by
   // AddFloat, and will then be more likely to be in the correct order).
   // FIXME: What if there's a continuation in our pushed floats list
   // whose prev-in-flow is in a previous continuation of this block
   // rather than this block?  Might we need to pull it back so we don't
   // report ourselves complete?
   // FIXME: Maybe we should just pull all of them back?
-  nsFrameList *ourPushedFloats = GetPushedFloats();
+  nsPresContext* presContext = PresContext();
+  nsFrameList* ourPushedFloats = GetPushedFloats();
   if (ourPushedFloats) {
     // When we pull back floats, we want to put them with the pushed
     // floats, which must live at the start of our float list, but we
     // want them at the end of those pushed floats.
     // FIXME: This isn't quite right!  What if they're all pushed floats?
     nsIFrame *insertionPrevSibling = nullptr; /* beginning of list */
     for (nsIFrame* f = mFloats.FirstChild();
          f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
          f = f->GetNextSibling()) {
       insertionPrevSibling = f;
     }
 
-    nsPresContext *presContext = PresContext();
     for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) {
       next = f->GetPrevSibling();
 
       if (f->GetPrevContinuation()) {
         // FIXME
       } else {
         nsPlaceholderFrame *placeholder =
           presContext->FrameManager()->GetPlaceholderFrameFor(f);
@@ -4504,30 +4512,27 @@ nsBlockFrame::DrainPushedFloats(nsBlockR
           // floats, but after other pushed floats.
           ourPushedFloats->RemoveFrame(f);
           mFloats.InsertFrame(nullptr, insertionPrevSibling, f);
         }
       }
     }
 
     if (ourPushedFloats->IsEmpty()) {
-      delete RemovePushedFloats();
+      RemovePushedFloats()->Delete(presContext->PresShell());
     }
   }
 
   // After our prev-in-flow has completed reflow, it may have a pushed
   // floats list, containing floats that we need to own.  Take these.
   nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
   if (prevBlock) {
-    nsFrameList *list = prevBlock->RemovePushedFloats();
-    if (list) {
-      if (list->NotEmpty()) {
-        mFloats.InsertFrames(this, nullptr, *list);
-      }
-      delete list;
+    AutoFrameListPtr list(presContext, prevBlock->RemovePushedFloats());
+    if (list && list->NotEmpty()) {
+      mFloats.InsertFrames(this, nullptr, *list);
     }
   }
 }
 
 nsBlockFrame::FrameLines*
 nsBlockFrame::GetOverflowLines() const
 {
   if (!HasOverflowLines()) {
@@ -4609,31 +4614,32 @@ nsBlockFrame::SetOverflowOutOfFlows(cons
 {
   NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) ==
                   !!aPropValue, "state does not match value");
 
   if (aList.IsEmpty()) {
     if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
       return;
     }
-    nsFrameList* list =
-      RemovePropTableFrames(PresContext(),
-                            OverflowOutOfFlowsProperty());
+    nsPresContext* pc = PresContext();
+    nsFrameList* list = RemovePropTableFrames(pc, OverflowOutOfFlowsProperty());
     NS_ASSERTION(aPropValue == list, "prop value mismatch");
-    delete list;
+    list->Clear();
+    list->Delete(pc->PresShell());
     RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
   }
   else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
     NS_ASSERTION(aPropValue == GetPropTableFrames(PresContext(),
                                  OverflowOutOfFlowsProperty()),
                  "prop value mismatch");
     *aPropValue = aList;
   }
   else {
-    SetPropTableFrames(PresContext(), new nsFrameList(aList),
+    nsPresContext* pc = PresContext();
+    SetPropTableFrames(pc, new (pc->PresShell()) nsFrameList(aList),
                        OverflowOutOfFlowsProperty());
     AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
   }
 }
 
 nsBulletFrame*
 nsBlockFrame::GetInsideBullet() const
 {
@@ -4685,17 +4691,17 @@ nsBlockFrame::GetPushedFloats() const
 
 nsFrameList*
 nsBlockFrame::EnsurePushedFloats()
 {
   nsFrameList *result = GetPushedFloats();
   if (result)
     return result;
 
-  result = new nsFrameList;
+  result = new (PresContext()->PresShell()) nsFrameList;
   Properties().Set(PushedFloatProperty(), result);
   AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
 
   return result;
 }
 
 nsFrameList*
 nsBlockFrame::RemovePushedFloats()
@@ -6581,17 +6587,17 @@ nsBlockFrame::SetInitialChildList(ChildL
       // it to the flow now.
       if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
             styleList->mListStylePosition) {
         nsFrameList bulletList(bullet, bullet);
         AddFrames(bulletList, nullptr);
         Properties().Set(InsideBulletProperty(), bullet);
         AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
       } else {
-        nsFrameList* bulletList = new nsFrameList(bullet, bullet);
+        nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet);
         Properties().Set(OutsideBulletProperty(), bulletList);
         AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
       }
     }
   }
 
   return NS_OK;
 }
--- a/layout/generic/nsBlockReflowState.cpp
+++ b/layout/generic/nsBlockReflowState.cpp
@@ -940,31 +940,40 @@ nsBlockReflowState::ClearFloats(nscoord 
   }
 #endif
 
 #ifdef NOISY_FLOAT_CLEARING
   printf("nsBlockReflowState::ClearFloats: aY=%d breakType=%d\n",
          aY, aBreakType);
   mFloatManager->List(stdout);
 #endif
-  
+
+  if (!mFloatManager->HasAnyFloats()) {
+    return aY;
+  }
+
   nscoord newY = aY;
 
   if (aBreakType != NS_STYLE_CLEAR_NONE) {
     newY = mFloatManager->ClearFloats(newY, aBreakType, aFlags);
   }
 
   if (aReplacedBlock) {
     for (;;) {
       nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newY);
+      if (!floatAvailableSpace.mHasFloats) {
+        // If there aren't any floats here, then we always fit.
+        // We check this before calling WidthToClearPastFloats, which is
+        // somewhat expensive.
+        break;
+      }
       nsBlockFrame::ReplacedElementWidthToClear replacedWidth =
         nsBlockFrame::WidthToClearPastFloats(*this, floatAvailableSpace.mRect,
                                              aReplacedBlock);
-      if (!floatAvailableSpace.mHasFloats ||
-          std::max(floatAvailableSpace.mRect.x - mContentArea.x,
+      if (std::max(floatAvailableSpace.mRect.x - mContentArea.x,
                  replacedWidth.marginLeft) +
             replacedWidth.borderBoxWidth +
             std::max(mContentArea.XMost() - floatAvailableSpace.mRect.XMost(),
                    replacedWidth.marginRight) <=
           mContentArea.width) {
         break;
       }
       // See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -22,20 +22,19 @@
 #include "nsDisplayList.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsFrameManager.h"
 
 // for focus
 #include "nsIScrollableFrame.h"
 #include "nsIDocShell.h"
 
-#ifdef DEBUG_rods
 //#define DEBUG_CANVAS_FOCUS
-#endif
 
+using namespace mozilla::layout;
 
 nsIFrame*
 NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsCanvasFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
@@ -425,17 +424,18 @@ nsCanvasFrame::Reflow(nsPresContext*    
   NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
 
   // Initialize OUT parameter
   aStatus = NS_FRAME_COMPLETE;
 
   nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>
                                                (GetPrevInFlow());
   if (prevCanvasFrame) {
-    nsAutoPtr<nsFrameList> overflow(prevCanvasFrame->StealOverflowFrames());
+    AutoFrameListPtr overflow(aPresContext,
+                              prevCanvasFrame->StealOverflowFrames());
     if (overflow) {
       NS_ASSERTION(overflow->OnlyChild(),
                    "must have doc root as canvas frame's only child");
       nsContainerFrame::ReparentFrameViewList(aPresContext, *overflow,
                                               prevCanvasFrame, this);
       // Prepend overflow to the our child list. There may already be
       // children placeholders for fixed-pos elements, which don't get
       // reflowed but must not be lost until the canvas frame is destroyed.
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -16,16 +16,17 @@
 #include "nsStyleConsts.h"
 #include "nsCOMPtr.h"
 #include "nsLayoutUtils.h"
 #include "nsDisplayList.h"
 #include "nsCSSRendering.h"
 #include <algorithm>
 
 using namespace mozilla;
+using namespace mozilla::layout;
 
 class nsColumnSetFrame : public nsContainerFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   nsColumnSetFrame(nsStyleContext* aContext);
 
   NS_IMETHOD SetInitialChildList(ChildListID     aListID,
@@ -860,30 +861,31 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR
     && !NS_FRAME_IS_TRUNCATED(aStatus);
 }
 
 void
 nsColumnSetFrame::DrainOverflowColumns()
 {
   // First grab the prev-in-flows overflows and reparent them to this
   // frame.
+  nsPresContext* presContext = PresContext();
   nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow());
   if (prev) {
-    nsAutoPtr<nsFrameList> overflows(prev->StealOverflowFrames());
+    AutoFrameListPtr overflows(presContext, prev->StealOverflowFrames());
     if (overflows) {
-      nsContainerFrame::ReparentFrameViewList(PresContext(), *overflows,
+      nsContainerFrame::ReparentFrameViewList(presContext, *overflows,
                                               prev, this);
 
       mFrames.InsertFrames(this, nullptr, *overflows);
     }
   }
   
   // Now pull back our own overflows and append them to our children.
   // We don't need to reparent them since we're already their parent.
-  nsAutoPtr<nsFrameList> overflows(StealOverflowFrames());
+  AutoFrameListPtr overflows(presContext, StealOverflowFrames());
   if (overflows) {
     // We're already the parent for these frames, so no need to set
     // their parent again.
     mFrames.AppendFrames(nullptr, *overflows);
   }
 }
 
 NS_IMETHODIMP 
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -43,16 +43,17 @@
 #ifdef DEBUG
 #undef NOISY
 #else
 #undef NOISY
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::layout;
 
 NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame)
 
 nsContainerFrame::~nsContainerFrame()
 {
 }
 
 NS_QUERYFRAME_HEAD(nsContainerFrame)
@@ -212,62 +213,65 @@ nsContainerFrame::DestroyAbsoluteFrames(
   if (IsAbsoluteContainer()) {
     GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot);
     MarkAsNotAbsoluteContainingBlock();
   }
 }
 
 void
 nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
+                                             nsIPresShell* aPresShell,
                                              FramePropertyTable* aPropTable,
                                              const FramePropertyDescriptor* aProp)
 {
   // Note that the last frame can be removed through another route and thus
   // delete the property -- that's why we fetch the property again before
   // removing each frame rather than fetching it once and iterating the list.
   while (nsFrameList* frameList =
            static_cast<nsFrameList*>(aPropTable->Get(this, aProp))) {
     nsIFrame* frame = frameList->RemoveFirstChild();
     if (MOZ_LIKELY(frame)) {
       frame->DestroyFrom(aDestructRoot);
     } else {
       aPropTable->Remove(this, aProp);
-      delete frameList;
+      frameList->Delete(aPresShell);
       return;
     }
   }
 }
 
 void
 nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
-  // Prevent event dispatch during destruction
+  // Prevent event dispatch during destruction.
   if (HasView()) {
     GetView()->SetFrame(nullptr);
   }
 
   DestroyAbsoluteFrames(aDestructRoot);
 
-  // Delete the primary child list
+  // Destroy frames on the principal child list.
   mFrames.DestroyFramesFrom(aDestructRoot);
 
-  // Destroy auxiliary frame lists
-  nsPresContext* prescontext = PresContext();
-
-  DestroyOverflowList(prescontext, aDestructRoot);
+  // Destroy frames on the auxiliary frame lists and delete the lists.
+  nsPresContext* pc = PresContext();
+  nsIPresShell* shell = pc->PresShell();
+  FramePropertyTable* props = pc->PropertyTable();
+  SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty());
 
-  if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
-    FramePropertyTable* props = prescontext->PropertyTable();
-    SafelyDestroyFrameListProp(aDestructRoot, props,
-                               OverflowContainersProperty());
-    SafelyDestroyFrameListProp(aDestructRoot, props,
-                               ExcessOverflowContainersProperty());
-  }
+  MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) ||
+             !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) ||
+               props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())),
+             "this type of frame should't have overflow containers");
 
-  // Destroy the frame and remove the flow pointers
+  SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+                             OverflowContainersProperty());
+  SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+                             ExcessOverflowContainersProperty());
+
   nsSplittableFrame::DestroyFrom(aDestructRoot);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Child frame enumeration
 
 const nsFrameList&
 nsContainerFrame::GetChildList(ChildListID aListID) const
@@ -1107,17 +1111,17 @@ nsContainerFrame::ReflowOverflowContaine
 
   // Our own excess overflow containers from a previous reflow can still be
   // present if our next-in-flow hasn't been reflown yet.
   nsFrameList* selfExcessOCFrames =
     RemovePropTableFrames(aPresContext, ExcessOverflowContainersProperty());
   if (selfExcessOCFrames) {
     if (overflowContainers) {
       overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames);
-      delete selfExcessOCFrames;
+      selfExcessOCFrames->Delete(aPresContext->PresShell());
     } else {
       overflowContainers = selfExcessOCFrames;
       SetPropTableFrames(aPresContext, overflowContainers,
                          OverflowContainersProperty());
     }
   }
   if (!overflowContainers) {
     return NS_OK; // nothing to reflow
@@ -1225,17 +1229,17 @@ static bool
 TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable,
                const FramePropertyDescriptor* aProp, nsIFrame* aChildToRemove)
 {
   nsFrameList* list = static_cast<nsFrameList*>(aPropTable->Get(aFrame, aProp));
   if (list && list->StartRemoveFrame(aChildToRemove)) {
     // aChildToRemove *may* have been removed from this list.
     if (list->IsEmpty()) {
       aPropTable->Remove(aFrame, aProp);
-      delete list;
+      list->Delete(aFrame->PresContext()->PresShell());
     }
     return true;
   }
   return false;
 }
 
 nsresult
 nsContainerFrame::StealFrame(nsPresContext* aPresContext,
@@ -1276,17 +1280,17 @@ nsContainerFrame::StealFrame(nsPresConte
     removed = mFrames.StartRemoveFrame(aChild);
     if (!removed) {
       // We didn't find the child in our principal child list.
       // Maybe it's on the overflow list?
       nsFrameList* frameList = GetOverflowFrames();
       if (frameList) {
         removed = frameList->ContinueRemoveFrame(aChild);
         if (frameList->IsEmpty()) {
-          DestroyOverflowList(aPresContext, nullptr);
+          DestroyOverflowList(aPresContext);
         }
       }
     }
   }
 
   NS_POSTCONDITION(removed, "StealFrame: can't find aChild");
   return removed ? NS_OK : NS_ERROR_UNEXPECTED;
 }
@@ -1323,30 +1327,16 @@ nsContainerFrame::StealFramesAfter(nsIFr
       }
     }
   }
 
   NS_ERROR("StealFramesAfter: can't find aChild");
   return nsFrameList::EmptyList();
 }
 
-void
-nsContainerFrame::DestroyOverflowList(nsPresContext* aPresContext,
-                                      nsIFrame*      aDestructRoot)
-{
-  nsFrameList* list =
-    RemovePropTableFrames(aPresContext, OverflowProperty());
-  if (list) {
-    if (aDestructRoot)
-      list->DestroyFrom(aDestructRoot);
-    else
-      list->Destroy();
-  }
-}
-
 /*
  * Create a next-in-flow for aFrame. Will return the newly created
  * frame in aNextInFlowResult <b>if and only if</b> a new frame is
  * created; otherwise nullptr is returned in aNextInFlowResult.
  */
 nsresult
 nsContainerFrame::CreateNextInFlow(nsPresContext* aPresContext,
                                    nsIFrame*      aFrame,
@@ -1429,17 +1419,17 @@ nsContainerFrame::DeleteNextInFlowChild(
 /**
  * Set the frames on the overflow list
  */
 void
 nsContainerFrame::SetOverflowFrames(nsPresContext* aPresContext,
                                     const nsFrameList& aOverflowFrames)
 {
   NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
-  nsFrameList* newList = new nsFrameList(aOverflowFrames);
+  nsFrameList* newList = new (aPresContext->PresShell()) nsFrameList(aOverflowFrames);
 
   aPresContext->PropertyTable()->Set(this, OverflowProperty(), newList);
 }
 
 nsFrameList*
 nsContainerFrame::GetPropTableFrames(nsPresContext*                 aPresContext,
                                      const FramePropertyDescriptor* aProperty) const
 {
@@ -1461,16 +1451,17 @@ nsContainerFrame::SetPropTableFrames(nsP
                                      const FramePropertyDescriptor* aProperty)
 {
   NS_PRECONDITION(aPresContext && aProperty && aFrameList, "null ptr");
   NS_PRECONDITION(
     (aProperty != nsContainerFrame::OverflowContainersProperty() &&
      aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
     IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
     "this type of frame can't have overflow containers");
+  MOZ_ASSERT(!GetPropTableFrames(aPresContext, aProperty));
   aPresContext->PropertyTable()->Set(this, aProperty, aFrameList);
 }
 
 /**
  * Push aFromChild and its next siblings to the next-in-flow. Change the
  * geometric parent of each frame that's pushed. If there is no next-in-flow
  * the frames are placed on the overflow list (and the geometric parent is
  * left unchanged).
@@ -1525,17 +1516,18 @@ nsContainerFrame::PushChildren(nsPresCon
 bool
 nsContainerFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
 {
   bool result = false;
 
   // Check for an overflow list with our prev-in-flow
   nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
   if (nullptr != prevInFlow) {
-    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+    AutoFrameListPtr prevOverflowFrames(aPresContext,
+                                        prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
       // Tables are special; they can have repeated header/footer
       // frames on mFrames at this point.
       NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame,
                    "bad overflow list");
       // When pushing and pulling frames we need to check for whether any
       // views need to be reparented.
       nsContainerFrame::ReparentFrameViewList(aPresContext,
@@ -1548,17 +1540,17 @@ nsContainerFrame::MoveOverflowToChildLis
 
   // It's also possible that we have an overflow list for ourselves.
   return DrainSelfOverflowList() || result;
 }
 
 bool
 nsContainerFrame::DrainSelfOverflowList()
 {
-  nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
+  AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
   if (overflowFrames) {
     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
     mFrames.AppendFrames(nullptr, *overflowFrames);
     return true;
   }
   return false;
 }
 
@@ -1691,17 +1683,17 @@ nsOverflowContinuationTracker::Insert(ns
       rv = static_cast<nsContainerFrame*>(aOverflowCont->GetParent())
              ->StealFrame(presContext, aOverflowCont);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
       aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
     }
     if (!mOverflowContList) {
-      mOverflowContList = new nsFrameList();
+      mOverflowContList = new (presContext->PresShell()) nsFrameList();
       mParent->SetPropTableFrames(presContext, mOverflowContList,
         nsContainerFrame::ExcessOverflowContainersProperty());
       SetUpListWalker();
     }
     if (aOverflowCont->GetParent() != mParent) {
       nsContainerFrame::ReparentFrameView(presContext, aOverflowCont,
                                           aOverflowCont->GetParent(),
                                           mParent);
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -7,17 +7,16 @@
 
 #ifndef nsContainerFrame_h___
 #define nsContainerFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "nsSplittableFrame.h"
 #include "nsFrameList.h"
 #include "nsLayoutUtils.h"
-#include "nsAutoPtr.h"
 
 // Option flags for ReflowChild() and FinishReflowChild()
 // member functions
 #define NS_FRAME_NO_MOVE_VIEW         0x0001
 #define NS_FRAME_NO_MOVE_FRAME        (0x0002 | NS_FRAME_NO_MOVE_VIEW)
 #define NS_FRAME_NO_SIZE_VIEW         0x0004
 #define NS_FRAME_NO_VISIBILITY        0x0008
 // Only applies to ReflowChild; if true, don't delete the next-in-flow, even
@@ -353,27 +352,31 @@ public:
    * paint the background/borders/outline of this frame. This should
    * probably be avoided and eventually removed. It's currently here
    * to emulate what nsContainerFrame::Paint did.
    */
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
-  // Destructor function for the proptable-stored framelists
+  /**
+   * Destructor function for the proptable-stored framelists --
+   * it should never be called.
+   */
   static void DestroyFrameList(void* aPropertyValue)
   {
-    if (aPropertyValue) {
-      static_cast<nsFrameList*>(aPropertyValue)->Destroy();
-    }
+    MOZ_ASSERT(false, "The owning frame should destroy its nsFrameList props");
   }
 
-  NS_DECLARE_FRAME_PROPERTY(OverflowProperty, DestroyFrameList)
-  NS_DECLARE_FRAME_PROPERTY(OverflowContainersProperty, DestroyFrameList)
-  NS_DECLARE_FRAME_PROPERTY(ExcessOverflowContainersProperty, DestroyFrameList)
+#define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop)                     \
+  NS_DECLARE_FRAME_PROPERTY(prop, nsContainerFrame::DestroyFrameList)
+
+  NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty)
+  NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty)
+  NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty)
 
 protected:
   nsContainerFrame(nsStyleContext* aContext) : nsSplittableFrame(aContext) {}
   ~nsContainerFrame();
 
   /**
    * Helper for DestroyFrom. DestroyAbsoluteFrames is called before
    * destroying frames on lists that can contain placeholders.
@@ -422,36 +425,33 @@ protected:
    * still owned by this frame.  A non-null return value indicates that the
    * list is nonempty.
    */
   inline nsFrameList* GetOverflowFrames() const;
 
   /**
    * As GetOverflowFrames, but removes the overflow frames property.  The
    * caller is responsible for deleting nsFrameList and either passing
-   * ownership of the frames to someone else or destroying the frames.  A
-   * non-null return value indicates that the list is nonempty.  The
+   * ownership of the frames to someone else or destroying the frames.
+   * A non-null return value indicates that the list is nonempty.  The
    * recommended way to use this function it to assign its return value
-   * into an nsAutoPtr.
+   * into an AutoFrameListPtr.
    */
   inline nsFrameList* StealOverflowFrames();
   
   /**
    * Set the overflow list.  aOverflowFrames must not be an empty list.
    */
   void SetOverflowFrames(nsPresContext*  aPresContext,
                          const nsFrameList& aOverflowFrames);
 
   /**
-   * Destroy the overflow list and any frames that are on it.
-   * Calls DestructFrom() insead of Destruct() on the frames if
-   * aDestructRoot is non-null.
+   * Destroy the overflow list, which must be empty.
    */
-  void DestroyOverflowList(nsPresContext* aPresContext,
-                           nsIFrame*      aDestructRoot);
+  inline void DestroyOverflowList(nsPresContext* aPresContext);
 
   /**
    * Moves any frames on both the prev-in-flow's overflow list and the
    * receiver's overflow to the receiver's child list.
    *
    * Resets the overlist pointers to nullptr, and updates the receiver's child
    * count and content mapping.
    *
@@ -506,16 +506,17 @@ protected:
                           const FramePropertyDescriptor* aProperty);
 
   /**
    * Safely destroy the frames on the nsFrameList stored on aProp for this
    * frame then remove the property and delete the frame list.
    * Nothing happens if the property doesn't exist.
    */
   void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
+                                  nsIPresShell* aPresShell,
                                   mozilla::FramePropertyTable* aPropTable,
                                   const FramePropertyDescriptor* aProp);
 
   // ==========================================================================
 
   nsFrameList mFrames;
 };
 
@@ -673,9 +674,17 @@ nsFrameList*
 nsContainerFrame::StealOverflowFrames()
 {
   nsFrameList* list =
     static_cast<nsFrameList*>(Properties().Remove(OverflowProperty()));
   NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
   return list;
 }
 
+inline void
+nsContainerFrame::DestroyOverflowList(nsPresContext* aPresContext)
+{
+  nsFrameList* list = RemovePropTableFrames(aPresContext, OverflowProperty());
+  MOZ_ASSERT(list && list->IsEmpty());
+  list->Delete(aPresContext->PresShell());
+}
+
 #endif /* nsContainerFrame_h___ */
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -13,17 +13,18 @@
 #include "nsLineLayout.h"
 #include "nsGkAtoms.h"
 #include "nsAutoPtr.h"
 #include "nsStyleSet.h"
 #include "nsFrameManager.h"
 #include "nsPlaceholderFrame.h"
 #include "nsCSSFrameConstructor.h"
 
-using namespace::mozilla;
+using namespace mozilla;
+using namespace mozilla::layout;
 
 nsIFrame*
 NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsFirstLetterFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame)
@@ -340,35 +341,34 @@ nsFirstLetterFrame::CreateContinuationFo
 
   *aContinuation = continuation;
   return rv;
 }
 
 void
 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
 {
-  nsAutoPtr<nsFrameList> overflowFrames;
-
   // Check for an overflow list with our prev-in-flow
   nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
-  if (nullptr != prevInFlow) {
-    overflowFrames = prevInFlow->StealOverflowFrames();
+  if (prevInFlow) {
+    AutoFrameListPtr overflowFrames(aPresContext,
+                                    prevInFlow->StealOverflowFrames());
     if (overflowFrames) {
       NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
 
       // When pushing and pulling frames we need to check for whether any
       // views need to be reparented.
       nsContainerFrame::ReparentFrameViewList(aPresContext, *overflowFrames,
                                               prevInFlow, this);
       mFrames.InsertFrames(this, nullptr, *overflowFrames);
     }
   }
 
   // It's also possible that we have an overflow list for ourselves
-  overflowFrames = StealOverflowFrames();
+  AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
   if (overflowFrames) {
     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
     mFrames.AppendFrames(nullptr, *overflowFrames);
   }
 
   // Now repair our first frames style context (since we only reflow
   // one frame there is no point in doing any other ones until they
   // are reflowed)
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -1,51 +1,47 @@
 /* -*- 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 "nsFrameList.h"
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
 
 #ifdef IBMBIDI
 #include "nsCOMPtr.h"
 #include "nsGkAtoms.h"
 #include "nsILineIterator.h"
 #include "nsBidiPresUtils.h"
 #endif // IBMBIDI
 
-const nsFrameList* nsFrameList::sEmptyList;
+namespace mozilla {
+namespace layout {
+namespace detail {
+const AlignedFrameListBytes gEmptyFrameListBytes = { 0 };
+}
+}
+}
 
-/* static */
-void
-nsFrameList::Init()
+void*
+nsFrameList::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
 {
-  NS_PRECONDITION(!sEmptyList, "Shouldn't be allocated");
-
-  sEmptyList = new nsFrameList();
+  return aPresShell->AllocateByObjectID(nsPresArena::nsFrameList_id, sz);
 }
 
 void
-nsFrameList::Destroy()
+nsFrameList::Delete(nsIPresShell* aPresShell)
 {
-  NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
-
-  DestroyFrames();
-  delete this;
-}
+  NS_PRECONDITION(this != &EmptyList(), "Shouldn't Delete() this list");
+  NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list");
 
-void
-nsFrameList::DestroyFrom(nsIFrame* aDestructRoot)
-{
-  NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
-
-  DestroyFramesFrom(aDestructRoot);
-  delete this;
+  aPresShell->FreeByObjectID(nsPresArena::nsFrameList_id, this);
 }
 
 void
 nsFrameList::DestroyFrames()
 {
   while (nsIFrame* frame = RemoveFirstChild()) {
     frame->Destroy();
   }
@@ -539,8 +535,21 @@ nsFrameList::VerifyList() const
 
   NS_ASSERTION(mLastChild == nsLayoutUtils::GetLastSibling(mFirstChild),
                "bogus mLastChild");
   // XXX we should also assert that all GetParent() are either null or
   // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
   // prevents that, e.g. table captions.
 }
 #endif
+
+namespace mozilla {
+namespace layout {
+
+AutoFrameListPtr::~AutoFrameListPtr()
+{
+  if (mFrameList) {
+    mFrameList->Delete(mPresContext->PresShell());
+  }
+}
+
+}
+}
--- a/layout/generic/nsFrameList.h
+++ b/layout/generic/nsFrameList.h
@@ -8,16 +8,19 @@
 
 #include "nscore.h"
 #include "nsTraceRefcnt.h"
 #include <stdio.h> /* for FILE* */
 #include "nsDebug.h"
 #include "nsTArray.h"
 
 class nsIFrame;
+class nsIPresShell;
+class nsPresContext;
+
 namespace mozilla {
 namespace layout {
   class FrameChildList;
   enum FrameChildListID {
       // The individual concrete child lists.
       kPrincipalList                = 0x1,
       kPopupList                    = 0x2,
       kCaptionList                  = 0x4,
@@ -45,63 +48,52 @@ namespace layout {
 /**
  * A class for managing a list of frames.
  */
 class nsFrameList {
 public:
   nsFrameList() :
     mFirstChild(nullptr), mLastChild(nullptr)
   {
-    MOZ_COUNT_CTOR(nsFrameList);
   }
 
   nsFrameList(nsIFrame* aFirstFrame, nsIFrame* aLastFrame) :
     mFirstChild(aFirstFrame), mLastChild(aLastFrame)
   {
-    MOZ_COUNT_CTOR(nsFrameList);
     VerifyList();
   }
 
   nsFrameList(const nsFrameList& aOther) :
     mFirstChild(aOther.mFirstChild), mLastChild(aOther.mLastChild)
   {
-    MOZ_COUNT_CTOR(nsFrameList);
   }
 
-  ~nsFrameList() {
-    MOZ_COUNT_DTOR(nsFrameList);
-    // Don't destroy our frames here, so that we can have temporary nsFrameLists
-  }
+  /**
+   * Allocate a nsFrameList from the shell arena.
+   */
+  void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW;
+
+  /**
+   * Deallocate this list that was allocated from the shell arena.
+   * The list is required to be empty.
+   */
+  void Delete(nsIPresShell* aPresShell);
 
   /**
    * For each frame in this list: remove it from the list then call
    * Destroy() on it.
    */
   void DestroyFrames();
 
   /**
    * For each frame in this list: remove it from the list then call
-   * DestroyFrom() on it.
+   * DestroyFrom(aDestructRoot) on it.
    */
   void DestroyFramesFrom(nsIFrame* aDestructRoot);
 
-  /**
-   * For each frame in this list: remove it from the list then call
-   * Destroy() on it. Finally <code>delete this</code>.
-   * 
-   */
-  void Destroy();
-
-  /**
-   * For each frame in this list: remove it from the list then call
-   * DestroyFrom() on it. Finally <code>delete this</code>.
-   *
-   */
-  void DestroyFrom(nsIFrame* aDestructRoot);
-
   void Clear() { mFirstChild = mLastChild = nullptr; }
 
   void SetFrames(nsIFrame* aFrameList);
 
   void SetFrames(nsFrameList& aFrameList) {
     NS_PRECONDITION(!mFirstChild, "Losing frames");
 
     mFirstChild = aFrameList.FirstChild();
@@ -286,19 +278,17 @@ public:
    */
   nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const;
 #endif // IBMBIDI
 
 #ifdef DEBUG
   void List(FILE* out) const;
 #endif
 
-  static void Init();
-  static void Shutdown() { delete sEmptyList; }
-  static const nsFrameList& EmptyList() { return *sEmptyList; }
+  static inline const nsFrameList& EmptyList();
 
   class Enumerator;
 
   /**
    * A class representing a slice of a frame list.
    */
   class Slice {
     friend class Enumerator;
@@ -453,30 +443,66 @@ public:
     nsIFrame* PrevFrame() const { return mPrev; }
     nsIFrame* NextFrame() const { return mFrame; }
 
   protected:
     nsIFrame* mPrev;
   };
 
 private:
+  void operator delete(void*) MOZ_DELETE;
+
 #ifdef DEBUG_FRAME_LIST
   void VerifyList() const;
 #else
   void VerifyList() const {}
 #endif
 
-  static const nsFrameList* sEmptyList;
-
 protected:
   /**
    * Disconnect aFrame from its siblings.  This must only be called if aFrame
    * is NOT the first or last sibling, because otherwise its nsFrameList will
    * have a stale mFirst/LastChild pointer.  This precondition is asserted.
    * This function is O(1).
    */
   static void UnhookFrameFromSiblings(nsIFrame* aFrame);
 
   nsIFrame* mFirstChild;
   nsIFrame* mLastChild;
 };
 
+namespace mozilla {
+namespace layout {
+
+/**
+ * Simple "auto_ptr" for nsFrameLists allocated from the shell arena.
+ * The frame list given to the constructor will be deallocated (if non-null)
+ * in the destructor.  The frame list must then be empty.
+ */
+class AutoFrameListPtr {
+public:
+  AutoFrameListPtr(nsPresContext* aPresContext, nsFrameList* aFrameList)
+    : mPresContext(aPresContext), mFrameList(aFrameList) {}
+  ~AutoFrameListPtr();
+  operator nsFrameList*() const { return mFrameList; }
+  nsFrameList* operator->() const { return mFrameList; }
+private:
+  nsPresContext* mPresContext;
+  nsFrameList* mFrameList;
+};
+
+namespace detail {
+union AlignedFrameListBytes {
+  void* ptr;
+  char bytes[sizeof(nsFrameList)];
+};
+extern const AlignedFrameListBytes gEmptyFrameListBytes;
+}
+}
+}
+
+/* static */ inline const nsFrameList&
+nsFrameList::EmptyList()
+{
+  return *reinterpret_cast<const nsFrameList*>(&mozilla::layout::detail::gEmptyFrameListBytes);
+}
+
 #endif /* nsFrameList_h___ */
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -22,16 +22,17 @@
 #include "nsDisplayList.h"
 #include "mozilla/Likely.h"
 
 #ifdef DEBUG
 #undef NOISY_PUSHING
 #endif
 
 using namespace mozilla;
+using namespace mozilla::layout;
 
 
 //////////////////////////////////////////////////////////////////////
 
 // Basic nsInlineFrame methods
 
 nsIFrame*
 NS_NewInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@@ -307,19 +308,19 @@ nsInlineFrame::Reflow(nsPresContext*    
   }
 
   bool    lazilySetParentPointer = false;
 
   nsIFrame* lineContainer = aReflowState.mLineLayout->GetLineContainerFrame();
 
    // Check for an overflow list with our prev-in-flow
   nsInlineFrame* prevInFlow = (nsInlineFrame*)GetPrevInFlow();
-  if (nullptr != prevInFlow) {
-    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
-
+  if (prevInFlow) {
+    AutoFrameListPtr prevOverflowFrames(aPresContext,
+                                        prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
       // When pushing and pulling frames we need to check for whether any
       // views need to be reparented.
       nsContainerFrame::ReparentFrameViewList(aPresContext,
                                               *prevOverflowFrames,
                                               prevInFlow, this);
 
       // Check if we should do the lazilySetParentPointer optimization.
@@ -365,17 +366,17 @@ nsInlineFrame::Reflow(nsPresContext*    
     // However, add an assertion in case we get reflowed more than once with
     // the initial reflow reason
     nsFrameList* overflowFrames = GetOverflowFrames();
     NS_ASSERTION(!overflowFrames || overflowFrames->IsEmpty(),
                  "overflow list is not empty for initial reflow");
   }
 #endif
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
-    nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
+    AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
     if (overflowFrames) {
       NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
       if (!lazilySetParentPointer) {
         // The frames on our own overflowlist may have been pushed by a
         // previous lazilySetParentPointer Reflow so we need to ensure
         // the correct parent pointer now since we're not setting it
         // lazily in this Reflow.
         nsIFrame* firstChild = overflowFrames->FirstChild();
@@ -430,20 +431,22 @@ nsInlineFrame::CanContinueTextRun() cons
   return true;
 }
 
 /* virtual */ void
 nsInlineFrame::PullOverflowsFromPrevInFlow()
 {
   nsInlineFrame* prevInFlow = static_cast<nsInlineFrame*>(GetPrevInFlow());
   if (prevInFlow) {
-    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+    nsPresContext* presContext = PresContext();
+    AutoFrameListPtr prevOverflowFrames(presContext,
+                                        prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
       // Assume that our prev-in-flow has the same line container that we do.
-      nsContainerFrame::ReparentFrameViewList(PresContext(),
+      nsContainerFrame::ReparentFrameViewList(presContext,
                                               *prevOverflowFrames,
                                               prevInFlow, this);
       mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
     }
   }
 }
 
 nsresult
@@ -762,39 +765,38 @@ nsIFrame*
 nsInlineFrame::PullOneFrame(nsPresContext* aPresContext,
                             InlineReflowState& irs,
                             bool* aIsComplete)
 {
   bool isComplete = true;
 
   nsIFrame* frame = nullptr;
   nsInlineFrame* nextInFlow = irs.mNextInFlow;
-  while (nullptr != nextInFlow) {
+  while (nextInFlow) {
     frame = nextInFlow->mFrames.FirstChild();
     if (!frame) {
       // The nextInFlow's principal list has no frames, try its overflow list.
       nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
       if (overflowFrames) {
-        frame = overflowFrames->FirstChild();
-        if (!frame->GetNextSibling()) {
+        frame = overflowFrames->RemoveFirstChild();
+        if (overflowFrames->IsEmpty()) {
           // We're stealing the only frame - delete the overflow list.
-          delete nextInFlow->StealOverflowFrames();
+          nextInFlow->DestroyOverflowList(aPresContext);
         } else {
           // We leave the remaining frames on the overflow list (rather than
           // putting them on nextInFlow's principal list) so we don't have to
           // set up the parent for them.
-          overflowFrames->RemoveFirstChild();
         }
         // ReparentFloatsForInlineChild needs it to be on a child list -
         // we remove it again below.
         nextInFlow->mFrames.SetFrames(frame);
       }
     }
 
-    if (nullptr != frame) {
+    if (frame) {
       // If our block has no next continuation, then any floats belonging to
       // the pulled frame must belong to our block already. This check ensures
       // we do no extra work in the common non-vertical-breaking case.
       if (irs.mLineContainer && irs.mLineContainer->GetNextContinuation()) {
         // The blockChildren.ContainsFrame check performed by
         // ReparentFloatsForInlineChild will be fast because frame's ancestor
         // will be the first child of its containing block.
         ReparentFloatsForInlineChild(irs.mLineContainer, frame, false);
@@ -805,17 +807,17 @@ nsInlineFrame::PullOneFrame(nsPresContex
       mFrames.InsertFrame(this, irs.mPrevFrame, frame);
       isComplete = false;
       if (irs.mLineLayout) {
         irs.mLineLayout->SetDirtyNextLine();
       }
       nsContainerFrame::ReparentFrameView(aPresContext, frame, nextInFlow, this);
       break;
     }
-    nextInFlow = (nsInlineFrame*) nextInFlow->GetNextInFlow();
+    nextInFlow = static_cast<nsInlineFrame*>(nextInFlow->GetNextInFlow());
     irs.mNextInFlow = nextInFlow;
   }
 
   *aIsComplete = isComplete;
   return frame;
 }
 
 void
@@ -973,33 +975,34 @@ nsFirstLineFrame::Reflow(nsPresContext* 
   if (nullptr == aReflowState.mLineLayout) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsIFrame* lineContainer = aReflowState.mLineLayout->GetLineContainerFrame();
 
   // Check for an overflow list with our prev-in-flow
   nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)GetPrevInFlow();
-  if (nullptr != prevInFlow) {
-    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+  if (prevInFlow) {
+    AutoFrameListPtr prevOverflowFrames(aPresContext,
+                                        prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
       // Assign all floats to our block if necessary
       if (lineContainer && lineContainer->GetPrevContinuation()) {
         ReparentFloatsForInlineChild(lineContainer,
                                      prevOverflowFrames->FirstChild(),
                                      true);
       }
       const nsFrameList::Slice& newFrames =
         mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
       ReparentChildListStyle(aPresContext, newFrames, this);
     }
   }
 
   // It's also possible that we have an overflow list for ourselves
-  nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
+  AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
   if (overflowFrames) {
     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
 
     const nsFrameList::Slice& newFrames =
       mFrames.AppendFrames(nullptr, *overflowFrames);
     ReparentChildListStyle(aPresContext, newFrames, this);
   }
 
@@ -1079,18 +1082,20 @@ nsFirstLineFrame::Reflow(nsPresContext* 
   return rv;
 }
 
 /* virtual */ void
 nsFirstLineFrame::PullOverflowsFromPrevInFlow()
 {
   nsFirstLineFrame* prevInFlow = static_cast<nsFirstLineFrame*>(GetPrevInFlow());
   if (prevInFlow) {
-    nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+    nsPresContext* presContext = PresContext();
+    AutoFrameListPtr prevOverflowFrames(presContext,
+                                        prevInFlow->StealOverflowFrames());
     if (prevOverflowFrames) {
       // Assume that our prev-in-flow has the same line container that we do.
       const nsFrameList::Slice& newFrames =
         mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
-      ReparentChildListStyle(PresContext(), newFrames, this);
+      ReparentChildListStyle(presContext, newFrames, this);
     }
   }
 }
 
--- a/layout/reftests/svg/text/reftest.list
+++ b/layout/reftests/svg/text/reftest.list
@@ -160,15 +160,18 @@ HTTP(../..) == clipPath-applied.svg clip
 
 # text and patterns
 # == pattern-content.svg pattern-content-ref.svg  # disabled due to infinite invalidation loop bug
 
 # text and filters
 HTTP(../..) == filter-applied.svg filter-applied-ref.svg
 
 # selection
+needs-focus == selectSubString.svg selectSubString-ref.svg
+needs-focus == selectSubString-2.svg selectSubString-2-ref.svg
+needs-focus == selectSubString-3.svg selectSubString-3-ref.svg
 needs-focus == simple-selection.svg simple-selection-ref.html
 needs-focus == simple-bidi-selection.svg simple-bidi-selection-ref.html
 needs-focus == simple-fill-color-selection.svg simple-fill-color-selection-ref.html
 needs-focus == simple-underline-selection.svg simple-underline-selection-ref.html
 needs-focus == multiple-text-selection.svg multiple-text-selection-ref.html
 needs-focus == multiple-chunks-selection.svg multiple-chunks-selection-ref.svg
 needs-focus == textpath-selection.svg textpath-selection-ref.svg
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-2-ref.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-2-ref.svg
@@ -4,13 +4,13 @@
 -->
 <svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
   <g transform="translate(100,100)" style="font: 16px sans-serif">
     <text>hello</text>
   </g>
   <script>
     var text = document.getElementsByTagName("text")[0];
     var range = document.createRange();
-    range.setStart(text.firstChild, 1);
-    range.setEnd(text.firstChild, 4);
+    range.setStart(text.firstChild, 0);
+    range.setEnd(text.firstChild, 5);
     window.getSelection().addRange(range);
   </script>
 </svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-2.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-2.svg
@@ -3,14 +3,11 @@
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
   <g transform="translate(100,100)" style="font: 16px sans-serif">
     <text>hello</text>
   </g>
   <script>
     var text = document.getElementsByTagName("text")[0];
-    var range = document.createRange();
-    range.setStart(text.firstChild, 1);
-    range.setEnd(text.firstChild, 4);
-    window.getSelection().addRange(range);
+    text.selectSubString(0, 5);
   </script>
 </svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-3-ref.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-3-ref.svg
@@ -1,16 +1,16 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
   <g transform="translate(100,100)" style="font: 16px sans-serif">
-    <text>hello</text>
+    <text>abc אבג 123 דהו def</text>
   </g>
   <script>
     var text = document.getElementsByTagName("text")[0];
     var range = document.createRange();
-    range.setStart(text.firstChild, 1);
-    range.setEnd(text.firstChild, 4);
+    range.setStart(text.firstChild, 0);
+    range.setEnd(text.firstChild, 9);
     window.getSelection().addRange(range);
   </script>
 </svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-3.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-3.svg
@@ -1,16 +1,15 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
   <g transform="translate(100,100)" style="font: 16px sans-serif">
-    <text>hello</text>
+    <text>
+        abc אבג 123 דהו def
+    </text>
   </g>
   <script>
     var text = document.getElementsByTagName("text")[0];
-    var range = document.createRange();
-    range.setStart(text.firstChild, 1);
-    range.setEnd(text.firstChild, 4);
-    window.getSelection().addRange(range);
+    text.selectSubString(0, 9);
   </script>
 </svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-ref.svg
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString.svg
@@ -3,14 +3,11 @@
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
   <g transform="translate(100,100)" style="font: 16px sans-serif">
     <text>hello</text>
   </g>
   <script>
     var text = document.getElementsByTagName("text")[0];
-    var range = document.createRange();
-    range.setStart(text.firstChild, 1);
-    range.setEnd(text.firstChild, 4);
-    window.getSelection().addRange(range);
+    text.selectSubString(1, 3);
   </script>
 </svg>
--- a/layout/svg/nsSVGTextFrame2.cpp
+++ b/layout/svg/nsSVGTextFrame2.cpp
@@ -2035,20 +2035,20 @@ public:
 
   /**
    * Advances ahead aCount matching characters.  Returns true if there were
    * enough characters to advance past, and false otherwise.
    */
   bool Next(uint32_t aCount);
 
   /**
-   * Advances ahead up to aCount matching characters, stopping early if we move
-   * past the subtree (if one was specified in the constructor).
+   * Advances ahead up to aCount matching characters, returns true if there
+   * were enough characters to advance to.
    */
-  void NextWithinSubtree(uint32_t aCount);
+  bool NextWithinSubtree(uint32_t aCount);
 
   /**
    * Advances to the character with the specified index.  The index is in the
    * space of original characters (i.e., all DOM characters under the <text>
    * that are within valid text content elements).
    */
   bool AdvanceToCharacter(uint32_t aTextElementCharIndex);
 
@@ -2300,25 +2300,26 @@ CharIterator::Next(uint32_t aCount)
     if (!Next()) {
       return false;
     }
     aCount--;
   }
   return true;
 }
 
-void
+bool
 CharIterator::NextWithinSubtree(uint32_t aCount)
 {
   while (IsWithinSubtree() && aCount) {
+    --aCount;
     if (!Next()) {
       break;
     }
-    aCount--;
-  }
+  }
+  return !aCount;
 }
 
 bool
 CharIterator::AdvanceToCharacter(uint32_t aTextElementCharIndex)
 {
   while (mTextElementCharIndex < aTextElementCharIndex) {
     if (!Next()) {
       return false;
@@ -3645,16 +3646,48 @@ nsSVGTextFrame2::GetComputedTextLength(n
     length += run.GetAdvanceWidth();
   }
 
   return PresContext()->AppUnitsToGfxUnits(length) *
            cssPxPerDevPx / mFontSizeScaleFactor;
 }
 
 /**
+ * Implements the SVG DOM SelectSubString method for the specified
+ * text content element.
+ */
+nsresult
+nsSVGTextFrame2::SelectSubString(nsIContent* aContent,
+                                 uint32_t charnum, uint32_t nchars)
+{
+  UpdateGlyphPositioning(false);
+
+  // Convert charnum/nchars from addressable characters relative to
+  // aContent to global character indices.
+  CharIterator chit(this, CharIterator::eAddressable, aContent);
+  if (!chit.AdvanceToSubtree() ||
+      !chit.Next(charnum) ||
+      chit.IsAfterSubtree()) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+  charnum = chit.TextElementCharIndex();
+  nsIContent* content = chit.TextFrame()->GetContent();
+  if (!chit.NextWithinSubtree(nchars)) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+  nchars = chit.TextElementCharIndex() - charnum;
+
+  nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
+
+  frameSelection->HandleClick(content, charnum, charnum + nchars,
+                              false, false, false);
+  return NS_OK;
+}
+
+/**
  * Implements the SVG DOM GetSubStringLength method for the specified
  * text content element.
  */
 nsresult
 nsSVGTextFrame2::GetSubStringLength(nsIContent* aContent,
                                     uint32_t charnum, uint32_t nchars,
                                     float* aResult)
 {
@@ -3670,17 +3703,19 @@ nsSVGTextFrame2::GetSubStringLength(nsIC
   }
 
   if (nchars == 0) {
     *aResult = 0.0f;
     return NS_OK;
   }
 
   charnum = chit.TextElementCharIndex();
-  chit.NextWithinSubtree(nchars);
+  if (!chit.NextWithinSubtree(nchars)) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
   nchars = chit.TextElementCharIndex() - charnum;
 
   // Find each rendered run that intersects with the range defined
   // by charnum/nchars.
   nscoord textLength = 0;
   TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames);
   TextRenderedRun run = it.Current();
   while (run.mFrame) {
--- a/layout/svg/nsSVGTextFrame2.h
+++ b/layout/svg/nsSVGTextFrame2.h
@@ -254,16 +254,17 @@ public:
                                       uint32_t aFlags);
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM(uint32_t aFor);
   
   // SVG DOM text methods:
   uint32_t GetNumberOfChars(nsIContent* aContent);
   float GetComputedTextLength(nsIContent* aContent);
+  nsresult SelectSubString(nsIContent* aContent, uint32_t charnum, uint32_t nchars);
   nsresult GetSubStringLength(nsIContent* aContent, uint32_t charnum,
                               uint32_t nchars, float* aResult);
   int32_t GetCharNumAtPosition(nsIContent* aContent, mozilla::nsISVGPoint* point);
 
   nsresult GetStartPositionOfChar(nsIContent* aContent, uint32_t aCharNum,
                                   mozilla::nsISVGPoint** aResult);
   nsresult GetEndPositionOfChar(nsIContent* aContent, uint32_t aCharNum,
                                 mozilla::nsISVGPoint** aResult);
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -17,16 +17,17 @@
 #include "nsHTMLParts.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDisplayList.h"
 
 #include "nsCellMap.h"//table cell navigation
 #include <algorithm>
 
 using namespace mozilla;
+using namespace mozilla::layout;
 
 nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext):
   nsContainerFrame(aContext)
 {
   SetRepeatable(false);
 }
 
 nsTableRowGroupFrame::~nsTableRowGroupFrame()
@@ -966,31 +967,31 @@ nsTableRowGroupFrame::UndoContinuedRow(n
 {
   if (!aRow) return; // allow null aRow to avoid callers doing null checks
 
   // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created
   nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
   NS_PRECONDITION(mFrames.ContainsFrame(rowBefore),
                   "rowBefore not in our frame list?");
 
-  nsAutoPtr<nsFrameList> overflows(StealOverflowFrames());
+  AutoFrameListPtr overflows(aPresContext, StealOverflowFrames());
   if (!rowBefore || !overflows || overflows->IsEmpty() ||
       overflows->FirstChild() != aRow) {
     NS_ERROR("invalid continued row");
     return;
   }
 
   // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split
   // will not have reflowed yet to pick up content from any overflow lines.
   overflows->DestroyFrame(aRow);
 
-  if (overflows->IsEmpty())
-    return;
   // Put the overflow rows into our child list
-  mFrames.InsertFrames(nullptr, rowBefore, *overflows);
+  if (!overflows->IsEmpty()) {
+    mFrames.InsertFrames(nullptr, rowBefore, *overflows);
+  }
 }
 
 static nsTableRowFrame* 
 GetRowBefore(nsTableRowFrame& aStartRow,
              nsTableRowFrame& aRow)
 {
   nsTableRowFrame* rowBefore = nullptr;
   for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) {
--- a/layout/xul/base/src/nsMenuFrame.cpp
+++ b/layout/xul/base/src/nsMenuFrame.cpp
@@ -301,28 +301,28 @@ void
 nsMenuFrame::DestroyPopupList()
 {
   NS_ASSERTION(HasPopup(), "huh?");
   nsFrameList* prop =
     static_cast<nsFrameList*>(Properties().Remove(PopupListProperty()));
   NS_ASSERTION(prop && prop->IsEmpty(),
                "popup list must exist and be empty when destroying");
   RemoveStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
-  delete prop;
+  prop->Delete(PresContext()->PresShell());
 }
 
 void
 nsMenuFrame::SetPopupFrame(nsFrameList& aFrameList)
 {
   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
     nsMenuPopupFrame* popupFrame = do_QueryFrame(e.get());
     if (popupFrame) {
       // Remove the frame from the list and store it in a nsFrameList* property.
       aFrameList.RemoveFrame(popupFrame);
-      nsFrameList* popupList = new nsFrameList(popupFrame, popupFrame);
+      nsFrameList* popupList = new (PresContext()->PresShell()) nsFrameList(popupFrame, popupFrame);
       Properties().Set(PopupListProperty(), popupList);
       AddStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
       break;
     }
   }
 }
 
 NS_IMETHODIMP
--- a/media/mtransport/test/sctp_unittest.cpp
+++ b/media/mtransport/test/sctp_unittest.cpp
@@ -70,16 +70,17 @@ class TransportTestPeer : public sigslot
         sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)),
         timer_(do_CreateInstance(NS_TIMER_CONTRACTID)),
         periodic_(nullptr) {
     std::cerr << "Creating TransportTestPeer; flow=" <<
         static_cast<void *>(flow_.get()) <<
         " local=" << local_port <<
         " remote=" << remote_port << std::endl;
 
+    usrsctp_register_address(static_cast<void *>(this));
     int r = usrsctp_set_non_blocking(sctp_, 1);
     EXPECT_GE(r, 0);
 
     struct linger l;
     l.l_onoff = 1;
     l.l_linger = 0;
     r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l,
                        (socklen_t)sizeof(l));
@@ -95,17 +96,17 @@ class TransportTestPeer : public sigslot
     EXPECT_GE(r, 0);
 
     memset(&local_addr_, 0, sizeof(local_addr_));
     local_addr_.sconn_family = AF_CONN;
 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
     local_addr_.sconn_len = sizeof(struct sockaddr_conn);
 #endif
     local_addr_.sconn_port = htons(local_port);
-    local_addr_.sconn_addr = nullptr;
+    local_addr_.sconn_addr = static_cast<void *>(this);
 
 
     memset(&remote_addr_, 0, sizeof(remote_addr_));
     remote_addr_.sconn_family = AF_CONN;
 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
     remote_addr_.sconn_len = sizeof(struct sockaddr_conn);
 #endif
     remote_addr_.sconn_port = htons(remote_port);
@@ -115,16 +116,17 @@ class TransportTestPeer : public sigslot
     res = loopback_->Init();
     EXPECT_EQ((nsresult)NS_OK, res);
   }
 
   ~TransportTestPeer() {
     std::cerr << "Destroying sctp connection flow=" <<
         static_cast<void *>(flow_.get()) << std::endl;
     usrsctp_close(sctp_);
+    usrsctp_deregister_address(static_cast<void *>(this));
 
     test_utils->sts_target()->Dispatch(WrapRunnable(this,
                                                    &TransportTestPeer::DisconnectInt),
                                       NS_DISPATCH_SYNC);
 
     std::cerr << "~TransportTestPeer() completed" << std::endl;
   }
 
@@ -342,17 +344,17 @@ class TransportTest : public ::testing::
   TransportTestPeer *p1_;
   TransportTestPeer *p2_;
 };
 
 TEST_F(TransportTest, TestConnect) {
   ConnectSocket();
 }
 
-TEST_F(TransportTest, DISABLED_TestConnectSymmetricalPorts) {
+TEST_F(TransportTest, TestConnectSymmetricalPorts) {
   ConnectSocket(5002,5002);
 }
 
 TEST_F(TransportTest, TestTransfer) {
   ConnectSocket();
   TestTransfer(50);
 }
 
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -46,19 +46,21 @@ extern void lsm_start_continuous_tone_ti
 extern void lsm_update_active_tone(vcm_tones_t tone, cc_call_handle_t call_handle);
 extern void lsm_stop_multipart_tone_timer(void);
 extern void lsm_stop_continuous_tone_timer(void);
 
 }//end extern "C"
 
 static const char* logTag = "VcmSipccBinding";
 
+// Cloned from ccapi.h
 typedef enum {
     CC_AUDIO_1,
-    CC_VIDEO_1
+    CC_VIDEO_1,
+    CC_DATACHANNEL_1
 } cc_media_cap_name;
 
 #define SIPSDP_ILBC_MODE20 20
 
 /* static */
 
 using namespace CSF;
 
@@ -1055,27 +1057,29 @@ short vcmGetDtlsIdentity(const char *pee
  *  @param[in]  peerconnection - the peerconnection in use
  *  @param[in]  streams - the number of streams for this data channel
  *  @param[in]  local_datachannel_port - the negotiated sctp port
  *  @param[in]  remote_datachannel_port - the negotiated sctp port
  *  @param[in]  protocol - the protocol as a string
  *
  *  @return 0 success, error failure
  */
-static short vcmInitializeDataChannel_m(const char *peerconnection, cc_uint16_t streams,
+static short vcmInitializeDataChannel_m(const char *peerconnection,
+  int track_id, cc_uint16_t streams,
   int local_datachannel_port, int remote_datachannel_port, const char* protocol)
 {
   nsresult res;
 
   CSFLogDebug( logTag, "%s: PC = %s", __FUNCTION__, peerconnection);
 
   sipcc::PeerConnectionWrapper pc(peerconnection);
   ENSURE_PC(pc, VCM_ERROR);
 
-  res = pc.impl()->InitializeDataChannel(local_datachannel_port, remote_datachannel_port, streams);
+  res = pc.impl()->InitializeDataChannel(track_id, local_datachannel_port,
+                                         remote_datachannel_port, streams);
   if (NS_FAILED(res)) {
     return VCM_ERROR;
   }
 
   return 0;
 }
 
 /* Set negotiated DataChannel parameters.
@@ -1083,24 +1087,26 @@ static short vcmInitializeDataChannel_m(
  *  @param[in]  peerconnection - the peerconnection in use
  *  @param[in]  streams - the number of streams for this data channel
  *  @param[in]  local_datachannel_port - the negotiated sctp port
  *  @param[in]  remote_datachannel_port - the negotiated sctp port
  *  @param[in]  protocol - the protocol as a string
  *
  *  @return 0 success, error failure
  */
-short vcmInitializeDataChannel(const char *peerconnection, cc_uint16_t streams,
+short vcmInitializeDataChannel(const char *peerconnection, int track_id,
+  cc_uint16_t streams,
   int local_datachannel_port, int remote_datachannel_port, const char* protocol)
 {
   short ret;
 
   mozilla::SyncRunnable::DispatchToThread(VcmSIPCCBinding::getMainThread(),
       WrapRunnableNMRet(&vcmInitializeDataChannel_m,
                         peerconnection,
+                        track_id,
                         streams,
                         local_datachannel_port,
                         remote_datachannel_port,
                         protocol,
                         &ret));
 
   return ret;
 }
@@ -1309,43 +1315,51 @@ static int vcmRxStartICE_m(cc_mcapid_t m
         vcm_mediaAttrs_t *attrs)
 {
   CSFLogDebug( logTag, "%s(%s)", __FUNCTION__, peerconnection);
 
   // Find the PC.
   sipcc::PeerConnectionWrapper pc(peerconnection);
   ENSURE_PC(pc, VCM_ERROR);
 
-  if(!payloads) {
-      CSFLogError( logTag, "Unitialized payload list");
-      return VCM_ERROR;
+  // Datachannel will use this though not for RTP
+  mozilla::RefPtr<TransportFlow> rtp_flow =
+    vcmCreateTransportFlow(pc.impl(), level, false,
+                           fingerprint_alg, fingerprint);
+  if (!rtp_flow) {
+    CSFLogError( logTag, "Could not create RTP flow");
+    return VCM_ERROR;
+  }
+
+  if (CC_IS_DATACHANNEL(mcap_id)) {
+    // That's all we need for DataChannels - a flow registered
+    CSFLogDebug( logTag, "%s success", __FUNCTION__);
+    return 0;
+  }
+
+  if (!payloads) {
+    CSFLogError( logTag, "Unitialized payload list");
+    return VCM_ERROR;
   }
 
   // Find the stream we need
   nsRefPtr<sipcc::RemoteSourceStreamInfo> stream =
     pc.impl()->media()->GetRemoteStream(pc_stream_id);
   if (!stream) {
     // This should never happen
     PR_ASSERT(PR_FALSE);
     return VCM_ERROR;
   }
-  // Create the transport flows
-  mozilla::RefPtr<TransportFlow> rtp_flow =
-      vcmCreateTransportFlow(pc.impl(), level, false,
-                             fingerprint_alg, fingerprint);
-  if (!rtp_flow) {
-      CSFLogError( logTag, "Could not create RTP flow");
-      return VCM_ERROR;
-  }
+
   mozilla::RefPtr<TransportFlow> rtcp_flow =
-      vcmCreateTransportFlow(pc.impl(), level, true,
-                             fingerprint_alg, fingerprint);
+    vcmCreateTransportFlow(pc.impl(), level, true,
+                           fingerprint_alg, fingerprint);
   if (!rtcp_flow) {
-      CSFLogError( logTag, "Could not create RTCP flow");
-      return VCM_ERROR;
+    CSFLogError( logTag, "Could not create RTCP flow");
+    return VCM_ERROR;
   }
 
   if (CC_IS_AUDIO(mcap_id)) {
     std::vector<mozilla::AudioCodecConfig *> configs;
 
     // Instantiate an appropriate conduit
     mozilla::RefPtr<mozilla::AudioSessionConduit> tx_conduit =
       pc.impl()->media()->GetConduit(level, false);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -265,17 +265,18 @@ PeerConnectionImpl::PeerConnectionImpl()
   , mReadyState(kNew)
   , mIceState(kIceGathering)
   , mPCObserver(NULL)
   , mWindow(NULL)
   , mIdentity(NULL)
   , mSTSThread(NULL)
   , mMedia(new PeerConnectionMedia(this))
   , mNumAudioStreams(0)
-  , mNumVideoStreams(0) {
+  , mNumVideoStreams(0)
+  , mHaveDataStream(false) {
 #ifdef MOZILLA_INTERNAL_API
   MOZ_ASSERT(NS_IsMainThread());
 #endif
 }
 
 PeerConnectionImpl::~PeerConnectionImpl()
 {
   // This aborts if not on main thread (in Debug builds)
@@ -622,96 +623,113 @@ PeerConnectionImpl::CreateFakeMediaStrea
     }
   }
 
   return NS_OK;
 }
 
 // Stubbing this call out for now.
 // We can remove it when we are confident of datachannels being started
-// correctly on SDP negotiation
+// correctly on SDP negotiation (bug 852908)
 NS_IMETHODIMP
 PeerConnectionImpl::ConnectDataConnection(uint16_t aLocalport,
                                           uint16_t aRemoteport,
                                           uint16_t aNumstreams)
 {
   return NS_OK; // InitializeDataChannel(aLocalport, aRemoteport, aNumstreams);
 }
 
 // Data channels won't work without a window, so in order for the C++ unit
 // tests to work (it doesn't have a window available) we ifdef the following
 // two implementations.
-nsresult
-PeerConnectionImpl::InitializeDataChannel(uint16_t aLocalport,
-                                          uint16_t aRemoteport,
-                                          uint16_t aNumstreams)
+NS_IMETHODIMP
+PeerConnectionImpl::EnsureDataConnection(uint16_t aNumstreams)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 
 #ifdef MOZILLA_INTERNAL_API
   if (mDataConnection) {
-    CSFLogError(logTag,"%s DataConnection already connected",__FUNCTION__);
+    CSFLogDebug(logTag,"%s DataConnection already connected",__FUNCTION__);
     // Ignore the request to connect when already connected.  This entire
     // implementation is temporary.  Ignore aNumstreams as it's merely advisory
     // and we increase the number of streams dynamically as needed.
     return NS_OK;
   }
   mDataConnection = new mozilla::DataChannelConnection(this);
-  if (!mDataConnection->Init(aLocalport, aNumstreams, true)) {
+  if (!mDataConnection->Init(5000, aNumstreams, true)) {
     CSFLogError(logTag,"%s DataConnection Init Failed",__FUNCTION__);
     return NS_ERROR_FAILURE;
   }
-  // XXX Fix! Get the correct flow for DataChannel. Also error handling.
-  for (int i = 2; i >= 0; i--) {
-    nsRefPtr<TransportFlow> flow = mMedia->GetTransportFlow(i,false).get();
-    CSFLogDebug(logTag, "Transportflow[%d] = %p", i, flow.get());
+#endif
+  return NS_OK;
+}
+
+nsresult
+PeerConnectionImpl::InitializeDataChannel(int track_id,
+                                          uint16_t aLocalport,
+                                          uint16_t aRemoteport,
+                                          uint16_t aNumstreams)
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+
+#ifdef MOZILLA_INTERNAL_API
+  nsresult rv = EnsureDataConnection(aNumstreams);
+  if (NS_SUCCEEDED(rv)) {
+    // use the specified TransportFlow
+    nsRefPtr<TransportFlow> flow = mMedia->GetTransportFlow(track_id, false).get();
+    CSFLogDebug(logTag, "Transportflow[%d] = %p", track_id, flow.get());
     if (flow) {
-      if (!mDataConnection->ConnectDTLS(flow, aLocalport, aRemoteport)) {
-        return NS_ERROR_FAILURE;
+      if (mDataConnection->ConnectViaTransportFlow(flow, aLocalport, aRemoteport)) {
+        return NS_OK;
       }
-      break;
     }
   }
-  return NS_OK;
-#else
-    return NS_ERROR_FAILURE;
+  mDataConnection = nullptr;
 #endif
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CreateDataChannel(const nsACString& aLabel,
+                                      const nsACString& aProtocol,
                                       uint16_t aType,
                                       bool outOfOrderAllowed,
                                       uint16_t aMaxTime,
                                       uint16_t aMaxNum,
+                                      bool aExternalNegotiated,
+                                      uint16_t aStream,
                                       nsIDOMDataChannel** aRetval)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aRetval);
 
 #ifdef MOZILLA_INTERNAL_API
   nsRefPtr<mozilla::DataChannel> dataChannel;
   mozilla::DataChannelConnection::Type theType =
     static_cast<mozilla::DataChannelConnection::Type>(aType);
 
-  if (!mDataConnection) {
-    return NS_ERROR_FAILURE;
+  nsresult rv = EnsureDataConnection(WEBRTC_DATACHANNEL_STREAMS_DEFAULT);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
   dataChannel = mDataConnection->Open(
-    aLabel, theType, !outOfOrderAllowed,
+    aLabel, aProtocol, theType, !outOfOrderAllowed,
     aType == mozilla::DataChannelConnection::PARTIAL_RELIABLE_REXMIT ? aMaxNum :
     (aType == mozilla::DataChannelConnection::PARTIAL_RELIABLE_TIMED ? aMaxTime : 0),
-    nullptr, nullptr
+    nullptr, nullptr, aExternalNegotiated, aStream
   );
   NS_ENSURE_TRUE(dataChannel,NS_ERROR_FAILURE);
 
   CSFLogDebug(logTag, "%s: making DOMDataChannel", __FUNCTION__);
 
-  // TODO -- need something like "mCall->addStream(stream_id, 0, DATA);" so
-  // the SDP can be generated correctly
+  if (!mHaveDataStream) {
+    // XXX stream_id of 0 might confuse things...
+    mCall->addStream(0, 2, DATA);
+    mHaveDataStream = true;
+  }
 
   return NS_NewDOMDataChannel(dataChannel.forget(), mWindow, aRetval);
 #else
   return NS_OK;
 #endif
 }
 
 void
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -231,18 +231,18 @@ public:
     return Initialize(aObserver, aWindow, &aConfiguration, nullptr, aThread, nullptr);
   }
 
   // Validate constraints and construct a MediaConstraints object
   // from a JS::Value.
   NS_IMETHODIMP CreateOffer(MediaConstraints& aConstraints);
   NS_IMETHODIMP CreateAnswer(MediaConstraints& aConstraints);
 
-  nsresult
-  InitializeDataChannel(uint16_t aLocalport, uint16_t aRemoteport, uint16_t aNumstreams);
+  nsresult InitializeDataChannel(int track_id, uint16_t aLocalport,
+                                 uint16_t aRemoteport, uint16_t aNumstreams);
 
   // Called whenever something is unrecognized by the parser
   // May be called more than once and does not necessarily mean
   // that parsing was stopped, only that something was unrecognized.
   void OnSdpParseError(const char* errorMessage);
 
   // Called when OnLocal/RemoteDescriptionSuccess/Error
   // is called to start the list over.
@@ -254,16 +254,17 @@ private:
   nsresult Initialize(IPeerConnectionObserver* aObserver,
                       nsIDOMWindow* aWindow,
                       const IceConfiguration* aConfiguration,
                       const JS::Value* aRTCConfiguration,
                       nsIThread* aThread,
                       JSContext* aCx);
   NS_IMETHODIMP CreateOfferInt(MediaConstraints& constraints);
   NS_IMETHODIMP CreateAnswerInt(MediaConstraints& constraints);
+  NS_IMETHODIMP EnsureDataConnection(uint16_t aNumstreams);
 
   nsresult CloseInt(bool aIsSynchronous);
   void ChangeReadyState(ReadyState aReadyState);
   nsresult CheckApiState(bool assert_ice_ready) const;
   void CheckThread() const {
     NS_ABORT_IF_FALSE(CheckThreadInt(), "Wrong thread");
   }
   bool CheckThreadInt() const {
@@ -333,16 +334,18 @@ private:
 
   // Temporary: used to prevent multiple audio streams or multiple video streams
   // in a single PC. This is tied up in the IETF discussion around proper
   // representation of multiple streams in SDP, and strongly related to
   // Bug 840728.
   int mNumAudioStreams;
   int mNumVideoStreams;
 
+  bool mHaveDataStream;
+
   // Holder for error messages from parsing SDP
   std::vector<std::string> mSDPParseErrorMessages;
 
 public:
   //these are temporary until the DataChannel Listen/Connect API is removed
   unsigned short listenPort;
   unsigned short connectPort;
   char *connectStr; // XXX ownership/free
--- a/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
@@ -3521,16 +3521,21 @@ fsmdef_ev_addstream(sm_event_t *event) {
         dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_SENDRECV;
         dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_stream = msg->data.track.stream_id;
         dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_track = msg->data.track.track_id;
     } else if (msg->data.track.media_type == AUDIO) {
         dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
         dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_SENDRECV;
         dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_stream = msg->data.track.stream_id;
         dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_track = msg->data.track.track_id;
+    } else if (msg->data.track.media_type == DATA) {
+        dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = TRUE;
+        dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].support_direction = SDP_DIRECTION_SENDRECV;
+        dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].pc_stream = msg->data.track.stream_id;
+        dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].pc_track = msg->data.track.track_id;
     } else {
         return (SM_RC_END);
     }
 
     return (SM_RC_END);
 }
 
 static sm_rcs_t
@@ -3559,21 +3564,21 @@ fsmdef_ev_removestream(sm_event_t *event
     }
 
     /*
      * This is temporary code to allow configuration of the two
      * default streams. When multiple streams > 2 are supported this
      * will be re-implemented.
      */
     if (msg->data.track.media_type == AUDIO) {
-        dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
+        PR_ASSERT(dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled);
         dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_RECVONLY;
         dcb->video_pref = SDP_DIRECTION_SENDRECV;
     } else if (msg->data.track.media_type == VIDEO) {
-        dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled = TRUE;
+        PR_ASSERT(dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled);
         dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_RECVONLY;
     } else {
         return (SM_RC_END);
     }
 
 	return (SM_RC_END);
 }
 
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -142,26 +142,17 @@ static const cc_media_cap_table_t *gsmsd
         dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].name = CC_DATACHANNEL_1;
 
         dcb_p->media_cap_tbl->cap[CC_AUDIO_1].type = SDP_MEDIA_AUDIO;
         dcb_p->media_cap_tbl->cap[CC_VIDEO_1].type = SDP_MEDIA_VIDEO;
         dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].type = SDP_MEDIA_APPLICATION;
 
         dcb_p->media_cap_tbl->cap[CC_AUDIO_1].enabled = FALSE;
         dcb_p->media_cap_tbl->cap[CC_VIDEO_1].enabled = FALSE;
-        /*
-         * This really should be set to FALSE unless we have added
-         * a data channel using createDataChannel(). Right now,
-         * though, those operations are not queued (and, in fact,
-         * the W3C hasn't specified the proper behavior here anyway, so
-         * we would only be implementing speculatively) -- so we'll
-         * always offer data channels until the standard is
-         * a bit more set.
-         */
-        dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = TRUE;
+        dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = FALSE;
 
         dcb_p->media_cap_tbl->cap[CC_AUDIO_1].support_security = TRUE;
         dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_security = TRUE;
         dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].support_security = TRUE;
 
         /* We initialize as RECVONLY to allow the application to
            display incoming media streams, even if it doesn't
            plan to send media for those streams. This will be
--- a/media/webrtc/signaling/src/sipcc/core/gsm/h/lsm.h
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/h/lsm.h
@@ -169,12 +169,12 @@ int lsm_get_video_mute (callid_t call_id
 void lsm_set_video_window (callid_t call_id, int flags, int x, int y, int h, int w);
 void lsm_get_video_window (callid_t call_id, int *flags, int *x, int *y, int *h, int *w);
 boolean lsm_is_kpml_subscribed (callid_t call_id);
 void
 lsm_util_tone_start_with_speaker_as_backup (vcm_tones_t tone, short alert_info,
                                     cc_call_handle_t call_handle, groupid_t group_id,
                                     streamid_t stream_id, uint16_t direction);
 
-void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media);
+void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media, int track_id);
 
 #endif //_LSM_H_
 
--- a/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
@@ -909,16 +909,24 @@ lsm_rx_start (lsm_lcb_t *lcb, const char
          */
         if (media->type != SDP_MEDIA_APPLICATION &&
             !gsmsdp_is_crypto_ready(media, TRUE)) {
             LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Not ready to open receive port (%d)\n",
                       DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname, media->src_port);
             continue;
         }
 
+        /* TODO(ekr@rtfm.com): Needs changing for when we
+           have > 2 streams. (adam@nostrum.com): For now,
+           we use all the same stream so pc_stream_id == 0
+           and the tracks are assigned in order and are
+           equal to the level in the media objects */
+        pc_stream_id = 0;
+        pc_track_id = media->level;
+
         /*
          * Open the RTP receive channel if it is not already open.
          */
         LSM_DEBUG(get_debug_string(LSM_DBG_INT1), dcb->call_id, dcb->line,
                   fname1, "rcv chan", media->rcv_chan);
         if (media->rcv_chan == FALSE) {
 
             memset(&open_rcv, 0, sizeof(open_rcv));
@@ -973,33 +981,24 @@ lsm_rx_start (lsm_lcb_t *lcb, const char
                 media->rcv_chan = TRUE; /* recevied channel is created */
                 /* save the source RX port */
                 if (media->is_multicast) {
                     media->multicast_port = open_rcv.port;
                 } else {
                     media->src_port = open_rcv.port;
                 }
 
-                /* TODO(ekr@rtfm.com): Needs changing for when we
-                   have > 2 streams. (adam@nostrum.com): For now,
-                   we use all the same stream so pc_stream_id == 0
-                   and the tracks are assigned in order and are
-                   equal to the level in the media objects */
                 if ( media->cap_index == CC_VIDEO_1 ) {
                     attrs.video.opaque = media->video;
-                    pc_stream_id = 0;
-                    pc_track_id = media->level;
                 } else {
                     attrs.audio.packetization_period = media->packetization_period;
                     attrs.audio.max_packetization_period = media->max_packetization_period;
                     attrs.audio.avt_payload_type = media->avt_payload_type;
                     attrs.audio.mixing_mode = mix_mode;
                     attrs.audio.mixing_party = mix_party;
-                    pc_stream_id = 0;
-                    pc_track_id = media->level;
                 }
                 dcb->cur_video_avail &= ~CC_ATTRIB_CAST;
 
                 config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
                 if (dcb->peerconnection) {
                     ret_val = vcmRxStartICE(media->cap_index, group_id, media->refid,
                     media->level,
                     pc_stream_id,
@@ -1051,20 +1050,20 @@ lsm_rx_start (lsm_lcb_t *lcb, const char
                                                                dcb->group_id,
                                                                ((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
                                                                direction);
                 }
             }
         }
 
         if (media->type == SDP_MEDIA_APPLICATION) {
-            /* Enable datachannels
-               Datachannels are always two-way so initializing only here in rx_start.
-            */
-            lsm_initialize_datachannel(dcb, media);
+          /* Enable datachannels
+             Datachannels are always two-way so initializing only here in rx_start.
+          */
+          lsm_initialize_datachannel(dcb, media, pc_track_id);
         }
     }
 }
 
 /**
  * The function starts transmit channel for a given media entry.
  *
  * @param[in] lcb       - pointer to the lsm_lcb_t.
@@ -5315,34 +5314,38 @@ void lsm_add_remote_stream (line_t line,
  *
  * Description:
  *    The function initializes the datachannel with port and
  *    protocol info.
  *
  * Parameters:
  *   [in]  dcb - pointer to get the peerconnection id
  *   [in]  media - pointer to get the datachannel info
+ *   [in]  track_id - track ID (aka m-line number)
  * Returns: None
  */
-void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media)
+void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media,
+                                 int track_id)
 {
     if (!dcb) {
         CSFLogError(logTag, "%s DCB is NULL", __FUNCTION__);
         return;
     }
 
     if (!media) {
         CSFLogError(logTag, "%s media is NULL", __FUNCTION__);
         return;
     }
 
     /*
-     * have access to media->streams, media->protocol, media->local/remote_datachannel_port
+     * have access to media->cap_index, media->streams, media->protocol,
+     * media->local/remote_datachannel_port
      */
-    vcmInitializeDataChannel(dcb->peerconnection, media->datachannel_streams,
+    vcmInitializeDataChannel(dcb->peerconnection,
+        track_id, media->datachannel_streams,
         media->local_datachannel_port, media->remote_datachannel_port,
         media->datachannel_protocol);
 }
 
 /**
  *
  * Peform non call related action
  *
--- a/media/webrtc/signaling/src/sipcc/include/cc_constants.h
+++ b/media/webrtc/signaling/src/sipcc/include/cc_constants.h
@@ -543,16 +543,17 @@ typedef unsigned int cc_media_stream_id_
 
 typedef unsigned int cc_media_track_id_t;
 
 
 typedef enum {
   NO_STREAM = -1,
   AUDIO,
   VIDEO,
+  DATA,
   TYPE_MAX
 } cc_media_type_t;
 
 
 typedef struct {
   char        *name;
   char        *value;
   cc_boolean   mandatory;
--- a/media/webrtc/signaling/src/sipcc/include/vcm.h
+++ b/media/webrtc/signaling/src/sipcc/include/vcm.h
@@ -24,16 +24,18 @@
 #include "cc_constants.h"
 #include "ccsdp.h"
 
 
 /** Evaluates to TRUE for audio media streams where id is the mcap_id of the given stream */
 #define CC_IS_AUDIO(id) ((id == CC_AUDIO_1) ? TRUE:FALSE)
 /** Evaluates to TRUE for video media streams where id is the mcap_id of the given stream */
 #define CC_IS_VIDEO(id) ((id == CC_VIDEO_1) ? TRUE:FALSE)
+/** Evaluates to TRUE for datachannel streams where id is the mcap_id of the given stream */
+#define CC_IS_DATACHANNEL(id) ((id == CC_DATACHANNEL_1) ? TRUE:FALSE)
 
 
 /** Definitions for direction requesting Play tone to user */
 #define VCM_PLAY_TONE_TO_EAR      1
 /** Definitions value for direction requesting Play tone to network stream or far end */
 #define VCM_PLAY_TONE_TO_NET      2
 /** Definitions value for direction requesting Play tone to both user and network */
 #define VCM_PLAY_TONE_TO_ALL      3
@@ -668,16 +670,17 @@ int vcmTxStart(cc_mcapid_t mcap_id,
   short vcmGetDtlsIdentity(const char *peerconnection,
         char *digest_alg,
         size_t max_digest_alg_len,
         char *digest,
         size_t max_digest_len);
 
 
   short vcmInitializeDataChannel(const char *peerconnection,
+        int track_id,
         cc_uint16_t streams,
         int local_datachannel_port,
         int remote_datachannel_port,
         const char* protocol);
 
 /*!
  *  Close the receive stream.
  *
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -378,17 +378,19 @@ public class GeckoLayerClient implements
                 // adjust the page dimensions to account for differences in zoom
                 // between the rendered content (which is what Gecko tells us)
                 // and our zoom level (which may have diverged).
                 float scaleFactor = oldMetrics.zoomFactor / messageMetrics.zoomFactor;
                 newMetrics = oldMetrics.setPageRect(RectUtils.scale(messageMetrics.getPageRect(), scaleFactor), messageMetrics.getCssPageRect());
                 break;
             }
 
-            final ImmutableViewportMetrics geckoMetrics = newMetrics;
+            // Update the Gecko-side viewport metrics. Make sure to do this
+            // before modifying the metrics below.
+            final ImmutableViewportMetrics geckoMetrics = newMetrics.clamp();
             post(new Runnable() {
                 @Override
                 public void run() {
                     mGeckoViewport = geckoMetrics;
                 }
             });
 
             // If we're meant to be scrolled to the top, take into account
@@ -401,17 +403,17 @@ public class GeckoLayerClient implements
             if (type == ViewportMessageType.UPDATE
                     && FloatUtils.fuzzyEquals(newMetrics.viewportRectTop,
                                               newMetrics.pageRectTop)
                     && oldMetrics.fixedLayerMarginTop > 0) {
                 newMetrics = newMetrics.setViewportOrigin(newMetrics.viewportRectLeft,
                                  newMetrics.pageRectTop - oldMetrics.fixedLayerMarginTop);
             }
 
-            setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE, true);
+            setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE);
             mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null);
         }
         return mDisplayPort;
     }
 
     public DisplayPortMetrics getDisplayPort(boolean pageSizeUpdate, boolean isBrowserContentDisplayed, int tabId, ImmutableViewportMetrics metrics) {
         Tabs tabs = Tabs.getInstance();
         if (tabs.isSelectedTab(tabs.getTab(tabId)) && isBrowserContentDisplayed) {
@@ -427,17 +429,17 @@ public class GeckoLayerClient implements
             return DisplayPortCalculator.calculate(metrics, null);
         }
     }
 
     /**
      * Sets margins on fixed-position layers, to be used when compositing.
      * Must be called on the UI thread!
      */
-    public void setFixedLayerMargins(float left, float top, float right, float bottom) {
+    public synchronized void setFixedLayerMargins(float left, float top, float right, float bottom) {
         ImmutableViewportMetrics oldMetrics = getViewportMetrics();
         ImmutableViewportMetrics newMetrics = oldMetrics.setFixedLayerMargins(left, top, right, bottom);
 
         if (mClampOnMarginChange) {
             // Only clamp on decreased margins
             boolean changed = false;
             float viewportRectLeft = oldMetrics.viewportRectLeft;
             float viewportRectTop = oldMetrics.viewportRectTop;
@@ -466,17 +468,19 @@ public class GeckoLayerClient implements
             }
 
             // Set the new metrics, if they're different.
             if (changed) {
                 newMetrics = newMetrics.setViewportOrigin(viewportRectLeft, viewportRectTop);
             }
         }
 
-        setViewportMetrics(newMetrics, false, false);
+        mViewportMetrics = newMetrics;
+        mView.requestRender();
+        setShadowVisibility();
     }
 
     public void setClampOnFixedLayerMarginsChange(boolean aClamp) {
         mClampOnMarginChange = aClamp;
     }
 
     // This is called on the Gecko thread to determine if we're still interested
     // in the update of this display-port to continue. We can return true here
@@ -586,38 +590,41 @@ public class GeckoLayerClient implements
       * this function is invoked on; and this function will always be called prior to syncViewportInfo.
       */
     public void setFirstPaintViewport(float offsetX, float offsetY, float zoom,
             float pageLeft, float pageTop, float pageRight, float pageBottom,
             float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
         synchronized (this) {
             ImmutableViewportMetrics currentMetrics = getViewportMetrics();
 
-            // If we're meant to be scrolled to the top, take into account any
-            // margin set on the pan zoom controller.
-            if (FloatUtils.fuzzyEquals(offsetY, pageTop)) {
-                offsetY = -currentMetrics.fixedLayerMarginTop;
-            }
-
             final ImmutableViewportMetrics newMetrics = currentMetrics
                 .setViewportOrigin(offsetX, offsetY)
                 .setZoomFactor(zoom)
                 .setPageRect(new RectF(pageLeft, pageTop, pageRight, pageBottom),
                              new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom));
             // Since we have switched to displaying a different document, we need to update any
             // viewport-related state we have lying around. This includes mGeckoViewport and
             // mViewportMetrics. Usually this information is updated via handleViewportMessage
             // while we remain on the same document.
             post(new Runnable() {
                 @Override
                 public void run() {
                     mGeckoViewport = newMetrics;
                 }
             });
-            setViewportMetrics(newMetrics);
+
+            // If we're meant to be scrolled to the top, take into account any
+            // margin set on the pan zoom controller.
+            if (FloatUtils.fuzzyEquals(offsetY, pageTop)
+                  && newMetrics.fixedLayerMarginTop > 0) {
+                setViewportMetrics(newMetrics.setViewportOrigin(offsetX,
+                    -newMetrics.fixedLayerMarginTop));
+            } else {
+                setViewportMetrics(newMetrics);
+            }
 
             Tab tab = Tabs.getInstance().getSelectedTab();
             mView.setBackgroundColor(tab.getBackgroundColor());
             setZoomConstraints(tab.getZoomConstraints());
 
             // At this point, we have just switched to displaying a different document than we
             // we previously displaying. This means we need to abort any panning/zooming animations
             // that are in progress and send an updated display port request to browser.js as soon
@@ -795,25 +802,32 @@ public class GeckoLayerClient implements
         }
     }
 
     /** Implementation of PanZoomTarget
      * You must hold the monitor while calling this.
      */
     @Override
     public void setViewportMetrics(ImmutableViewportMetrics metrics) {
-        setViewportMetrics(metrics, true, true);
+        setViewportMetrics(metrics, true);
     }
 
-    private void setViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko, boolean keepFixedMargins) {
-        if (keepFixedMargins) {
-            mViewportMetrics = metrics.setFixedLayerMarginsFrom(mViewportMetrics);
-        } else {
-            mViewportMetrics = metrics;
-        }
+    /*
+     * You must hold the monitor while calling this.
+     */
+    private void setViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko) {
+        // This class owns the viewport size and the fixed layer margins; don't let other pieces
+        // of code clobber either of them. The only place the viewport size should ever be
+        // updated is in GeckoLayerClient.setViewportSize, and the only place the margins should
+        // ever be updated is in GeckoLayerClient.setFixedLayerMargins; both of these assign to
+        // mViewportMetrics directly.
+        metrics = metrics.setViewportSize(mViewportMetrics.getWidth(), mViewportMetrics.getHeight());
+        metrics = metrics.setFixedLayerMarginsFrom(mViewportMetrics);
+        mViewportMetrics = metrics;
+
         mView.requestRender();
         if (notifyGecko && mGeckoIsReady) {
             geometryChanged();
         }
         setShadowVisibility();
     }
 
     private void setShadowVisibility() {
--- a/mobile/android/base/gfx/ImmutableViewportMetrics.java
+++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java
@@ -160,16 +160,20 @@ public class ImmutableViewportMetrics {
             FloatUtils.interpolate(fixedLayerMarginLeft, to.fixedLayerMarginLeft, t),
             FloatUtils.interpolate(fixedLayerMarginTop, to.fixedLayerMarginTop, t),
             FloatUtils.interpolate(fixedLayerMarginRight, to.fixedLayerMarginRight, t),
             FloatUtils.interpolate(fixedLayerMarginBottom, to.fixedLayerMarginBottom, t),
             FloatUtils.interpolate(zoomFactor, to.zoomFactor, t));
     }
 
     public ImmutableViewportMetrics setViewportSize(float width, float height) {
+        if (FloatUtils.fuzzyEquals(width, getWidth()) && FloatUtils.fuzzyEquals(height, getHeight())) {
+            return this;
+        }
+
         return new ImmutableViewportMetrics(
             pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
             cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
             viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height,
             fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom,
             zoomFactor);
     }
 
@@ -200,16 +204,23 @@ public class ImmutableViewportMetrics {
             pageRect.left, pageRect.top, pageRect.right, pageRect.bottom,
             cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom,
             viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom,
             fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom,
             zoomFactor);
     }
 
     public ImmutableViewportMetrics setFixedLayerMargins(float left, float top, float right, float bottom) {
+        if (FloatUtils.fuzzyEquals(left, fixedLayerMarginLeft)
+                && FloatUtils.fuzzyEquals(top, fixedLayerMarginTop)
+                && FloatUtils.fuzzyEquals(right, fixedLayerMarginRight)
+                && FloatUtils.fuzzyEquals(bottom, fixedLayerMarginBottom)) {
+            return this;
+        }
+
         return new ImmutableViewportMetrics(
             pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
             cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
             viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom,
             left, top, right, bottom, zoomFactor);
     }
 
     public ImmutableViewportMetrics setFixedLayerMarginsFrom(ImmutableViewportMetrics fromMetrics) {
--- a/mobile/android/base/gfx/JavaPanZoomController.java
+++ b/mobile/android/base/gfx/JavaPanZoomController.java
@@ -64,16 +64,19 @@ class JavaPanZoomController
     private static final float MAX_ZOOM = 4.0f;
 
     // The maximum amount we would like to scroll with the mouse
     private static final float MAX_SCROLL = 0.075f * GeckoAppShell.getDpi();
 
     // The maximum zoom factor adjustment per frame of the AUTONAV animation
     private static final float MAX_ZOOM_DELTA = 0.125f;
 
+    // Length of the bounce animation in ms
+    private static final int BOUNCE_ANIMATION_DURATION = 250;
+
     private enum PanZoomState {
         NOTHING,        /* no touch-start events received */
         FLING,          /* all touches removed, but we're still scrolling page */
         TOUCHING,       /* one touch-start event received */
         PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
         PANNING,        /* panning without axis lock */
         PANNING_HOLD,   /* in panning, but not moving.
                          * similar to TOUCHING but after starting a pan */
@@ -803,31 +806,31 @@ class JavaPanZoomController
              * out.
              */
             if (!(mState == PanZoomState.BOUNCE || mState == PanZoomState.ANIMATED_ZOOM)) {
                 finishAnimation();
                 return;
             }
 
             /* Perform the next frame of the bounce-back animation. */
-            if (mBounceFrame < (int)(256f/Axis.MS_PER_FRAME)) {
+            if (mBounceFrame < (int)(BOUNCE_ANIMATION_DURATION / Axis.MS_PER_FRAME)) {
                 advanceBounce();
                 return;
             }
 
             /* Finally, if there's nothing else to do, complete the animation and go to sleep. */
             finishBounce();
             finishAnimation();
             setState(PanZoomState.NOTHING);
         }
 
         /* Performs one frame of a bounce animation. */
         private void advanceBounce() {
             synchronized (mTarget.getLock()) {
-                float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / 256f);
+                float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / BOUNCE_ANIMATION_DURATION);
                 ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
                 mTarget.setViewportMetrics(newMetrics);
                 mBounceFrame++;
             }
         }
 
         /* Concludes a bounce animation and snaps the viewport into place. */
         private void finishBounce() {
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -326,16 +326,26 @@ pref("accessibility.tabfocus_applies_to_
 //
 // This pref is checked only once, and the browser needs a restart to
 // pick up any changes.
 //
 // Values are -1 always on. 1 always off, 0 is auto as some platform perform
 // further checks.
 pref("accessibility.force_disabled", 0);
 
+#ifdef XP_WIN
+// Some accessibility tools poke at windows in the plugin process during setup
+// which can cause hangs.  To hack around this set accessibility.delay_plugins
+// to true, you can also try increasing accessibility.delay_plugin_time if your
+// machine is slow and you still experience hangs.
+// See bug 781791.
+pref("accessibility.delay_plugins", false);
+pref("accessibility.delay_plugin_time", 10000);
+#endif
+
 pref("focusmanager.testmode", false);
 
 pref("accessibility.usetexttospeech", "");
 pref("accessibility.usebrailledisplay", "");
 pref("accessibility.accesskeycausesactivation", true);
 pref("accessibility.mouse_focuses_formcontrol", false);
 
 // Type Ahead Find
@@ -1697,21 +1707,17 @@ pref("layout.css.devPixelsPerPx", "-1.0"
 // Is support for CSS Masking features enabled?
 #ifdef RELEASE_BUILD
 pref("layout.css.masking.enabled", false);
 #else
 pref("layout.css.masking.enabled", true);
 #endif
 
 // Is support for the the @supports rule enabled?
-#ifdef RELEASE_BUILD
-pref("layout.css.supports-rule.enabled", false);
-#else
 pref("layout.css.supports-rule.enabled", true);
-#endif
 
 // Is support for CSS Flexbox enabled?
 pref("layout.css.flexbox.enabled", true);
 
 // Are sets of prefixed properties supported?
 pref("layout.css.prefixes.border-image", true);
 pref("layout.css.prefixes.transforms", true);
 pref("layout.css.prefixes.transitions", true);
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -212,24 +212,29 @@ DataChannelConnection::Destroy()
   // Though it's probably ok to do this and close the sockets;
   // if we really want it to do true clean shutdowns it can
   // create a dependant Internal object that would remain around
   // until the network shut down the association or timed out.
   LOG(("Destroying DataChannelConnection %p", (void *) this));
   ASSERT_WEBRTC(NS_IsMainThread());
   CloseAll();
 
+  MutexAutoLock lock(mLock);
   if (mSocket && mSocket != mMasterSocket)
     usrsctp_close(mSocket);
   if (mMasterSocket)
     usrsctp_close(mMasterSocket);
 
   mSocket = nullptr;
-  mMasterSocket = nullptr;
+  mMasterSocket = nullptr; // also a flag that we've Destroyed this connection
 
+  if (mUsingDtls) {
+    usrsctp_deregister_address(static_cast<void *>(this));
+    LOG(("Deregistered %p from the SCTP stack.", static_cast<void *>(this)));
+  }
   // We can't get any more new callbacks from the SCTP library
   // All existing callbacks have refs to DataChannelConnection
 
   // nsDOMDataChannel objects have refs to DataChannels that have refs to us
 
   if (mTransportFlow) {
     MOZ_ASSERT(mSTS);
     ASSERT_WEBRTC(NS_IsMainThread());
@@ -277,17 +282,17 @@ DataChannelConnection::Init(unsigned sho
 #else
                      nullptr
 #endif
                     );
 #else
         NS_ASSERTION(!aUsingDtls, "Trying to use SCTP/DTLS without mtransport");
 #endif
       } else {
-        LOG(("sctp_init(%d)", aPort));
+        LOG(("sctp_init(%u)", aPort));
         usrsctp_init(aPort,
                      nullptr,
 #ifdef PR_LOGGING
                      debug_printf
 #else
                      nullptr
 #endif
                     );
@@ -295,16 +300,18 @@ DataChannelConnection::Init(unsigned sho
 
 #ifdef PR_LOGGING
       // Set logging to SCTP:PR_LOG_DEBUG to get SCTP debugs
       if (PR_LOG_TEST(GetSCTPLog(), PR_LOG_ALWAYS)) {
         usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
       }
 #endif
       usrsctp_sysctl_set_sctp_blackhole(2);
+      // ECN is currently not supported by the Firefox code
+      usrsctp_sysctl_set_sctp_ecn_enable(0);
       sctp_initialized = true;
 
       gDataChannelShutdown = new DataChannelShutdown();
       gDataChannelShutdown->Init();
     }
   }
 
   // XXX FIX! make this a global we get once
@@ -381,21 +388,19 @@ DataChannelConnection::Init(unsigned sho
     event.se_type = event_types[i];
     if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) < 0) {
       LOG(("*** failed setsockopt SCTP_EVENT errno %d", errno));
       goto error_cleanup;
     }
   }
 
   // Update number of streams
-  mStreamsOut.AppendElements(aNumStreams);
-  mStreamsIn.AppendElements(aNumStreams); // make sure both are the same length
+  mStreams.AppendElements(aNumStreams);
   for (uint32_t i = 0; i < aNumStreams; ++i) {
-    mStreamsOut[i] = nullptr;
-    mStreamsIn[i]  = nullptr;
+    mStreams[i] = nullptr;
   }
   memset(&initmsg, 0, sizeof(initmsg));
   len = sizeof(initmsg);
   if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, &len) < 0) {
     LOG(("*** failed getsockopt SCTP_INITMSG"));
     goto error_cleanup;
   }
   LOG(("Setting number of SCTP streams to %u, was %u/%u", aNumStreams,
@@ -404,21 +409,29 @@ DataChannelConnection::Init(unsigned sho
   initmsg.sinit_max_instreams = MAX_NUM_STREAMS;
   if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
                          (socklen_t)sizeof(initmsg)) < 0) {
     LOG(("*** failed setsockopt SCTP_INITMSG, errno %d", errno));
     goto error_cleanup;
   }
 
   mSocket = nullptr;
+  if (aUsingDtls) {
+    mUsingDtls = true;
+    usrsctp_register_address(static_cast<void *>(this));
+    LOG(("Registered %p within the SCTP stack.", static_cast<void *>(this)));
+  } else {
+    mUsingDtls = false;
+  }
   return true;
 
 error_cleanup:
   usrsctp_close(mMasterSocket);
   mMasterSocket = nullptr;
+  mUsingDtls = false;
   return false;
 }
 
 void
 DataChannelConnection::StartDefer()
 {
   nsresult rv;
   if (!NS_IsMainThread()) {
@@ -467,78 +480,133 @@ DataChannelConnection::Notify(nsITimer *
       LOG(("Turned off deferred send timer"));
       mTimerRunning = false;
     }
   }
   return NS_OK;
 }
 
 #ifdef MOZ_PEERCONNECTION
-bool
-DataChannelConnection::ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport)
+void
+DataChannelConnection::SetEvenOdd()
 {
-  LOG(("Connect DTLS local %d, remote %d", localport, remoteport));
+  ASSERT_WEBRTC(IsSTSThread());
 
-  NS_PRECONDITION(mMasterSocket, "SCTP wasn't initialized before ConnectDTLS!");
+  TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
+      mTransportFlow->GetLayer(TransportLayerDtls::ID()));
+  MOZ_ASSERT(dtls);  // DTLS is mandatory
+  mAllocateEven = (dtls->role() == TransportLayerDtls::CLIENT);
+}
+
+bool
+DataChannelConnection::ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport)
+{
+  LOG(("Connect DTLS local %u, remote %u", localport, remoteport));
+
+  NS_PRECONDITION(mMasterSocket, "SCTP wasn't initialized before ConnectViaTransportFlow!");
   NS_ENSURE_TRUE(aFlow, false);
 
   mTransportFlow = aFlow;
-  mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
   mLocalPort = localport;
   mRemotePort = remoteport;
   mState = CONNECTING;
 
+  RUN_ON_THREAD(mSTS, WrapRunnable(nsRefPtr<DataChannelConnection>(this),
+                                   &DataChannelConnection::SetSignals),
+                NS_DISPATCH_NORMAL);
+  return true;
+}
+
+void
+DataChannelConnection::SetSignals()
+{
+  ASSERT_WEBRTC(IsSTSThread());
+  ASSERT_WEBRTC(mTransportFlow);
+  LOG(("Setting transport signals, state: %d", mTransportFlow->state()));
+  mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
+  // SignalStateChange() doesn't call you with the initial state
+  mTransportFlow->SignalStateChange.connect(this, &DataChannelConnection::CompleteConnect);
+  CompleteConnect(mTransportFlow, mTransportFlow->state());
+}
+
+void
+DataChannelConnection::CompleteConnect(TransportFlow *flow, TransportLayer::State state)
+{
+  LOG(("Data transport state: %d", state));
+  MutexAutoLock lock(mLock);
+  ASSERT_WEBRTC(IsSTSThread());
+  // We should abort connection on TS_ERROR.
+  // Note however that the association will also fail (perhaps with a delay) and
+  // notify us in that way
+  if (state != TransportLayer::TS_OPEN || !mMasterSocket)
+    return;
+
   struct sockaddr_conn addr;
   memset(&addr, 0, sizeof(addr));
   addr.sconn_family = AF_CONN;
 #if defined(__Userspace_os_Darwin)
   addr.sconn_len = sizeof(addr);
 #endif
   addr.sconn_port = htons(mLocalPort);
+  addr.sconn_addr = static_cast<void *>(this);
 
   LOG(("Calling usrsctp_bind"));
   int r = usrsctp_bind(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
                        sizeof(addr));
   if (r < 0) {
     LOG(("usrsctp_bind failed: %d", r));
   } else {
     // This is the remote addr
     addr.sconn_port = htons(mRemotePort);
-    addr.sconn_addr = static_cast<void *>(this);
     LOG(("Calling usrsctp_connect"));
     r = usrsctp_connect(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
                         sizeof(addr));
     if (r < 0) {
       if (errno == EINPROGRESS) {
         // non-blocking
-        return true;
+        return;
       } else {
         LOG(("usrsctp_connect failed: %d", errno));
         mState = CLOSED;
       }
     } else {
-      // Notify Connection open
-      LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
-      mSocket = mMasterSocket;
-      mState = OPEN;
-      LOG(("DTLS connect() succeeded!  Entering connected mode"));
-
-      NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                                DataChannelOnMessageAvailable::ON_CONNECTION,
-                                this, true));
-      return true;
+      // We set Even/Odd and fire ON_CONNECTION via SCTP_COMM_UP when we get that
+      // This also avoids issues with calling TransportFlow stuff on Mainthread
+      return;
     }
   }
   // Note: currently this doesn't actually notify the application
   NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                             DataChannelOnMessageAvailable::ON_CONNECTION,
                             this, false));
-  return false;
+  return;
 }
 
+// Process any pending Opens
+void
+DataChannelConnection::ProcessQueuedOpens()
+{
+  // Can't copy nsDeque's.  Move into temp array since any that fail will
+  // go back to mPending
+  nsDeque temp;
+  DataChannel *temp_channel; // really already_AddRefed<>
+  while (nullptr != (temp_channel = static_cast<DataChannel *>(mPending.PopFront()))) {
+    temp.Push(static_cast<void *>(temp_channel));
+  }
+
+  nsRefPtr<DataChannel> channel;
+  while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(temp.PopFront())))) {
+    if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
+      LOG(("Processing queued open for %p (%u)", channel.get(), channel->mStream));
+      channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_OPEN;
+      OpenFinish(channel.forget()); // may reset the flag and re-push
+    }
+  }
+
+}
 void
 DataChannelConnection::SctpDtlsInput(TransportFlow *flow,
                                      const unsigned char *data, size_t len)
 {
 #ifdef PR_LOGGING
   if (PR_LOG_TEST(GetSCTPLog(), PR_LOG_DEBUG)) {
     char *buf;
 
@@ -600,35 +668,39 @@ DataChannelConnection::SctpDtlsOutput(vo
                            &DataChannelConnection::SendPacket, data, length, true),
                          NS_DISPATCH_NORMAL);
     res = 0; // cheat!  Packets can always be dropped later anyways
   }
   return res;
 }
 #endif
 
+#ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT
 // listen for incoming associations
 // Blocks! - Don't call this from main thread!
+
+#error This code will not work as-is since SetEvenOdd() runs on Mainthread
+
 bool
 DataChannelConnection::Listen(unsigned short port)
 {
   struct sockaddr_in addr;
   socklen_t addr_len;
 
   NS_WARN_IF_FALSE(!NS_IsMainThread(), "Blocks, do not call from main thread!!!");
 
   /* Acting as the 'server' */
   memset((void *)&addr, 0, sizeof(addr));
 #ifdef HAVE_SIN_LEN
   addr.sin_len = sizeof(struct sockaddr_in);
 #endif
   addr.sin_family = AF_INET;
   addr.sin_port = htons(port);
   addr.sin_addr.s_addr = htonl(INADDR_ANY);
-  LOG(("Waiting for connections on port %d", ntohs(addr.sin_port)));
+  LOG(("Waiting for connections on port %u", ntohs(addr.sin_port)));
   mState = CONNECTING;
   if (usrsctp_bind(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(struct sockaddr_in)) < 0) {
     LOG(("***Failed userspace_bind"));
     return false;
   }
   if (usrsctp_listen(mMasterSocket, 1) < 0) {
     LOG(("***Failed userspace_listen"));
     return false;
@@ -645,16 +717,18 @@ DataChannelConnection::Listen(unsigned s
   struct linger l;
   l.l_onoff = 1;
   l.l_linger = 0;
   if (usrsctp_setsockopt(mSocket, SOL_SOCKET, SO_LINGER,
                          (const void *)&l, (socklen_t)sizeof(struct linger)) < 0) {
     LOG(("Couldn't set SO_LINGER on SCTP socket"));
   }
 
+  SetEvenOdd();
+
   // Notify Connection open
   // XXX We need to make sure connection sticks around until the message is delivered
   LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
   NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                             DataChannelOnMessageAvailable::ON_CONNECTION,
                             this, (DataChannel *) nullptr));
   return true;
 }
@@ -721,81 +795,70 @@ DataChannelConnection::Connect(const cha
   }
 #endif
 
   mSocket = mMasterSocket;
 
   LOG(("connect() succeeded!  Entering connected mode"));
   mState = OPEN;
 
+  SetEvenOdd();
+
   // Notify Connection open
   // XXX We need to make sure connection sticks around until the message is delivered
   LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
   NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                             DataChannelOnMessageAvailable::ON_CONNECTION,
                             this, (DataChannel *) nullptr));
   return true;
 }
+#endif
 
 DataChannel *
-DataChannelConnection::FindChannelByStreamIn(uint16_t streamIn)
+DataChannelConnection::FindChannelByStream(uint16_t streamOut)
 {
-  // Auto-extend mStreamsIn as needed
-  if (((uint32_t) streamIn) + 1 > mStreamsIn.Length()) {
-    uint32_t old_len = mStreamsIn.Length();
-    LOG(("Extending mStreamsIn[] to %d elements", ((int32_t) streamIn)+1));
-    mStreamsIn.AppendElements((streamIn+1) - mStreamsIn.Length());
-    for (uint32_t i = old_len; i < mStreamsIn.Length(); ++i)
-      mStreamsIn[i] = nullptr;
-  }
-  // Should always be safe in practice
-  return mStreamsIn.SafeElementAt(streamIn);
-}
-
-DataChannel *
-DataChannelConnection::FindChannelByStreamOut(uint16_t streamOut)
-{
-  return mStreamsOut.SafeElementAt(streamOut);
+  return mStreams.SafeElementAt(streamOut);
 }
 
 uint16_t
-DataChannelConnection::FindFreeStreamOut()
+DataChannelConnection::FindFreeStream()
 {
   uint32_t i, limit;
 
-  limit = mStreamsOut.Length();
+  limit = mStreams.Length();
   if (limit > MAX_NUM_STREAMS)
     limit = MAX_NUM_STREAMS;
-  for (i = 0; i < limit; ++i) {
-    if (!mStreamsOut[i]) {
+
+  for (i = (mAllocateEven ? 0 : 1); i < limit; i += 2) {
+    if (!mStreams[i]) {
       // Verify it's not still in the process of closing
       for (uint32_t j = 0; j < mStreamsResetting.Length(); ++j) {
         if (mStreamsResetting[j] == i) {
           continue;
         }
       }
       break;
     }
   }
   if (i == limit) {
     return INVALID_STREAM;
   }
   return i;
 }
 
 bool
-DataChannelConnection::RequestMoreStreamsOut(int32_t aNeeded)
+DataChannelConnection::RequestMoreStreams(int32_t aNeeded)
 {
   struct sctp_status status;
   struct sctp_add_streams sas;
   uint32_t outStreamsNeeded;
   socklen_t len;
 
-  if (aNeeded + mStreamsOut.Length() > MAX_NUM_STREAMS)
-    aNeeded = MAX_NUM_STREAMS - mStreamsOut.Length();
+  if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS)
+    aNeeded = MAX_NUM_STREAMS - mStreams.Length();
   if (aNeeded <= 0)
     return false;
 
   len = (socklen_t)sizeof(struct sctp_status);
   if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) {
     LOG(("***failed: getsockopt SCTP_STATUS"));
     return false;
   }
@@ -813,66 +876,44 @@ DataChannelConnection::RequestMoreStream
     LOG(("***failed: setsockopt ADD errno=%d", errno));
     return false;
   }
   LOG(("Requested %u more streams", outStreamsNeeded));
   return true;
 }
 
 int32_t
-DataChannelConnection::SendControlMessage(void *msg, uint32_t len, uint16_t streamOut)
+DataChannelConnection::SendControlMessage(void *msg, uint32_t len, uint16_t stream)
 {
   struct sctp_sndinfo sndinfo;
 
   // Note: Main-thread IO, but doesn't block
   memset(&sndinfo, 0, sizeof(struct sctp_sndinfo));
-  sndinfo.snd_sid = streamOut;
+  sndinfo.snd_sid = stream;
   sndinfo.snd_ppid = htonl(DATA_CHANNEL_PPID_CONTROL);
   if (usrsctp_sendv(mSocket, msg, len, nullptr, 0,
                     &sndinfo, (socklen_t)sizeof(struct sctp_sndinfo),
                     SCTP_SENDV_SNDINFO, 0) < 0) {
     //LOG(("***failed: sctp_sendv")); don't log because errno is a return!
     return (0);
   }
   return (1);
 }
 
 int32_t
-DataChannelConnection::SendOpenResponseMessage(uint16_t streamOut, uint16_t streamIn)
-{
-  struct rtcweb_datachannel_open_response rsp;
-
-  memset(&rsp, 0, sizeof(struct rtcweb_datachannel_open_response));
-  rsp.msg_type = DATA_CHANNEL_OPEN_RESPONSE;
-  rsp.reverse_stream = htons(streamIn);
-
-  return SendControlMessage(&rsp, sizeof(rsp), streamOut);
-}
-
-
-int32_t
-DataChannelConnection::SendOpenAckMessage(uint16_t streamOut)
-{
-  struct rtcweb_datachannel_ack ack;
-
-  memset(&ack, 0, sizeof(struct rtcweb_datachannel_ack));
-  ack.msg_type = DATA_CHANNEL_ACK;
-
-  return SendControlMessage(&ack, sizeof(ack), streamOut);
-}
-
-int32_t
 DataChannelConnection::SendOpenRequestMessage(const nsACString& label,
-                                              uint16_t streamOut, bool unordered,
+                                              const nsACString& protocol,
+                                              uint16_t stream, bool unordered,
                                               uint16_t prPolicy, uint32_t prValue)
 {
-  int len = label.Length(); // not including nul
+  int label_len = label.Length(); // not including nul
+  int proto_len = protocol.Length(); // not including nul
   struct rtcweb_datachannel_open_request *req =
-    (struct rtcweb_datachannel_open_request*) moz_xmalloc(sizeof(*req)+len);
-   // careful - ok because request includes 1 char label
+    (struct rtcweb_datachannel_open_request*) moz_xmalloc((sizeof(*req)-1) + label_len + proto_len);
+   // careful - request includes 1 char label
 
   memset(req, 0, sizeof(struct rtcweb_datachannel_open_request));
   req->msg_type = DATA_CHANNEL_OPEN_REQUEST;
   switch (prPolicy) {
   case SCTP_PR_SCTP_NONE:
     req->channel_type = DATA_CHANNEL_RELIABLE;
     break;
   case SCTP_PR_SCTP_TTL:
@@ -881,25 +922,30 @@ DataChannelConnection::SendOpenRequestMe
   case SCTP_PR_SCTP_RTX:
     req->channel_type = DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT;
     break;
   default:
     // FIX! need to set errno!  Or make all these SendXxxx() funcs return 0 or errno!
     moz_free(req);
     return (0);
   }
-  req->flags = htons(0);
   if (unordered) {
-    req->flags |= htons(DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED);
+    // Per the current types, all differ by 0x80 between ordered and unordered
+    req->channel_type |= 0x80; // NOTE: be careful if new types are added in the future
   }
+
   req->reliability_params = htons((uint16_t)prValue); /* XXX Why 16-bit */
   req->priority = htons(0); /* XXX: add support */
-  strcpy(&req->label[0], PromiseFlatCString(label).get());
+  req->label_length = label_len;
+  req->protocol_length = proto_len;
+  memcpy(&req->label[0], PromiseFlatCString(label).get(), label_len);
+  memcpy(&req->label[req->label_length], PromiseFlatCString(protocol).get(), proto_len);
 
-  int32_t result = SendControlMessage(req, sizeof(*req)+len, streamOut);
+  // sizeof(*req) already includes +1 byte for label, need nul for both strings
+  int32_t result = SendControlMessage(req, (sizeof(*req)-1) + label_len + proto_len, stream);
 
   moz_free(req);
   return result;
 }
 
 // XXX This should use a separate thread (outbound queue) which should
 // select() to know when to *try* to send data to the socket again.
 // Alternatively, it can use a timeout, but that's guaranteed to be wrong
@@ -918,85 +964,46 @@ DataChannelConnection::SendDeferredMessa
   bool still_blocked = false;
   bool sent = false;
 
   // This may block while something is modifying channels, but should not block for IO
   MutexAutoLock lock(mLock);
 
   // XXX For total fairness, on a still_blocked we'd start next time at the
   // same index.  Sorry, not going to bother for now.
-  for (i = 0; i < mStreamsOut.Length(); ++i) {
-    channel = mStreamsOut[i];
+  for (i = 0; i < mStreams.Length(); ++i) {
+    channel = mStreams[i];
     if (!channel)
       continue;
 
     // Only one of these should be set....
     if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_REQ) {
-      if (SendOpenRequestMessage(channel->mLabel, channel->mStreamOut,
-                                 channel->mFlags & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED,
+      if (SendOpenRequestMessage(channel->mLabel, channel->mProtocol,
+                                 channel->mStream,
+                                 channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED,
                                  channel->mPrPolicy, channel->mPrValue)) {
         channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_REQ;
         sent = true;
       } else {
         if (errno == EAGAIN || errno == EWOULDBLOCK) {
           still_blocked = true;
         } else {
           // Close the channel, inform the user
-          mStreamsOut[channel->mStreamOut] = nullptr;
+          mStreams[channel->mStream] = nullptr;
           channel->mState = CLOSED;
           // Don't need to reset; we didn't open it
           NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                                     DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
                                     channel));
         }
       }
     }
     if (still_blocked)
       break;
 
-    if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_RSP) {
-      if (SendOpenResponseMessage(channel->mStreamOut, channel->mStreamIn)) {
-        channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_RSP;
-        sent = true;
-      } else {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-          still_blocked = true;
-        } else {
-          // Close the channel
-          // Don't need to reset; we didn't open it
-          // The other side may be left with a hanging Open.  Our inability to
-          // send the open response means we can't easily tell them about it
-          // We haven't informed the user/DOM of the creation yet, so just
-          // delete the channel.
-          mStreamsIn[channel->mStreamIn]   = nullptr;
-          mStreamsOut[channel->mStreamOut] = nullptr;
-          channel->mState = CLOSED;
-        }
-      }
-    }
-    if (still_blocked)
-      break;
-
-    if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_ACK) {
-      if (SendOpenAckMessage(channel->mStreamOut)) {
-        channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_ACK;
-        sent = true;
-      } else {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-          still_blocked = true;
-        } else {
-          // Close the channel, inform the user
-          CloseInt(channel);
-          // XXX send error via DataChannelOnMessageAvailable (bug 843625)
-        }
-      }
-    }
-    if (still_blocked)
-      break;
-
     if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_DATA) {
       bool failed_send = false;
       int32_t result;
 
       if (channel->mState == CLOSED || channel->mState == CLOSING) {
         channel->mBufferedData.Clear();
       }
       while (!channel->mBufferedData.IsEmpty() &&
@@ -1047,247 +1054,190 @@ DataChannelConnection::SendDeferredMessa
     mDeferTimeout--;
 
   return true;
 }
 
 void
 DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req,
                                                 size_t length,
-                                                uint16_t streamIn)
+                                                uint16_t stream)
 {
   nsRefPtr<DataChannel> channel;
   uint32_t prValue;
   uint16_t prPolicy;
   uint32_t flags;
-  nsCString label(nsDependentCString(req->label));
 
   mLock.AssertCurrentThreadOwns();
 
-  if ((channel = FindChannelByStreamIn(streamIn))) {
-    LOG(("ERROR: HandleOpenRequestMessage: channel for stream %d is in state %d instead of CLOSED.",
-         streamIn, channel->mState));
-    /* XXX: some error handling */
-    return;
+  if (length != (sizeof(*req) - 1) + req->label_length + req->protocol_length) {
+    LOG(("Inconsistent length: %u, should be %u", length,
+         (sizeof(*req) - 1) + req->label_length + req->protocol_length));
+    if (length < (sizeof(*req) - 1) + req->label_length + req->protocol_length)
+      return;
   }
+
   switch (req->channel_type) {
     case DATA_CHANNEL_RELIABLE:
+    case DATA_CHANNEL_RELIABLE_UNORDERED:
       prPolicy = SCTP_PR_SCTP_NONE;
       break;
     case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT:
+    case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED:
       prPolicy = SCTP_PR_SCTP_RTX;
       break;
     case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED:
+    case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED:
       prPolicy = SCTP_PR_SCTP_TTL;
       break;
     default:
       /* XXX error handling */
       return;
   }
   prValue = ntohs(req->reliability_params);
-  flags = ntohs(req->flags) & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED;
+  flags = (req->channel_type & 0x80) ? DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED : 0;
+
+  if ((channel = FindChannelByStream(stream))) {
+    if (!(channel->mFlags & DATA_CHANNEL_FLAGS_EXTERNAL_NEGOTIATED)) {
+      LOG(("ERROR: HandleOpenRequestMessage: channel for stream %u is in state %d instead of CLOSED.",
+           stream, channel->mState));
+     /* XXX: some error handling */
+    } else {
+      LOG(("Open for externally negotiated channel %u", stream));
+      // XXX should also check protocol, maybe label
+      if (prPolicy != channel->mPrPolicy ||
+          prValue != channel->mPrValue ||
+          flags != (channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED))
+      {
+        LOG(("WARNING: external negotiation mismatch with OpenRequest:"
+             "channel %u, policy %u/%u, value %u/%u, flags %x/%x",
+             stream, prPolicy, channel->mPrPolicy,
+             prValue, channel->mPrValue, flags, channel->mFlags));
+      }
+    }
+    return;
+  }
+
+  nsCString label(nsDependentCSubstring(&req->label[0], req->label_length));
+  nsCString protocol(nsDependentCSubstring(&req->label[req->label_length],
+                                           req->protocol_length));
+
   channel = new DataChannel(this,
-                            INVALID_STREAM, streamIn,
+                            stream,
                             DataChannel::CONNECTING,
                             label,
+                            protocol,
                             prPolicy, prValue,
                             flags,
                             nullptr, nullptr);
-  mStreamsIn[streamIn] = channel;
+  mStreams[stream] = channel;
+
+  channel->mState = DataChannel::WAITING_TO_OPEN;
 
-  OpenResponseFinish(channel.forget());
+  LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u", __FUNCTION__,
+       channel->mLabel.get(), channel->mProtocol.get(), stream));
+  NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+                            DataChannelOnMessageAvailable::ON_CHANNEL_CREATED,
+                            this, channel));
+
+  LOG(("%s: deferring sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
+
+  // Now process any queued data messages for the channel (which will
+  // themselves likely get queued until we leave WAITING_TO_OPEN, plus any
+  // more that come in before that happens)
+  DeliverQueuedData(stream);
 }
 
 void
-DataChannelConnection::OpenResponseFinish(already_AddRefed<DataChannel> aChannel)
+DataChannelConnection::DeliverQueuedData(uint16_t stream)
 {
-  nsRefPtr<DataChannel> channel(aChannel);
-  uint16_t streamOut = FindFreeStreamOut(); // may be INVALID_STREAM!
-
   mLock.AssertCurrentThreadOwns();
 
-  LOG(("Finished response: channel %p, streamOut = %u", channel.get(), streamOut));
-
-  if (streamOut == INVALID_STREAM) {
-    if (!RequestMoreStreamsOut()) {
-      channel->mState = CLOSED;
-      if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_RSP) {
-        // We already returned the channel to the app.
-        NS_ERROR("Failed to request more streams");
-        NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                                  DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
-                                  channel));
-      }
-      // If we weren't deferred, we'll be destroying the channel, but it
-      // never really got set up
-      // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and
-      // Dispatch it to ourselves
-      mStreamsIn[channel->mStreamIn] = nullptr;
-      /* XXX: Signal error to the other end (and maybe fire onError: bug 843625) */
-      return;
+  uint32_t i = 0;
+  while (i < mQueuedData.Length()) {
+    // Careful! we may modify the array length from within the loop!
+    if (mQueuedData[i]->mStream == stream) {
+      LOG(("Delivering queued data for stream %u, length %u",
+           stream, mQueuedData[i]->mLength));
+      // Deliver the queued data
+      HandleDataMessage(mQueuedData[i]->mPpid,
+                        mQueuedData[i]->mData, mQueuedData[i]->mLength,
+                        mQueuedData[i]->mStream);
+      mQueuedData.RemoveElementAt(i);
+      continue; // don't bump index since we removed the element
     }
-    LOG(("Queuing channel %d to finish response", channel->mStreamIn));
-    channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_RSP;
-    DataChannel *temp = channel.get(); // Can't cast away already_AddRefed<> from channel.forget()
-    channel.forget();
-    mPending.Push(temp);
-    // can't notify the user until we can send an OpenResponse
-  } else {
-    channel->mStreamOut = streamOut;
-    mStreamsOut[streamOut] = channel;
-    if (SendOpenResponseMessage(streamOut, channel->mStreamIn)) {
-      /* Notify ondatachannel */
-      // XXX We need to make sure connection sticks around until the message is delivered
-      LOG(("%s: sending ON_CHANNEL_CREATED for %s: %d/%d", __FUNCTION__,
-           channel->mLabel.get(), streamOut, channel->mStreamIn));
-      NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                                DataChannelOnMessageAvailable::ON_CHANNEL_CREATED,
-                                this, channel));
-    } else {
-      if (errno == EAGAIN || errno == EWOULDBLOCK) {
-        channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_RSP;
-        StartDefer();
-      } else {
-        if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_RSP) {
-          // We already returned the channel to the app.
-          NS_ERROR("Failed to send open response");
-          NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                                    DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
-                                    channel));
-        }
-
-        /* XXX: Signal error to the other end. */
-        mStreamsIn[channel->mStreamIn] = nullptr;
-        mStreamsOut[streamOut] = nullptr;
-        channel->mStreamOut = INVALID_STREAM;
-        // we'll be destroying the channel if it wasn't already returned
-        channel->mState = CLOSED;
-        return;
-      }
-    }
-  }
-}
-
-
-void
-DataChannelConnection::HandleOpenResponseMessage(const struct rtcweb_datachannel_open_response *rsp,
-                                                 size_t length, uint16_t streamIn)
-{
-  uint16_t streamOut;
-  DataChannel *channel;
-
-  mLock.AssertCurrentThreadOwns();
-
-  streamOut = ntohs(rsp->reverse_stream);
-  channel = FindChannelByStreamOut(streamOut);
-
-  NS_ENSURE_TRUE_VOID(channel);
-  NS_ENSURE_TRUE_VOID(channel->mState == CONNECTING);
-
-  if (rsp->error) {
-    LOG(("%s: error in response to open of channel %d (%s)",
-         __FUNCTION__, streamOut, channel->mLabel.get()));
-
-  } else {
-    NS_ENSURE_TRUE_VOID(!FindChannelByStreamIn(streamIn));
-
-    channel->mStreamIn = streamIn;
-    channel->mState = OPEN;
-    channel->mReady = true;
-    mStreamsIn[streamIn] = channel;
-    if (SendOpenAckMessage(streamOut)) {
-      channel->mFlags = 0;
-    } else {
-      // XXX Only on EAGAIN!?  And if not, then close the channel??
-      channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_ACK;
-      StartDefer();
-    }
-    LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel));
-    NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                              DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
-                              channel));
+    i++;
   }
 }
 
 void
-DataChannelConnection::HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack,
-                                            size_t length, uint16_t streamIn)
-{
-  DataChannel *channel;
-
-  mLock.AssertCurrentThreadOwns();
-
-  channel = FindChannelByStreamIn(streamIn);
-
-  NS_ENSURE_TRUE_VOID(channel);
-  NS_ENSURE_TRUE_VOID(channel->mState == CONNECTING);
-
-  channel->mState = channel->mReady ? DataChannel::OPEN : DataChannel::WAITING_TO_OPEN;
-  if (channel->mState == OPEN) {
-    LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel));
-    NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                              DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
-                              channel));
-  } else {
-    LOG(("%s: deferring sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel));
-  }
-}
-
-void
-DataChannelConnection::HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t streamIn)
+DataChannelConnection::HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t stream)
 {
   /* XXX: Send an error message? */
-  LOG(("unknown DataChannel message received: %u, len %ld on stream %lu", ppid, length, streamIn));
+  LOG(("unknown DataChannel message received: %u, len %ld on stream %lu", ppid, length, stream));
   // XXX Log to JS error console if possible
 }
 
 void
 DataChannelConnection::HandleDataMessage(uint32_t ppid,
                                          const void *data, size_t length,
-                                         uint16_t streamIn)
+                                         uint16_t stream)
 {
   DataChannel *channel;
   const char *buffer = (const char *) data;
 
   mLock.AssertCurrentThreadOwns();
 
-  channel = FindChannelByStreamIn(streamIn);
+  channel = FindChannelByStream(stream);
 
   // XXX A closed channel may trip this... check
-  NS_ENSURE_TRUE_VOID(channel);
-  NS_ENSURE_TRUE_VOID(channel->mState != CONNECTING);
+  if (!channel) {
+    // In the updated 0-RTT open case, the sender can send data immediately
+    // after Open, and doesn't set the in-order bit (since we don't have a
+    // response or ack).  Also, with external negotiation, data can come in
+    // before we're told about the external negotiation.  We need to buffer
+    // data until either a) Open comes in, if the ordering get messed up,
+    // or b) the app tells us this channel was externally negotiated.  When
+    // these occur, we deliver the data.
+
+    // Since this is rare and non-performance, keep a single list of queued
+    // data messages to deliver once the channel opens.
+    LOG(("Queuing data for stream %u, length %u", stream, length));
+    mQueuedData.AppendElement(new QueuedDataMessage(stream, ppid, data, length));
+    return;
+  }
 
   // XXX should this be a simple if, no warnings/debugbreaks?
   NS_ENSURE_TRUE_VOID(channel->mState != CLOSED);
 
   {
     nsAutoCString recvData(buffer, length);
 
     switch (ppid) {
       case DATA_CHANNEL_PPID_DOMSTRING:
-        LOG(("DataChannel: String message received of length %lu on channel %d: %.*s",
-             length, channel->mStreamOut, (int)PR_MIN(length, 80), buffer));
+        LOG(("DataChannel: String message received of length %lu on channel %u: %.*s",
+             length, channel->mStream, (int)PR_MIN(length, 80), buffer));
         length = -1; // Flag for DOMString
 
         // WebSockets checks IsUTF8() here; we can try to deliver it
 
         NS_WARN_IF_FALSE(channel->mBinaryBuffer.IsEmpty(), "Binary message aborted by text message!");
         if (!channel->mBinaryBuffer.IsEmpty())
           channel->mBinaryBuffer.Truncate(0);
         break;
 
       case DATA_CHANNEL_PPID_BINARY:
         channel->mBinaryBuffer += recvData;
-        LOG(("DataChannel: Received binary message of length %lu (total %u) on channel id %d",
-             length, channel->mBinaryBuffer.Length(), channel->mStreamOut));
+        LOG(("DataChannel: Received binary message of length %lu (total %u) on channel id %u",
+             length, channel->mBinaryBuffer.Length(), channel->mStream));
         return; // Not ready to notify application
 
       case DATA_CHANNEL_PPID_BINARY_LAST:
-        LOG(("DataChannel: Received binary message of length %lu on channel id %d",
-             length, channel->mStreamOut));
+        LOG(("DataChannel: Received binary message of length %lu on channel id %u",
+             length, channel->mStream));
         if (!channel->mBinaryBuffer.IsEmpty()) {
           channel->mBinaryBuffer += recvData;
           LOG(("%s: sending ON_DATA (binary fragmented) for %p", __FUNCTION__, channel));
           channel->SendOrQueue(new DataChannelOnMessageAvailable(
                                  DataChannelOnMessageAvailable::ON_DATA, this,
                                  channel, channel->mBinaryBuffer,
                                  channel->mBinaryBuffer.Length()));
           channel->mBinaryBuffer.Truncate(0);
@@ -1305,82 +1255,73 @@ DataChannelConnection::HandleDataMessage
     channel->SendOrQueue(new DataChannelOnMessageAvailable(
                            DataChannelOnMessageAvailable::ON_DATA, this,
                            channel, recvData, length));
   }
 }
 
 // Called with mLock locked!
 void
-DataChannelConnection::HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t streamIn)
+DataChannelConnection::HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t stream)
 {
   const struct rtcweb_datachannel_open_request *req;
-  const struct rtcweb_datachannel_open_response *rsp;
-  const struct rtcweb_datachannel_ack *ack, *msg;
 
   mLock.AssertCurrentThreadOwns();
 
   switch (ppid) {
     case DATA_CHANNEL_PPID_CONTROL:
-      NS_ENSURE_TRUE_VOID(length >= sizeof(*ack)); // Ack is the smallest
+      NS_ENSURE_TRUE_VOID(length >= sizeof(*req));
 
-      msg = static_cast<const struct rtcweb_datachannel_ack *>(buffer);
-      switch (msg->msg_type) {
+      req = static_cast<const struct rtcweb_datachannel_open_request *>(buffer);
+      switch (req->msg_type) {
         case DATA_CHANNEL_OPEN_REQUEST:
           LOG(("length %u, sizeof(*req) = %u", length, sizeof(*req)));
           NS_ENSURE_TRUE_VOID(length >= sizeof(*req));
 
-          req = static_cast<const struct rtcweb_datachannel_open_request *>(buffer);
-          HandleOpenRequestMessage(req, length, streamIn);
-          break;
-        case DATA_CHANNEL_OPEN_RESPONSE:
-          NS_ENSURE_TRUE_VOID(length >= sizeof(*rsp));
-
-          rsp = static_cast<const struct rtcweb_datachannel_open_response *>(buffer);
-          HandleOpenResponseMessage(rsp, length, streamIn);
-          break;
-        case DATA_CHANNEL_ACK:
-          // >= sizeof(*ack) checked above
-
-          ack = static_cast<const struct rtcweb_datachannel_ack *>(buffer);
-          HandleOpenAckMessage(ack, length, streamIn);
+          HandleOpenRequestMessage(req, length, stream);
           break;
         default:
-          HandleUnknownMessage(ppid, length, streamIn);
+          HandleUnknownMessage(ppid, length, stream);
           break;
       }
       break;
     case DATA_CHANNEL_PPID_DOMSTRING:
     case DATA_CHANNEL_PPID_BINARY:
     case DATA_CHANNEL_PPID_BINARY_LAST:
-      HandleDataMessage(ppid, buffer, length, streamIn);
+      HandleDataMessage(ppid, buffer, length, stream);
       break;
     default:
       LOG(("Message of length %lu, PPID %u on stream %u received.",
-           length, ppid, streamIn));
+           length, ppid, stream));
       break;
   }
 }
 
 void
 DataChannelConnection::HandleAssociationChangeEvent(const struct sctp_assoc_change *sac)
 {
   uint32_t i, n;
 
   switch (sac->sac_state) {
   case SCTP_COMM_UP:
     LOG(("Association change: SCTP_COMM_UP"));
     if (mState == CONNECTING) {
       mSocket = mMasterSocket;
       mState = OPEN;
 
+      SetEvenOdd();
+
       NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                                 DataChannelOnMessageAvailable::ON_CONNECTION,
                                 this, true));
       LOG(("DTLS connect() succeeded!  Entering connected mode"));
+
+      // Open any streams pending...
+      ProcessQueuedOpens();
+
     } else if (mState == OPEN) {
       LOG(("DataConnection Already OPEN"));
     } else {
       LOG(("Unexpected state: %d", mState));
     }
     break;
   case SCTP_COMM_LOST:
     LOG(("Association change: SCTP_COMM_LOST"));
@@ -1548,32 +1489,32 @@ DataChannelConnection::HandleSendFailedE
     LOG(("Unsent "));
   }
    if (ssfe->ssfe_flags & SCTP_DATA_SENT) {
     LOG(("Sent "));
   }
   if (ssfe->ssfe_flags & ~(SCTP_DATA_SENT | SCTP_DATA_UNSENT)) {
     LOG(("(flags = %x) ", ssfe->ssfe_flags));
   }
-  LOG(("message with PPID = %d, SID = %d, flags: 0x%04x due to error = 0x%08x",
+  LOG(("message with PPID = %u, SID = %d, flags: 0x%04x due to error = 0x%08x",
        ntohl(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid,
        ssfe->ssfe_info.snd_flags, ssfe->ssfe_error));
   n = ssfe->ssfe_length - sizeof(struct sctp_send_failed_event);
   for (i = 0; i < n; ++i) {
     LOG((" 0x%02x", ssfe->ssfe_data[i]));
   }
 }
 
 void
 DataChannelConnection::ResetOutgoingStream(uint16_t streamOut)
 {
   uint32_t i;
 
   mLock.AssertCurrentThreadOwns();
-  LOG(("Connection %p: Resetting outgoing stream %d",
+  LOG(("Connection %p: Resetting outgoing stream %u",
        (void *) this, streamOut));
   // Rarely has more than a couple items and only for a short time
   for (i = 0; i < mStreamsResetting.Length(); ++i) {
     if (mStreamsResetting[i] == streamOut) {
       return;
     }
   }
   mStreamsResetting.AppendElement(streamOut);
@@ -1615,65 +1556,62 @@ DataChannelConnection::HandleStreamReset
   uint32_t n, i;
   nsRefPtr<DataChannel> channel; // since we may null out the ref to the channel
 
   if (!(strrst->strreset_flags & SCTP_STREAM_RESET_DENIED) &&
       !(strrst->strreset_flags & SCTP_STREAM_RESET_FAILED)) {
     n = (strrst->strreset_length - sizeof(struct sctp_stream_reset_event)) / sizeof(uint16_t);
     for (i = 0; i < n; ++i) {
       if (strrst->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
-        channel = FindChannelByStreamIn(strrst->strreset_stream_list[i]);
+        channel = FindChannelByStream(strrst->strreset_stream_list[i]);
         if (channel) {
           // The other side closed the channel
           // We could be in three states:
           // 1. Normal state (input and output streams (OPEN)
           //    Notify application, send a RESET in response on our
           //    outbound channel.  Go to CLOSED
           // 2. We sent our own reset (CLOSING); either they crossed on the
           //    wire, or this is a response to our Reset.
           //    Go to CLOSED
           // 3. We've sent a open but haven't gotten a response yet (OPENING)
           //    I believe this is impossible, as we don't have an input stream yet.
 
-          LOG(("Incoming: Channel %d outgoing/%d incoming closed, state %d",
-               channel->mStreamOut, channel->mStreamIn, channel->mState));
+          LOG(("Incoming: Channel %u  closed, state %d",
+               channel->mStream, channel->mState));
           ASSERT_WEBRTC(channel->mState == DataChannel::OPEN ||
                         channel->mState == DataChannel::CLOSING ||
                         channel->mState == DataChannel::WAITING_TO_OPEN);
           if (channel->mState == DataChannel::OPEN ||
               channel->mState == DataChannel::WAITING_TO_OPEN) {
-            ResetOutgoingStream(channel->mStreamOut);
+            ResetOutgoingStream(channel->mStream);
             SendOutgoingStreamReset();
             NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                                       DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
                                       channel));
-            mStreamsOut[channel->mStreamOut] = nullptr;
           }
-          mStreamsIn[channel->mStreamIn] = nullptr;
+          mStreams[channel->mStream] = nullptr;
 
           LOG(("Disconnected DataChannel %p from connection %p",
                (void *) channel.get(), (void *) channel->mConnection.get()));
           channel->Destroy();
           // At this point when we leave here, the object is a zombie held alive only by the DOM object
         } else {
           LOG(("Can't find incoming channel %d",i));
         }
       }
 
       if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) {
-        channel = FindChannelByStreamOut(strrst->strreset_stream_list[i]);
+        channel = FindChannelByStream(strrst->strreset_stream_list[i]);
         if (channel) {
-          LOG(("Outgoing: Connection %p channel %p  streams: %d outgoing/%d incoming closed",
-               (void *) this, (void *) channel.get(), channel->mStreamOut, channel->mStreamIn));
+          LOG(("Outgoing: Connection %p channel %p  stream: %u closed",
+               (void *) this, (void *) channel.get(), channel->mStream));
 
           ASSERT_WEBRTC(channel->mState == CLOSING);
           if (channel->mState == CLOSING) {
-            mStreamsOut[channel->mStreamOut] = nullptr;
-            if (channel->mStreamIn != INVALID_STREAM)
-              mStreamsIn[channel->mStreamIn] = nullptr;
+            mStreams[channel->mStream] = nullptr;
             LOG(("Disconnected DataChannel %p from connection %p (refcnt will be %u)",
                  (void *) channel.get(), (void *) channel->mConnection.get(),
                  (uint32_t) channel->mConnection->mRefCnt-1));
             channel->Destroy();
             // At this point when we leave here, the object is a zombie held alive only by the DOM object
           }
         } else {
           LOG(("Can't find outgoing channel %d",i));
@@ -1681,112 +1619,89 @@ DataChannelConnection::HandleStreamReset
       }
     }
   }
 }
 
 void
 DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_event *strchg)
 {
-  uint16_t streamOut;
+  uint16_t stream;
   uint32_t i;
   nsRefPtr<DataChannel> channel;
 
   if (strchg->strchange_flags == SCTP_STREAM_CHANGE_DENIED) {
     LOG(("*** Failed increasing number of streams from %u (%u/%u)",
-         mStreamsOut.Length(),
+         mStreams.Length(),
          strchg->strchange_instrms,
          strchg->strchange_outstrms));
     // XXX FIX! notify pending opens of failure
     return;
   } else {
-    if (strchg->strchange_instrms > mStreamsIn.Length()) {
+    if (strchg->strchange_instrms > mStreams.Length()) {
       LOG(("Other side increased streamds from %u to %u",
-           mStreamsIn.Length(), strchg->strchange_instrms));
+           mStreams.Length(), strchg->strchange_instrms));
     }
-    if (strchg->strchange_outstrms > mStreamsOut.Length()) {
-      uint16_t old_len = mStreamsOut.Length();
+    if (strchg->strchange_outstrms > mStreams.Length()) {
+      uint16_t old_len = mStreams.Length();
       LOG(("Increasing number of streams from %u to %u - adding %u (in: %u)",
            old_len,
            strchg->strchange_outstrms,
            strchg->strchange_outstrms - old_len,
            strchg->strchange_instrms));
       // make sure both are the same length
-      mStreamsOut.AppendElements(strchg->strchange_outstrms - old_len);
-      LOG(("New length = %d (was %d)", mStreamsOut.Length(), old_len));
-      for (uint32_t i = old_len; i < mStreamsOut.Length(); ++i) {
-        mStreamsOut[i] = nullptr;
+      mStreams.AppendElements(strchg->strchange_outstrms - old_len);
+      LOG(("New length = %d (was %d)", mStreams.Length(), old_len));
+      for (uint32_t i = old_len; i < mStreams.Length(); ++i) {
+        mStreams[i] = nullptr;
       }
       // Re-process any channels waiting for streams.
       // Linear search, but we don't increase channels often and
       // the array would only get long in case of an app error normally
 
       // Make sure we request enough streams if there's a big jump in streams
       // Could make a more complex API for OpenXxxFinish() and avoid this loop
       int32_t num_needed = mPending.GetSize();
       LOG(("%d of %d new streams already needed", num_needed,
            strchg->strchange_outstrms - old_len));
       num_needed -= (strchg->strchange_outstrms - old_len); // number we added
       if (num_needed > 0) {
         if (num_needed < 16)
           num_needed = 16;
         LOG(("Not enough new streams, asking for %d more", num_needed));
-        RequestMoreStreamsOut(num_needed);
-      }
-
-      // Can't copy nsDeque's.  Move into temp array since any that fail will
-      // go back to mPending
-      nsDeque temp;
-      DataChannel *temp_channel; // really already_AddRefed<>
-      while (nullptr != (temp_channel = static_cast<DataChannel *>(mPending.PopFront()))) {
-        temp.Push(static_cast<void *>(temp_channel));
+        RequestMoreStreams(num_needed);
       }
 
-      // Now assign our new streams
-      while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(temp.PopFront())))) {
-        if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_RSP) {
-          channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_RSP;
-          OpenResponseFinish(channel.forget()); // may reset the flag and re-push
-        } else if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
-          channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_OPEN;
-          OpenFinish(channel.forget()); // may reset the flag and re-push
-        }
-      }
+      ProcessQueuedOpens();
     }
     // else probably not a change in # of streams
   }
 
-  for (i = 0; i < mStreamsOut.Length(); ++i) {
-    channel = mStreamsOut[i];
+  for (i = 0; i < mStreams.Length(); ++i) {
+    channel = mStreams[i];
     if (!channel)
       continue;
 
     if ((channel->mState == CONNECTING) &&
-        (channel->mStreamOut == INVALID_STREAM)) {
+        (channel->mStream == INVALID_STREAM)) {
       if ((strchg->strchange_flags & SCTP_STREAM_CHANGE_DENIED) ||
           (strchg->strchange_flags & SCTP_STREAM_CHANGE_FAILED)) {
         /* XXX: Signal to the other end. */
-        if (channel->mStreamIn != INVALID_STREAM) {
-          mStreamsIn[channel->mStreamIn] = nullptr;
-        }
         channel->mState = CLOSED;
         NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                                   DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
                                   channel));
         // maybe fire onError (bug 843625)
       } else {
-        streamOut = FindFreeStreamOut();
-        if (streamOut != INVALID_STREAM) {
-          channel->mStreamOut = streamOut;
-          mStreamsOut[streamOut] = channel;
-          if (channel->mStreamIn == INVALID_STREAM) {
-            channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
-          } else {
-            channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_RSP;
-          }
+        stream = FindFreeStream();
+        if (stream != INVALID_STREAM) {
+          channel->mStream = stream;
+          mStreams[stream] = channel;
+          channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
+          /// XXX fix
           StartDefer();
         } else {
           /* We will not find more ... */
           break;
         }
       }
     }
   }
@@ -1863,137 +1778,193 @@ DataChannelConnection::ReceiveCallback(s
       HandleMessage(data, datalen, ntohl(rcv.rcv_ppid), rcv.rcv_sid);
     }
   }
   // usrsctp defines the callback as returning an int, but doesn't use it
   return 1;
 }
 
 already_AddRefed<DataChannel>
-DataChannelConnection::Open(const nsACString& label, Type type, bool inOrder,
+DataChannelConnection::Open(const nsACString& label, const nsACString& protocol,
+                            Type type, bool inOrder,
                             uint32_t prValue, DataChannelListener *aListener,
-                            nsISupports *aContext)
+                            nsISupports *aContext, bool aExternalNegotiated,
+                            uint16_t aStream)
 {
+  // aStream == INVALID_STREAM to have the protocol allocate
   uint16_t prPolicy = SCTP_PR_SCTP_NONE;
   uint32_t flags;
 
-  LOG(("DC Open: label %s, type %u, inorder %d, prValue %u, listener %p, context %p",
-       PromiseFlatCString(label).get(), type, inOrder, prValue, aListener, aContext));
+  LOG(("DC Open: label %s/%s, type %u, inorder %d, prValue %u, listener %p, context %p, external: %s, stream %u",
+       PromiseFlatCString(label).get(), PromiseFlatCString(protocol).get(),
+       type, inOrder, prValue, aListener, aContext,
+       aExternalNegotiated ? "true" : "false", aStream));
   switch (type) {
     case DATA_CHANNEL_RELIABLE:
       prPolicy = SCTP_PR_SCTP_NONE;
       break;
     case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT:
       prPolicy = SCTP_PR_SCTP_RTX;
       break;
     case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED:
       prPolicy = SCTP_PR_SCTP_TTL;
       break;
   }
   if ((prPolicy == SCTP_PR_SCTP_NONE) && (prValue != 0)) {
     return nullptr;
   }
 
-  flags = !inOrder ? DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED : 0;
+  if (aStream != INVALID_STREAM && mStreams[aStream]) {
+    LOG(("ERROR: external negotiation of already-open channel %u", aStream));
+    // XXX How do we indicate this up to the application?  Probably the
+    // caller's job, but we may need to return an error code.
+    return nullptr;
+  }
+
+  flags = !inOrder ? DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED : 0;
   nsRefPtr<DataChannel> channel(new DataChannel(this,
-                                                INVALID_STREAM, INVALID_STREAM,
+                                                aStream,
                                                 DataChannel::CONNECTING,
-                                                label, type, prValue,
+                                                label, protocol,
+                                                type, prValue,
                                                 flags,
                                                 aListener, aContext));
+  if (aExternalNegotiated) {
+    channel->mFlags |= DATA_CHANNEL_FLAGS_EXTERNAL_NEGOTIATED;
+  }
 
   MutexAutoLock lock(mLock); // OpenFinish assumes this
   return OpenFinish(channel.forget());
 }
 
 // Separate routine so we can also call it to finish up from pending opens
 already_AddRefed<DataChannel>
 DataChannelConnection::OpenFinish(already_AddRefed<DataChannel> aChannel)
 {
-  uint16_t streamOut = FindFreeStreamOut(); // may be INVALID_STREAM!
   nsRefPtr<DataChannel> channel(aChannel);
+  uint16_t stream = channel->mStream;
 
   mLock.AssertCurrentThreadOwns();
 
-  LOG(("Finishing open: channel %p, streamOut = %u", channel.get(), streamOut));
+  if (stream == INVALID_STREAM || mState != OPEN) {
+    if (mState == OPEN) { // implies INVALID_STREAM
+      // Don't try to find a stream if not open - mAllocateEven isn't set yet
+      stream = FindFreeStream(); // may be INVALID_STREAM if we need more
+      if (stream == INVALID_STREAM) {
+        if (!RequestMoreStreams()) {
+          channel->mState = CLOSED;
+          if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
+            // We already returned the channel to the app.
+            NS_ERROR("Failed to request more streams");
+            NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+                                      DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
+                                      channel));
+            return channel.forget();
+          }
+          // we'll be destroying the channel, but it never really got set up
+          // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and
+          // Dispatch it to ourselves
+          return nullptr;
+        }
+      }
+      // if INVALID here, we need to queue
+    }
+    if (stream != INVALID_STREAM) {
+      // just allocated (& OPEN), or externally negotiated
+      mStreams[stream] = channel;
+      channel->mStream = stream;
+    }
 
-  if (streamOut == INVALID_STREAM) {
-    if (!RequestMoreStreamsOut()) {
-      channel->mState = CLOSED;
-      if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
-        // We already returned the channel to the app.
-        NS_ERROR("Failed to request more streams");
-        NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                                  DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
-                                  channel));
-        return channel.forget();
-      }
-      // we'll be destroying the channel, but it never really got set up
-      // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and
-      // Dispatch it to ourselves
-      return nullptr;
-    }
-    LOG(("Queuing channel %p to finish open", channel.get()));
-    // Also serves to mark we told the app
-    channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN;
-    channel->AddRef(); // we need a ref for the nsDeQue and one to return
-    mPending.Push(channel);
-    return channel.forget();
+    LOG(("Finishing open: channel %p, stream = %u", channel.get(), stream));
+
+    if (stream == INVALID_STREAM || mState != OPEN) {
+      // we're going to queue
+
+      LOG(("Queuing channel %p (%u) to finish open", channel.get(), stream));
+      // Also serves to mark we told the app
+      channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN;
+      channel->AddRef(); // we need a ref for the nsDeQue and one to return
+      mPending.Push(channel);
+      return channel.forget();
+    } // else OPEN and we selected a stream
+  } else {
+    // OPEN and externally negotiated stream
+    mStreams[stream] = channel;
   }
-  mStreamsOut[streamOut] = channel;
-  channel->mStreamOut = streamOut;
+
+#ifdef TEST_QUEUED_DATA
+  // It's painful to write a test for this...
+  channel->mState = OPEN;
+  channel->mReady = true;
+  SendMsgInternal(channel, "Help me!", 8, DATA_CHANNEL_PPID_DOMSTRING);
+#endif
+
+  if (!(channel->mFlags & DATA_CHANNEL_FLAGS_EXTERNAL_NEGOTIATED)) {
+    if (!SendOpenRequestMessage(channel->mLabel, channel->mProtocol,
+                                stream,
+                                !!(channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED),
+                                channel->mPrPolicy, channel->mPrValue)) {
+      LOG(("SendOpenRequest failed, errno = %d", errno));
+      if (errno == EAGAIN || errno == EWOULDBLOCK) {
+        channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
+        StartDefer();
 
-  if (!SendOpenRequestMessage(channel->mLabel, streamOut,
-                              !!(channel->mFlags & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED),
-                              channel->mPrPolicy, channel->mPrValue)) {
-    LOG(("SendOpenRequest failed, errno = %d", errno));
-    if (errno == EAGAIN || errno == EWOULDBLOCK) {
-      channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
-      StartDefer();
-    } else {
-      if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
-        // We already returned the channel to the app.
-        NS_ERROR("Failed to send open request");
-        NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
-                                  DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
-                                  channel));
+        return channel.forget();
+      } else {
+        if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
+          // We already returned the channel to the app.
+          NS_ERROR("Failed to send open request");
+          NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+                                    DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
+                                    channel));
+        }
+        // If we haven't returned the channel yet, it will get destroyed when we exit
+        // this function.
+        mStreams[stream] = nullptr;
+        channel->mStream = INVALID_STREAM;
+        // we'll be destroying the channel
+        channel->mState = CLOSED;
+        return nullptr;
       }
-      // If we haven't returned the channel yet, it will get destroyed when we exit
-      // this function.
-      mStreamsOut[streamOut] = nullptr;
-      channel->mStreamOut = INVALID_STREAM;
-      // we'll be destroying the channel
-      channel->mState = CLOSED;
-      return nullptr;
+      /* NOTREACHED */
     }
   }
+  // Either externally negotiated or we sent Open
+  channel->mState = OPEN;
+  channel->mReady = true;
+  // FIX?  Move into DOMDataChannel?  I don't think we can send it yet here
+  LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
+  NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+                            DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
+                            channel));
+
   return channel.forget();
 }
 
 int32_t
 DataChannelConnection::SendMsgInternal(DataChannel *channel, const char *data,
                                        uint32_t length, uint32_t ppid)
 {
   uint16_t flags;
   struct sctp_sendv_spa spa;
   int32_t result;
 
   NS_ENSURE_TRUE(channel->mState == OPEN || channel->mState == CONNECTING, 0);
   NS_WARN_IF_FALSE(length > 0, "Length is 0?!");
 
-  flags = (channel->mFlags & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED) ? SCTP_UNORDERED : 0;
+  flags = (channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED) ? SCTP_UNORDERED : 0;
 
   // To avoid problems where an in-order OPEN_RESPONSE is lost and an
   // out-of-order data message "beats" it, require data to be in-order
   // until we get an ACK.
   if (channel->mState == CONNECTING) {
     flags &= ~SCTP_UNORDERED;
   }
   spa.sendv_sndinfo.snd_ppid = htonl(ppid);
-  spa.sendv_sndinfo.snd_sid = channel->mStreamOut;
+  spa.sendv_sndinfo.snd_sid = channel->mStream;
   spa.sendv_sndinfo.snd_flags = flags;
   spa.sendv_sndinfo.snd_context = 0;
   spa.sendv_sndinfo.snd_assoc_id = 0;
   spa.sendv_flags = SCTP_SEND_SNDINFO_VALID;
 
   if (channel->mPrPolicy != SCTP_PR_SCTP_NONE) {
     spa.sendv_prinfo.pr_policy = channel->mPrPolicy;
     spa.sendv_prinfo.pr_value = channel->mPrValue;
@@ -2046,28 +2017,30 @@ DataChannelConnection::SendBinary(DataCh
   // large message.  On an unreliable channel, we can't and don't know how
   // long to wait, and there are no retransmissions, and no easy way to
   // tell the user "this part is missing", so on unreliable channels we
   // need to return an error if sending more bytes than the network buffers
   // can hold, and perhaps a lower number.
 
   // We *really* don't want to do this from main thread! - and SendMsgInternal
   // avoids blocking.
+  // This MUST be reliable and in-order for the reassembly to work
   if (len > DATA_CHANNEL_MAX_BINARY_FRAGMENT &&
-      channel->mPrPolicy == DATA_CHANNEL_RELIABLE) {
+      channel->mPrPolicy == DATA_CHANNEL_RELIABLE &&
+      !(channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED)) {
     int32_t sent=0;
     uint32_t origlen = len;
     LOG(("Sending binary message length %u in chunks", len));
     // XXX check flags for out-of-order, or force in-order for large binary messages
     while (len > 0) {
       uint32_t sendlen = PR_MIN(len, DATA_CHANNEL_MAX_BINARY_FRAGMENT);
       uint32_t ppid;
       len -= sendlen;
       ppid = len > 0 ? DATA_CHANNEL_PPID_BINARY : DATA_CHANNEL_PPID_BINARY_LAST;
-      LOG(("Send chunk of %d bytes, ppid %d", sendlen, ppid));
+      LOG(("Send chunk of %u bytes, ppid %u", sendlen, ppid));
       // Note that these might end up being deferred and queued.
       sent += SendMsgInternal(channel, data, sendlen, ppid);
       data += sendlen;
     }
     LOG(("Sent %d buffers for %u bytes, %d sent immediately, %d buffers queued",
          (origlen+DATA_CHANNEL_MAX_BINARY_FRAGMENT-1)/DATA_CHANNEL_MAX_BINARY_FRAGMENT,
          origlen, sent,
          channel->mBufferedData.Length()));
@@ -2078,17 +2051,17 @@ DataChannelConnection::SendBinary(DataCh
 
   // This will fail if the message is too large
   return SendMsgInternal(channel, data, len, DATA_CHANNEL_PPID_BINARY_LAST);
 }
 
 int32_t
 DataChannelConnection::SendBlob(uint16_t stream, nsIInputStream *aBlob)
 {
-  DataChannel *channel = mStreamsOut[stream];
+  DataChannel *channel = mStreams[stream];
   NS_ENSURE_TRUE(channel, 0);
   // Spawn a thread to send the data
 
   LOG(("Sending blob to stream %u", stream));
 
   // XXX to do this safely, we must enqueue these atomically onto the
   // output socket.  We need a sender thread(s?) to enque data into the
   // socket and to avoid main-thread IO that might block.  Even on a
@@ -2123,25 +2096,25 @@ DataChannelConnection::SendBlob(uint16_t
 
 int32_t
 DataChannelConnection::SendMsgCommon(uint16_t stream, const nsACString &aMsg,
                                      bool isBinary)
 {
   ASSERT_WEBRTC(NS_IsMainThread());
   // We really could allow this from other threads, so long as we deal with
   // asynchronosity issues with channels closing, in particular access to
-  // mStreamsOut, and issues with the association closing (access to mSocket).
+  // mStreams, and issues with the association closing (access to mSocket).
 
   const char *data = aMsg.BeginReading();
   uint32_t len     = aMsg.Length();
   DataChannel *channel;
 
   LOG(("Sending %sto stream %u: %u bytes", isBinary ? "binary " : "", stream, len));
   // XXX if we want more efficiency, translate flags once at open time
-  channel = mStreamsOut[stream];
+  channel = mStreams[stream];
   NS_ENSURE_TRUE(channel, 0);
 
   if (isBinary)
     return SendBinary(channel, data, len);
   return SendMsgInternal(channel, data, len, DATA_CHANNEL_PPID_DOMSTRING);
 }
 
 void
@@ -2155,68 +2128,68 @@ DataChannelConnection::Close(DataChannel
 // Called from someone who holds a ref via ::Close(), or from ~DataChannel
 void
 DataChannelConnection::CloseInt(DataChannel *aChannel)
 {
   MOZ_ASSERT(aChannel);
   nsRefPtr<DataChannel> channel(aChannel); // make sure it doesn't go away on us
 
   mLock.AssertCurrentThreadOwns();
-  LOG(("Connection %p/Channel %p: Closing stream %d",
-       aChannel->mConnection.get(), aChannel, aChannel->mStreamOut));
+  LOG(("Connection %p/Channel %p: Closing stream %u",
+       channel->mConnection.get(), channel.get(), channel->mStream));
   // re-test since it may have closed before the lock was grabbed
   if (aChannel->mState == CLOSED || aChannel->mState == CLOSING) {
-    LOG(("Channel already closing/closed (%d)", aChannel->mState));
+    LOG(("Channel already closing/closed (%u)", aChannel->mState));
     return;
   }
   aChannel->mBufferedData.Clear();
-  if (aChannel->mStreamOut != INVALID_STREAM) {
-    ResetOutgoingStream(aChannel->mStreamOut);
+  if (channel->mStream != INVALID_STREAM) {
+    ResetOutgoingStream(channel->mStream);
     if (mState == CLOSED) { // called from CloseAll()
       // Let resets accumulate then send all at once in CloseAll()
       // we're not going to hang around waiting
-      mStreamsOut[aChannel->mStreamOut] = nullptr;
+      mStreams[channel->mStream] = nullptr;
     } else {
       SendOutgoingStreamReset();
     }
   }
   aChannel->mState = CLOSING;
   if (mState == CLOSED) {
     // we're not going to hang around waiting
-    if (channel->mStreamOut != INVALID_STREAM) {
-      mStreamsIn[channel->mStreamIn] = nullptr;
-    }
     channel->Destroy();
   }
   // At this point when we leave here, the object is a zombie held alive only by the DOM object
 }
 
 void DataChannelConnection::CloseAll()
 {
   LOG(("Closing all channels"));
   // Don't need to lock here
 
   // Make sure no more channels will be opened
-  mState = CLOSED;
+  {
+    MutexAutoLock lock(mLock);
+    mState = CLOSED;
+  }
 
   // Close current channels
   // If there are runnables, they hold a strong ref and keep the channel
   // and/or connection alive (even if in a CLOSED state)
   bool closed_some = false;
-  for (uint32_t i = 0; i < mStreamsOut.Length(); ++i) {
-    if (mStreamsOut[i]) {
-      mStreamsOut[i]->Close();
+  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+    if (mStreams[i]) {
+      mStreams[i]->Close();
       closed_some = true;
     }
   }
 
   // Clean up any pending opens for channels
   nsRefPtr<DataChannel> channel;
   while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(mPending.PopFront())))) {
-    LOG(("closing pending channel %p, stream %d", channel.get(), channel->mStreamOut));
+    LOG(("closing pending channel %p, stream %u", channel.get(), channel->mStream));
     channel->Close(); // also releases the ref on each iteration
     closed_some = true;
   }
   // It's more efficient to let the Resets queue in shutdown and then
   // SendOutgoingStreamReset() here.
   if (closed_some) {
     MutexAutoLock lock(mLock);
     SendOutgoingStreamReset();
@@ -2239,23 +2212,20 @@ DataChannel::Close()
 }
 
 // Used when disconnecting from the DataChannelConnection
 void
 DataChannel::Destroy()
 {
   ENSURE_DATACONNECTION;
 
-  LOG(("Destroying Data channel %d/%d", mStreamOut, mStreamIn));
-  MOZ_ASSERT_IF(mStreamOut != INVALID_STREAM,
-                !mConnection->FindChannelByStreamOut(mStreamOut));
-  MOZ_ASSERT_IF(mStreamIn != INVALID_STREAM,
-                !mConnection->FindChannelByStreamIn(mStreamIn));
-  mStreamIn  = INVALID_STREAM;
-  mStreamOut = INVALID_STREAM;
+  LOG(("Destroying Data channel %u", mStream));
+  MOZ_ASSERT_IF(mStream != INVALID_STREAM,
+                !mConnection->FindChannelByStream(mStream));
+  mStream = INVALID_STREAM;
   mState = CLOSED;
   mConnection = nullptr;
 }
 
 void
 DataChannel::SetListener(DataChannelListener *aListener, nsISupports *aContext)
 {
   MutexAutoLock mLock(mListenerLock);
--- a/netwerk/sctp/datachannel/DataChannel.h
+++ b/netwerk/sctp/datachannel/DataChannel.h
@@ -23,16 +23,17 @@
 #include "nsIInputStream.h"
 #include "nsITimer.h"
 #include "mozilla/Mutex.h"
 #include "DataChannelProtocol.h"
 #ifdef SCTP_DTLS_SUPPORTED
 #include "mtransport/sigslot.h"
 #include "mtransport/transportflow.h"
 #include "mtransport/transportlayer.h"
+#include "mtransport/transportlayerdtls.h"
 #include "mtransport/transportlayerprsock.h"
 #endif
 
 #ifndef DATACHANNEL_LOG
 #define DATACHANNEL_LOG(args)
 #endif
 
 #ifndef EALREADY
@@ -46,28 +47,55 @@ extern "C" {
 
 namespace mozilla {
 
 class DTLSConnection;
 class DataChannelConnection;
 class DataChannel;
 class DataChannelOnMessageAvailable;
 
+// For queuing outgoing messages
 class BufferedMsg
 {
 public:
   BufferedMsg(struct sctp_sendv_spa &spa,const char *data,
               uint32_t length);
   ~BufferedMsg();
 
   struct sctp_sendv_spa *mSpa;
   const char *mData;
   uint32_t mLength;
 };
 
+// for queuing incoming data messages before the Open or
+// external negotiation is indicated to us
+class QueuedDataMessage
+{
+public:
+  QueuedDataMessage(uint16_t stream, uint32_t ppid,
+             const void *data, size_t length)
+    : mStream(stream)
+    , mPpid(ppid)
+    , mLength(length)
+  {
+    mData = static_cast<char *>(moz_xmalloc(length)); // infallible
+    memcpy(mData, data, length);
+  }
+
+  ~QueuedDataMessage()
+  {
+    moz_free(mData);
+  }
+
+  uint16_t mStream;
+  uint32_t mPpid;
+  size_t   mLength;
+  char     *mData;
+};
+
 // Implemented by consumers of a Channel to receive messages.
 // Can't nest it in DataChannelConnection because C++ doesn't allow forward
 // refs to embedded classes
 class DataChannelListener {
 public:
   virtual ~DataChannelListener() {}
 
   // Called when a DOMString message is received.
@@ -112,38 +140,46 @@ public:
   };
 
   DataChannelConnection(DataConnectionListener *listener);
   virtual ~DataChannelConnection();
 
   bool Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls);
   void Destroy(); // So we can spawn refs tied to runnables in shutdown
 
+#ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT
   // These block; they require something to decide on listener/connector
   // (though you can do simultaneous Connect()).  Do not call these from
   // the main thread!
   bool Listen(unsigned short port);
   bool Connect(const char *addr, unsigned short port);
+#endif
 
 #ifdef SCTP_DTLS_SUPPORTED
   // Connect using a TransportFlow (DTLS) channel
-  bool ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
+  void SetEvenOdd();
+  bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
+  void CompleteConnect(TransportFlow *flow, TransportLayer::State state);
+  void SetSignals();
 #endif
 
   typedef enum {
     RELIABLE=0,
     PARTIAL_RELIABLE_REXMIT = 1,
     PARTIAL_RELIABLE_TIMED = 2
   } Type;
 
   already_AddRefed<DataChannel> Open(const nsACString& label,
+                                     const nsACString& protocol,
                                      Type type, bool inOrder,
                                      uint32_t prValue,
                                      DataChannelListener *aListener,
-                                     nsISupports *aContext);
+                                     nsISupports *aContext,
+                                     bool aExternalNegotiated,
+                                     uint16_t aStream);
 
   void Close(DataChannel *aChannel);
   // CloseInt() must be called with mLock held
   void CloseInt(DataChannel *aChannel);
   void CloseAll();
 
   int32_t SendMsg(uint16_t stream, const nsACString &aMsg)
     {
@@ -162,17 +198,17 @@ public:
 
   // Find out state
   enum {
     CONNECTING = 0U,
     OPEN = 1U,
     CLOSING = 2U,
     CLOSED = 3U
   };
-  uint16_t GetReadyState() { return mState; }
+  uint16_t GetReadyState() { MutexAutoLock lock(mLock); return mState; }
 
   friend class DataChannel;
   Mutex  mLock;
 
 protected:
   friend class DataChannelOnMessageAvailable;
   // Avoid cycles with PeerConnectionImpl
   // Use from main thread only as WeakPtr is not threadsafe
@@ -182,45 +218,41 @@ private:
   friend class DataChannelConnectRunnable;
 
 #ifdef SCTP_DTLS_SUPPORTED
   static void DTLSConnectThread(void *data);
   int SendPacket(const unsigned char* data, size_t len, bool release);
   void SctpDtlsInput(TransportFlow *flow, const unsigned char *data, size_t len);
   static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df);
 #endif
-  DataChannel* FindChannelByStreamIn(uint16_t streamIn);
-  DataChannel* FindChannelByStreamOut(uint16_t streamOut);
-  uint16_t FindFreeStreamOut();
-  bool RequestMoreStreamsOut(int32_t aNeeded = 16);
+  DataChannel* FindChannelByStream(uint16_t stream);
+  uint16_t FindFreeStream();
+  bool RequestMoreStreams(int32_t aNeeded = 16);
   int32_t SendControlMessage(void *msg, uint32_t len, uint16_t streamOut);
-  int32_t SendOpenRequestMessage(const nsACString& label,uint16_t streamOut,
+  int32_t SendOpenRequestMessage(const nsACString& label, const nsACString& protocol,
+                                 uint16_t streamOut,
                                  bool unordered, uint16_t prPolicy, uint32_t prValue);
-  int32_t SendOpenResponseMessage(uint16_t streamOut, uint16_t streamIn);
-  int32_t SendOpenAckMessage(uint16_t streamOut);
   int32_t SendMsgInternal(DataChannel *channel, const char *data,
                           uint32_t length, uint32_t ppid);
   int32_t SendBinary(DataChannel *channel, const char *data,
                      uint32_t len);
   int32_t SendMsgCommon(uint16_t stream, const nsACString &aMsg, bool isBinary);
 
+  void DeliverQueuedData(uint16_t stream);
+
   already_AddRefed<DataChannel> OpenFinish(already_AddRefed<DataChannel> channel);
 
   void StartDefer();
   bool SendDeferredMessages();
+  void ProcessQueuedOpens();
   void SendOutgoingStreamReset();
   void ResetOutgoingStream(uint16_t streamOut);
   void HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req,
                                 size_t length,
                                 uint16_t streamIn);
-  void OpenResponseFinish(already_AddRefed<DataChannel> channel);
-  void HandleOpenResponseMessage(const struct rtcweb_datachannel_open_response *rsp,
-                                 size_t length, uint16_t streamIn);
-  void HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack,
-                            size_t length, uint16_t streamIn);
   void HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t streamIn);
   void HandleDataMessage(uint32_t ppid, const void *buffer, size_t length, uint16_t streamIn);
   void HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t streamIn);
   void HandleAssociationChangeEvent(const struct sctp_assoc_change *sac);
   void HandlePeerAddressChangeEvent(const struct sctp_paddr_change *spc);
   void HandleRemoteErrorEvent(const struct sctp_remote_error *sre);
   void HandleShutdownEvent(const struct sctp_shutdown_event *sse);
   void HandleAdaptationIndication(const struct sctp_adaptation_event *sai);
@@ -238,35 +270,38 @@ private:
     return on;
   }
 #endif
 
   // Exists solely for proxying release of the TransportFlow to the STS thread
   static void ReleaseTransportFlow(nsRefPtr<TransportFlow> aFlow) {}
 
   // Data:
-  // NOTE: while these arrays will auto-expand, increases in the number of
+  // NOTE: while this array will auto-expand, increases in the number of
   // channels available from the stack must be negotiated!
-  nsAutoTArray<nsRefPtr<DataChannel>,16> mStreamsOut;
-  nsAutoTArray<nsRefPtr<DataChannel>,16> mStreamsIn;
+  bool mAllocateEven;
+  nsAutoTArray<nsRefPtr<DataChannel>,16> mStreams;
   nsDeque mPending; // Holds already_AddRefed<DataChannel>s -- careful!
+  // holds data that's come in before a channel is open
+  nsTArray<nsAutoPtr<QueuedDataMessage> > mQueuedData;
 
   // Streams pending reset
   nsAutoTArray<uint16_t,4> mStreamsResetting;
 
-  struct socket *mMasterSocket; // accessed from connect thread
-  struct socket *mSocket; // cloned from mMasterSocket on successful Connect on connect thread
-  uint16_t mState; // modified on connect thread (to OPEN)
+  struct socket *mMasterSocket; // accessed from STS thread
+  struct socket *mSocket; // cloned from mMasterSocket on successful Connect on STS thread
+  uint16_t mState; // Protected with mLock
 
 #ifdef SCTP_DTLS_SUPPORTED
   nsRefPtr<TransportFlow> mTransportFlow;
   nsCOMPtr<nsIEventTarget> mSTS;
 #endif
   uint16_t mLocalPort; // Accessed from connect thread
   uint16_t mRemotePort;
+  bool mUsingDtls;
 
   // Timer to control when we try to resend blocked messages
   nsCOMPtr<nsITimer> mDeferredTimer;
   uint32_t mDeferTimeout; // in ms
   bool mTimerRunning;
 };
 
 #define ENSURE_DATACONNECTION \