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 idunknown
push userunknown
push dateunknown
milestone22.0a1
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 \
@@ -281,32 +316,33 @@ public:
     CONNECTING = 0U,
     OPEN = 1U,
     CLOSING = 2U,
     CLOSED = 3U,
     WAITING_TO_OPEN = 4U
   };
 
   DataChannel(DataChannelConnection *connection,
-              uint16_t streamOut, uint16_t streamIn,
+              uint16_t stream,
               uint16_t state,
               const nsACString& label,
+              const nsACString& protocol,
               uint16_t policy, uint32_t value,
               uint32_t flags,
               DataChannelListener *aListener,
               nsISupports *aContext)
     : mListenerLock("netwerk::sctp::DataChannel")
     , mListener(aListener)
     , mContext(aContext)
     , mConnection(connection)
     , mLabel(label)
+    , mProtocol(protocol)
     , mState(state)
     , mReady(false)
-    , mStreamOut(streamOut)
-    , mStreamIn(streamIn)
+    , mStream(stream)
     , mPrPolicy(policy)
     , mPrValue(value)
     , mFlags(0)