Merge mozilla-central into holly
authorMike Conley <mconley@mozilla.com>
Sat, 07 Dec 2013 13:13:00 -0500
changeset 174998 4d071978bc6082b3627a28ddd2ca52b1c14e0068
parent 174930 3a79902737827be0389239a2ba269bb41b50b9f6 (current diff)
parent 174997 2099f78b7deaa46274cbb90db960629e8fba1a3b (diff)
child 175029 f9bb0f904d4eb072278bd49acb70c861a60e84db
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.0a1
Merge mozilla-central into holly
CLOBBER
config/elf-dynstr-gc.c
media/libogg/src/moz.build
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -597,51 +597,65 @@ HyperTextAccessible::HypertextOffsetsToD
 
   rv = GetDOMPointByFrameOffset(endFrame, endOffset, endAcc, &endPoint);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return aRange->SetEnd(endPoint.node, endPoint.idx);
 }
 
 int32_t
-HyperTextAccessible::GetRelativeOffset(nsIPresShell* aPresShell,
-                                       nsIFrame* aFromFrame,
-                                       int32_t aFromOffset,
-                                       Accessible* aFromAccessible,
-                                       nsSelectionAmount aAmount,
-                                       nsDirection aDirection,
-                                       bool aNeedsStart,
-                                       EWordMovementType aWordMovementType)
+HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
+                                nsSelectionAmount aAmount,
+                                EWordMovementType aWordMovementType)
 {
-  const bool kIsJumpLinesOk = true;          // okay to jump lines
-  const bool kIsScrollViewAStop = false;     // do not stop at scroll views
-  const bool kIsKeyboardSelect = true;       // is keyboard selection
-  const bool kIsVisualBidi = false;          // use visual order for bidi text
-
-  // Ask layout for the new node and offset, after moving the appropriate amount
+  // Convert hypertext offset to frame-relative offset.
+  int32_t offsetInFrame = aOffset, notUsedOffset = aOffset;
+  nsRefPtr<Accessible> accAtOffset;
+  nsIFrame* frameAtOffset =
+    GetPosAndText(offsetInFrame, notUsedOffset, 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;
 
-  nsresult rv;
-  int32_t contentOffset = aFromOffset;
-  nsIFrame *frame = aFromAccessible->GetFrame();
-  NS_ENSURE_TRUE(frame, -1);
+    // We're on the last continuation since we're on the last character.
+    frameAtOffset = frameAtOffset->LastContinuation();
+  }
 
-  if (frame->GetType() == nsGkAtoms::textFrame) {
-    rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset);
+  // Return hypertext offset of the boundary of the found word.
+  int32_t contentOffset = offsetInFrame;
+  nsIFrame* primaryFrame = accAtOffset->GetFrame();
+  NS_ENSURE_TRUE(primaryFrame, -1);
+
+  nsresult rv = NS_OK;
+  if (primaryFrame->GetType() == nsGkAtoms::textFrame) {
+    rv = RenderedToContentOffset(primaryFrame, offsetInFrame, &contentOffset);
     NS_ENSURE_SUCCESS(rv, -1);
   }
 
+  const bool kIsJumpLinesOk = true; // okay to jump lines
+  const bool kIsScrollViewAStop = false; // do not stop at scroll views
+  const bool kIsKeyboardSelect = true; // is keyboard selection
+  const bool kIsVisualBidi = false; // use visual order for bidi text
   nsPeekOffsetStruct pos(aAmount, aDirection, contentOffset,
-                         0, kIsJumpLinesOk, kIsScrollViewAStop, kIsKeyboardSelect, kIsVisualBidi,
+                         0, kIsJumpLinesOk, kIsScrollViewAStop,
+                         kIsKeyboardSelect, kIsVisualBidi,
                          aWordMovementType);
-  rv = aFromFrame->PeekOffset(&pos);
+  rv = frameAtOffset->PeekOffset(&pos);
 
   // PeekOffset fails on last/first lines of the text in certain cases.
   if (NS_FAILED(rv) && aAmount == eSelectLine) {
     pos.mAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
-    aFromFrame->PeekOffset(&pos);
+    frameAtOffset->PeekOffset(&pos);
   }
   if (!pos.mResultContent)
     return -1;
 
   // Turn the resulting node and offset into a hyperTextOffset
   // If finalAccessible is nullptr, then DOMPointToHypertextOffset() searched
   // through the hypertext children without finding the node/offset position.
   int32_t hyperTextOffset;
@@ -658,57 +672,26 @@ HyperTextAccessible::GetRelativeOffset(n
     Accessible* firstChild = mChildren.SafeElementAt(0, nullptr);
     // For line selection with needsStart, set start of line exactly to line break
     if (pos.mContentOffset == 0 && firstChild &&
         firstChild->Role() == roles::STATICTEXT &&
         static_cast<int32_t>(nsAccUtils::TextLength(firstChild)) == hyperTextOffset) {
       // XXX Bullet hack -- we should remove this once list bullets use anonymous content
       hyperTextOffset = 0;
     }
-    if (!aNeedsStart && hyperTextOffset > 0) {
+    if (aWordMovementType != eStartWord && aAmount != eSelectBeginLine &&
+        hyperTextOffset > 0) {
       -- hyperTextOffset;
     }
   }
 
   return hyperTextOffset;
 }
 
 int32_t
-HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
-                                nsSelectionAmount aAmount,
-                                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,
-                  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->LastContinuation();
-  }
-
-  // Return hypertext offset of the boundary of the found word.
-  return GetRelativeOffset(mDoc->PresShell(), frameAtOffset, offsetInFrame,
-                           accAtOffset, aAmount, aDirection,
-                           (aWordMovementType == eStartWord || aAmount == eSelectBeginLine),
-                           aWordMovementType);
-}
-
-int32_t
 HyperTextAccessible::FindLineBoundary(int32_t aOffset,
                                       EWhichLineBoundary aWhichLineBoundary)
 {
   // Note: empty last line doesn't have own frame (a previous line contains '\n'
   // character instead) thus when it makes a difference we need to process this
   // case separately (otherwise operations are performed on previous line).
   switch (aWhichLineBoundary) {
     case ePrevLineBegin: {
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -432,36 +432,16 @@ protected:
    * Return an offset corresponding to the given direction and selection amount
    * relative the given offset. A helper used to find word or line boundaries.
    */
   int32_t FindOffset(int32_t aOffset, nsDirection aDirection,
                      nsSelectionAmount aAmount,
                      EWordMovementType aWordMovementType = eDefaultBehavior);
 
   /**
-    * Used by FindOffset() to move backward/forward from a given point
-    * by word/line/etc.
-    *
-    * @param  aPresShell       the current presshell we're moving in
-    * @param  aFromFrame       the starting frame we're moving from
-    * @param  aFromOffset      the starting offset we're moving from
-    * @param  aFromAccessible  the starting accessible we're moving from
-    * @param  aAmount          how much are we moving (word/line/etc.) ?
-    * @param  aDirection       forward or backward?
-    * @param  aNeedsStart      for word and line cases, are we basing this on
-    *                          the start or end?
-    * @return                  the resulting offset into this hypertext
-    */
-  int32_t GetRelativeOffset(nsIPresShell *aPresShell, nsIFrame *aFromFrame,
-                            int32_t aFromOffset, Accessible* aFromAccessible,
-                            nsSelectionAmount aAmount, nsDirection aDirection,
-                            bool aNeedsStart,
-                            EWordMovementType aWordMovementType);
-
-  /**
     * Provides information for substring that is defined by the given start
     * and end offsets for this hyper text.
     *
     * @param  aStartOffset  [inout] the start offset into the hyper text. This
     *                       is also an out parameter used to return the offset
     *                       into the start frame's rendered text content
     *                       (start frame is the @return)
     *
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -9,28 +9,28 @@
 #include "nsAccUtils.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsContentList.h"
-#include "nsCxPusher.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "nsIAccessibleRelation.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIEditor.h"
 #include "nsIFormControl.h"
 #include "nsINameSpaceManager.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextControlFrame.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::a11y;
 
@@ -465,18 +465,17 @@ HTMLTextFieldAccessible::GetEditor() con
 {
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent));
   if (!editableElt)
     return nullptr;
 
   // nsGenericHTMLElement::GetEditor has a security check.
   // Make sure we're not restricted by the permissions of
   // whatever script is currently running.
-  nsCxPusher pusher;
-  pusher.PushNull();
+  mozilla::dom::AutoSystemCaller asc;
 
   nsCOMPtr<nsIEditor> editor;
   editableElt->GetEditor(getter_AddRefs(editor));
 
   return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/src/windows/msaa/IUnknownImpl.h
+++ b/accessible/src/windows/msaa/IUnknownImpl.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. *
  */
 
 #ifndef mozilla_a11y_IUnknownImpl_h_
 #define mozilla_a11y_IUnknownImpl_h_
 
 #include <windows.h>
+#undef CreateEvent // thank you windows you're such a helper
 #include "nsError.h"
 
 // Avoid warning C4509 like "nonstandard extension used:
 // 'AccessibleWrap::[acc_getName]' uses SEH and 'name' has destructor.
 // At this point we're catching a crash which is of much greater
 // importance than the missing dereference for the nsCOMPtr<>
 #ifdef _MSC_VER
 #pragma warning( disable : 4509 )
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "63d432c3395f95c0ba19578487b796c1707042bd", 
+    "revision": "961def304c067c112f9f3c149431dba70887c5e0", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/devtools/styleeditor/test/browser.ini
+++ b/browser/devtools/styleeditor/test/browser.ini
@@ -30,13 +30,15 @@ support-files =
 [browser_styleeditor_filesave.js]
 [browser_styleeditor_import.js]
 [browser_styleeditor_import_rule.js]
 [browser_styleeditor_init.js]
 [browser_styleeditor_loading.js]
 [browser_styleeditor_new.js]
 [browser_styleeditor_nostyle.js]
 [browser_styleeditor_pretty.js]
+# Disabled because of intermittent failures - See Bug 942473
+skip-if = true
 [browser_styleeditor_private_perwindowpb.js]
 [browser_styleeditor_reload.js]
 [browser_styleeditor_sv_keynav.js]
 [browser_styleeditor_sv_resize.js]
-[browser_styleeditor_selectstylesheet.js]
\ No newline at end of file
+[browser_styleeditor_selectstylesheet.js]
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -3,24 +3,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # STDCXX_COMPAT is not needed here, and will actually fail because
 # libstdc++-compat is not built yet.
 MOZ_LIBSTDCXX_HOST_VERSION =
 
-ifndef CROSS_COMPILE
-ifdef COMPILE_ENVIRONMENT
-ifdef USE_ELF_DYNSTR_GC
-export:: elf-dynstr-gc
-endif
-endif
-endif
-
 # IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have
 # a recursive rule for finding nsinstall and the Perl scripts.
 ifdef NSBUILDROOT
 override NSBUILDROOT :=
 endif
 
 ifdef GNU_CC
 MODULE_OPTIMIZE_FLAGS = -O3
@@ -109,23 +101,16 @@ export:: $(STL_WRAPPERS_SENTINEL)
 
 GARBAGE += $(STL_WRAPPERS_SENTINEL)
 GARBAGE_DIRS += stl_wrappers
 endif
 
 GARBAGE += \
   $(FINAL_LINK_COMPS) $(FINAL_LINK_LIBS) $(FINAL_LINK_COMP_NAMES) buildid $(srcdir)/*.pyc *.pyc
 
-ifndef CROSS_COMPILE
-ifdef USE_ELF_DYNSTR_GC
-elf-dynstr-gc: elf-dynstr-gc.c $(GLOBAL_DEPS) $(call mkdir_deps,$(MDDEPDIR))
-	$(CC) $(COMPILE_CFLAGS) $(GLIB_CFLAGS) -DELFDYNSTRGC_BUILD -o $@ $< $(LDFLAGS) $(GLIB_LIBS)
-endif
-endif
-
 FORCE:
 
 check-preqs = \
   check-jar-mn \
   check-makefiles \
   $(NULL)
 
 check:: $(check-preqs)
--- a/config/config.mk
+++ b/config/config.mk
@@ -626,30 +626,16 @@ endif
 
 SDK_LIB_DIR = $(DIST)/sdk/lib
 SDK_BIN_DIR = $(DIST)/sdk/bin
 
 DEPENDENCIES	= .md
 
 MOZ_COMPONENT_LIBS=$(XPCOM_LIBS) $(MOZ_COMPONENT_NSPR_LIBS)
 
-ifeq ($(OS_ARCH),OS2)
-ELF_DYNSTR_GC	= echo
-else
-ELF_DYNSTR_GC	= :
-endif
-
-ifndef CROSS_COMPILE
-ifdef USE_ELF_DYNSTR_GC
-ifdef MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS
-ELF_DYNSTR_GC 	= $(DEPTH)/config/elf-dynstr-gc
-endif
-endif
-endif
-
 ifdef MACOSX_DEPLOYMENT_TARGET
 export MACOSX_DEPLOYMENT_TARGET
 endif # MACOSX_DEPLOYMENT_TARGET
 
 ifdef MOZ_USING_CCACHE
 ifdef CLANG_CXX
 export CCACHE_CPP2=1
 endif
deleted file mode 100644
--- a/config/elf-dynstr-gc.c
+++ /dev/null
@@ -1,1212 +0,0 @@
-/* elf_gc_dynst
- *
- * This is a program that removes unreferenced strings from the .dynstr
- * section in ELF shared objects. It also shrinks the .dynstr section and
- * relocates all symbols after it.
- *
- * This program was written and copyrighted by:
- *   Alexander Larsson <alla@lysator.liu.se>
- *
- *
- *
- * 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 <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include <elf.h>
-#include <glib.h>
-#include <string.h>
-
-
-Elf32_Ehdr *elf_header = NULL;
-#define FILE_OFFSET(offset) ((unsigned char *)(elf_header) + (offset))
-
-struct dynamic_symbol {
-  Elf32_Word old_index;
-  Elf32_Word new_index;
-  char *string;
-};
-
-GHashTable *used_dynamic_symbols = NULL;
-/* Data is dynamic_symbols, hashes on old_index */
-Elf32_Word hole_index;
-Elf32_Word hole_end;
-Elf32_Word hole_len;
-
-Elf32_Addr hole_addr_start;
-Elf32_Addr hole_addr_remap_start;
-Elf32_Addr hole_addr_remap_end;
-
-int need_byteswap;
-
-unsigned char machine_type;
-
-Elf32_Word
-read_word(Elf32_Word w)
-{
-  if (need_byteswap) 
-    w = GUINT32_SWAP_LE_BE(w);
-  return w;
-}
-
-Elf32_Sword
-read_sword(Elf32_Sword w)
-{
-  if (need_byteswap) 
-    w = (Elf32_Sword)GUINT32_SWAP_LE_BE((guint32)w);
-  return w;
-}
-
-void 
-write_word(Elf32_Word *ptr, Elf32_Word w)
-{
-  if (need_byteswap) 
-    w = GUINT32_SWAP_LE_BE(w);
-  *ptr = w;
-}
-
-Elf32_Half
-read_half(Elf32_Half h)
-{
-  if (need_byteswap) 
-    h = GUINT16_SWAP_LE_BE(h);
-  return h;
-}
-
-void 
-write_half(Elf32_Half *ptr, Elf32_Half h)
-{
-  if (need_byteswap) 
-    h = GUINT16_SWAP_LE_BE(h);
-  *ptr = h;
-}
-
-void
-setup_byteswapping(unsigned char ei_data)
-{
-  need_byteswap = 0;
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-  if (ei_data == ELFDATA2LSB)
-    need_byteswap = 1;
-#endif
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-  if (ei_data == ELFDATA2MSB)
-    need_byteswap = 1;
-#endif
-}
-
-
-Elf32_Shdr *
-elf_find_section_num(int section_index)
-{
-  Elf32_Shdr *section;
-  Elf32_Word sectionsize;
-
-  section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff));
-  sectionsize = read_half(elf_header->e_shentsize);
-
-  section = (Elf32_Shdr *)((char *)section + sectionsize*section_index);
-
-  return section;
-}
-
-Elf32_Shdr *
-elf_find_section_named(char *name)
-{
-  Elf32_Shdr *section;
-  Elf32_Shdr *strtab_section;
-  Elf32_Word sectionsize;
-  int numsections;
-  char *strtab;
-  int i = 0;
-
-  section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff));
-
-  strtab_section = elf_find_section_num(read_half(elf_header->e_shstrndx));
-  
-  strtab = (char *)FILE_OFFSET(read_word(strtab_section->sh_offset));
-  
-  sectionsize = read_half(elf_header->e_shentsize);
-  numsections = read_half(elf_header->e_shnum);
-
-  for (i=0;i<numsections;i++) {
-    if (strcmp(&strtab[read_word(section->sh_name)], name) == 0) {
-      return section;
-    }
-    section = (Elf32_Shdr *)((char *)section + sectionsize);
-  }
-  return NULL;
-}
-
-
-Elf32_Shdr *
-elf_find_section(Elf32_Word sh_type)
-{
-  Elf32_Shdr *section;
-  Elf32_Word sectionsize;
-  int numsections;
-  int i = 0;
-
-  section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff));
-  sectionsize = read_half(elf_header->e_shentsize);
-  numsections = read_half(elf_header->e_shnum);
-
-  for (i=0;i<numsections;i++) {
-    if (read_word(section->sh_type) == sh_type) {
-      return section;
-    }
-    section = (Elf32_Shdr *)((char *)section + sectionsize);
-  }
-  return NULL;
-}
-
-Elf32_Shdr *
-elf_find_next_higher_section(Elf32_Word offset)
-{
-  Elf32_Shdr *section;
-  Elf32_Shdr *higher;
-  Elf32_Word sectionsize;
-  int numsections;
-  int i = 0;
-
-  section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff));
-  sectionsize = read_half(elf_header->e_shentsize);
-  numsections = read_half(elf_header->e_shnum);
-
-  higher = NULL;
-
-  for (i=0;i<numsections;i++) {
-    if (read_word(section->sh_offset) >= offset) {
-      if (higher == NULL) {
-	higher = section;
-      } else if (read_word(section->sh_offset) < read_word(higher->sh_offset)) {
-	higher = section;
-      }
-    }
-    
-    section = (Elf32_Shdr *)((char *)section + sectionsize);
-  }
-  
-  return higher;
-}
-
-Elf32_Word
-vma_to_offset(Elf32_Addr addr)
-{
-  Elf32_Shdr *section;
-  Elf32_Shdr *higher;
-  Elf32_Word sectionsize;
-  int numsections;
-  int i = 0;
-
-  section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff));
-  sectionsize = read_half(elf_header->e_shentsize);
-  numsections = read_half(elf_header->e_shnum);
-
-  higher = NULL;
-
-  for (i=0;i<numsections;i++) {
-    if ( (addr >= read_word(section->sh_addr)) &&
-	 (addr < read_word(section->sh_addr) + read_word(section->sh_size)) ) {
-      return read_word(section->sh_offset) + (addr - read_word(section->sh_addr));
-    }
-    
-    section = (Elf32_Shdr *)((char *)section + sectionsize);
-  }
-
-  fprintf(stderr, "Warning, unable to convert address %d (0x%x) to file offset\n",
-	 addr, addr);
-  return 0;
-}
-
-
-void
-find_segment_addr_min_max(Elf32_Word file_offset,
-			  Elf32_Addr *start, Elf32_Addr *end)
-{
-  Elf32_Phdr *segment;
-  Elf32_Word segmentsize;
-  int numsegments;
-  int i = 0;
-
-  segment = (Elf32_Phdr *)FILE_OFFSET(read_word(elf_header->e_phoff));
-  segmentsize = read_half(elf_header->e_phentsize);
-  numsegments = read_half(elf_header->e_phnum);
-
-  for (i=0;i<numsegments;i++) {
-    if ((file_offset >= read_word(segment->p_offset)) &&
-	(file_offset < read_word(segment->p_offset) + read_word(segment->p_filesz)))  {
-      *start = read_word(segment->p_vaddr);
-      *end = read_word(segment->p_vaddr) + read_word(segment->p_memsz);
-      return;
-    }
-
-    segment = (Elf32_Phdr *)((char *)segment + segmentsize);
-  }
-  fprintf(stderr, "Error: Couldn't find segment in find_segment_addr_min_max()\n");
-}
-
-void *
-dynamic_find_tag(Elf32_Shdr *dynamic, Elf32_Sword d_tag)
-{
-  int i;
-  Elf32_Dyn *element;
-
-  element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset));
-  for (i=0; read_sword(element[i].d_tag) != DT_NULL; i++) {
-    if (read_sword(element[i].d_tag) == d_tag) {
-      return FILE_OFFSET(read_word(element[i].d_un.d_ptr));
-    }
-  }
-  
-  return NULL;
-}
-
-Elf32_Word
-fixup_offset(Elf32_Word offset)
-{
-  if (offset >= hole_index) {
-    return offset - hole_len;
-  }
-  return offset;
-}
-
-Elf32_Word
-fixup_size(Elf32_Word offset, Elf32_Word size)
-{
-  /* Note: Doesn't handle the cases where the hole and the size intersect
-     partially. */
-  
-  if ( (hole_index >= offset) &&
-       (hole_index < offset + size)){
-    return size - hole_len;
-  }
-  
-  return size;
-}
-
-Elf32_Addr
-fixup_addr(Elf32_Addr addr)
-{
-  if (addr == 0)
-    return 0;
-
-  /*
-  if ( (addr < hole_addr_remap_start) ||
-       (addr >= hole_addr_remap_end))
-    return addr;
-  */
-  
-  if (addr >= hole_addr_start) {
-    return addr - hole_len;
-  }
-  return addr;
-}
-
-Elf32_Word
-fixup_addr_size(Elf32_Addr addr, Elf32_Word size)
-{
-  /* Note: Doesn't handle the cases where the hole and the size intersect
-     partially. */
-  /*
-  if ( (addr < hole_addr_remap_start) ||
-       (addr >= hole_addr_remap_end))
-    return size;
-  */
-  if ( (hole_addr_start >= addr) &&
-       (hole_addr_start < addr + size)){
-    return size - hole_len;
-  }
-  
-  return size;
-}
-
-void
-possibly_add_string(int name_idx, const char *name)
-{
-  struct dynamic_symbol *dynamic_symbol;
-  if (name_idx != 0) {
-    dynamic_symbol = g_hash_table_lookup(used_dynamic_symbols, (gpointer) name_idx);
-    
-    if (dynamic_symbol == NULL) {
-      
-      dynamic_symbol = g_new(struct dynamic_symbol, 1);
-      
-      dynamic_symbol->old_index = name_idx;
-      dynamic_symbol->new_index = 0;
-      dynamic_symbol->string = g_strdup(name);
-      
-      g_hash_table_insert(used_dynamic_symbols, (gpointer)name_idx, dynamic_symbol);
-      /*printf("added dynamic string: %s (%d)\n", dynamic_symbol->string, name_idx);*/
-    }
-  }
-}
-
-Elf32_Word
-fixup_string(Elf32_Word old_idx)
-{
-  struct dynamic_symbol *dynamic_symbol;
-
-  if (old_idx == 0)
-    return 0;
-  
-  dynamic_symbol = g_hash_table_lookup(used_dynamic_symbols, (gpointer) old_idx);
-
-  if (dynamic_symbol == NULL) {
-    fprintf(stderr, "AAAAAAAAAAAARGH!? Unknown string found in fixup (index: %d)!\n", old_idx);
-    return 0;
-  }
-  
-  return dynamic_symbol->new_index;
-}
-
-
-
-void
-add_strings_from_dynsym(Elf32_Shdr *dynsym, char *strtab)
-{
-  Elf32_Sym *symbol;
-  Elf32_Sym *symbol_end;
-  Elf32_Word entry_size;
-  
-
-  symbol = (Elf32_Sym *)FILE_OFFSET(read_word(dynsym->sh_offset));
-  symbol_end = (Elf32_Sym *)FILE_OFFSET(read_word(dynsym->sh_offset) + read_word(dynsym->sh_size));
-  entry_size = read_word(dynsym->sh_entsize);
-
-  while (symbol < symbol_end) {
-    int name_idx;
-    struct dynamic_symbol *dynamic_symbol;
-
-    name_idx = read_word(symbol->st_name);
-    possibly_add_string(name_idx, &strtab[name_idx]);
-
-    
-    symbol = (Elf32_Sym *)((char *)symbol + entry_size);
-  }
-}
-
-
-void
-fixup_strings_in_dynsym(Elf32_Shdr *dynsym)
-{
-  Elf32_Sym *symbol;
-  Elf32_Sym *symbol_end;
-  Elf32_Word entry_size;
-  
-
-  symbol = (Elf32_Sym *)FILE_OFFSET(read_word(dynsym->sh_offset));
-  symbol_end = (Elf32_Sym *)FILE_OFFSET(read_word(dynsym->sh_offset) + read_word(dynsym->sh_size));
-  entry_size = read_word(dynsym->sh_entsize);
-  
-  while (symbol < symbol_end) {
-    struct dynamic_symbol *dynamic_symbol;
-
-    write_word(&symbol->st_name,
-	       fixup_string(read_word(symbol->st_name)));
-			 
-    symbol = (Elf32_Sym *)((char *)symbol + entry_size);
-  }
-}
-
-
-void
-add_strings_from_dynamic(Elf32_Shdr *dynamic, char *strtab)
-{
-  int i;
-  int name_idx;
-  Elf32_Dyn *element;
-  Elf32_Word entry_size;
-
-  entry_size = read_word(dynamic->sh_entsize);
-  
-
-  element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset));
-  while (read_sword(element->d_tag) != DT_NULL) {
-
-    switch(read_sword(element->d_tag)) {
-    case DT_NEEDED:
-    case DT_SONAME:
-    case DT_RPATH:
-      name_idx = read_word(element->d_un.d_val);
-      /*if (name_idx) printf("d_tag: %d\n", element->d_tag);*/
-      possibly_add_string(name_idx, &strtab[name_idx]);
-      break;
-    default:
-      ;
-      /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/
-    }
-
-    element = (Elf32_Dyn *)((char *)element + entry_size);
-  }
-  
-}
-
-void
-fixup_strings_in_dynamic(Elf32_Shdr *dynamic)
-{
-  int i;
-  int name_idx;
-  Elf32_Dyn *element;
-  Elf32_Word entry_size;
-
-  entry_size = read_word(dynamic->sh_entsize);
-
-  element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset));
-  while (read_sword(element->d_tag) != DT_NULL) {
-
-    switch(read_sword(element->d_tag)) {
-    case DT_NEEDED:
-    case DT_SONAME:
-    case DT_RPATH:
-      write_word(&element->d_un.d_val,
-		 fixup_string(read_word(element->d_un.d_val)));
-      break;
-    default:
-      ;
-      /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/
-    }
-
-    element = (Elf32_Dyn *)((char *)element + entry_size);
-  }
-  
-}
-
-
-void
-add_strings_from_ver_d(Elf32_Shdr *ver_d, char *strtab)
-{
-  Elf32_Verdaux *veraux;
-  Elf32_Verdef *verdef;
-  int num_aux;
-  int name_idx;
-  int i;
-  int cont;
-
-  verdef = (Elf32_Verdef *)FILE_OFFSET(read_word(ver_d->sh_offset));
-
-  do {
-    num_aux = read_half(verdef->vd_cnt);
-    veraux = (Elf32_Verdaux *)((char *)verdef + read_word(verdef->vd_aux));
-    for (i=0; i<num_aux; i++) {
-      name_idx = read_word(veraux->vda_name);
-      possibly_add_string(name_idx, &strtab[name_idx]);
-      veraux = (Elf32_Verdaux *)((char *)veraux + read_word(veraux->vda_next));
-    }
-
-    cont = read_word(verdef->vd_next) != 0;
-    verdef = (Elf32_Verdef *)((char *)verdef + read_word(verdef->vd_next));
-  } while (cont);
-  
-}
-
-void
-fixup_strings_in_ver_d(Elf32_Shdr *ver_d)
-{
-  Elf32_Verdaux *veraux;
-  Elf32_Verdef *verdef;
-  int num_aux;
-  int name_idx;
-  int i;
-  int cont;
-
-  verdef = (Elf32_Verdef *)FILE_OFFSET(read_word(ver_d->sh_offset));
-
-  do {
-    num_aux = read_half(verdef->vd_cnt);
-    veraux = (Elf32_Verdaux *)((char *)verdef + read_word(verdef->vd_aux));
-    for (i=0; i<num_aux; i++) {
-      write_word(&veraux->vda_name,
-		 fixup_string(read_word(veraux->vda_name)));
-      veraux = (Elf32_Verdaux *)((char *)veraux + read_word(veraux->vda_next));
-    }
-
-    cont = read_word(verdef->vd_next) != 0;
-    verdef = (Elf32_Verdef *)((char *)verdef + read_word(verdef->vd_next));
-  } while (cont);
-  
-}
-
-void
-add_strings_from_ver_r(Elf32_Shdr *ver_r, char *strtab)
-{
-  Elf32_Vernaux *veraux;
-  Elf32_Verneed *verneed;
-  int num_aux;
-  int name_idx;
-  int i;
-  int cont;
-
-  verneed = (Elf32_Verneed *)FILE_OFFSET(read_word(ver_r->sh_offset));
-
-  do {
-    name_idx = read_word(verneed->vn_file);
-    possibly_add_string(name_idx, &strtab[name_idx]);
-    num_aux = read_half(verneed->vn_cnt);
-    veraux = (Elf32_Vernaux *)((char *)verneed + read_word(verneed->vn_aux));
-    for (i=0; i<num_aux; i++) {
-      name_idx = read_word(veraux->vna_name);
-      possibly_add_string(name_idx, &strtab[name_idx]);
-      veraux = (Elf32_Vernaux *)((char *)veraux + read_word(veraux->vna_next));
-    }
-
-    cont = read_word(verneed->vn_next) != 0;
-    verneed = (Elf32_Verneed *)((char *)verneed + read_word(verneed->vn_next));
-  } while (cont);
-}
-
-void
-fixup_strings_in_ver_r(Elf32_Shdr *ver_r)
-{
-  Elf32_Vernaux *veraux;
-  Elf32_Verneed *verneed;
-  int num_aux;
-  int name_idx;
-  int i;
-  int cont;
-
-  verneed = (Elf32_Verneed *)FILE_OFFSET(read_word(ver_r->sh_offset));
-
-  do {
-    write_word(&verneed->vn_file,
-	       fixup_string(read_word(verneed->vn_file)));
-    num_aux = read_half(verneed->vn_cnt);
-    veraux = (Elf32_Vernaux *)((char *)verneed + read_word(verneed->vn_aux));
-    for (i=0; i<num_aux; i++) {
-      write_word(&veraux->vna_name,
-		 fixup_string(read_word(veraux->vna_name)));
-      veraux = (Elf32_Vernaux *)((char *)veraux + read_word(veraux->vna_next));
-    }
-
-    cont = read_word(verneed->vn_next) != 0;
-    verneed = (Elf32_Verneed *)((char *)verneed + read_word(verneed->vn_next));
-  } while (cont);
-}
-
-gboolean sum_size(gpointer	key,
-		  struct dynamic_symbol *sym,
-		  int *size)
-{
-  *size += strlen(sym->string) + 1;
-  return 1;
-}
-
-struct index_n_dynstr {
-  int index;
-  unsigned char *dynstr;
-};
-
-gboolean output_string(gpointer	key,
-		       struct dynamic_symbol *sym,
-		       struct index_n_dynstr *x)
-{
-  sym->new_index = x->index;
-  memcpy(x->dynstr + x->index, sym->string, strlen(sym->string) + 1);
-  x->index += strlen(sym->string) + 1;
-  return 1;
-}
-
-
-unsigned char *
-generate_new_dynstr(Elf32_Word *size_out)
-{
-  int size;
-  unsigned char *new_dynstr;
-  struct index_n_dynstr x;
-
-  size = 1; /* first a zero */
-  g_hash_table_foreach	(used_dynamic_symbols,
-			 (GHFunc)sum_size,
-			 &size);
-
-
-  new_dynstr = g_malloc(size);
-
-  new_dynstr[0] = 0;
-  x.index = 1;
-  x.dynstr = new_dynstr;
-  g_hash_table_foreach	(used_dynamic_symbols,
-			 (GHFunc)output_string,
-			 &x);
-  
-  *size_out = size;
-  return new_dynstr;
-}
-
-void
-remap_sections(void)
-{
-  Elf32_Shdr *section;
-  Elf32_Word sectionsize;
-  int numsections;
-  int i = 0;
-
-  section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff));
-  sectionsize = read_half(elf_header->e_shentsize);
-  numsections = read_half(elf_header->e_shnum);
-
-  for (i=0;i<numsections;i++) {
-    write_word(&section->sh_size,
-	       fixup_size(read_word(section->sh_offset),
-			  read_word(section->sh_size)));
-    write_word(&section->sh_offset,
-	       fixup_offset(read_word(section->sh_offset)));
-    write_word(&section->sh_addr,
-	       fixup_addr(read_word(section->sh_addr)));
-    
-    section = (Elf32_Shdr *)((char *)section + sectionsize);
-  }
-}
-
-
-void
-remap_segments(void)
-{
-  Elf32_Phdr *segment;
-  Elf32_Word segmentsize;
-  Elf32_Word p_align;
-  int numsegments;
-  int i = 0;
-
-  segment = (Elf32_Phdr *)FILE_OFFSET(read_word(elf_header->e_phoff));
-  segmentsize = read_half(elf_header->e_phentsize);
-  numsegments = read_half(elf_header->e_phnum);
-
-  for (i=0;i<numsegments;i++) {
-    write_word(&segment->p_filesz,
-	       fixup_size(read_word(segment->p_offset),
-			  read_word(segment->p_filesz)));
-    write_word(&segment->p_offset,
-	       fixup_offset(read_word(segment->p_offset)));
-
-    write_word(&segment->p_memsz,
-	       fixup_addr_size(read_word(segment->p_vaddr),
-			       read_word(segment->p_memsz)));
-    write_word(&segment->p_vaddr,
-	       fixup_addr(read_word(segment->p_vaddr)));
-    write_word(&segment->p_paddr,
-	       read_word(segment->p_vaddr));
-
-    /* Consistancy checking: */
-    p_align = read_word(segment->p_align);
-    if (p_align > 1) {
-      if ((read_word(segment->p_vaddr) - read_word(segment->p_offset))%p_align != 0) {
-	fprintf(stderr, "Warning, creating non-aligned segment addr: %x offset: %x allign: %x\n",
-		read_word(segment->p_vaddr), read_word(segment->p_offset), p_align);
-      }
-    }
-    
-    segment = (Elf32_Phdr *)((char *)segment + segmentsize);
-  }
-}
-
-void
-remap_elf_header(void)
-{
-  write_word(&elf_header->e_phoff,
-	     fixup_offset(read_word(elf_header->e_phoff)));
-  write_word(&elf_header->e_shoff,
-	     fixup_offset(read_word(elf_header->e_shoff)));
-
-  write_word(&elf_header->e_entry,
-	     fixup_addr(read_word(elf_header->e_entry)));
-}
-
-void
-remap_symtab(Elf32_Shdr *symtab)
-{
-  Elf32_Sym *symbol;
-  Elf32_Sym *symbol_end;
-  Elf32_Word entry_size;
-
-  symbol = (Elf32_Sym *)FILE_OFFSET(read_word(symtab->sh_offset));
-  symbol_end = (Elf32_Sym *)FILE_OFFSET(read_word(symtab->sh_offset) +
-					read_word(symtab->sh_size));
-  entry_size = read_word(symtab->sh_entsize);
-
-  while (symbol < symbol_end) {
-    write_word(&symbol->st_value,
-	       fixup_addr(read_word(symbol->st_value)));
-    symbol = (Elf32_Sym *)((char *)symbol + entry_size);
-  }
-}
-
-
-/* Ugly global variables: */
-Elf32_Addr got_data_start = 0;
-Elf32_Addr got_data_end = 0;
-
-
-void
-remap_rel_section(Elf32_Rel *rel, Elf32_Word size, Elf32_Word entry_size)
-{
-  Elf32_Rel *rel_end;
-  Elf32_Word offset;
-  Elf32_Addr *addr;
-  Elf32_Word type;
-
-  rel_end = (Elf32_Rel *)((char *)rel + size);
-
-  while (rel < rel_end) {
-    type = ELF32_R_TYPE(read_word(rel->r_info)); 
-    switch (machine_type) {
-    case EM_386:
-      if ((type == R_386_RELATIVE) || (type == R_386_JMP_SLOT)) {
-	/* We need to relocate the data this is pointing to too. */
-	offset = vma_to_offset(read_word(rel->r_offset));
-	
-	addr =  (Elf32_Addr *)FILE_OFFSET(offset);
-	write_word(addr, 
-		   fixup_addr(read_word(*addr)));
-      }
-      write_word(&rel->r_offset,
-		 fixup_addr(read_word(rel->r_offset)));
-      break;
-    case EM_PPC:
-      /* The PPC always uses RELA relocations */
-      break;
-    }
-
-    
-    rel = (Elf32_Rel *)((char *)rel + entry_size);
-  }
-}
-
-void
-remap_rela_section(Elf32_Rela *rela, Elf32_Word size, Elf32_Word entry_size)
-{
-  Elf32_Rela *rela_end;
-  Elf32_Addr *addr;
-  Elf32_Word offset;
-  Elf32_Word type;
-  Elf32_Word bitmask;
-
-  rela_end = (Elf32_Rela *)((char *)rela + size);
-
-  while (rela < rela_end) {
-    type = ELF32_R_TYPE(read_word(rela->r_info));
-    switch (machine_type) {
-    case EM_386:
-      if ((type == R_386_RELATIVE) || (type == R_386_JMP_SLOT)) {
-	/* We need to relocate the data this is pointing to too. */
-	offset = vma_to_offset(read_word(rela->r_offset));
-	
-	addr =  (Elf32_Addr *)FILE_OFFSET(offset);
-	write_word(addr,
-		   fixup_addr(read_word(*addr)));
-      }
-      write_word(&rela->r_offset,
-		 fixup_addr(read_word(rela->r_offset)));
-      break;
-    case EM_PPC:
-/* Some systems do not have PowerPC relocations defined */
-#ifdef R_PPC_NONE
-      switch (type) {
-      case R_PPC_RELATIVE:
-	write_word((Elf32_Word *)&rela->r_addend,
-		   fixup_addr(read_word(rela->r_addend)));
-	/* Fall through for 32bit offset fixup */
-      case R_PPC_ADDR32:
-      case R_PPC_GLOB_DAT:
-      case R_PPC_JMP_SLOT:
-	write_word(&rela->r_offset,
-		   fixup_addr(read_word(rela->r_offset)));
-	break;
-      case R_PPC_NONE:
-	break;
-      default:
-	fprintf(stderr, "Warning, unhandled PPC relocation type %d\n", type);
-      }
-#endif
-      break;
-    }
-    
-    rela = (Elf32_Rela *)((char *)rela + entry_size);
-  }
-}
-
-void 
-remap_i386_got(void)
-{
-  Elf32_Shdr *got_section;
-  Elf32_Addr *got;
-  Elf32_Addr *got_end;
-  Elf32_Word entry_size;
-
-  got_section = elf_find_section_named(".got");
-  if (got_section == NULL) {
-    fprintf(stderr, "Warning, no .got section\n");
-    return;
-  }
-
-  got_data_start = read_word(got_section->sh_offset);
-  got_data_end = got_data_start + read_word(got_section->sh_size);
-  
-  got = (Elf32_Addr *)FILE_OFFSET(got_data_start);
-  got_end = (Elf32_Addr *)FILE_OFFSET(got_data_end);
-  entry_size = read_word(got_section->sh_entsize);
-
-  write_word(got,
-	     fixup_addr(read_word(*got))); /* Pointer to .dynamic */
-}
-
-void 
-remap_ppc_got(void)
-{
-  Elf32_Shdr *got_section;
-  Elf32_Addr *got;
-  Elf32_Addr *got_end;
-  Elf32_Word entry_size;
-
-  got_section = elf_find_section_named(".got");
-  if (got_section == NULL) {
-    fprintf(stderr, "Warning, no .got section\n");
-    return;
-  }
-
-  got_data_start = read_word(got_section->sh_offset);
-  got_data_end = got_data_start + read_word(got_section->sh_size);
-  
-  got = (Elf32_Addr *)FILE_OFFSET(got_data_start);
-  got_end = (Elf32_Addr *)FILE_OFFSET(got_data_end);
-  entry_size = read_word(got_section->sh_entsize);
-
-  /* Skip reserved part.
-   * Note that this should really be found by finding the
-   * _GLOBAL_OFFSET_TABLE symbol, as it could (according to
-   * the spec) point to the middle of the got.
-   */
-  got = (Elf32_Addr *)((char *)got + entry_size); /* Skip blrl instruction */
-  write_word(got,
-	     fixup_addr(read_word(*got))); /* Pointer to .dynamic */
-}
-
-
-Elf32_Word
-get_dynamic_val(Elf32_Shdr *dynamic, Elf32_Sword tag)
-{
-  Elf32_Dyn *element;
-  Elf32_Word entry_size;
-
-  entry_size = read_word(dynamic->sh_entsize);
-
-  element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset));
-  while (read_sword(element->d_tag) != DT_NULL) {
-    if (read_sword(element->d_tag) == tag) {
-      return read_word(element->d_un.d_val);
-    }
-    element = (Elf32_Dyn *)((char *)element + entry_size);
-  }
-  return 0;
-}
-
-void
-remap_dynamic(Elf32_Shdr *dynamic, Elf32_Word new_dynstr_size)
-{
-  Elf32_Dyn *element;
-  Elf32_Word entry_size;
-  Elf32_Word rel_size;
-  Elf32_Word rel_entry_size;
-  Elf32_Rel *rel;
-  Elf32_Rela *rela;
-  int jmprel_overlaps;
-  Elf32_Word rel_start, rel_end, jmprel_start, jmprel_end;
-    
-  entry_size = read_word(dynamic->sh_entsize);
-
-  /* Find out if REL/RELA and JMPREL overlaps: */
-  if (get_dynamic_val(dynamic, DT_PLTREL) == DT_REL) {
-    rel_start = get_dynamic_val(dynamic, DT_REL);
-    rel_end = rel_start + get_dynamic_val(dynamic, DT_RELSZ);
-  } else {
-    rel_start = get_dynamic_val(dynamic, DT_RELA);
-    rel_end = rel_start + get_dynamic_val(dynamic, DT_RELASZ);
-  }
-  jmprel_start = get_dynamic_val(dynamic, DT_JMPREL);
-  
-  jmprel_overlaps = 0;
-  if ((jmprel_start >= rel_start) && (jmprel_start < rel_end))
-    jmprel_overlaps = 1;
-    
-  element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset));
-  while (read_sword(element->d_tag) != DT_NULL) {
-    switch(read_sword(element->d_tag)) {
-    case DT_STRSZ:
-      write_word(&element->d_un.d_val, new_dynstr_size);
-      break;
-    case DT_PLTGOT:
-    case DT_HASH:
-    case DT_STRTAB:
-    case DT_INIT:
-    case DT_FINI:
-    case DT_VERDEF:
-    case DT_VERNEED:
-    case DT_VERSYM:
-      write_word(&element->d_un.d_ptr,
-		 fixup_addr(read_word(element->d_un.d_ptr)));
-      break;
-    case DT_JMPREL:
-      rel_size = get_dynamic_val(dynamic, DT_PLTRELSZ);
-      if (!jmprel_overlaps) {
-	if (get_dynamic_val(dynamic, DT_PLTREL) == DT_REL) {
-	  rel_entry_size = get_dynamic_val(dynamic, DT_RELENT);
-	  rel = (Elf32_Rel *)FILE_OFFSET(vma_to_offset(read_word(element->d_un.d_ptr)));
-	  remap_rel_section(rel, rel_size, rel_entry_size);
-	} else {
-	  rel_entry_size = get_dynamic_val(dynamic, DT_RELAENT);
-	  rela = (Elf32_Rela *)FILE_OFFSET(vma_to_offset(read_word(element->d_un.d_ptr)));
-	  remap_rela_section(rela, rel_size, rel_entry_size);
-	}
-      }
-      write_word(&element->d_un.d_ptr,
-		 fixup_addr(read_word(element->d_un.d_ptr)));
-      break;
-    case DT_REL:
-      rel_size = get_dynamic_val(dynamic, DT_RELSZ);
-      rel_entry_size = get_dynamic_val(dynamic, DT_RELENT);
-      rel = (Elf32_Rel *)FILE_OFFSET(vma_to_offset(read_word(element->d_un.d_ptr)));
-      remap_rel_section(rel, rel_size, rel_entry_size);
-
-      write_word(&element->d_un.d_ptr,
-		 fixup_addr(read_word(element->d_un.d_ptr)));
-      break;
-    case DT_RELA:
-      rel_size = get_dynamic_val(dynamic, DT_RELASZ);
-      rel_entry_size = get_dynamic_val(dynamic, DT_RELAENT);
-      rela = (Elf32_Rela *)FILE_OFFSET(vma_to_offset(read_word(element->d_un.d_ptr)));
-      remap_rela_section(rela, rel_size, rel_entry_size);
-
-      write_word(&element->d_un.d_ptr,
-		 fixup_addr(read_word(element->d_un.d_ptr)));
-      break;
-    default:
-      /*printf("unhandled d_tag: %d (0x%x)\n", read_sword(element->d_tag), read_sword(element->d_tag));*/
-      break;
-    }
-
-    element = (Elf32_Dyn *)((char *)element + entry_size);
-  }
-}
-
-void
-align_hole(Elf32_Word *start, Elf32_Word *end)
-{
-  Elf32_Word len;
-  Elf32_Word align;
-  Elf32_Shdr *section;
-  Elf32_Word sectionsize;
-  int numsections;
-  int i = 0;
-  int unaligned;
-  
-  len = *end - *start;
-  align = 0;
-    
-  sectionsize = read_half(elf_header->e_shentsize);
-  numsections = read_half(elf_header->e_shnum);
-  do {
-    section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff));
-    unaligned = 0;
-    
-    for (i=0;i<numsections;i++) {
-      if ( (read_word(section->sh_addralign) > 1) &&
-	   ( (read_word(section->sh_offset) - len + align)%read_word(section->sh_addralign) != 0) ) {
-	unaligned = 1;
-      }
-      
-      section = (Elf32_Shdr *)((char *)section + sectionsize);
-    }
-
-    if (unaligned) {
-      align++;
-    }
-      
-  } while (unaligned);
-
-  *start += align;
-}
-
-int
-main(int argc, char *argv[])
-{
-  int fd;
-  unsigned char *mapping;
-  Elf32_Word size;
-  struct stat statbuf;
-  Elf32_Shdr *dynamic;
-  Elf32_Shdr *dynsym;
-  Elf32_Shdr *symtab;
-  Elf32_Shdr *dynstr;
-  Elf32_Shdr *hash;
-  Elf32_Shdr *higher_section;
-  Elf32_Word dynstr_index;
-  Elf32_Shdr *ver_r;
-  Elf32_Shdr *ver_d;
-  char *dynstr_data;
-  unsigned char *new_dynstr;
-  Elf32_Word old_dynstr_size;
-  Elf32_Word new_dynstr_size;
-  
-  if (argc != 2) {
-    fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
-    return 1;
-  }
-
-  fd = open(argv[1], O_RDWR);
-  if (fd == -1) {
-    fprintf(stderr, "Cannot open file %s\n", argv[1]);
-    return 1;
-  }
-  
-  if (fstat(fd, &statbuf) == -1) {
-    fprintf(stderr, "Cannot stat file %s\n", argv[1]);
-    return 1;
-  }
-  
-  size = statbuf.st_size;
-    
-  mapping = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-
-  if (mapping == (unsigned char *)-1) {
-    fprintf(stderr, "Cannot mmap file %s\n", argv[1]);
-    return 1;
-  }
-
-  used_dynamic_symbols = g_hash_table_new(g_direct_hash, g_direct_equal);
-
-  elf_header = (Elf32_Ehdr *)mapping;
-
-  if (strncmp((void *)elf_header, ELFMAG, SELFMAG)!=0) {
-    fprintf(stderr, "Not an ELF file\n");
-    return 1;
-  }
-
-  if (elf_header->e_ident[EI_VERSION] != EV_CURRENT) {
-    fprintf(stderr, "Wrong ELF file version\n");
-    return 1;
-  }
-
-  if (elf_header->e_ident[EI_CLASS] != ELFCLASS32) {
-    fprintf(stderr, "Only 32bit ELF files supported\n");
-    return 1;
-  }
-  
-  setup_byteswapping(elf_header->e_ident[EI_DATA]);
-
-  machine_type = read_half(elf_header->e_machine);
-  if ( (machine_type != EM_386) &&
-       (machine_type != EM_PPC) ) {
-    fprintf(stderr, "Unsupported architecture. Supported are: x86, ppc\n");
-    return 1;
-  }
-
-  if (read_half(elf_header->e_type) != ET_DYN) {
-    fprintf(stderr, "Not an ELF shared object\n");
-    return 1;
-  }
-  
-  dynamic = elf_find_section(SHT_DYNAMIC);
-  dynsym = elf_find_section(SHT_DYNSYM);
-  symtab = elf_find_section(SHT_SYMTAB);
-  dynstr_index = read_word(dynsym->sh_link);
-  dynstr = elf_find_section_num(dynstr_index);
-  dynstr_data = (char *)FILE_OFFSET(read_word(dynstr->sh_offset));
-  old_dynstr_size = read_word(dynstr->sh_size);
-  ver_d = elf_find_section(SHT_GNU_verdef);
-  ver_r = elf_find_section(SHT_GNU_verneed);
-  hash = elf_find_section(SHT_HASH);
-
-  /* Generate hash table with all used strings: */
-  
-  add_strings_from_dynsym(dynsym, dynstr_data);
-  add_strings_from_dynamic(dynamic, dynstr_data);
-  if (ver_d && (read_word(ver_d->sh_link) == dynstr_index))
-    add_strings_from_ver_d(ver_d, dynstr_data);
-  if (ver_r && (read_word(ver_r->sh_link) == dynstr_index))
-    add_strings_from_ver_r(ver_r, dynstr_data);
-
-  /* Generate new dynstr section from the used strings hashtable: */
-  
-  new_dynstr = generate_new_dynstr(&new_dynstr_size);
-  /*
-  printf("New dynstr size: %d\n", new_dynstr_size);
-  printf("Old dynstr size: %d\n", old_dynstr_size);
-  */
-  
-  if (new_dynstr_size >= old_dynstr_size) {
-    return 0;
-  }
-
-  /* Fixup all references: */
-  fixup_strings_in_dynsym(dynsym);
-  fixup_strings_in_dynamic(dynamic);
-  if (ver_d && (read_word(ver_d->sh_link) == dynstr_index))
-    fixup_strings_in_ver_d(ver_d);
-  if (ver_r && (read_word(ver_r->sh_link) == dynstr_index))
-    fixup_strings_in_ver_r(ver_r);
-  
-  /* Copy over the new dynstr: */
-  memcpy(dynstr_data, new_dynstr, new_dynstr_size);
-  memset(dynstr_data + new_dynstr_size, ' ', old_dynstr_size-new_dynstr_size);
-
-  /* Compact the dynstr section and the file: */
-
-  /* 1. Set up the data for the fixup_offset() function: */
-  hole_index = read_word(dynstr->sh_offset) + new_dynstr_size;
-  higher_section = elf_find_next_higher_section(hole_index);
-  hole_end = read_word(higher_section->sh_offset);
-
-  align_hole(&hole_index, &hole_end);
-  hole_len = hole_end - hole_index;
-
-  hole_addr_start = hole_index; /* TODO: Fix this to something better */
-
-  find_segment_addr_min_max(read_word(dynstr->sh_offset),
-			    &hole_addr_remap_start, &hole_addr_remap_end);
-  
-  /*
-  printf("Hole remap: 0x%lx - 0x%lx\n", hole_addr_remap_start, hole_addr_remap_end);
-
-  printf("hole: %lu - %lu (%lu bytes)\n", hole_index, hole_end, hole_len);
-  printf("hole: 0x%lx - 0x%lx (0x%lx bytes)\n", hole_index, hole_end, hole_len);
-  */
-  
-  /* 2. Change all section and segment sizes and offsets: */
-  remap_symtab(dynsym);
-  if (symtab)
-    remap_symtab(symtab);
-
-  if (machine_type == EM_386)
-    remap_i386_got();
-  if (machine_type == EM_PPC)
-    remap_ppc_got();
-  
-  remap_dynamic(dynamic, new_dynstr_size);
-  remap_sections(); /* After this line the section headers are wrong */
-  remap_segments();
-  remap_elf_header();
-    
-  /* 3. Do the real compacting. */
-
-  memmove(mapping + hole_index,
-	  mapping + hole_index + hole_len,
-	  size - (hole_index + hole_len));
-  
-  munmap(mapping, size);
-
-  ftruncate(fd, size - hole_len);
-  close(fd);
-
-  return 0;
-}
-
-
-
--- a/config/makefiles/target_binaries.mk
+++ b/config/makefiles/target_binaries.mk
@@ -18,17 +18,16 @@ GARBAGE += $(foreach lib,$(LIBRARY),$(EX
 endif
 endif # EXPORT_LIBRARY
 
 binaries libs:: $(SUBMAKEFILES) $(TARGETS)
 ifndef NO_DIST_INSTALL
 ifdef SHARED_LIBRARY
 ifdef IS_COMPONENT
 	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components
-	$(ELF_DYNSTR_GC) $(FINAL_TARGET)/components/$(SHARED_LIBRARY)
 ifndef NO_COMPONENTS_MANIFEST
 	$(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest 'manifest components/components.manifest')
 	$(call py_action,buildlist,$(FINAL_TARGET)/components/components.manifest 'binary-component $(SHARED_LIBRARY)')
 endif
 endif # IS_COMPONENT
 endif # SHARED_LIBRARY
 endif # !NO_DIST_INSTALL
 
--- a/configure.in
+++ b/configure.in
@@ -1994,19 +1994,16 @@ ia64*-hpux*)
 
     MOZ_MEMORY=1
 
     case "${target_cpu}" in
     alpha*)
     	CFLAGS="$CFLAGS -mieee"
     	CXXFLAGS="$CXXFLAGS -mieee"
     ;;
-    i*86)
-    	USE_ELF_DYNSTR_GC=1
-    ;;
     esac
 
     if test -z "$MC"; then
         MC=mc.exe
     fi
     ;;
 *-mingw*)
     DSO_CFLAGS=
@@ -4344,17 +4341,16 @@ cairo-qt)
       MOZ_ENABLE_XREMOTE=1
       MOZ_GL_DEFAULT_PROVIDER=GLX
       MOZ_X11=1
       AC_DEFINE(MOZ_X11)
       XT_LIBS=
     fi
 
     MOZ_WEBGL=1
-    USE_ELF_DYNSTR_GC=
     USE_FC_FREETYPE=1
     TK_CFLAGS='$(MOZ_QT_CFLAGS)'
     TK_LIBS='$(MOZ_QT_LIBS)'
     AC_DEFINE(MOZ_WIDGET_QT)
     MOZ_PDF_PRINTING=1
     AC_DEFINE(QT_NO_KEYWORDS)
     ;;
 
@@ -5396,17 +5392,17 @@ if test -n "$MOZ_VP8"; then
         AC_DEFINE(MOZ_VP8_ENCODER)
     fi
 
     if test -n "$MOZ_NATIVE_LIBVPX"; then
         dnl ============================
         dnl === libvpx Version check ===
         dnl ============================
         dnl Check to see if we have a system libvpx package.
-        PKG_CHECK_MODULES(MOZ_LIBVPX, vpx >= 1.0.0)
+        PKG_CHECK_MODULES(MOZ_LIBVPX, vpx >= 1.3.0)
 
         MOZ_CHECK_HEADER([vpx/vpx_decoder.h], [], 
          [AC_MSG_ERROR([Couldn't find vpx/vpx_decoder.h which is required for build with system libvpx. Use --without-system-libvpx to build with in-tree libvpx.])])
 
         _SAVE_LIBS=$LIBS
         AC_CHECK_LIB(vpx, vpx_codec_dec_init_ver, [], 
          [AC_MSG_ERROR([--with-system-libvpx requested but symbol vpx_codec_dec_init_ver not found])])
         LIBS=$_SAVE_LIBS
@@ -6774,17 +6770,16 @@ dnl ====================================
 NS_TRACE_MALLOC=${MOZ_TRACE_MALLOC}
 MOZ_ARG_ENABLE_BOOL(trace-malloc,
 [  --enable-trace-malloc   Enable malloc tracing; also disables DMD and jemalloc],
     NS_TRACE_MALLOC=1,
     NS_TRACE_MALLOC= )
 if test "$NS_TRACE_MALLOC"; then
   # Please, Mr. Linker Man, don't take away our symbol names
   MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=
-  USE_ELF_DYNSTR_GC=
   AC_DEFINE(NS_TRACE_MALLOC)
 fi
 AC_SUBST(NS_TRACE_MALLOC)
 
 dnl ========================================================
 dnl = Enable DMD
 dnl ========================================================
 
@@ -6794,17 +6789,16 @@ MOZ_ARG_ENABLE_BOOL(dmd,
     MOZ_DMD= )
 
 dnl The two options are conflicting. Fails the configure to alert the user.
 if test "$NS_TRACE_MALLOC" -a "$MOZ_DMD"; then
     AC_MSG_ERROR([--enable-trace-malloc and --enable-dmd are conflicting options])
 fi
 
 if test "$MOZ_DMD"; then
-    USE_ELF_DYNSTR_GC=
     AC_DEFINE(MOZ_DMD)
 
     if test "${CPU_ARCH}" = "arm"; then
         CFLAGS="$CFLAGS -funwind-tables"
         CXXFLAGS="$CXXFLAGS -funwind-tables"
     fi
 
     MOZ_MEMORY=1                        # DMD enables jemalloc
@@ -7164,24 +7158,16 @@ dnl ====================================
 dnl = Enable stripping of libs & executables when packaging
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(install-strip,
 [  --enable-install-strip  Enable stripping of libs & executables when packaging ],
     PKG_SKIP_STRIP= ,
     PKG_SKIP_STRIP=1)
 
 dnl ========================================================
-dnl = --enable-elf-dynstr-gc
-dnl ========================================================
-MOZ_ARG_ENABLE_BOOL(elf-dynstr-gc,
-[  --enable-elf-dynstr-gc  Enable elf dynstr garbage collector (opt builds only)],
-    USE_ELF_DYNSTR_GC=1,
-    USE_ELF_DYNSTR_GC= )
-
-dnl ========================================================
 dnl = --disable-elf-hack
 dnl ========================================================
 
 USE_ELF_HACK=1
 MOZ_ARG_DISABLE_BOOL(elf-hack,
 [  --disable-elf-hack      Disable elf hacks],
     [USE_ELF_HACK=],
     [USE_ELF_HACK=F # Force enable elf-hack])
@@ -7769,17 +7755,17 @@ dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Standalone module options (Not for building Mozilla))
 
 dnl Check for GLib.
 dnl ========================================================
 
 if test -z "$SKIP_PATH_CHECKS"; then
 if test -z "${GLIB_CFLAGS}" -o -z "${GLIB_LIBS}" ; then
-    if test "$MOZ_ENABLE_GTK2" -o "$USE_ELF_DYNSTR_GC" ; then
+    if test "$MOZ_ENABLE_GTK2" ; then
         PKG_CHECK_MODULES(GLIB, glib-2.0 >= 1.3.7 gobject-2.0)
     fi
 fi
 fi
 
 if test -z "${GLIB_GMODULE_LIBS}" \
    -a -n "${GLIB_CONFIG}"\
     -a "${GLIB_CONFIG}" != no\
@@ -8335,17 +8321,16 @@ AC_SUBST(MOZ_METRO)
 
 AC_SUBST(MOZ_ANDROID_HISTORY)
 AC_SUBST(MOZ_WEBSMS_BACKEND)
 AC_SUBST(MOZ_ANDROID_BEAM)
 AC_SUBST(MOZ_DISABLE_GECKOVIEW)
 AC_SUBST(ENABLE_STRIP)
 AC_SUBST(PKG_SKIP_STRIP)
 AC_SUBST(STRIP_FLAGS)
-AC_SUBST(USE_ELF_DYNSTR_GC)
 AC_SUBST(USE_ELF_HACK)
 AC_SUBST(INCREMENTAL_LINKER)
 AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS)
 AC_SUBST(MOZ_COMPONENT_NSPR_LIBS)
 
 AC_SUBST(MOZ_FIX_LINK_PATHS)
 AC_SUBST(XPCOM_LIBS)
 AC_SUBST(XPCOM_FROZEN_LDOPTS)
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -258,16 +258,17 @@ public:
   {
     mRefCnt.RemovePurple();
   }
 
   static void ClearContentUnbinder();
   static bool CanSkip(nsINode* aNode, bool aRemovingAllowed);
   static bool CanSkipInCC(nsINode* aNode);
   static bool CanSkipThis(nsINode* aNode);
+  static void RemoveBlackMarkedNode(nsINode* aNode);
   static void MarkNodeChildren(nsINode* aNode);
   static void InitCCCallbacks();
   static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
                            void *aData);
   static void MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild,
                                   void* aData);
 
 protected:
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1540,16 +1540,17 @@ public:
   static nsresult ProcessViewportInfo(nsIDocument *aDocument,
                                       const nsAString &viewportInfo);
 
   static nsIScriptContext* GetContextForEventHandlers(nsINode* aNode,
                                                       nsresult* aRv);
 
   static JSContext *GetCurrentJSContext();
   static JSContext *GetSafeJSContext();
+  static JSContext *GetCurrentJSContextForThread();
   static JSContext *GetDefaultJSContextForThread();
 
   /**
    * Case insensitive comparison between two strings. However it only ignores
    * case for ASCII characters a-z.
    */
   static bool EqualsIgnoreASCIICase(const nsAString& aStr1,
                                     const nsAString& aStr2);
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -5,19 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Base class for all element classes; this provides an implementation
  * of DOM Core's nsIDOMElement, implements nsIContent, provides
  * utility methods for subclasses, and so forth.
  */
 
+#include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/Util.h"
-#include "mozilla/Likely.h"
 
 #include "mozilla/dom/FragmentOrElement.h"
 
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "nsINodeInfo.h"
 #include "nsIDocumentInlines.h"
@@ -1314,39 +1315,52 @@ FindOptimizableSubtreeRoot(nsINode* aNod
 {
   nsINode* p;
   while ((p = aNode->GetParentNode())) {
     if (aNode->UnoptimizableCCNode()) {
       return nullptr;
     }
     aNode = p;
   }
-  
+
   if (aNode->UnoptimizableCCNode()) {
     return nullptr;
   }
   return aNode;
 }
 
-nsAutoTArray<nsINode*, 1020>* gCCBlackMarkedNodes = nullptr;
+StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
 
-void
+static PLDHashOperator
+VisitBlackMarkedNode(nsPtrHashKey<nsINode>* aEntry, void*)
+{
+  nsINode* n = aEntry->GetKey();
+  n->SetCCMarkedRoot(false);
+  n->SetInCCBlackTree(false);
+  return PL_DHASH_NEXT;
+}
+
+static void
 ClearBlackMarkedNodes()
 {
   if (!gCCBlackMarkedNodes) {
     return;
   }
-  uint32_t len = gCCBlackMarkedNodes->Length();
-  for (uint32_t i = 0; i < len; ++i) {
-    nsINode* n = gCCBlackMarkedNodes->ElementAt(i);
-    n->SetCCMarkedRoot(false);
-    n->SetInCCBlackTree(false);
+  gCCBlackMarkedNodes->EnumerateEntries(VisitBlackMarkedNode, nullptr);
+  gCCBlackMarkedNodes = nullptr;
+}
+
+// static
+void
+FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
+{
+  if (!gCCBlackMarkedNodes) {
+    return;
   }
-  delete gCCBlackMarkedNodes;
-  gCCBlackMarkedNodes = nullptr;
+  gCCBlackMarkedNodes->RemoveEntry(aNode);
 }
 
 // static
 bool
 FragmentOrElement::CanSkipInCC(nsINode* aNode)
 {
   // Don't try to optimize anything during shutdown.
   if (nsCCUncollectableMarker::sGeneration == 0) {
@@ -1366,24 +1380,24 @@ FragmentOrElement::CanSkipInCC(nsINode* 
   }
 
   nsINode* root =
     currentDoc ? static_cast<nsINode*>(currentDoc) :
                  FindOptimizableSubtreeRoot(aNode);
   if (!root) {
     return false;
   }
-  
+
   // Subtree has been traversed already.
   if (root->CCMarkedRoot()) {
     return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
   }
 
   if (!gCCBlackMarkedNodes) {
-    gCCBlackMarkedNodes = new nsAutoTArray<nsINode*, 1020>;
+    gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
   }
 
   // nodesToUnpurple contains nodes which will be removed
   // from the purple buffer if the DOM tree is black.
   nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
   // grayNodes need script traverse, so they aren't removed from
   // the purple buffer, but are marked to be in black subtree so that
   // traverse is faster.
@@ -1417,33 +1431,33 @@ FragmentOrElement::CanSkipInCC(nsINode* 
       grayNodes.AppendElement(node);
     } else if (node->IsPurple()) {
       nodesToUnpurple.AppendElement(node);
     }
   }
 
   root->SetCCMarkedRoot(true);
   root->SetInCCBlackTree(foundBlack);
-  gCCBlackMarkedNodes->AppendElement(root);
+  gCCBlackMarkedNodes->PutEntry(root);
 
   if (!foundBlack) {
     return false;
   }
 
   if (currentDoc) {
     // Special case documents. If we know the document is black,
     // we can mark the document to be in CCGeneration.
     currentDoc->
       MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
   } else {
     for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
       nsINode* node = grayNodes[i];
       node->SetInCCBlackTree(true);
+      gCCBlackMarkedNodes->PutEntry(node);
     }
-    gCCBlackMarkedNodes->AppendElements(grayNodes);
   }
 
   // Subtree is black, we can remove non-gray purple nodes from
   // purple buffer.
   for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
     nsIContent* purple = nodesToUnpurple[i];
     // Can't remove currently handled purple node.
     if (purple != aNode) {
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5258,16 +5258,27 @@ nsContentUtils::GetDefaultJSContextForTh
   if (MOZ_LIKELY(NS_IsMainThread())) {
     return GetSafeJSContext();
   } else {
     return workers::GetCurrentThreadJSContext();
   }
 }
 
 /* static */
+JSContext *
+nsContentUtils::GetCurrentJSContextForThread()
+{
+  if (MOZ_LIKELY(NS_IsMainThread())) {
+    return GetCurrentJSContext();
+  } else {
+    return workers::GetCurrentThreadJSContext();
+  }
+}
+
+/* static */
 nsresult
 nsContentUtils::ASCIIToLower(nsAString& aStr)
 {
   PRUnichar* iter = aStr.BeginWriting();
   PRUnichar* end = aStr.EndWriting();
   if (MOZ_UNLIKELY(!iter || !end)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -34,24 +34,24 @@
 
 #include "nsIChannel.h"
 #include "nsIStreamListener.h"
 
 #include "nsIFrame.h"
 #include "nsIDOMNode.h"
 
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsEventDispatcher.h"
 #include "nsSVGEffects.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
 using namespace mozilla;
 
@@ -1189,22 +1189,16 @@ nsImageLoadingContent::ClearCurrentReque
 
 void
 nsImageLoadingContent::ClearPendingRequest(nsresult aReason,
                                            uint32_t aFlags)
 {
   if (!mPendingRequest)
     return;
 
-  // Push a null JSContext on the stack so that code that runs within
-  // the below code doesn't think it's being called by JS. See bug
-  // 604262.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   // Deregister this image from the refresh driver so it no longer receives
   // notifications.
   nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
                                         &mPendingRequestRegistered);
 
   UntrackImage(mPendingRequest, aFlags);
   mPendingRequest->CancelAndForgetObserver(aReason);
   mPendingRequest = nullptr;
@@ -1254,41 +1248,31 @@ nsImageLoadingContent::BindToTree(nsIDoc
                                   nsIContent* aBindingParent,
                                   bool aCompileEventHandlers)
 {
   // We may be entering the document, so if our image should be tracked,
   // track it.
   if (!aDocument)
     return;
 
-  // Push a null JSContext on the stack so that callbacks triggered by the
-  // below code won't think they're being called from JS.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   TrackImage(mCurrentRequest);
   TrackImage(mPendingRequest);
 
   if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
     aDocument->BlockOnload();
 }
 
 void
 nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // We may be leaving the document, so if our image is tracked, untrack it.
   nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc();
   if (!doc)
     return;
 
-  // Push a null JSContext on the stack so that callbacks triggered by the
-  // below code won't think they're being called from JS.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   UntrackImage(mCurrentRequest);
   UntrackImage(mPendingRequest);
 
   if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
     doc->UnblockOnload(false);
 }
 
 void
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -248,30 +248,32 @@ nsNodeUtils::LastRelease(nsINode* aNode)
     nsContentUtils::RemoveListenerManager(aNode);
     aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER);
   }
 
   if (aNode->IsElement()) {
     nsIDocument* ownerDoc = aNode->OwnerDoc();
     Element* elem = aNode->AsElement();
     ownerDoc->ClearBoxObjectFor(elem);
-    
+
     NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
                  !elem->GetXBLBinding(),
                  "Non-forced node has binding on destruction");
 
     // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding
     // attached
     if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) &&
         ownerDoc->BindingManager()) {
       ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc);
     }
   }
 
   aNode->ReleaseWrapper(aNode);
+
+  FragmentOrElement::RemoveBlackMarkedNode(aNode);
 }
 
 struct MOZ_STACK_CLASS nsHandlerData
 {
   uint16_t mOperation;
   nsCOMPtr<nsIDOMNode> mSource;
   nsCOMPtr<nsIDOMNode> mDest;
   nsCxPusher mPusher;
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMEventTargetHelper.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsIDocument.h"
 #include "prprf.h"
 #include "nsGlobalWindow.h"
+#include "ScriptSettings.h"
 #include "mozilla/Likely.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMEventTargetHelper)
@@ -269,17 +270,17 @@ nsresult
 nsDOMEventTargetHelper::SetEventHandler(nsIAtom* aType,
                                         JSContext* aCx,
                                         const JS::Value& aValue)
 {
   nsRefPtr<EventHandlerNonNull> handler;
   JSObject* callable;
   if (aValue.isObject() &&
       JS_ObjectIsCallable(aCx, callable = &aValue.toObject())) {
-    handler = new EventHandlerNonNull(callable);
+    handler = new EventHandlerNonNull(callable, mozilla::dom::GetIncumbentGlobal());
   }
   SetEventHandler(aType, EmptyString(), handler);
   return NS_OK;
 }
 
 void
 nsDOMEventTargetHelper::GetEventHandler(nsIAtom* aType,
                                         JSContext* aCx,
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -878,29 +878,33 @@ nsEventListenerManager::CompileEventHand
 
   if (handler) {
     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget);
     // Bind it
     JS::Rooted<JSObject*> boundHandler(cx);
     JS::Rooted<JSObject*> scope(cx, listener->GetEventScope());
     context->BindCompiledEventHandler(mTarget, scope, handler, &boundHandler);
     aListenerStruct = nullptr;
+    // Note - We pass null for aIncumbentGlobal below. We could also pass the
+    // compilation global, but since the handler is guaranteed to be scripted,
+    // there's no need to use an override, since the JS engine will always give
+    // us the right answer.
     if (!boundHandler) {
       listener->ForgetHandler();
     } else if (listener->EventName() == nsGkAtoms::onerror && win) {
       nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback =
-        new OnErrorEventHandlerNonNull(boundHandler);
+        new OnErrorEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
       listener->SetHandler(handlerCallback);
     } else if (listener->EventName() == nsGkAtoms::onbeforeunload && win) {
       nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
-        new OnBeforeUnloadEventHandlerNonNull(boundHandler);
+        new OnBeforeUnloadEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
       listener->SetHandler(handlerCallback);
     } else {
       nsRefPtr<EventHandlerNonNull> handlerCallback =
-        new EventHandlerNonNull(boundHandler);
+        new EventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
       listener->SetHandler(handlerCallback);
     }
   }
 
   return result;
 }
 
 nsresult
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -33,21 +33,21 @@
 #include "nsISelectionPrivate.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIEditor.h"
 #include "nsTextEditRules.h"
 #include "mozilla/Selection.h"
 #include "nsEventListenerManager.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "mozilla/Preferences.h"
 #include "nsTextNode.h"
 #include "nsIController.h"
 #include "mozilla/TextEvents.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
 
 static nsINativeKeyBindings *sNativeInputBindings = nullptr;
 static nsINativeKeyBindings *sNativeTextAreaBindings = nullptr;
@@ -1278,23 +1278,22 @@ nsTextEditorState::PrepareEditor(const n
 
     // Get the DOM document
     nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
     if (!domdoc)
       return NS_ERROR_FAILURE;
 
     // What follows is a bit of a hack.  The editor uses the public DOM APIs
     // for its content manipulations, and it causes it to fail some security
-    // checks deep inside when initializing.  So we push a null JSContext
-    // on the JS stack here to make it clear that we're native code.
+    // checks deep inside when initializing. So we explictly make it clear that
+    // we're native code.
     // Note that any script that's directly trying to access our value
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
-    nsCxPusher pusher;
-    pusher.PushNull();
+    AutoSystemCaller asc;
 
     rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Initialize the controller for the editor
 
   if (!SuppressEventHandlers(presContext)) {
@@ -1772,19 +1771,18 @@ nsTextEditorState::GetValue(nsAString& a
     // access its own DOM nodes!  Let's try to deal with that by pushing a null
     // JSContext on the JSContext stack to make it clear that we're native
     // code.  Note that any script that's directly trying to access our value
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
     // XXXbz if we could just get the textContent of our anonymous content (eg
     // if plaintext editor didn't create <br> nodes all over), we wouldn't need
     // this.
-    { /* Scope for context pusher */
-      nsCxPusher pusher;
-      pusher.PushNull();
+    { /* Scope for AutoSystemCaller. */
+      AutoSystemCaller asc;
 
       mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
                               aValue);
     }
     if (canCache) {
       mCachedValue = aValue;
     } else {
       mCachedValue.Truncate();
@@ -1852,19 +1850,18 @@ nsTextEditorState::SetValue(const nsAStr
       if (!domDoc) {
         NS_WARNING("Why don't we have a document?");
         return;
       }
 
       // Time to mess with our security context... See comments in GetValue()
       // for why this is needed.  Note that we have to do this up here, because
       // otherwise SelectAll() will fail.
-      { /* Scope for context pusher */
-        nsCxPusher pusher;
-        pusher.PushNull();
+      {
+        AutoSystemCaller asc;
 
         nsCOMPtr<nsISelection> domSel;
         nsCOMPtr<nsISelectionPrivate> selPriv;
         mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                               getter_AddRefs(domSel));
         if (domSel)
         {
           selPriv = do_QueryInterface(domSel);
--- a/content/media/webrtc/LoadMonitor.cpp
+++ b/content/media/webrtc/LoadMonitor.cpp
@@ -16,17 +16,16 @@
 #include "nsNetUtil.h"
 #include "nsILineInputStream.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Util.h"
 #include "mozilla/Services.h"
-#include "mozilla/Preferences.h"
 
 #if defined(ANDROID) || defined(LINUX) || defined(XP_MACOSX)
 #include <sys/time.h>
 #include <sys/resource.h>
 #endif
 
 // NSPR_LOG_MODULES=LoadMonitor:5
 PRLogModuleInfo *gLoadMonitorLog = nullptr;
@@ -318,20 +317,16 @@ LoadMonitor::GetSystemLoad() {
   MutexAutoLock lock(mLock);
   float load = mSystemLoad;
   return load;
 }
 
 nsresult
 LoadMonitor::Init(nsRefPtr<LoadMonitor> &self)
 {
-  if (!Preferences::GetBool("media.navigator.load_adapt", false)) {
-    return NS_OK;
-  }
-
 #if defined(PR_LOGGING)
   if (!gLoadMonitorLog)
     gLoadMonitorLog = PR_NewLogModule("LoadMonitor");
   LOG(("Initializing LoadMonitor"));
 #endif
 
 #if defined(ANDROID) || defined(LINUX)
   nsRefPtr<LoadMonitorAddObserver> addObsRunner = new LoadMonitorAddObserver(self);
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -40,16 +40,17 @@ class MediaEngine
 public:
   virtual ~MediaEngine() {}
 
   static const int DEFAULT_VIDEO_FPS = 30;
   static const int DEFAULT_VIDEO_MIN_FPS = 10;
   static const int DEFAULT_VIDEO_WIDTH = 640;
   static const int DEFAULT_VIDEO_HEIGHT = 480;
   static const int DEFAULT_AUDIO_TIMER_MS = 10;
+  static const bool DEFAULT_LOAD_ADAPT = false;
 
   /* Populate an array of video sources in the nsTArray. Also include devices
    * that are currently unavailable. */
   virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*) = 0;
 
   /* Populate an array of audio sources in the nsTArray. Also include devices
    * that are currently unavailable. */
   virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*) = 0;
@@ -125,16 +126,17 @@ protected:
 /**
  * Video source and friends.
  */
 struct MediaEnginePrefs {
   int32_t mWidth;
   int32_t mHeight;
   int32_t mFPS;
   int32_t mMinFPS;
+  bool mLoadAdapt;
 };
 
 class MediaEngineVideoSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineVideoSource() {}
 };
 
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -38,31 +38,33 @@ GetUserMediaLog()
 #include "AndroidBridge.h"
 #endif
 
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
 #ifndef MOZ_B2G_CAMERA
-MediaEngineWebRTC::MediaEngineWebRTC()
+MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
   : mMutex("mozilla::MediaEngineWebRTC")
   , mVideoEngine(nullptr)
   , mVoiceEngine(nullptr)
   , mVideoEngineInit(false)
   , mAudioEngineInit(false)
   , mHasTabVideoSource(false)
 {
   nsCOMPtr<nsIComponentRegistrar> compMgr;
   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
   if (compMgr) {
     compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
   }
-  mLoadMonitor = new LoadMonitor();
-  mLoadMonitor->Init(mLoadMonitor);
+  if (aPrefs.mLoadAdapt) {
+      mLoadMonitor = new LoadMonitor();
+      mLoadMonitor->Init(mLoadMonitor);
+  }
 }
 #endif
 
 
 void
 MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
 #ifdef MOZ_B2G_CAMERA
@@ -354,12 +356,13 @@ MediaEngineWebRTC::Shutdown()
   if (mVoiceEngine) {
     mAudioSources.Clear();
     webrtc::VoiceEngine::Delete(mVoiceEngine);
   }
 
   mVideoEngine = nullptr;
   mVoiceEngine = nullptr;
 
-  mLoadMonitor->Shutdown();
+  if (mLoadMonitor)
+    mLoadMonitor->Shutdown();
 }
 
 }
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -362,17 +362,17 @@ public:
     , mWindowId(aWindowId)
     , mHasTabVideoSource(false)
   {
     AsyncLatencyLogger::Get(true)->AddRef();
     mLoadMonitor = new LoadMonitor();
     mLoadMonitor->Init(mLoadMonitor);
   }
 #else
-  MediaEngineWebRTC();
+  MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
 #endif
   ~MediaEngineWebRTC() {
     Shutdown();
 #ifdef MOZ_B2G_CAMERA
     AsyncLatencyLogger::Get()->Release();
 #endif
   }
 
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -317,17 +317,17 @@ nsXBLPrototypeHandler::ExecuteHandler(Ev
 
   // Now, wrap the bound handler into the content compartment and use it.
   JSAutoCompartment ac2(cx, globalObject);
   if (!JS_WrapObject(cx, &bound)) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<EventHandlerNonNull> handlerCallback =
-    new EventHandlerNonNull(bound);
+    new EventHandlerNonNull(bound, /* aIncumbentGlobal = */ nullptr);
 
   nsEventHandler eventHandler(handlerCallback);
 
   // Execute it.
   nsCOMPtr<nsIJSEventListener> eventListener;
   rv = NS_NewJSEventListener(globalObject,
                              scriptTarget, onEventAtom,
                              eventHandler,
new file mode 100644
--- /dev/null
+++ b/dom/base/ScriptSettings.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim: ft=cpp tw=78 sw=2 et ts=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 "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ThreadLocal.h"
+#include "mozilla/Assertions.h"
+
+#include "jsapi.h"
+#include "xpcpublic.h"
+#include "nsIGlobalObject.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsContentUtils.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class ScriptSettingsStack;
+static mozilla::ThreadLocal<ScriptSettingsStack*> sScriptSettingsTLS;
+
+ScriptSettingsStackEntry ScriptSettingsStackEntry::SystemSingleton;
+
+class ScriptSettingsStack {
+public:
+  static ScriptSettingsStack& Ref() {
+    return *sScriptSettingsTLS.get();
+  }
+  ScriptSettingsStack() {};
+
+  void Push(ScriptSettingsStackEntry* aSettings) {
+    // The bottom-most entry must always be a candidate entry point.
+    MOZ_ASSERT_IF(mStack.Length() == 0 || mStack.LastElement()->IsSystemSingleton(),
+                  aSettings->mIsCandidateEntryPoint);
+    mStack.AppendElement(aSettings);
+  }
+
+  void PushSystem() {
+    mStack.AppendElement(&ScriptSettingsStackEntry::SystemSingleton);
+  }
+
+  void Pop() {
+    MOZ_ASSERT(mStack.Length() > 0);
+    mStack.RemoveElementAt(mStack.Length() - 1);
+  }
+
+  nsIGlobalObject* Incumbent() {
+    if (!mStack.Length()) {
+      return nullptr;
+    }
+    return mStack.LastElement()->mGlobalObject;
+  }
+
+  nsIGlobalObject* EntryPoint() {
+    if (!mStack.Length())
+      return nullptr;
+    for (int i = mStack.Length() - 1; i >= 0; --i) {
+      if (mStack[i]->mIsCandidateEntryPoint) {
+        return mStack[i]->mGlobalObject;
+      }
+    }
+    MOZ_ASSUME_UNREACHABLE("Non-empty stack should always have an entry point");
+  }
+
+private:
+  // These pointers are caller-owned.
+  nsTArray<ScriptSettingsStackEntry*> mStack;
+};
+
+void
+InitScriptSettings()
+{
+  if (!sScriptSettingsTLS.initialized()) {
+    bool success = sScriptSettingsTLS.init();
+    if (!success) {
+      MOZ_CRASH();
+    }
+  }
+
+  ScriptSettingsStack* ptr = new ScriptSettingsStack();
+  sScriptSettingsTLS.set(ptr);
+}
+
+void DestroyScriptSettings()
+{
+  ScriptSettingsStack* ptr = sScriptSettingsTLS.get();
+  MOZ_ASSERT(ptr);
+  sScriptSettingsTLS.set(nullptr);
+  delete ptr;
+}
+
+// Note: When we're ready to expose it, GetEntryGlobal will look similar to
+// GetIncumbentGlobal below.
+
+nsIGlobalObject*
+GetIncumbentGlobal()
+{
+  // We need the current JSContext in order to check the JS for
+  // scripted frames that may have appeared since anyone last
+  // manipulated the stack. If it's null, that means that there
+  // must be no entry point on the stack, and therefore no incumbent
+  // global either.
+  JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
+  if (!cx) {
+    MOZ_ASSERT(ScriptSettingsStack::Ref().EntryPoint() == nullptr);
+    return nullptr;
+  }
+
+  // See what the JS engine has to say. If we've got a scripted caller
+  // override in place, the JS engine will lie to us and pretend that
+  // there's nothing on the JS stack, which will cause us to check the
+  // incumbent script stack below.
+  JS::RootedScript script(cx);
+  if (JS_DescribeScriptedCaller(cx, &script, nullptr)) {
+    JS::RootedObject global(cx, JS_GetGlobalFromScript(script));
+    MOZ_ASSERT(global);
+    return xpc::GetNativeForGlobal(global);
+  }
+
+  // Ok, nothing from the JS engine. Let's use whatever's on the
+  // explicit stack.
+  return ScriptSettingsStack::Ref().Incumbent();
+}
+
+AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
+                                 bool aIsMainThread,
+                                 JSContext* aCx)
+  : mStack(ScriptSettingsStack::Ref())
+  , mEntry(aGlobalObject, /* aCandidate = */ true)
+{
+  MOZ_ASSERT(aGlobalObject);
+  if (!aCx) {
+    // If the caller didn't provide a cx, hunt one down. This isn't exactly
+    // fast, but the callers that care about performance can pass an explicit
+    // cx for now. Eventually, the whole cx pushing thing will go away
+    // entirely.
+    MOZ_ASSERT(aIsMainThread, "cx is mandatory off-main-thread");
+    nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobalObject);
+    if (sgo && sgo->GetScriptContext()) {
+      aCx = sgo->GetScriptContext()->GetNativeContext();
+    }
+    if (!aCx) {
+      aCx = nsContentUtils::GetSafeJSContext();
+    }
+  }
+  if (aIsMainThread) {
+    mCxPusher.Push(aCx);
+  }
+  mAc.construct(aCx, aGlobalObject->GetGlobalJSObject());
+  mStack.Push(&mEntry);
+}
+
+AutoEntryScript::~AutoEntryScript()
+{
+  MOZ_ASSERT(mStack.Incumbent() == mEntry.mGlobalObject);
+  mStack.Pop();
+}
+
+AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
+  : mStack(ScriptSettingsStack::Ref())
+  , mEntry(aGlobalObject, /* aCandidate = */ false)
+  , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
+{
+  mStack.Push(&mEntry);
+}
+
+AutoIncumbentScript::~AutoIncumbentScript()
+{
+  MOZ_ASSERT(mStack.Incumbent() == mEntry.mGlobalObject);
+  mStack.Pop();
+}
+
+AutoSystemCaller::AutoSystemCaller(bool aIsMainThread)
+  : mStack(ScriptSettingsStack::Ref())
+{
+  if (aIsMainThread) {
+    mCxPusher.PushNull();
+  }
+  mStack.PushSystem();
+}
+
+AutoSystemCaller::~AutoSystemCaller()
+{
+  mStack.Pop();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/ScriptSettings.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim: ft=cpp tw=78 sw=2 et ts=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/. */
+
+/* Utilities for managing the script settings object stack defined in webapps */
+
+#ifndef mozilla_dom_ScriptSettings_h
+#define mozilla_dom_ScriptSettings_h
+
+#include "nsCxPusher.h"
+#include "MainThreadUtils.h"
+#include "nsIGlobalObject.h"
+
+#include "mozilla/Maybe.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * System-wide setup/teardown routines. Init and Destroy should be invoked
+ * once each, at startup and shutdown (respectively).
+ */
+void InitScriptSettings();
+void DestroyScriptSettings();
+
+// Note: We don't yet expose GetEntryGlobal, because in order for it to be
+// correct, we first need to replace a bunch of explicit cx pushing in the
+// browser with AutoEntryScript. But GetIncumbentGlobal is simpler, because it
+// can mostly be inferred from the JS stack.
+nsIGlobalObject* GetIncumbentGlobal();
+
+class ScriptSettingsStack;
+struct ScriptSettingsStackEntry {
+  nsCOMPtr<nsIGlobalObject> mGlobalObject;
+  bool mIsCandidateEntryPoint;
+
+  ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate)
+    : mGlobalObject(aGlobal)
+    , mIsCandidateEntryPoint(aCandidate)
+  {
+    MOZ_ASSERT(mGlobalObject);
+    MOZ_ASSERT(mGlobalObject->GetGlobalJSObject(),
+               "Must have an actual JS global for the duration on the stack");
+    MOZ_ASSERT(JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()),
+               "No outer windows allowed");
+  }
+
+  ~ScriptSettingsStackEntry() {
+    // We must have an actual JS global for the entire time this is on the stack.
+    MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
+  }
+
+  bool IsSystemSingleton() { return this == &SystemSingleton; }
+  static ScriptSettingsStackEntry SystemSingleton;
+
+private:
+  ScriptSettingsStackEntry() : mGlobalObject(nullptr)
+                             , mIsCandidateEntryPoint(true)
+  {}
+};
+
+/*
+ * A class that represents a new script entry point.
+ */
+class AutoEntryScript {
+public:
+  AutoEntryScript(nsIGlobalObject* aGlobalObject,
+                  bool aIsMainThread = NS_IsMainThread(),
+                  // Note: aCx is mandatory off-main-thread.
+                  JSContext* aCx = nullptr);
+  ~AutoEntryScript();
+
+private:
+  dom::ScriptSettingsStack& mStack;
+  dom::ScriptSettingsStackEntry mEntry;
+  nsCxPusher mCxPusher;
+  mozilla::Maybe<JSAutoCompartment> mAc; // This can de-Maybe-fy when mCxPusher
+                                         // goes away.
+};
+
+/*
+ * A class that can be used to force a particular incumbent script on the stack.
+ */
+class AutoIncumbentScript {
+public:
+  AutoIncumbentScript(nsIGlobalObject* aGlobalObject);
+  ~AutoIncumbentScript();
+private:
+  dom::ScriptSettingsStack& mStack;
+  dom::ScriptSettingsStackEntry mEntry;
+  JS::AutoHideScriptedCaller mCallerOverride;
+};
+
+/*
+ * A class used for C++ to indicate that existing entry and incumbent scripts
+ * should not apply to anything in scope, and that callees should act as if
+ * they were invoked "from C++".
+ */
+class AutoSystemCaller {
+public:
+  AutoSystemCaller(bool aIsMainThread = NS_IsMainThread());
+  ~AutoSystemCaller();
+private:
+  dom::ScriptSettingsStack& mStack;
+  nsCxPusher mCxPusher;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ScriptSettings_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -54,16 +54,17 @@ EXPORTS.mozilla.dom += [
     'DOMError.h',
     'DOMException.h',
     'DOMRequest.h',
     'MessageChannel.h',
     'MessagePort.h',
     'MessagePortList.h',
     'Navigator.h',
     'ScreenOrientation.h',
+    'ScriptSettings.h',
     'StructuredCloneTags.h',
     'URL.h',
 ]
 
 UNIFIED_SOURCES += [
     'BarProps.cpp',
     'CompositionStringSynthesizer.cpp',
     'Crypto.cpp',
@@ -89,16 +90,17 @@ UNIFIED_SOURCES += [
     'nsPerformance.cpp',
     'nsQueryContentEventResult.cpp',
     'nsScreen.cpp',
     'nsScriptNameSpaceManager.cpp',
     'nsStructuredCloneContainer.cpp',
     'nsWindowMemoryReporter.cpp',
     'nsWindowRoot.cpp',
     'nsWrapperCache.cpp',
+    'ScriptSettings.cpp',
     'URL.cpp',
     'WindowNamedPropertiesHandler.cpp',
 ]
 
 # these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
 SOURCES += [
     # this file doesn't like windows.h
     'MessagePort.cpp',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -39,16 +39,17 @@
 // Helper Classes
 #include "nsJSUtils.h"
 #include "jsapi.h"              // for JSAutoRequest
 #include "js/OldDebugAPI.h"     // for JS_ClearWatchPointsForObject
 #include "jswrapper.h"
 #include "nsReadableUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsJSEnvironment.h"
+#include "ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 
 // Other Classes
 #include "nsEventListenerManager.h"
 #include "mozilla/dom/BarProps.h"
 #include "nsContentCID.h"
 #include "nsLayoutStatics.h"
@@ -206,16 +207,17 @@
 #include "TimeChangeObserver.h"
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "nsIDOMMediaQueryList.h"
 #include "mozilla/dom/DOMJSClass.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
 #ifdef MOZ_JSDEBUGGER
 #include "jsdIDebuggerService.h"
 #endif
@@ -4933,17 +4935,17 @@ nsGlobalWindow::RequestAnimationFrame(co
                                       JSContext* cx,
                                       int32_t* aHandle)
 {
   if (!aCallback.isObject() || !JS_ObjectIsCallable(cx, &aCallback.toObject())) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsRefPtr<FrameRequestCallback> callback =
-    new FrameRequestCallback(&aCallback.toObject());
+    new FrameRequestCallback(&aCallback.toObject(), GetIncumbentGlobal());
 
   ErrorResult rv;
   *aHandle = RequestAnimationFrame(*callback, rv);
 
   return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
@@ -11197,28 +11199,28 @@ nsGlobalWindow::OpenInternal(const nsASt
     if (!aCalledNoScript) {
       // We asserted at the top of this function that aNavigate is true for
       // !aCalledNoScript.
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ true,
                                 aDialog, aNavigate, argv,
                                 getter_AddRefs(domReturn));
     } else {
-      // Push a null JSContext here so that the window watcher won't screw us
+      // Force a system caller here so that the window watcher won't screw us
       // up.  We do NOT want this case looking at the JS context on the stack
       // when searching.  Compare comments on
       // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
 
       // Note: Because nsWindowWatcher is so broken, it's actually important
-      // that we don't push a null cx here, because that screws it up when it
-      // tries to compute the caller principal to associate with dialog
+      // that we don't force a system caller here, because that screws it up
+      // when it tries to compute the caller principal to associate with dialog
       // arguments. That whole setup just really needs to be rewritten. :-(
-      nsCxPusher pusher;
+      Maybe<AutoSystemCaller> asc;
       if (!aContentModal) {
-        pusher.PushNull();
+        asc.construct();
       }
 
 
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ false,
                                 aDialog, aNavigate, aExtraArgument,
                                 getter_AddRefs(domReturn));
 
@@ -13220,17 +13222,17 @@ nsGlobalWindow::DisableNetworkEvent(uint
     return NS_OK;                                                            \
   }                                                                          \
   NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx,                  \
                                              const JS::Value &v) {           \
     nsRefPtr<EventHandlerNonNull> handler;                                   \
     JSObject *callable;                                                      \
     if (v.isObject() &&                                                      \
         JS_ObjectIsCallable(cx, callable = &v.toObject())) {                 \
-      handler = new EventHandlerNonNull(callable);                           \
+      handler = new EventHandlerNonNull(callable, GetIncumbentGlobal());     \
     }                                                                        \
     SetOn##name_(handler);                                                   \
     return NS_OK;                                                            \
   }
 #define ERROR_EVENT(name_, id_, type_, struct_)                              \
   NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx,                  \
                                              JS::Value *vp) {                \
     nsEventListenerManager *elm = GetExistingListenerManager();              \
@@ -13250,17 +13252,17 @@ nsGlobalWindow::DisableNetworkEvent(uint
     if (!elm) {                                                              \
       return NS_ERROR_OUT_OF_MEMORY;                                         \
     }                                                                        \
                                                                              \
     nsRefPtr<OnErrorEventHandlerNonNull> handler;                            \
     JSObject *callable;                                                      \
     if (v.isObject() &&                                                      \
         JS_ObjectIsCallable(cx, callable = &v.toObject())) {                 \
-      handler = new OnErrorEventHandlerNonNull(callable);                    \
+      handler = new OnErrorEventHandlerNonNull(callable, GetIncumbentGlobal()); \
     }                                                                        \
     elm->SetEventHandler(handler);                                           \
     return NS_OK;                                                            \
   }
 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_)                       \
   NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx,                  \
                                              JS::Value *vp) {                \
     nsEventListenerManager *elm = GetExistingListenerManager();              \
@@ -13281,17 +13283,17 @@ nsGlobalWindow::DisableNetworkEvent(uint
     if (!elm) {                                                              \
       return NS_ERROR_OUT_OF_MEMORY;                                         \
     }                                                                        \
                                                                              \
     nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handler;                     \
     JSObject *callable;                                                      \
     if (v.isObject() &&                                                      \
         JS_ObjectIsCallable(cx, callable = &v.toObject())) {                 \
-      handler = new OnBeforeUnloadEventHandlerNonNull(callable);             \
+      handler = new OnBeforeUnloadEventHandlerNonNull(callable, GetIncumbentGlobal()); \
     }                                                                        \
     elm->SetEventHandler(handler);                                           \
     return NS_OK;                                                            \
   }
 #define WINDOW_ONLY_EVENT EVENT
 #define TOUCH_EVENT EVENT
 #include "nsEventNameList.h"
 #undef TOUCH_EVENT
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -355,17 +355,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
     *aAllowEval = true;
 
     mozilla::HoldJSObjects(this);
 
-    mFunction = new Function(funobj);
+    mFunction = new Function(funobj, GetIncumbentGlobal());
 
     // Create our arg array.  argc is the number of arguments passed
     // to setTimeout or setInterval; the first two are our callback
     // and the delay, so only arguments after that need to go in our
     // array.
     // std::max(argc - 2, 0) wouldn't work right because argc is unsigned.
     uint32_t argCount = std::max(argc, 2u) - 2;
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -22,16 +22,17 @@
 #include "nsIXPConnect.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XPCQuickStubs.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "prprf.h"
 
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMErrorBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 #include "WorkerPrivate.h"
@@ -2010,22 +2011,22 @@ ConstructJSImplementation(JSContext* aCx
 {
   // Get the window to use as a parent and for initialization.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  // Make sure to have nothing on the JS context stack while creating and
+  // Make sure to divorce ourselves from the calling JS while creating and
   // initializing the object, so exceptions from that will get reported
   // properly, since those are never exceptions that a spec wants to be thrown.
-  {  // Scope for the nsCxPusher
-    nsCxPusher pusher;
-    pusher.PushNull();
+  {
+    AutoSystemCaller asc;
+
     // Get the XPCOM component containing the JS implementation.
     nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId);
     if (!implISupports) {
       NS_WARNING("Failed to get JS implementation for contract");
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
     // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer.
--- a/dom/bindings/CallbackFunction.h
+++ b/dom/bindings/CallbackFunction.h
@@ -20,18 +20,19 @@
 #include "mozilla/dom/CallbackObject.h"
 
 namespace mozilla {
 namespace dom {
 
 class CallbackFunction : public CallbackObject
 {
 public:
-  explicit CallbackFunction(JSObject* aCallable)
-    : CallbackObject(aCallable)
+  explicit CallbackFunction(JSObject* aCallable,
+                            nsIGlobalObject* aIncumbentGlobal)
+    : CallbackObject(aCallable, aIncumbentGlobal)
   {
     MOZ_ASSERT(JS_ObjectIsCallable(nullptr, mCallback));
   }
 
   JS::Handle<JSObject*> Callable() const
   {
     return Callback();
   }
--- a/dom/bindings/CallbackInterface.h
+++ b/dom/bindings/CallbackInterface.h
@@ -19,18 +19,19 @@
 #include "mozilla/dom/CallbackObject.h"
 
 namespace mozilla {
 namespace dom {
 
 class CallbackInterface : public CallbackObject
 {
 public:
-  explicit CallbackInterface(JSObject* aCallback)
-    : CallbackObject(aCallback)
+  explicit CallbackInterface(JSObject* aCallback,
+                             nsIGlobalObject *aIncumbentGlobal)
+    : CallbackObject(aCallback, aIncumbentGlobal)
   {
   }
 
 protected:
   bool GetCallableProperty(JSContext* cx, const char* aPropName,
                            JS::MutableHandle<JS::Value> aCallable);
 
 };
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -14,41 +14,44 @@
 #include "nsIScriptContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "nsIScriptSecurityManager.h"
 #include "xpcprivate.h"
 #include "WorkerPrivate.h"
 #include "nsGlobalWindow.h"
+#include "WorkerScope.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallbackObject)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::CallbackObject)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(CallbackObject)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(CallbackObject)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(CallbackObject)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CallbackObject)
   tmp->DropCallback();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncumbentGlobal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CallbackObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncumbentGlobal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackObject)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCallback)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
+CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
                                      ErrorResult& aRv,
                                      ExceptionHandling aExceptionHandling,
                                      JSCompartment* aCompartment)
   : mCx(nullptr)
   , mCompartment(aCompartment)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
   , mIsMainThread(NS_IsMainThread())
@@ -58,18 +61,19 @@ CallbackObject::CallSetup::CallSetup(JS:
   }
   // We need to produce a useful JSContext here.  Ideally one that the callback
   // is in some sense associated with, so that we can sort of treat it as a
   // "script entry point".  Though once we actually have script entry points,
   // we'll need to do the script entry point bits once we have an actual
   // callable.
 
   // First, find the real underlying callback.
-  JSObject* realCallback = js::UncheckedUnwrap(aCallback);
+  JSObject* realCallback = js::UncheckedUnwrap(aCallback->CallbackPreserveColor());
   JSContext* cx = nullptr;
+  nsIGlobalObject* globalObject = nullptr;
 
   if (mIsMainThread) {
     // Now get the global and JSContext for this callback.
     nsGlobalWindow* win = xpc::WindowGlobalOrNull(realCallback);
     if (win) {
       // Make sure that if this is a window it's the current inner, since the
       // nsIScriptContext and hence JSContext are associated with the outer
       // window.  Which means that if someone holds on to a function from a
@@ -80,52 +84,63 @@ CallbackObject::CallSetup::CallSetup(JS:
       if (!outer || win != outer->GetCurrentInnerWindow()) {
         // Just bail out from here
         return;
       }
       cx = win->GetContext() ? win->GetContext()->GetNativeContext()
                              // This happens - Removing it causes
                              // test_bug293235.xul to go orange.
                              : nsContentUtils::GetSafeJSContext();
+      globalObject = win;
     } else {
-      // No DOM Window. Use the SafeJSContext.
+      // No DOM Window. Store the global and use the SafeJSContext.
+      JSObject* glob = js::GetGlobalForObjectCrossCompartment(realCallback);
+      globalObject = xpc::GetNativeForGlobal(glob);
+      MOZ_ASSERT(globalObject);
       cx = nsContentUtils::GetSafeJSContext();
     }
-
-    // Make sure our JSContext is pushed on the stack.
-    mCxPusher.Push(cx);
   } else {
     cx = workers::GetCurrentThreadJSContext();
+    globalObject = workers::GetCurrentThreadWorkerPrivate()->GlobalScope();
   }
 
-  // Unmark the callable, and stick it in a Rooted before it can go gray again.
+  mAutoEntryScript.construct(globalObject, mIsMainThread, cx);
+  if (aCallback->IncumbentGlobalOrNull()) {
+    mAutoIncumbentScript.construct(aCallback->IncumbentGlobalOrNull());
+  }
+
+  // Unmark the callable (by invoking Callback() and not the CallbackPreserveColor()
+  // variant), and stick it in a Rooted before it can go gray again.
   // Nothing before us in this function can trigger a CC, so it's safe to wait
   // until here it do the unmark. This allows us to order the following two
   // operations _after_ the Push() above, which lets us take advantage of the
   // JSAutoRequest embedded in the pusher.
   //
   // We can do this even though we're not in the right compartment yet, because
   // Rooted<> does not care about compartments.
-  JS::ExposeObjectToActiveJS(aCallback);
-  mRootedCallable.construct(cx, aCallback);
+  mRootedCallable.construct(cx, aCallback->Callback());
 
   if (mIsMainThread) {
     // Check that it's ok to run this callback at all.
-    // Make sure to unwrap aCallback before passing it in to get the global of
-    // the callback object, not the wrapper.
+    // Make sure to use realCallback to get the global of the callback object,
+    // not the wrapper.
     bool allowed = nsContentUtils::GetSecurityManager()->
-      ScriptAllowed(js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(aCallback)));
+      ScriptAllowed(js::GetGlobalForObjectCrossCompartment(realCallback));
 
     if (!allowed) {
       return;
     }
   }
 
   // Enter the compartment of our callback, so we can actually work with it.
-  mAc.construct(cx, aCallback);
+  //
+  // Note that if the callback is a wrapper, this will not be the same
+  // compartment that we ended up in with mAutoEntryScript above, because the
+  // entry point is based off of the unwrapped callback (realCallback).
+  mAc.construct(cx, mRootedCallable.ref());
 
   // And now we're ready to go.
   mCx = cx;
 
   // Make sure the JS engine doesn't report exceptions we want to re-throw
   if (mExceptionHandling == eRethrowContentExceptions ||
       mExceptionHandling == eRethrowExceptions) {
     mSavedJSContextOptions = JS::ContextOptionsRef(cx);
@@ -189,27 +204,21 @@ CallbackObject::CallSetup::~CallSetup()
       nsJSUtils::ReportPendingException(mCx);
     }
   }
 
   // To get our nesting right we have to destroy our JSAutoCompartment first.
   // But be careful: it might not have been constructed at all!
   mAc.destroyIfConstructed();
 
-  // XXXbz For that matter why do we need to manually call ScriptEvaluated at
-  // all?  nsCxPusher::Pop will do that nowadays if !mScriptIsRunning, so the
-  // concerns from bug 295983 don't seem relevant anymore.  Do we want to make
-  // sure it's still called when !mScriptIsRunning?  I guess play it safe for
-  // now and do what CallEventHandler did, which is call always.
-
-  // Popping an nsCxPusher is safe even if it never got pushed.
-  mCxPusher.Pop();
+  mAutoIncumbentScript.destroyIfConstructed();
+  mAutoEntryScript.destroyIfConstructed();
 
   // It is important that this is the last thing we do, after leaving the
-  // compartment and popping the context.
+  // compartment and undoing all our entry/incumbent script changes
   if (mIsMainThread) {
     nsContentUtils::LeaveMicroTask();
   }
 }
 
 already_AddRefed<nsISupports>
 CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
                                           const nsIID& aIID) const
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -20,18 +20,18 @@
 #include "nsISupports.h"
 #include "nsISupportsImpl.h"
 #include "nsCycleCollectionParticipant.h"
 #include "jswrapper.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "mozilla/Util.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "nsWrapperCache.h"
 #include "nsJSEnvironment.h"
 #include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
 #define DOM_CALLBACKOBJECT_IID \
@@ -41,19 +41,23 @@ namespace dom {
 class CallbackObject : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(DOM_CALLBACKOBJECT_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallbackObject)
 
-  explicit CallbackObject(JSObject* aCallback)
+  // The caller may pass a global object which will act as an override for the
+  // incumbent script settings object when the callback is invoked (overriding
+  // the entry point computed from aCallback). If no override is required, the
+  // caller should pass null.
+  explicit CallbackObject(JSObject* aCallback, nsIGlobalObject *aIncumbentGlobal)
   {
-    Init(aCallback);
+    Init(aCallback, aIncumbentGlobal);
   }
 
   virtual ~CallbackObject()
   {
     DropCallback();
   }
 
   JS::Handle<JSObject*> Callback() const
@@ -72,69 +76,77 @@ public:
    */
   JS::Handle<JSObject*> CallbackPreserveColor() const
   {
     // Calling fromMarkedLocation() is safe because we trace our mCallback, and
     // because the value of mCallback cannot change after if has been set.
     return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address());
   }
 
+  nsIGlobalObject* IncumbentGlobalOrNull() const
+  {
+    return mIncumbentGlobal;
+  }
+
   enum ExceptionHandling {
     // Report any exception and don't throw it to the caller code.
     eReportExceptions,
     // Throw an exception to the caller code if the thrown exception is a
     // binding object for a DOMError from the caller's scope, otherwise report
     // it.
     eRethrowContentExceptions,
     // Throw any exception to the caller code.
     eRethrowExceptions
   };
 
 protected:
   explicit CallbackObject(CallbackObject* aCallbackObject)
   {
-    Init(aCallbackObject->mCallback);
+    Init(aCallbackObject->mCallback, aCallbackObject->mIncumbentGlobal);
   }
 
 private:
-  inline void Init(JSObject* aCallback)
+  inline void Init(JSObject* aCallback, nsIGlobalObject* aIncumbentGlobal)
   {
     MOZ_ASSERT(aCallback && !mCallback);
     // Set mCallback before we hold, on the off chance that a GC could somehow
     // happen in there... (which would be pretty odd, granted).
     mCallback = aCallback;
     mozilla::HoldJSObjects(this);
+
+    mIncumbentGlobal = aIncumbentGlobal;
   }
 
   CallbackObject(const CallbackObject&) MOZ_DELETE;
   CallbackObject& operator =(const CallbackObject&) MOZ_DELETE;
 
 protected:
   void DropCallback()
   {
     if (mCallback) {
       mCallback = nullptr;
       mozilla::DropJSObjects(this);
     }
   }
 
   JS::Heap<JSObject*> mCallback;
+  nsCOMPtr<nsIGlobalObject> mIncumbentGlobal;
 
   class MOZ_STACK_CLASS CallSetup
   {
     /**
      * A class that performs whatever setup we need to safely make a
      * call while this class is on the stack, After the constructor
      * returns, the call is safe to make if GetContext() returns
      * non-null.
      */
   public:
     // If aExceptionHandling == eRethrowContentExceptions then aCompartment
     // needs to be set to the caller's compartment.
-    CallSetup(JS::Handle<JSObject*> aCallable, ErrorResult& aRv,
+    CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
               ExceptionHandling aExceptionHandling,
               JSCompartment* aCompartment = nullptr);
     ~CallSetup();
 
     JSContext* GetContext() const
     {
       return mCx;
     }
@@ -148,27 +160,27 @@ protected:
     // Members which can go away whenever
     JSContext* mCx;
 
     // Caller's compartment. This will only have a sensible value if
     // mExceptionHandling == eRethrowContentExceptions.
     JSCompartment* mCompartment;
 
     // And now members whose construction/destruction order we need to control.
-
-    nsCxPusher mCxPusher;
+    Maybe<AutoEntryScript> mAutoEntryScript;
+    Maybe<AutoIncumbentScript> mAutoIncumbentScript;
 
     // Constructed the rooter within the scope of mCxPusher above, so that it's
     // always within a request during its lifetime.
     Maybe<JS::Rooted<JSObject*> > mRootedCallable;
 
     // Can't construct a JSAutoCompartment without a JSContext either.  Also,
-    // Put mAc after mCxPusher so that we exit the compartment before we pop the
-    // JSContext.  Though in practice we'll often manually order those two
-    // things.
+    // Put mAc after mAutoEntryScript so that we exit the compartment before
+    // we pop the JSContext. Though in practice we'll often manually order
+    // those two things.
     Maybe<JSAutoCompartment> mAc;
 
     // An ErrorResult to possibly re-throw exceptions on and whether
     // we should re-throw them.
     ErrorResult& mErrorResult;
     const ExceptionHandling mExceptionHandling;
     JS::ContextOptions mSavedJSContextOptions;
     const bool mIsMainThread;
@@ -332,38 +344,17 @@ public:
 
   // Try to return a WebIDLCallbackT version of this object.
   already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const
   {
     if (HasWebIDLCallback()) {
       nsRefPtr<WebIDLCallbackT> callback = GetWebIDLCallback();
       return callback.forget();
     }
-
-    XPCOMCallbackT* callback = GetXPCOMCallback();
-    if (!callback) {
-      return nullptr;
-    }
-
-    nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(callback);
-    if (!wrappedJS) {
-      return nullptr;
-    }
-
-    AutoSafeJSContext cx;
-
-    JS::Rooted<JSObject*> obj(cx, wrappedJS->GetJSObject());
-    if (!obj) {
-      return nullptr;
-    }
-
-    JSAutoCompartment ac(cx, obj);
-
-    nsRefPtr<WebIDLCallbackT> newCallback = new WebIDLCallbackT(obj);
-    return newCallback.forget();
+    return nullptr;
   }
 
 private:
   static const uintptr_t XPCOMCallbackFlag = 1u;
 
   friend void
   ImplCycleCollectionUnlink<WebIDLCallbackT,
                             XPCOMCallbackT>(CallbackObjectHolder& aField);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3338,17 +3338,17 @@ for (uint32_t i = 0; i < length; ++i) {
 
         if descriptor.interface.isCallback():
             name = descriptor.interface.identifier.name
             if type.nullable() or isCallbackReturnValue:
                 declType = CGGeneric("nsRefPtr<%s>" % name);
             else:
                 declType = CGGeneric("OwningNonNull<%s>" % name)
             conversion = (
-                "${declName} = new %s(&${val}.toObject());\n" % name)
+                "${declName} = new %s(&${val}.toObject(), mozilla::dom::GetIncumbentGlobal());\n" % name)
 
             template = wrapObjectTemplate(conversion, type,
                                           "${declName} = nullptr",
                                           failureCode)
             return JSToNativeConversionInfo(template, declType=declType,
                                             dealWithOptional=isOptional)
 
         # This is an interface that we implement as a concrete class
@@ -3671,17 +3671,17 @@ for (uint32_t i = 0; i < length; ++i) {
         assert not type.treatNonCallableAsNull() or type.nullable()
 
         name = type.unroll().identifier.name
         if type.nullable():
             declType = CGGeneric("nsRefPtr<%s>" % name);
         else:
             declType = CGGeneric("OwningNonNull<%s>" % name)
         conversion = (
-            "  ${declName} = new %s(&${val}.toObject());\n" % name)
+            "  ${declName} = new %s(&${val}.toObject(), mozilla::dom::GetIncumbentGlobal());\n" % name)
 
         if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
             haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())"
             if not isDefinitelyObject:
                 haveCallable = "${val}.isObject() && " + haveCallable
             if defaultValue is not None:
                 assert(isinstance(defaultValue, IDLNullValue))
                 haveCallable = "${haveValue} && " + haveCallable
@@ -10416,17 +10416,17 @@ class CGJSImplClass(CGBindingImplClass):
         if descriptor.interface.hasChildInterfaces():
             decorators = ""
             # We need a public virtual destructor our subclasses can use
             destructor = ClassDestructor(virtual=True, visibility="public")
         else:
             decorators = "MOZ_FINAL"
             destructor = None
 
-        baseConstructors=["mImpl(new %s(aJSImplObject))" % jsImplName(descriptor.name),
+        baseConstructors=["mImpl(new %s(aJSImplObject, /* aIncumbentGlobal = */ nullptr))" % jsImplName(descriptor.name),
                           "mParent(aParent)"]
         parentInterface = descriptor.interface.parent
         while parentInterface:
             if parentInterface.isJSImplemented():
                 baseConstructors.insert(
                     0, "%s(aJSImplObject, aParent)" % parentClass )
                 break
             parentInterface = parentInterface.parent
@@ -10543,22 +10543,22 @@ class CGCallback(CGClass):
                 realMethods.extend(self.getMethodImpls(method))
         CGClass.__init__(self, name,
                          bases=[ClassBase(baseName)],
                          constructors=self.getConstructors(),
                          methods=realMethods+getters+setters)
 
     def getConstructors(self):
         return [ClassConstructor(
-            [Argument("JSObject*", "aCallback")],
+            [Argument("JSObject*", "aCallback"), Argument("nsIGlobalObject*", "aIncumbentGlobal")],
             bodyInHeader=True,
             visibility="public",
             explicit=True,
             baseConstructors=[
-                "%s(aCallback)" % self.baseName
+                "%s(aCallback, aIncumbentGlobal)" % self.baseName,
                 ])]
 
     def getMethodImpls(self, method):
         assert method.needThisHandling
         args = list(method.args)
         # Strip out the JSContext*/JSObject* args
         # that got added.
         assert args[0].name == "cx" and args[0].argType == "JSContext*"
@@ -10573,17 +10573,17 @@ class CGCallback(CGClass):
         # method, insert our optional argument for deciding whether the
         # CallSetup should re-throw exceptions on aRv.
         args.append(Argument("ExceptionHandling", "aExceptionHandling",
                              "eReportExceptions"))
         # And now insert our template argument.
         argsWithoutThis = list(args)
         args.insert(0, Argument("const T&",  "thisObj"))
 
-        setupCall = ("CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n"
+        setupCall = ("CallSetup s(this, aRv, aExceptionHandling);\n"
                      "if (!s.GetContext()) {\n"
                      "  aRv.Throw(NS_ERROR_UNEXPECTED);\n"
                      "  return${errorReturn};\n"
                      "}\n")
 
         bodyWithThis = string.Template(
             setupCall+
             "JS::Rooted<JSObject*> thisObjJS(s.GetContext(),\n"
@@ -10863,17 +10863,17 @@ class CallbackMember(CGNativeMember):
         # well as a JSContext.
         return [Argument("JSContext*", "cx"),
                 Argument("JS::Handle<JSObject*>", "aThisObj")] + args
 
     def getCallSetup(self):
         if self.needThisHandling:
             # It's been done for us already
             return ""
-        callSetup = "CallSetup s(CallbackPreserveColor(), aRv"
+        callSetup = "CallSetup s(this, aRv"
         if self.rethrowContentException:
             # getArgs doesn't add the aExceptionHandling argument but does add
             # aCompartment for us.
             callSetup += ", eRethrowContentExceptions, aCompartment"
         else:
             callSetup += ", aExceptionHandling"
         callSetup += ");"
         return string.Template(
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2487,16 +2487,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED_1(Tab
                                      mMessageManager)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
+  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(TabChildGlobal, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TabChildGlobal, nsDOMEventTargetHelper)
 
 /* [notxpcom] boolean markForCC (); */
 // This method isn't automatically forwarded safely because it's notxpcom, so
@@ -2551,15 +2552,25 @@ TabChildGlobal::Atob(const nsAString& aA
 }
 
 JSContext*
 TabChildGlobal::GetJSContextForEventHandlers()
 {
   return nsContentUtils::GetSafeJSContext();
 }
 
-nsIPrincipal* 
+nsIPrincipal*
 TabChildGlobal::GetPrincipal()
 {
   if (!mTabChild)
     return nullptr;
   return mTabChild->GetPrincipal();
 }
+
+JSObject*
+TabChildGlobal::GetGlobalJSObject()
+{
+  NS_ENSURE_TRUE(mTabChild, nullptr);
+  nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mTabChild->GetGlobal();
+  NS_ENSURE_TRUE(ref, nullptr);
+  return ref->GetJSObject();
+}
+
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -46,17 +46,18 @@ class RenderFrameChild;
 namespace dom {
 
 class TabChild;
 class PContentDialogChild;
 class ClonedMessageData;
 
 class TabChildGlobal : public nsDOMEventTargetHelper,
                        public nsIContentFrameMessageManager,
-                       public nsIScriptObjectPrincipal
+                       public nsIScriptObjectPrincipal,
+                       public nsIGlobalObject
 {
 public:
   TabChildGlobal(TabChild* aTabChild);
   void Init();
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TabChildGlobal, nsDOMEventTargetHelper)
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
@@ -122,16 +123,17 @@ public:
   PreHandleEvent(nsEventChainPreVisitor& aVisitor)
   {
     aVisitor.mForceContentDispatch = true;
     return NS_OK;
   }
 
   virtual JSContext* GetJSContextForEventHandlers() MOZ_OVERRIDE;
   virtual nsIPrincipal* GetPrincipal() MOZ_OVERRIDE;
+  virtual JSObject* GetGlobalJSObject() MOZ_OVERRIDE;
 
   nsCOMPtr<nsIContentFrameMessageManager> mMessageManager;
   TabChild* mTabChild;
 };
 
 class ContentListener MOZ_FINAL : public nsIDOMEventListener
 {
 public:
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1039,16 +1039,17 @@ private:
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mMutex("mozilla::MediaManager")
   , mBackend(nullptr) {
   mPrefs.mWidth  = MediaEngine::DEFAULT_VIDEO_WIDTH;
   mPrefs.mHeight = MediaEngine::DEFAULT_VIDEO_HEIGHT;
   mPrefs.mFPS    = MediaEngine::DEFAULT_VIDEO_FPS;
   mPrefs.mMinFPS = MediaEngine::DEFAULT_VIDEO_MIN_FPS;
+  mPrefs.mLoadAdapt = MediaEngine::DEFAULT_LOAD_ADAPT;
 
   nsresult rv;
   nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
     if (branch) {
       GetPrefs(branch, nullptr);
     }
@@ -1083,16 +1084,17 @@ MediaManager::Get() {
     }
     // else MediaManager won't work properly and will leak (see bug 837874)
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
       prefs->AddObserver("media.navigator.video.default_width", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_height", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_fps", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_minfps", sSingleton, false);
+      prefs->AddObserver("media.navigator.load_adapt", sSingleton, false);
     }
   }
   return sSingleton;
 }
 
 /* static */ already_AddRefed<MediaManager>
 MediaManager::GetInstance()
 {
@@ -1406,17 +1408,17 @@ MediaManager::GetBackend(uint64_t aWindo
 {
   // Plugin backends as appropriate. The default engine also currently
   // includes picture support for Android.
   // This IS called off main-thread.
   MutexAutoLock lock(mMutex);
   if (!mBackend) {
 #if defined(MOZ_WEBRTC)
   #ifndef MOZ_B2G_CAMERA
-    mBackend = new MediaEngineWebRTC();
+    mBackend = new MediaEngineWebRTC(mPrefs);
   #else
     mBackend = new MediaEngineWebRTC(mCameraManager, aWindowId);
   #endif
 #else
     mBackend = new MediaEngineDefault();
 #endif
   }
   return mBackend;
@@ -1505,22 +1507,35 @@ MediaManager::GetPref(nsIPrefBranch *aBr
   if (aData == nullptr || strcmp(aPref,aData) == 0) {
     if (NS_SUCCEEDED(aBranch->GetIntPref(aPref, &temp))) {
       *aVal = temp;
     }
   }
 }
 
 void
+MediaManager::GetPrefBool(nsIPrefBranch *aBranch, const char *aPref,
+                          const char *aData, bool *aVal)
+{
+  bool temp;
+  if (aData == nullptr || strcmp(aPref,aData) == 0) {
+    if (NS_SUCCEEDED(aBranch->GetBoolPref(aPref, &temp))) {
+      *aVal = temp;
+    }
+  }
+}
+
+void
 MediaManager::GetPrefs(nsIPrefBranch *aBranch, const char *aData)
 {
   GetPref(aBranch, "media.navigator.video.default_width", aData, &mPrefs.mWidth);
   GetPref(aBranch, "media.navigator.video.default_height", aData, &mPrefs.mHeight);
   GetPref(aBranch, "media.navigator.video.default_fps", aData, &mPrefs.mFPS);
   GetPref(aBranch, "media.navigator.video.default_minfps", aData, &mPrefs.mMinFPS);
+  GetPrefBool(aBranch, "media.navigator.load_adapt", aData, &mPrefs.mLoadAdapt);
 }
 
 nsresult
 MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
   const PRUnichar* aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "Observer invoked off the main thread");
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@@ -1539,16 +1554,17 @@ MediaManager::Observe(nsISupports* aSubj
     obs->RemoveObserver(this, "getUserMedia:revoke");
 
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
       prefs->RemoveObserver("media.navigator.video.default_width", this);
       prefs->RemoveObserver("media.navigator.video.default_height", this);
       prefs->RemoveObserver("media.navigator.video.default_fps", this);
       prefs->RemoveObserver("media.navigator.video.default_minfps", this);
+      prefs->RemoveObserver("media.navigator.load_adapt", this);
     }
 
     // Close off any remaining active windows.
     {
       MutexAutoLock lock(mMutex);
       GetActiveWindows()->Clear();
       mActiveCallbacks.Clear();
       LOG(("Releasing MediaManager singleton and thread"));
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -501,16 +501,18 @@ public:
 private:
   WindowTable *GetActiveWindows() {
     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
     return &mActiveWindows;
   }
 
   void GetPref(nsIPrefBranch *aBranch, const char *aPref,
                const char *aData, int32_t *aVal);
+  void GetPrefBool(nsIPrefBranch *aBranch, const char *aPref,
+                   const char *aData, bool *aVal);
   void GetPrefs(nsIPrefBranch *aBranch, const char *aData);
 
   // Make private because we want only one instance of this class
   MediaManager();
 
   ~MediaManager() {
     delete mBackend;
   }
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -243,16 +243,33 @@ MmsConnection.prototype = {
       this.radioDisabled = Services.prefs.getBoolPref(kPrefRilRadioDisabled);
     } catch (e) {
       if (DEBUG) debug("Getting preference 'ril.radio.disabled' fails.");
       this.radioDisabled = false;
     }
 
     this.connected = this.radioInterface.getDataCallStateByType("mms") ==
       Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
+    // If the MMS network is connected during the initialization, it means the
+    // MMS network must share the same APN with the mobile network by default.
+    // Under this case, |networkManager.active| should keep the mobile network,
+    // which is supposed be an instance of |nsIRilNetworkInterface| for sure.
+    if (this.connected) {
+      let networkManager =
+        Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
+      let activeNetwork = networkManager.active;
+      if (activeNetwork.serviceId != this.serviceId) {
+        return;
+      }
+
+      let rilNetwork = activeNetwork.QueryInterface(Ci.nsIRilNetworkInterface);
+      // Set up the MMS APN setting based on the connected MMS network,
+      // which is going to be used for the HTTP requests later.
+      this.setApnSetting(rilNetwork);
+    }
   },
 
   /**
    * Return the roaming status of voice call.
    *
    * @return true if voice call is roaming.
    */
   isVoiceRoaming: function isVoiceRoaming() {
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1002,21 +1002,18 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
     // No hash yet (or any more), initialize it.
     if (!sJSObjWrappers.init(16)) {
       NS_ERROR("Error initializing PLDHashTable!");
 
       return nullptr;
     }
   }
 
-  nsJSObjWrapperKey key(obj, npp);
-
-  JSObjWrapperTable::AddPtr p = sJSObjWrappers.lookupForAdd(key);
-
-  if (p/* && p->value()*/) {
+  JSObjWrapperTable::Ptr p = sJSObjWrappers.lookupForAdd(nsJSObjWrapperKey(obj, npp));
+  if (p) {
     MOZ_ASSERT(p->value());
     // Found a live nsJSObjWrapper, return it.
 
     return _retainobject(p->value());
   }
 
   // No existing nsJSObjWrapper, create one.
 
@@ -1025,17 +1022,18 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
 
   if (!wrapper) {
     // Out of memory, entry not yet added to table.
     return nullptr;
   }
 
   wrapper->mJSObj = obj;
 
-  if (!sJSObjWrappers.add(p, key, wrapper)) {
+  nsJSObjWrapperKey key(obj, npp);
+  if (!sJSObjWrappers.putNew(key, wrapper)) {
     // Out of memory, free the wrapper we created.
     _releaseobject(wrapper);
     return nullptr;
   }
 
   NS_ASSERTION(wrapper->mNpp == npp, "nsJSObjWrapper::mNpp not initialized!");
 
   // Root the JSObject, its lifetime is now tied to that of the
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -9,17 +9,16 @@
 
 #include "nsISettingsService.h"
 
 #include "nsGeolocation.h"
 #include "nsDOMClassInfoID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "nsIDocument.h"
 #include "nsIObserverService.h"
 #include "nsPIDOMWindow.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
@@ -289,20 +288,16 @@ JSObject*
 PositionError::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return PositionErrorBinding::Wrap(aCx, aScope, this);
 }
 
 void
 PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
 {
-  // Ensure that the proper context is on the stack (bug 452762)
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   nsAutoMicroTask mt;
   if (aCallback.HasWebIDLCallback()) {
     PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
 
     if (callback) {
       ErrorResult err;
       callback->Call(*this, err);
     }
@@ -524,19 +519,16 @@ nsGeolocationRequest::SendLocation(nsIDO
 
   mLocator->SetCachedPosition(wrapped);
   if (!mIsWatchPositionRequest) {
     // Cancel timer and position updates in case the position
     // callback spins the event loop
     Shutdown();
   }
 
-  // Ensure that the proper context is on the stack (bug 452762)
-  nsCxPusher pusher;
-  pusher.PushNull();
   nsAutoMicroTask mt;
   if (mCallback.HasWebIDLCallback()) {
     ErrorResult err;
     PositionCallback* callback = mCallback.GetWebIDLCallback();
 
     MOZ_ASSERT(callback);
     callback->Call(*wrapped, err);
   } else {
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -947,32 +947,30 @@ private:
                  gfxXlibSurface *aPixmap,
                  LibType libType)
         : GLContext(caps, shareContext, isOffscreen),//aDeleteDrawable ? true : false, aShareContext, ),
           mContext(aContext),
           mDisplay(aDisplay),
           mDrawable(aDrawable),
           mDeleteDrawable(aDeleteDrawable),
           mDoubleBuffered(aDoubleBuffered),
-          mLibType(libType),
           mGLX(&sGLXLibrary[libType]),
           mPixmap(aPixmap)
     {
         MOZ_ASSERT(mGLX);
         // See 899855
         SetProfileVersion(ContextProfile::OpenGLCompatibility, 200);
     }
 
     GLXContext mContext;
     Display *mDisplay;
     GLXDrawable mDrawable;
     bool mDeleteDrawable;
     bool mDoubleBuffered;
 
-    LibType mLibType;
     GLXLibrary* mGLX;
 
     nsRefPtr<gfxXlibSurface> mPixmap;
 };
 
 static GLContextGLX *
 GetGlobalContextGLX(const ContextFlags aFlags = ContextFlagsNone)
 {
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -33,17 +33,17 @@ load 385719-1.html
 load 389326-1.html
 skip load 390476.html # bug 585185
 load 393746-1.xhtml
 load 393749-1.html
 load 393822-1.html
 load 394384-1.html
 load 394246-1.html
 load 394246-2.html
-skip-if(Android&&AndroidVersion<15) load 394751.xhtml # bug 922976
+skip-if(Android&&(AndroidVersion<15||AndroidVersion>=17)) load 394751.xhtml # bug 922976
 load 395335-1.xhtml
 load 395458-1.html
 load 396321-1.svg
 load 398042-1.xhtml
 load 398042-2.xhtml
 load 402307-1.html
 load 403464-1.html
 load 404112-1.html
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -31,20 +31,21 @@
 
 /* XXX DOM dependency */
 #include "nsIScriptContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMJSUtils.h"
 #include "SandboxPrivate.h"
 #include "nsJSPrincipals.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 using mozilla::AutoSafeJSContext;
 using mozilla::AutoPushJSContext;
+using mozilla::dom::AutoSystemCaller;
 
 /*
  * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the
  * script hook.  This was a hack to avoid some js engine problems that should
  * be fixed now (see Mozilla bug 77636).
  */
 #undef CAUTIOUS_SCRIPTHOOK
 
@@ -2999,18 +3000,17 @@ jsdService::WrapValue(const JS::Value &v
 
 
 NS_IMETHODIMP
 jsdService::EnterNestedEventLoop (jsdINestCallback *callback, uint32_t *_rval)
 {
     // Nesting event queues is a thing of the past.  Now, we just spin the
     // current event loop.
     nsresult rv = NS_OK;
-    nsCxPusher pusher;
-    pusher.PushNull();
+    AutoSystemCaller asc;
     uint32_t nestLevel = ++mNestedLoopLevel;
     nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
 
     if (callback) {
         DoPause(nullptr, true);
         rv = callback->OnNest();
         DoUnPause(nullptr, true);
     }
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -626,30 +626,16 @@ endif
 
 SDK_LIB_DIR = $(DIST)/sdk/lib
 SDK_BIN_DIR = $(DIST)/sdk/bin
 
 DEPENDENCIES	= .md
 
 MOZ_COMPONENT_LIBS=$(XPCOM_LIBS) $(MOZ_COMPONENT_NSPR_LIBS)
 
-ifeq ($(OS_ARCH),OS2)
-ELF_DYNSTR_GC	= echo
-else
-ELF_DYNSTR_GC	= :
-endif
-
-ifndef CROSS_COMPILE
-ifdef USE_ELF_DYNSTR_GC
-ifdef MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS
-ELF_DYNSTR_GC 	= $(DEPTH)/config/elf-dynstr-gc
-endif
-endif
-endif
-
 ifdef MACOSX_DEPLOYMENT_TARGET
 export MACOSX_DEPLOYMENT_TARGET
 endif # MACOSX_DEPLOYMENT_TARGET
 
 ifdef MOZ_USING_CCACHE
 ifdef CLANG_CXX
 export CCACHE_CPP2=1
 endif
--- a/js/src/config/makefiles/target_binaries.mk
+++ b/js/src/config/makefiles/target_binaries.mk
@@ -18,17 +18,16 @@ GARBAGE += $(foreach lib,$(LIBRARY),$(EX
 endif
 endif # EXPORT_LIBRARY
 
 binaries libs:: $(SUBMAKEFILES) $(TARGETS)
 ifndef NO_DIST_INSTALL
 ifdef SHARED_LIBRARY
 ifdef IS_COMPONENT
 	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components
-	$(ELF_DYNSTR_GC) $(FINAL_TARGET)/components/$(SHARED_LIBRARY)
 ifndef NO_COMPONENTS_MANIFEST
 	$(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest 'manifest components/components.manifest')
 	$(call py_action,buildlist,$(FINAL_TARGET)/components/components.manifest 'binary-component $(SHARED_LIBRARY)')
 endif
 endif # IS_COMPONENT
 endif # SHARED_LIBRARY
 endif # !NO_DIST_INSTALL
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5505,17 +5505,17 @@ EmitCallOrNew(ExclusiveContext *cx, Byte
          * create new types and scripts for their contents, to increase
          * the quality of type information within them and enable more
          * backend optimizations. Note that this does not depend on the
          * lambda being invoked at most once (it may be named or be
          * accessed via foo.caller indirection), as multiple executions
          * will just cause the inner scripts to be repeatedly cloned.
          */
         JS_ASSERT(!bce->emittingRunOnceLambda);
-        if (bce->checkSingletonContext()) {
+        if (bce->checkSingletonContext() || (!bce->isInLoop() && bce->isRunOnceLambda())) {
             bce->emittingRunOnceLambda = true;
             if (!EmitTree(cx, bce, pn2))
                 return false;
             bce->emittingRunOnceLambda = false;
         } else {
             if (!EmitTree(cx, bce, pn2))
                 return false;
         }
--- a/js/src/jit-test/tests/basic/metadata-hook.js
+++ b/js/src/jit-test/tests/basic/metadata-hook.js
@@ -5,17 +5,22 @@ assertEq(getObjectMetadata(x).y, 0);
 
 incallback = false;
 count = 0;
 
 function callback(obj) {
     if (incallback)
 	return null;
     incallback = true;
-    var res = {count:++count, location:Error().stack};
+    var res =
+      {
+        count: ++count,
+        location: Error().stack,
+        message: Error().message // no .message => Error.prototype.message => ""
+      };
     incallback = false;
     return res;
 }
 callback({});
 
 setObjectMetadataCallback(callback);
 
 function Foo() {
@@ -35,8 +40,9 @@ var wc = getObjectMetadata(w).count;
 var xc = getObjectMetadata(x).count;
 var yc = getObjectMetadata(y).count;
 var zc = getObjectMetadata(z).count;
 
 assertEq(xc > wc, true);
 assertEq(yc > xc, true);
 assertEq(zc > yc, true);
 assertEq(/\.js/.test(getObjectMetadata(x).location), true);
+assertEq(getObjectMetadata(x).message, "");
--- a/js/src/jit-test/tests/debug/Object-deleteProperty-error-01.js
+++ b/js/src/jit-test/tests/debug/Object-deleteProperty-error-01.js
@@ -6,11 +6,11 @@ dbg.onDebuggerStatement = function (fram
     try {
         frame.arguments[0].deleteProperty("x");
     } catch (exc) {
         return;
     }
     throw new Error("deleteProperty should throw");
 };
 
-g.eval("function h(x) { debugger; }");
+g.eval("function h(obj) { debugger; }");
 g.eval("h(Proxy.create({delete: function () { throw Error.prototype; }}));");
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-deleteProperty-error-02.js
@@ -0,0 +1,27 @@
+var g = newGlobal();
+var dbg = Debugger(g);
+dbg.onDebuggerStatement = function (frame) {
+    try {
+        frame.arguments[0].deleteProperty("x");
+    } catch (exc) {
+        assertEq(exc instanceof ReferenceError, true);
+        assertEq(exc.message, "diaf");
+        assertEq(exc.fileName, "fail");
+        assertEq(exc.lineNumber, 4);
+
+        // Arrant nonsense?  Sure -- but different from lineNumber is all this
+        // test exists to verify.  If you're the person to make column numbers
+        // actually work, change this accordingly.
+        assertEq(exc.columnNumber, 0);
+        return;
+    }
+    throw new Error("deleteProperty should throw");
+};
+
+g.evaluate("function h(obj) { debugger; } \n" +
+           "h(new Proxy({}, \n" +
+           "            { deleteProperty: function () { \n" +
+           "                var e = new ReferenceError('diaf', 'fail'); \n" +
+           "                throw e; \n" +
+           "              } \n" +
+           "            }));");
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -4976,16 +4976,23 @@ CheckFunction(ModuleCompiler &m, LifoAll
     return true;
 }
 
 static bool
 GenerateCode(ModuleCompiler &m, ModuleCompiler::Func &func, MIRGenerator &mir, LIRGraph &lir)
 {
     int64_t before = PRMJ_Now();
 
+    // A single MacroAssembler is reused for all function compilations so
+    // that there is a single linear code segment for each module. To avoid
+    // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
+    // after each function is compiled. This method is responsible for cleaning
+    // out any dangling pointers that the MacroAssembler may have kept.
+    m.masm().resetForNewCodeGenerator(mir.alloc());
+
     m.masm().bind(func.code());
 
     ScopedJSDeletePtr<CodeGenerator> codegen(jit::GenerateCode(&mir, &lir, &m.masm()));
     if (!codegen)
         return m.fail(nullptr, "internal codegen failure (probably out of memory)");
 
     if (!m.collectAccesses(mir))
         return false;
@@ -5006,23 +5013,16 @@ GenerateCode(ModuleCompiler &m, ModuleCo
         if (!m.trackPerfProfiledBlocks(mir.perfSpewer(), func, m.masm().size()))
             return false;
     } else if (PerfFuncEnabled()) {
         if (!m.trackPerfProfiledFunction(func, m.masm().size()))
             return false;
     }
 #endif
 
-    // A single MacroAssembler is reused for all function compilations so
-    // that there is a single linear code segment for each module. To avoid
-    // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
-    // after each function is compiled. This method is responsible for cleaning
-    // out any dangling pointers that the MacroAssembler may have kept.
-    m.masm().resetForNewCodeGenerator();
-
     // Align internal function headers.
     m.masm().align(CodeAlignment);
 
     func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
     if (!m.maybeReportCompileTime(func))
         return false;
 
     // Unlike regular IonMonkey which links and generates a new IonCode for
@@ -6397,16 +6397,18 @@ static bool
 FinishModule(ModuleCompiler &m,
              ScopedJSDeletePtr<AsmJSModule> *module,
              AsmJSStaticLinkData *linkData)
 {
     LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     TempAllocator alloc(&lifo);
     IonContext ionContext(m.cx(), &alloc);
 
+    m.masm().resetForNewCodeGenerator(alloc);
+
     if (!GenerateStubs(m))
         return false;
 
     return m.extractModule(module, linkData);
 }
 
 static bool
 CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList,
--- a/js/src/jit/AsmJSSignalHandlers.cpp
+++ b/js/src/jit/AsmJSSignalHandlers.cpp
@@ -677,17 +677,17 @@ HandleMachException(JSRuntime *rt, const
 }
 
 // Taken from mach_exc in /usr/include/mach/mach_exc.defs.
 static const mach_msg_id_t sExceptionId = 2405;
 
 // The choice of id here is arbitrary, the only constraint is that sQuitId != sExceptionId.
 static const mach_msg_id_t sQuitId = 42;
 
-void *
+void
 AsmJSMachExceptionHandlerThread(void *threadArg)
 {
     JSRuntime *rt = reinterpret_cast<JSRuntime*>(threadArg);
     mach_port_t port = rt->asmJSMachExceptionHandler.port();
     kern_return_t kret;
 
     while(true) {
         ExceptionRequest request;
@@ -728,18 +728,16 @@ AsmJSMachExceptionHandlerThread(void *th
         reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port;
         reply.Head.msgh_local_port = MACH_PORT_NULL;
         reply.Head.msgh_id = request.body.Head.msgh_id + 100;
         reply.NDR = NDR_record;
         reply.RetCode = replyCode;
         mach_msg(&reply.Head, MACH_SEND_MSG, sizeof(reply), 0, MACH_PORT_NULL,
                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
     }
-
-    return nullptr;
 }
 
 AsmJSMachExceptionHandler::AsmJSMachExceptionHandler()
   : installed_(false),
     thread_(nullptr),
     port_(MACH_PORT_NULL)
 {}
 
@@ -770,17 +768,17 @@ AsmJSMachExceptionHandler::uninstall()
         kern_return_t kret = mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL,
                                       MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
         if (kret != KERN_SUCCESS) {
             fprintf(stderr, "AsmJSMachExceptionHandler: failed to send quit message: %d\n", (int)kret);
             MOZ_CRASH();
         }
 
         // Wait for the handler thread to complete before deallocating the port.
-        pthread_join(thread_, nullptr);
+        PR_JoinThread(thread_);
         thread_ = nullptr;
     }
     if (port_ != MACH_PORT_NULL) {
         DebugOnly<kern_return_t> kret = mach_port_destroy(mach_task_self(), port_);
         JS_ASSERT(kret == KERN_SUCCESS);
         port_ = MACH_PORT_NULL;
     }
 }
@@ -796,17 +794,19 @@ AsmJSMachExceptionHandler::install(JSRun
     kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port_);
     if (kret != KERN_SUCCESS)
         goto error;
     kret = mach_port_insert_right(mach_task_self(), port_, port_, MACH_MSG_TYPE_MAKE_SEND);
     if (kret != KERN_SUCCESS)
         goto error;
 
     // Create a thread to block on reading port_.
-    if (pthread_create(&thread_, nullptr, AsmJSMachExceptionHandlerThread, rt))
+    thread_ = PR_CreateThread(PR_USER_THREAD, AsmJSMachExceptionHandlerThread, rt,
+                              PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+    if (!thread_)
         goto error;
 
     // Direct exceptions on this thread to port_ (and thus our handler thread).
     // Note: we are totally clobbering any existing *thread* exception ports and
     // not even attempting to forward. Breakpad and gdb both use the *process*
     // exception ports which are only called if the thread doesn't handle the
     // exception, so we should be fine.
     thread = mach_thread_self();
--- a/js/src/jit/AsmJSSignalHandlers.h
+++ b/js/src/jit/AsmJSSignalHandlers.h
@@ -6,17 +6,17 @@
 
 #ifndef jit_AsmJSSignalHandlers_h
 #define jit_AsmJSSignalHandlers_h
 
 struct JSRuntime;
 
 #ifdef XP_MACOSX
 # include <mach/mach.h>
-# include <pthread.h>
+# include "jslock.h"
 #endif
 
 namespace js {
 
 // Returns whether signal handlers for asm.js and for JitRuntime access
 // violations have been installed.
 bool
 EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt);
@@ -31,17 +31,17 @@ TriggerOperationCallbackForAsmJSCode(JSR
 // rather require an extra thread. For simplicity, we create one such thread
 // per JSRuntime (upon the first use of asm.js in the JSRuntime). This thread
 // and related resources are owned by AsmJSMachExceptionHandler which is owned
 // by JSRuntime.
 #ifdef XP_MACOSX
 class AsmJSMachExceptionHandler
 {
     bool installed_;
-    pthread_t thread_;
+    PRThread *thread_;
     mach_port_t port_;
 
     void uninstall();
 
   public:
     AsmJSMachExceptionHandler();
     ~AsmJSMachExceptionHandler() { uninstall(); }
     mach_port_t port() const { return port_; }
--- a/js/src/jit/IonAllocPolicy.h
+++ b/js/src/jit/IonAllocPolicy.h
@@ -171,37 +171,34 @@ struct TempObject
     template <class T>
     inline void *operator new(size_t nbytes, T *pos) {
         static_assert(mozilla::IsConvertible<T*, TempObject*>::value,
                       "Placement new argument type must inherit from TempObject");
         return pos;
     }
 };
 
-// Deprecated, don't use for (new) classes. Will be removed when all classes have
-// been converted to placement new/TempObject (bug 937540).
-struct OldTempObject
-  : public TempObject
-{
-    using TempObject::operator new;
-
-    inline void *operator new(size_t nbytes) {
-        return GetIonContext()->temp->allocateInfallible(nbytes);
-    }
-};
-
 template <typename T>
 class TempObjectPool
 {
+    TempAllocator *alloc_;
     InlineForwardList<T> freed_;
 
   public:
+    TempObjectPool()
+      : alloc_(nullptr)
+    {}
+    void setAllocator(TempAllocator &alloc) {
+        JS_ASSERT(freed_.empty());
+        alloc_ = &alloc;
+    }
     T *allocate() {
+        JS_ASSERT(alloc_);
         if (freed_.empty())
-            return new T();
+            return new(*alloc_) T();
         return freed_.popFront();
     }
     void free(T *obj) {
         freed_.pushFront(obj);
     }
     void clear() {
         freed_.clear();
     }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4787,35 +4787,37 @@ IonBuilder::jsop_funcall(uint32_t argc)
 
     // Remove the MPassArg(JSFunction *).
     passFunc->replaceAllUsesWith(passFunc->getArgument());
     passFunc->block()->discard(passFunc);
 
     // Shimmy the slots down to remove the native 'call' function.
     current->shimmySlots(funcDepth - 1);
 
+    bool zeroArguments = (argc == 0);
+
     // If no |this| argument was provided, explicitly pass Undefined.
     // Pushing is safe here, since one stack slot has been removed.
-    if (argc == 0) {
+    if (zeroArguments) {
         MConstant *undef = MConstant::New(alloc(), UndefinedValue());
         current->add(undef);
         MPassArg *pass = MPassArg::New(alloc(), undef);
         current->add(pass);
         current->push(pass);
     } else {
         // |this| becomes implicit in the call.
         argc -= 1;
     }
 
     CallInfo callInfo(alloc(), false);
     if (!callInfo.init(current, argc))
         return false;
 
     // Try to inline the call.
-    if (argc > 0) {
+    if (!zeroArguments) {
         InliningDecision decision = makeInliningDecision(target, callInfo);
         switch (decision) {
           case InliningDecision_Error:
             return false;
           case InliningDecision_DontInline:
             break;
           case InliningDecision_Inline:
             if (target->isInterpreted())
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -188,41 +188,44 @@ class MacroAssembler : public MacroAssem
     // If instrumentation should be emitted, then the sps parameter should be
     // provided, but otherwise it can be safely omitted to prevent all
     // instrumentation from being emitted.
     MacroAssembler()
       : enoughMemory_(true),
         embedsNurseryPointers_(false),
         sps_(nullptr)
     {
-        JSContext *cx = GetIonContext()->cx;
+        IonContext *icx = GetIonContext();
+        JSContext *cx = icx->cx;
         if (cx)
             constructRoot(cx);
 
-        if (!GetIonContext()->temp) {
+        if (!icx->temp) {
             JS_ASSERT(cx);
             alloc_.construct(cx);
         }
 
+        moveResolver_.setAllocator(*icx->temp);
 #ifdef JS_CPU_ARM
         initWithAllocator();
-        m_buffer.id = GetIonContext()->getNextAssemblerId();
+        m_buffer.id = icx->getNextAssemblerId();
 #endif
     }
 
     // This constructor should only be used when there is no IonContext active
     // (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp).
     MacroAssembler(JSContext *cx)
       : enoughMemory_(true),
         embedsNurseryPointers_(false),
         sps_(nullptr)
     {
         constructRoot(cx);
         ionContext_.construct(cx, (js::jit::TempAllocator *)nullptr);
         alloc_.construct(cx);
+        moveResolver_.setAllocator(*ionContext_.ref().temp);
 #ifdef JS_CPU_ARM
         initWithAllocator();
         m_buffer.id = GetIonContext()->getNextAssemblerId();
 #endif
     }
 
     // asm.js compilation handles its own IonContet-pushing
     struct AsmJSToken {};
@@ -232,38 +235,24 @@ class MacroAssembler : public MacroAssem
         sps_(nullptr)
     {
 #ifdef JS_CPU_ARM
         initWithAllocator();
         m_buffer.id = 0;
 #endif
     }
 
-    MacroAssembler(JSContext *cx, IonScript *ion)
-      : enoughMemory_(true),
-        embedsNurseryPointers_(false),
-        sps_(nullptr)
-    {
-        constructRoot(cx);
-         ionContext_.construct(cx, (js::jit::TempAllocator *)nullptr);
-         alloc_.construct(cx);
-#ifdef JS_CPU_ARM
-         initWithAllocator();
-         m_buffer.id = GetIonContext()->getNextAssemblerId();
-#endif
-        setFramePushed(ion->frameSize());
-    }
-
     void setInstrumentation(IonInstrumentation *sps) {
         sps_ = sps;
     }
 
-    void resetForNewCodeGenerator() {
+    void resetForNewCodeGenerator(TempAllocator &alloc) {
         setFramePushed(0);
         moveResolver_.clearTempObjectPool();
+        moveResolver_.setAllocator(alloc);
     }
 
     void constructRoot(JSContext *cx) {
         autoRooter_.construct(cx, this);
     }
 
     MoveResolver &moveResolver() {
         return moveResolver_;
--- a/js/src/jit/MoveResolver.h
+++ b/js/src/jit/MoveResolver.h
@@ -152,17 +152,17 @@ class MoveResolver
         Kind kind() const {
             return kind_;
         }
     };
 
   private:
     struct PendingMove
       : public Move,
-        public OldTempObject,
+        public TempObject,
         public InlineListNode<PendingMove>
     {
         PendingMove()
         { }
         PendingMove(const MoveOperand &from, const MoveOperand &to, Kind kind)
           : Move(from, to, kind, false)
         { }
         
@@ -210,14 +210,17 @@ class MoveResolver
         return orderedMoves_[i];
     }
     bool hasCycles() const {
         return hasCycles_;
     }
     void clearTempObjectPool() {
         movePool_.clear();
     }
+    void setAllocator(TempAllocator &alloc) {
+        movePool_.setAllocator(alloc);
+    }
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MoveResolver_h */
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -3657,23 +3657,34 @@ MacroAssemblerARMCompat::callWithABIPre(
 }
 
 void
 MacroAssemblerARMCompat::callWithABIPost(uint32_t stackAdjust, Result result)
 {
     if (secondScratchReg_ != lr)
         ma_mov(secondScratchReg_, lr);
 
-    if (result == DOUBLE) {
-#ifdef JS_CPU_ARM_HARDFP
-        as_vmov(ReturnFloatReg, d0);
-#else
+    switch (result) {
+      case DOUBLE:
+#ifndef JS_CPU_ARM_HARDFP
         // Move double from r0/r1 to ReturnFloatReg.
         as_vxfer(r0, r1, ReturnFloatReg, CoreToFloat);
+        break;
 #endif
+      case FLOAT:
+#ifndef JS_CPU_ARM_HARDFP
+        // Move float32 from r0 to ReturnFloatReg.
+        as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), CoreToFloat);
+        break;
+#endif
+      case GENERAL:
+        break;
+
+      default:
+        MOZ_ASSUME_UNREACHABLE("unexpected callWithABI result");
     }
 
     freeStack(stackAdjust);
 
     if (dynamicAlignment_) {
         // x86 supports pop esp.  on arm, that isn't well defined, so just
         // do it manually
         as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0)));
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4496,27 +4496,17 @@ JS::Compile(JSContext *cx, HandleObject 
     options.setFileAndLine(filename, 1);
     JSScript *script = Compile(cx, obj, options, file.fp());
     return script;
 }
 
 JS_PUBLIC_API(bool)
 JS::CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options)
 {
-    if (!cx->runtime()->canUseParallelParsing())
-        return false;
-
-    // Off thread compilation can't occur during incremental collections on the
-    // atoms compartment, to avoid triggering barriers. Outside the atoms
-    // compartment, the compilation will use a new zone which doesn't require
-    // barriers itself.
-    if (cx->runtime()->activeGCInAtomsZone())
-        return false;
-
-    return true;
+    return cx->runtime()->canUseParallelParsing();
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileOffThread(JSContext *cx, Handle<JSObject*> obj, const ReadOnlyCompileOptions &options,
                      const jschar *chars, size_t length,
                      OffThreadCompileCallback callback, void *callbackData)
 {
     JS_ASSERT(CanCompileOffThread(cx, options));
@@ -6103,22 +6093,54 @@ JS_DescribeScriptedCaller(JSContext *cx,
     script.set(nullptr);
     if (lineno)
         *lineno = 0;
 
     NonBuiltinScriptFrameIter i(cx);
     if (i.done())
         return false;
 
+    // If the caller is hidden, the embedding wants us to return null here so
+    // that it can check its own stack.
+    if (i.activation()->scriptedCallerIsHidden())
+        return false;
+
     script.set(i.script());
     if (lineno)
         *lineno = js::PCToLineNumber(i.script(), i.pc());
     return true;
 }
 
+namespace JS {
+
+JS_PUBLIC_API(void)
+HideScriptedCaller(JSContext *cx)
+{
+    MOZ_ASSERT(cx);
+
+    // If there's no accessible activation on the stack, we'll return null from
+    // JS_DescribeScriptedCaller anyway, so there's no need to annotate
+    // anything.
+    Activation *act = cx->runtime()->mainThread.activation();
+    if (!act)
+        return;
+    act->hideScriptedCaller();
+}
+
+JS_PUBLIC_API(void)
+UnhideScriptedCaller(JSContext *cx)
+{
+    Activation *act = cx->runtime()->mainThread.activation();
+    if (!act)
+        return;
+    act->unhideScriptedCaller();
+}
+
+} /* namespace JS */
+
 #ifdef JS_THREADSAFE
 static PRStatus
 CallOnce(void *func)
 {
     JSInitCallback init = JS_DATA_TO_FUNC_PTR(JSInitCallback, func);
     return init() ? PR_SUCCESS : PR_FAILURE;
 }
 #endif
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4571,20 +4571,63 @@ JS_CharsToId(JSContext* cx, JS::TwoByteC
  *  Test if the given string is a valid ECMAScript identifier
  */
 extern JS_PUBLIC_API(bool)
 JS_IsIdentifier(JSContext *cx, JS::HandleString str, bool *isIdentifier);
 
 /*
  * Return the current script and line number of the most currently running
  * frame. Returns true if a scripted frame was found, false otherwise.
+ *
+ * If a the embedding has hidden the scripted caller for the topmost activation
+ * record, this will also return false.
  */
 extern JS_PUBLIC_API(bool)
 JS_DescribeScriptedCaller(JSContext *cx, JS::MutableHandleScript script, unsigned *lineno);
 
+namespace JS {
+
+/*
+ * Informs the JS engine that the scripted caller should be hidden. This can be
+ * used by the embedding to maintain an override of the scripted caller in its
+ * calculations, by hiding the scripted caller in the JS engine and pushing data
+ * onto a separate stack, which it inspects when JS_DescribeScriptedCaller
+ * returns null.
+ *
+ * We maintain a counter on each activation record. Add() increments the counter
+ * of the topmost activation, and Remove() decrements it. The count may never
+ * drop below zero, and must always be exactly zero when the activation is
+ * popped from the stack.
+ */
+extern JS_PUBLIC_API(void)
+HideScriptedCaller(JSContext *cx);
+
+extern JS_PUBLIC_API(void)
+UnhideScriptedCaller(JSContext *cx);
+
+class AutoHideScriptedCaller
+{
+  public:
+    AutoHideScriptedCaller(JSContext *cx
+                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mContext(cx)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        HideScriptedCaller(mContext);
+    }
+    ~AutoHideScriptedCaller() {
+        UnhideScriptedCaller(mContext);
+    }
+
+  protected:
+    JSContext *mContext;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+} /* namepsace JS */
 
 /*
  * Encode/Decode interpreted scripts and functions to/from memory.
  */
 
 extern JS_PUBLIC_API(void *)
 JS_EncodeScript(JSContext *cx, JS::HandleScript script, uint32_t *lengthp);
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -35,76 +35,38 @@
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 using mozilla::PodArrayZero;
 using mozilla::PodZero;
 
-/* Forward declarations for ErrorObject::class_'s initializer. */
-static bool
-Exception(JSContext *cx, unsigned argc, Value *vp);
-
-static void
-exn_trace(JSTracer *trc, JSObject *obj);
-
 static void
 exn_finalize(FreeOp *fop, JSObject *obj);
 
-static bool
-exn_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
-            MutableHandleObject objp);
-
 const Class ErrorObject::class_ = {
     js_Error_str,
-    JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
+    JSCLASS_IMPLEMENTS_BARRIERS |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Error) |
+    JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
-    (JSResolveOp)exn_resolve,
+    JS_ResolveStub,
     JS_ConvertStub,
     exn_finalize,
     nullptr,                 /* checkAccess */
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
-    nullptr,                 /* construct   */
-    exn_trace
-};
-
-template <typename T>
-struct JSStackTraceElemImpl
-{
-    T                   funName;
-    const char          *filename;
-    unsigned            ulineno;
+    nullptr                  /* construct   */
 };
 
-typedef JSStackTraceElemImpl<HeapPtrString> JSStackTraceElem;
-typedef JSStackTraceElemImpl<JSString *>    JSStackTraceStackElem;
-
-struct JSExnPrivate
-{
-    /* A copy of the JSErrorReport originally generated. */
-    JSErrorReport       *errorReport;
-    js::HeapPtrString   message;
-    js::HeapPtrString   filename;
-    unsigned            lineno;
-    unsigned            column;
-    size_t              stackDepth;
-    int                 exnType;
-    JSStackTraceElem    stackElems[1];
-};
-
-static JSString *
-StackTraceToString(JSContext *cx, JSExnPrivate *priv);
-
 static JSErrorReport *
 CopyErrorReport(JSContext *cx, JSErrorReport *report)
 {
     /*
      * We use a single malloc block to make a deep copy of JSErrorReport with
      * the following layout:
      *   JSErrorReport
      *   array of copies of report->messageArgs
@@ -237,368 +199,161 @@ struct SuppressErrorsGuard
 
     ~SuppressErrorsGuard()
     {
         JS_RestoreExceptionState(cx, prevState);
         JS_SetErrorReporter(cx, prevReporter);
     }
 };
 
-static void
-SetExnPrivate(ErrorObject &exnObject, JSExnPrivate *priv);
-
-static bool
-InitExnPrivate(JSContext *cx, HandleObject exnObject, HandleString message,
-               HandleString filename, unsigned lineno, unsigned column,
-               JSErrorReport *report, int exnType)
+static JSString *
+ComputeStackString(JSContext *cx)
 {
-    JS_ASSERT(exnObject->is<ErrorObject>());
-    JS_ASSERT(!exnObject->getPrivate());
-
     JSCheckAccessOp checkAccess = cx->runtime()->securityCallbacks->checkObjectAccess;
 
-    Vector<JSStackTraceStackElem> frames(cx);
+    StringBuffer sb(cx);
+
     {
+        RootedAtom atom(cx);
         SuppressErrorsGuard seg(cx);
         for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) {
-
-            /* Ask the crystal CAPS ball whether we can see across compartments. */
+            // Cut off the stack if this callee crosses a trust boundary.
             if (checkAccess && i.isNonEvalFunctionFrame()) {
                 RootedValue v(cx);
                 RootedId callerid(cx, NameToId(cx->names().caller));
                 RootedObject obj(cx, i.callee());
                 if (!checkAccess(cx, obj, callerid, JSACC_READ, &v))
                     break;
             }
 
-            if (!frames.growBy(1))
-                return false;
-            JSStackTraceStackElem &frame = frames.back();
-            if (i.isNonEvalFunctionFrame()) {
-                JSAtom *atom = i.callee()->displayAtom();
-                if (atom == nullptr)
-                    atom = cx->runtime()->emptyString;
-                frame.funName = atom;
-            } else {
-                frame.funName = nullptr;
-            }
+            /* First append the function name, if any. */
+            atom = nullptr;
+            if (i.isNonEvalFunctionFrame() && i.callee()->displayAtom())
+                atom = i.callee()->displayAtom();
+            if (atom && !sb.append(atom))
+                return nullptr;
+
+            /* Next a @ separating function name from source location. */
+            if (!sb.append('@'))
+                return nullptr;
+
+            /* Now the filename. */
             RootedScript script(cx, i.script());
             const char *cfilename = script->filename();
             if (!cfilename)
                 cfilename = "";
-            frame.filename = cfilename;
-            frame.ulineno = PCToLineNumber(script, i.pc());
+            if (!sb.appendInflated(cfilename, strlen(cfilename)))
+                return nullptr;
+
+            /* Finally, : followed by the line number and a newline. */
+            uint32_t line = PCToLineNumber(script, i.pc());
+            if (!sb.append(':') || !NumberValueToStringBuffer(cx, NumberValue(line), sb) ||
+                !sb.append('\n'))
+            {
+                return nullptr;
+            }
+
+            /*
+             * Cut off the stack if it gets too deep (most commonly for
+             * infinite recursion errors).
+             */
+            const size_t MaxReportedStackDepth = 1u << 20;
+            if (sb.length() > MaxReportedStackDepth)
+                break;
         }
     }
 
-    /* Do not need overflow check: the vm stack is already bigger. */
-    JS_STATIC_ASSERT(sizeof(JSStackTraceElem) <= sizeof(StackFrame));
-
-    size_t nbytes = offsetof(JSExnPrivate, stackElems) +
-                    frames.length() * sizeof(JSStackTraceElem);
-
-    JSExnPrivate *priv = (JSExnPrivate *)cx->malloc_(nbytes);
-    if (!priv)
-        return false;
-
-    /* Initialize to zero so that write barriers don't witness undefined values. */
-    memset(priv, 0, nbytes);
-
-    if (report) {
-        /*
-         * Construct a new copy of the error report struct. We can't use the
-         * error report struct that was passed in, because it's allocated on
-         * the stack, and also because it may point to transient data in the
-         * TokenStream.
-         */
-        priv->errorReport = CopyErrorReport(cx, report);
-        if (!priv->errorReport) {
-            js_free(priv);
-            return false;
-        }
-    } else {
-        priv->errorReport = nullptr;
-    }
-
-    priv->message.init(message);
-    priv->filename.init(filename);
-    priv->lineno = lineno;
-    priv->column = column;
-    priv->stackDepth = frames.length();
-    priv->exnType = exnType;
-    for (size_t i = 0; i < frames.length(); ++i) {
-        priv->stackElems[i].funName.init(frames[i].funName);
-        priv->stackElems[i].filename = JS_strdup(cx, frames[i].filename);
-        if (!priv->stackElems[i].filename)
-            return false;
-        priv->stackElems[i].ulineno = frames[i].ulineno;
-    }
-
-    SetExnPrivate(exnObject->as<ErrorObject>(), priv);
-    return true;
-}
-
-static void
-exn_trace(JSTracer *trc, JSObject *obj)
-{
-    if (JSExnPrivate *priv = obj->as<ErrorObject>().getExnPrivate()) {
-        if (priv->message)
-            MarkString(trc, &priv->message, "exception message");
-        if (priv->filename)
-            MarkString(trc, &priv->filename, "exception filename");
-
-        for (size_t i = 0; i != priv->stackDepth; ++i) {
-            JSStackTraceElem &elem = priv->stackElems[i];
-            if (elem.funName)
-                MarkString(trc, &elem.funName, "stack trace function name");
-        }
-    }
-}
-
-/* NB: An error object's private must be set through this function. */
-static void
-SetExnPrivate(ErrorObject &exnObject, JSExnPrivate *priv)
-{
-    JS_ASSERT(!exnObject.getExnPrivate());
-    if (JSErrorReport *report = priv->errorReport) {
-        if (JSPrincipals *prin = report->originPrincipals)
-            JS_HoldPrincipals(prin);
-    }
-    exnObject.setPrivate(priv);
+    return sb.finishString();
 }
 
 static void
 exn_finalize(FreeOp *fop, JSObject *obj)
 {
-    if (JSExnPrivate *priv = obj->as<ErrorObject>().getExnPrivate()) {
-        if (JSErrorReport *report = priv->errorReport) {
-            /* HOLD called by SetExnPrivate. */
-            if (JSPrincipals *prin = report->originPrincipals)
-                JS_DropPrincipals(fop->runtime(), prin);
-            fop->free_(report);
-        }
-        for (size_t i = 0; i < priv->stackDepth; i++)
-            js_free(const_cast<char *>(priv->stackElems[i].filename));
-        fop->free_(priv);
+    if (JSErrorReport *report = obj->as<ErrorObject>().getErrorReport()) {
+        /* These were held by ErrorObject::init. */
+        if (JSPrincipals *prin = report->originPrincipals)
+            JS_DropPrincipals(fop->runtime(), prin);
+        fop->free_(report);
     }
 }
 
-static bool
-exn_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
-            MutableHandleObject objp)
-{
-    JSExnPrivate *priv;
-    const char *prop;
-    jsval v;
-    unsigned attrs;
-
-    objp.set(nullptr);
-    priv = obj->as<ErrorObject>().getExnPrivate();
-    if (priv && JSID_IS_ATOM(id)) {
-        RootedString str(cx, JSID_TO_STRING(id));
-
-        RootedAtom atom(cx, cx->names().message);
-        if (str == atom) {
-            prop = js_message_str;
-
-            /*
-             * Per ES5 15.11.1.1, if Error is called with no argument or with
-             * undefined as the argument, it returns an Error object with no
-             * own message property.
-             */
-            if (!priv->message)
-                return true;
-
-            v = STRING_TO_JSVAL(priv->message);
-            attrs = 0;
-            goto define;
-        }
-
-        atom = cx->names().fileName;
-        if (str == atom) {
-            prop = js_fileName_str;
-            v = STRING_TO_JSVAL(priv->filename);
-            attrs = JSPROP_ENUMERATE;
-            goto define;
-        }
-
-        atom = cx->names().lineNumber;
-        if (str == atom) {
-            prop = js_lineNumber_str;
-            v = UINT_TO_JSVAL(priv->lineno);
-            attrs = JSPROP_ENUMERATE;
-            goto define;
-        }
-
-        atom = cx->names().columnNumber;
-        if (str == atom) {
-            prop = js_columnNumber_str;
-            v = UINT_TO_JSVAL(priv->column);
-            attrs = JSPROP_ENUMERATE;
-            goto define;
-        }
-
-        atom = cx->names().stack;
-        if (str == atom) {
-            JSString *stack = StackTraceToString(cx, priv);
-            if (!stack)
-                return false;
-
-            prop = js_stack_str;
-            v = STRING_TO_JSVAL(stack);
-            attrs = JSPROP_ENUMERATE;
-            goto define;
-        }
-    }
-    return true;
-
-  define:
-    if (!JS_DefineProperty(cx, obj, prop, v, nullptr, nullptr, attrs))
-        return false;
-    objp.set(obj);
-    return true;
-}
-
 JSErrorReport *
 js_ErrorFromException(jsval exn)
 {
     if (JSVAL_IS_PRIMITIVE(exn))
         return nullptr;
 
     // It's ok to UncheckedUnwrap here, since all we do is get the
     // JSErrorReport, and consumers are careful with the information they get
     // from that anyway.  Anyone doing things that would expose anything in the
     // JSErrorReport to page script either does a security check on the
     // JSErrorReport's principal or also tries to do toString on our object and
     // will fail if they can't unwrap it.
     JSObject *obj = UncheckedUnwrap(JSVAL_TO_OBJECT(exn));
     if (!obj->is<ErrorObject>())
         return nullptr;
 
-    JSExnPrivate *priv = obj->as<ErrorObject>().getExnPrivate();
-    if (!priv)
-        return nullptr;
-
-    return priv->errorReport;
-}
-
-static JSString *
-StackTraceToString(JSContext *cx, JSExnPrivate *priv)
-{
-    StringBuffer sb(cx);
-
-    JSStackTraceElem *element = priv->stackElems, *end = element + priv->stackDepth;
-    for (; element < end; element++) {
-        /* Try to reserve required space upfront, so we don't fail inbetween. */
-        size_t length = ((element->funName ? element->funName->length() : 0) +
-                         (element->filename ? strlen(element->filename) * 2 : 0) +
-                         13); /* "@" + ":" + "4294967295" + "\n" */
-
-        if (!sb.reserve(length) || sb.length() > JS_BIT(20))
-            break; /* Return as much as we got. */
-
-        if (element->funName) {
-            if (!sb.append(element->funName))
-                return nullptr;
-        }
-        if (!sb.append('@'))
-            return nullptr;
-        if (element->filename) {
-            if (!sb.appendInflated(element->filename, strlen(element->filename)))
-                return nullptr;
-        }
-        if (!sb.append(':') || !NumberValueToStringBuffer(cx, NumberValue(element->ulineno), sb) || 
-            !sb.append('\n'))
-        {
-            return nullptr;
-        }
-    }
-
-    return sb.finishString();
-}
-
-/* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
-         with these two functions. */
-static JSString *
-FilenameToString(JSContext *cx, const char *filename)
-{
-    return JS_NewStringCopyZ(cx, filename);
+    return obj->as<ErrorObject>().getErrorReport();
 }
 
 static bool
-Exception(JSContext *cx, unsigned argc, Value *vp)
+Error(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    /*
-     * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
-     * called as functions, without operator new.  But as we do not give
-     * each constructor a distinct JSClass, whose .name member is used by
-     * NewNativeClassInstance to find the class prototype, we must get the
-     * class prototype ourselves.
-     */
-    RootedObject callee(cx, &args.callee());
-    RootedValue protov(cx);
-    if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
-        return false;
-
-    if (!protov.isObject()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE, "Error");
-        return false;
-    }
-
-    RootedObject obj(cx, NewObjectWithGivenProto(cx, &ErrorObject::class_, &protov.toObject(),
-                     nullptr));
-    if (!obj)
-        return false;
-
-    /* Set the 'message' property. */
-    RootedString message(cx);
+    /* Compute the error message, if any. */
+    RootedString message(cx, nullptr);
     if (args.hasDefined(0)) {
         message = ToString<CanGC>(cx, args[0]);
         if (!message)
             return false;
-        args[0].setString(message);
-    } else {
-        message = nullptr;
     }
 
     /* Find the scripted caller. */
     NonBuiltinScriptFrameIter iter(cx);
 
     /* Set the 'fileName' property. */
     RootedScript script(cx, iter.done() ? nullptr : iter.script());
-    RootedString filename(cx);
+    RootedString fileName(cx);
     if (args.length() > 1) {
-        filename = ToString<CanGC>(cx, args[1]);
-        if (!filename)
-            return false;
-        args[1].setString(filename);
+        fileName = ToString<CanGC>(cx, args[1]);
     } else {
-        filename = cx->runtime()->emptyString;
+        fileName = cx->runtime()->emptyString;
         if (!iter.done()) {
-            if (const char *cfilename = script->filename()) {
-                filename = FilenameToString(cx, cfilename);
-                if (!filename)
-                    return false;
-            }
+            if (const char *cfilename = script->filename())
+                fileName = JS_NewStringCopyZ(cx, cfilename);
         }
     }
+    if (!fileName)
+        return false;
 
     /* Set the 'lineNumber' property. */
-    uint32_t lineno, column = 0;
+    uint32_t lineNumber, columnNumber = 0;
     if (args.length() > 2) {
-        if (!ToUint32(cx, args[2], &lineno))
+        if (!ToUint32(cx, args[2], &lineNumber))
             return false;
     } else {
-        lineno = iter.done() ? 0 : PCToLineNumber(script, iter.pc(), &column);
+        lineNumber = iter.done() ? 0 : PCToLineNumber(script, iter.pc(), &columnNumber);
     }
 
-    int exnType = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();
-    if (!InitExnPrivate(cx, obj, message, filename, lineno, column, nullptr, exnType))
+    Rooted<JSString*> stack(cx, ComputeStackString(cx));
+    if (!stack)
+        return false;
+
+    /*
+     * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
+     * called as functions, without operator new.  But as we do not give
+     * each constructor a distinct JSClass, we must get the exception type
+     * ourselves.
+     */
+    JSExnType exnType = JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
+
+    RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName,
+                                             lineNumber, columnNumber, nullptr, message));
+    if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
 /* ES5 15.11.4.4 (NB: with subsequent errata). */
 static bool
@@ -769,90 +524,81 @@ JS_STATIC_ASSERT(JSEXN_ERR == 0);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR  == JSProto_InternalError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR      == JSProto_EvalError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR     == JSProto_RangeError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR    == JSProto_SyntaxError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR      == JSProto_TypeError);
 JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR       == JSProto_URIError);
 
-static JSObject *
-InitErrorClass(JSContext *cx, Handle<GlobalObject*> global, int type, HandleObject proto)
+/* static */ ErrorObject *
+ErrorObject::createProto(JSContext *cx, JS::Handle<GlobalObject*> global, JSExnType type,
+                         JS::HandleObject proto)
 {
-    JSProtoKey key = GetExceptionProtoKey(type);
-    RootedAtom name(cx, ClassName(key, cx));
-    RootedObject errorProto(cx, global->createBlankPrototypeInheriting(cx, &ErrorObject::class_,
-                            *proto));
+    RootedObject errorProto(cx);
+    errorProto = global->createBlankPrototypeInheriting(cx, &ErrorObject::class_, *proto);
     if (!errorProto)
         return nullptr;
 
+    Rooted<ErrorObject*> err(cx, &errorProto->as<ErrorObject>());
+    RootedString emptyStr(cx, cx->names().empty);
+    if (!ErrorObject::init(cx, err, type, nullptr, emptyStr, emptyStr, 0, 0, emptyStr))
+        return nullptr;
+
+    // The various prototypes also have .name in addition to the normal error
+    // instance properties.
+    JSProtoKey key = GetExceptionProtoKey(type);
+    RootedPropertyName name(cx, ClassName(key, cx));
     RootedValue nameValue(cx, StringValue(name));
-    RootedValue zeroValue(cx, Int32Value(0));
-    RootedValue empty(cx, StringValue(cx->runtime()->emptyString));
-    RootedId nameId(cx, NameToId(cx->names().name));
-    RootedId messageId(cx, NameToId(cx->names().message));
-    RootedId fileNameId(cx, NameToId(cx->names().fileName));
-    RootedId lineNumberId(cx, NameToId(cx->names().lineNumber));
-    RootedId columnNumberId(cx, NameToId(cx->names().columnNumber));
-    if (!DefineNativeProperty(cx, errorProto, nameId, nameValue,
-                              JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
-        !DefineNativeProperty(cx, errorProto, messageId, empty,
-                              JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
-        !DefineNativeProperty(cx, errorProto, fileNameId, empty,
-                              JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
-        !DefineNativeProperty(cx, errorProto, lineNumberId, zeroValue,
-                              JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
-        !DefineNativeProperty(cx, errorProto, columnNumberId, zeroValue,
-                              JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0))
+    if (!JSObject::defineProperty(cx, err, cx->names().name, nameValue,
+                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
     {
         return nullptr;
     }
 
-    /* Create the corresponding constructor. */
-    RootedFunction ctor(cx, global->createConstructor(cx, Exception, name, 1,
+    // Create the corresponding constructor.
+    RootedFunction ctor(cx, global->createConstructor(cx, Error, name, 1,
                                                       JSFunction::ExtendedFinalizeKind));
     if (!ctor)
         return nullptr;
     ctor->setExtendedSlot(0, Int32Value(int32_t(type)));
 
-    if (!LinkConstructorAndPrototype(cx, ctor, errorProto))
+    if (!LinkConstructorAndPrototype(cx, ctor, err))
         return nullptr;
 
-    if (!DefineConstructorAndPrototype(cx, global, key, ctor, errorProto))
+    if (!DefineConstructorAndPrototype(cx, global, key, ctor, err))
         return nullptr;
 
-    JS_ASSERT(!errorProto->getPrivate());
-
-    return errorProto;
+    return err;
 }
 
 JSObject *
 js_InitExceptionClasses(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->is<GlobalObject>());
     JS_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
-    RootedObject objectProto(cx, global->getOrCreateObjectPrototype(cx));
-    if (!objectProto)
+    RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+    if (!objProto)
         return nullptr;
 
     /* Initialize the base Error class first. */
-    RootedObject errorProto(cx, InitErrorClass(cx, global, JSEXN_ERR, objectProto));
+    RootedObject errorProto(cx, ErrorObject::createProto(cx, global, JSEXN_ERR, objProto));
     if (!errorProto)
         return nullptr;
 
     /* |Error.prototype| alone has method properties. */
     if (!DefinePropertiesAndBrand(cx, errorProto, nullptr, exception_methods))
         return nullptr;
 
     /* Define all remaining *Error constructors. */
     for (int i = JSEXN_ERR + 1; i < JSEXN_LIMIT; i++) {
-        if (!InitErrorClass(cx, global, i, errorProto))
+        if (!ErrorObject::createProto(cx, global, JSExnType(i), errorProto))
             return nullptr;
     }
 
     return errorProto;
 }
 
 const JSErrorFormatString*
 js_GetLocalizedErrorMessage(ExclusiveContext *cx, void *userRef, const char *locale,
@@ -883,111 +629,80 @@ js::GetErrorTypeName(JSRuntime* rt, int1
      * JSEXN_INTERNALERR returns null to prevent that "InternalError: "
      * is prepended before "uncaught exception: "
      */
     if (exnType <= JSEXN_NONE || exnType >= JSEXN_LIMIT ||
         exnType == JSEXN_INTERNALERR)
     {
         return nullptr;
     }
-    JSProtoKey key = GetExceptionProtoKey(exnType);
+    JSProtoKey key = GetExceptionProtoKey(JSExnType(exnType));
     return ClassName(key, rt)->chars();
 }
 
-#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
-/* For use below... get character strings for error name and exception name */
-static const struct exnname { char *name; char *exception; } errortoexnname[] = {
-#define MSG_DEF(name, number, count, exception, format) \
-    {#name, #exception},
-#include "js.msg"
-#undef MSG_DEF
-};
-#endif /* DEBUG */
-
 bool
 js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
                     JSErrorCallback callback, void *userRef)
 {
-    JSErrNum errorNumber;
-    const JSErrorFormatString *errorString;
-    JSExnType exn;
-    jsval tv[4];
-
     /*
      * Tell our caller to report immediately if this report is just a warning.
      */
     JS_ASSERT(reportp);
     if (JSREPORT_IS_WARNING(reportp->flags))
         return false;
 
     /* Find the exception index associated with this error. */
-    errorNumber = (JSErrNum) reportp->errorNumber;
+    JSErrNum errorNumber = static_cast<JSErrNum>(reportp->errorNumber);
+    const JSErrorFormatString *errorString;
     if (!callback || callback == js_GetErrorMessage)
         errorString = js_GetLocalizedErrorMessage(cx, nullptr, nullptr, errorNumber);
     else
         errorString = callback(userRef, nullptr, errorNumber);
-    exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
-    JS_ASSERT(exn < JSEXN_LIMIT);
-
-#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
-    /* Print the error name and the associated exception name to stderr */
-    fprintf(stderr, "%s\t%s\n",
-            errortoexnname[errorNumber].name,
-            errortoexnname[errorNumber].exception);
-#endif
+    JSExnType exnType = errorString ? static_cast<JSExnType>(errorString->exnType) : JSEXN_NONE;
+    MOZ_ASSERT(exnType < JSEXN_LIMIT);
 
     /*
      * Return false (no exception raised) if no exception is associated
      * with the given error number.
      */
-    if (exn == JSEXN_NONE)
+    if (exnType == JSEXN_NONE)
         return false;
 
     /* Prevent infinite recursion. */
     if (cx->generatingError)
         return false;
     AutoScopedAssign<bool> asa(&cx->generatingError, true);
 
-    /* Protect the newly-created strings below from nesting GCs. */
-    PodArrayZero(tv);
-    AutoArrayRooter tvr(cx, ArrayLength(tv), tv);
-
-    /*
-     * Try to get an appropriate prototype by looking up the corresponding
-     * exception constructor name in the scope chain of the current context's
-     * top stack frame, or in the global object if no frame is active.
-     */
-    RootedObject errProto(cx);
-    if (!js_GetClassPrototype(cx, GetExceptionProtoKey(exn), &errProto))
-        return false;
-    tv[0] = OBJECT_TO_JSVAL(errProto);
-
-    RootedObject errObject(cx, NewObjectWithGivenProto(cx, &ErrorObject::class_,
-                                                       errProto, nullptr));
-    if (!errObject)
-        return false;
-    tv[1] = OBJECT_TO_JSVAL(errObject);
-
     RootedString messageStr(cx, reportp->ucmessage ? JS_NewUCStringCopyZ(cx, reportp->ucmessage)
                                                    : JS_NewStringCopyZ(cx, message));
     if (!messageStr)
         return false;
-    tv[2] = STRING_TO_JSVAL(messageStr);
+
+    RootedString fileName(cx, JS_NewStringCopyZ(cx, reportp->filename));
+    if (!fileName)
+        return false;
 
-    RootedString filenameStr(cx, JS_NewStringCopyZ(cx, reportp->filename));
-    if (!filenameStr)
+    uint32_t lineNumber = reportp->lineno;
+    uint32_t columnNumber = reportp->column;
+
+    RootedString stack(cx, ComputeStackString(cx));
+    if (!stack)
         return false;
-    tv[3] = STRING_TO_JSVAL(filenameStr);
 
-    if (!InitExnPrivate(cx, errObject, messageStr, filenameStr,
-                        reportp->lineno, reportp->column, reportp, exn)) {
+    js::ScopedJSFreePtr<JSErrorReport> report(CopyErrorReport(cx, reportp));
+    if (!report)
         return false;
-    }
 
-    RootedValue errValue(cx, OBJECT_TO_JSVAL(errObject));
+    RootedObject errObject(cx, ErrorObject::create(cx, exnType, stack, fileName,
+                                                   lineNumber, columnNumber, &report,
+                                                   messageStr));
+    if (!errObject)
+        return false;
+
+    RootedValue errValue(cx, ObjectValue(*errObject));
     JS_SetPendingException(cx, errValue);
 
     /* Flag the error report passed in to indicate an exception was raised. */
     reportp->flags |= JSREPORT_EXCEPTION;
     return true;
 }
 
 static bool
@@ -1010,18 +725,16 @@ IsDuckTypedErrorObject(JSContext *cx, Ha
 
     *filename_strp = filename_str;
     return true;
 }
 
 bool
 js_ReportUncaughtException(JSContext *cx)
 {
-    JSErrorReport *reportp, report;
-
     if (!cx->isExceptionPending())
         return true;
 
     RootedValue exn(cx, cx->getPendingException());
     AutoValueVector roots(cx);
     roots.resize(6);
 
     /*
@@ -1034,23 +747,25 @@ js_ReportUncaughtException(JSContext *cx
     if (JSVAL_IS_PRIMITIVE(exn)) {
         exnObject = nullptr;
     } else {
         exnObject = JSVAL_TO_OBJECT(exn);
         roots[0] = exn;
     }
 
     JS_ClearPendingException(cx);
-    reportp = js_ErrorFromException(exn);
+    JSErrorReport *reportp = js_ErrorFromException(exn);
 
     /* XXX L10N angels cry once again. see also everywhere else */
     RootedString str(cx, ToString<CanGC>(cx, exn));
     if (str)
         roots[1] = StringValue(str);
 
+    JSErrorReport report;
+
     const char *filename_str = js_fileName_str;
     JSAutoByteString filename;
     if (!reportp && exnObject &&
         (exnObject->is<ErrorObject>() || IsDuckTypedErrorObject(cx, exnObject, &filename_str)))
     {
         RootedString name(cx);
         if (JS_GetProperty(cx, exnObject, js_name_str, roots.handleAt(2)) && roots[2].isString())
             name = roots[2].toString();
@@ -1125,55 +840,37 @@ js_ReportUncaughtException(JSContext *cx
         JS_SetPendingException(cx, exn);
         js_ReportErrorAgain(cx, bytes, reportp);
     }
 
     JS_ClearPendingException(cx);
     return true;
 }
 
-extern JSObject *
-js_CopyErrorObject(JSContext *cx, HandleObject errobj, HandleObject scope)
+JSObject *
+js_CopyErrorObject(JSContext *cx, Handle<ErrorObject*> err, HandleObject scope)
 {
     assertSameCompartment(cx, scope);
-    JSExnPrivate *priv = errobj->as<ErrorObject>().getExnPrivate();
 
-    size_t size = offsetof(JSExnPrivate, stackElems) +
-                  priv->stackDepth * sizeof(JSStackTraceElem);
-
-    ScopedJSFreePtr<JSExnPrivate> copy(static_cast<JSExnPrivate *>(cx->malloc_(size)));
-    if (!copy)
-        return nullptr;
-
-    if (priv->errorReport) {
-        copy->errorReport = CopyErrorReport(cx, priv->errorReport);
-        if (!copy->errorReport)
+    js::ScopedJSFreePtr<JSErrorReport> copyReport;
+    if (JSErrorReport *errorReport = err->getErrorReport()) {
+        copyReport = CopyErrorReport(cx, errorReport);
+        if (!copyReport)
             return nullptr;
-    } else {
-        copy->errorReport = nullptr;
     }
-    ScopedJSFreePtr<JSErrorReport> autoFreeErrorReport(copy->errorReport);
 
-    copy->message.init(priv->message);
-    if (!cx->compartment()->wrap(cx, &copy->message))
+    RootedString message(cx, err->getMessage());
+    if (message && !cx->compartment()->wrap(cx, message.address()))
         return nullptr;
-    JS::Anchor<JSString *> messageAnchor(copy->message);
-    copy->filename.init(priv->filename);
-    if (!cx->compartment()->wrap(cx, &copy->filename))
+    RootedString fileName(cx, err->fileName());
+    if (!cx->compartment()->wrap(cx, fileName.address()))
         return nullptr;
-    JS::Anchor<JSString *> filenameAnchor(copy->filename);
-    copy->lineno = priv->lineno;
-    copy->column = priv->column;
-    copy->stackDepth = 0;
-    copy->exnType = priv->exnType;
+    RootedString stack(cx, err->stack());
+    if (!cx->compartment()->wrap(cx, stack.address()))
+        return nullptr;
+    uint32_t lineNumber = err->lineNumber();
+    uint32_t columnNumber = err->columnNumber();
+    JSExnType errorType = err->type();
 
     // Create the Error object.
-    RootedObject proto(cx, scope->global().getOrCreateCustomErrorPrototype(cx, copy->exnType));
-    if (!proto)
-        return nullptr;
-    RootedObject copyobj(cx, NewObjectWithGivenProto(cx, &ErrorObject::class_, proto, nullptr));
-    if (!copyobj)
-        return nullptr;
-    SetExnPrivate(copyobj->as<ErrorObject>(), copy);
-    copy.forget();
-    autoFreeErrorReport.forget();
-    return copyobj;
+    return ErrorObject::create(cx, errorType, stack, fileName,
+                               lineNumber, columnNumber, &copyReport, message);
 }
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -9,21 +9,19 @@
  */
 
 #ifndef jsexn_h
 #define jsexn_h
 
 #include "jsapi.h"
 #include "NamespaceImports.h"
 
-/*
- * Initialize the exception constructor/prototype hierarchy.
- */
-extern JSObject *
-js_InitExceptionClasses(JSContext *cx, js::HandleObject obj);
+namespace js {
+class ErrorObject;
+}
 
 /*
  * Given a JSErrorReport, check to see if there is an exception associated with
  * the error number.  If there is, then create an appropriate exception object,
  * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the
  * error report.  Exception-aware host error reporters should probably ignore
  * error reports so flagged.  Returns true if an associated exception is
  * found and set, false otherwise.
@@ -62,19 +60,19 @@ js_GetLocalizedErrorMessage(js::Exclusiv
  * Make a copy of errobj parented to scope.
  *
  * cx must be in the same compartment as scope. errobj may be in a different
  * compartment, but it must be an Error object (not a wrapper of one) and it
  * must not be one of the prototype objects created by js_InitExceptionClasses
  * (errobj->getPrivate() must not be nullptr).
  */
 extern JSObject *
-js_CopyErrorObject(JSContext *cx, js::HandleObject errobj, js::HandleObject scope);
+js_CopyErrorObject(JSContext *cx, JS::Handle<js::ErrorObject*> errobj, js::HandleObject scope);
 
 static inline JSProtoKey
-GetExceptionProtoKey(int exn)
+GetExceptionProtoKey(JSExnType exn)
 {
     JS_ASSERT(JSEXN_ERR <= exn);
     JS_ASSERT(exn < JSEXN_LIMIT);
-    return JSProtoKey(JSProto_Error + exn);
+    return JSProtoKey(JSProto_Error + int(exn));
 }
 
 #endif /* jsexn_h */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4941,16 +4941,22 @@ Collect(JSRuntime *rt, bool incremental,
         /*
          * If we reset an existing GC, we need to start a new one. Also, we
          * repeat GCs that happen during shutdown (the gcShouldCleanUpEverything
          * case) until we can be sure that no additional garbage is created
          * (which typically happens if roots are dropped during finalizers).
          */
         repeat = (rt->gcPoke && rt->gcShouldCleanUpEverything) || wasReset;
     } while (repeat);
+
+    if (rt->gcIncrementalState == NO_INCREMENTAL) {
+#ifdef JS_WORKER_THREADS
+        EnqueuePendingParseTasksAfterGC(rt);
+#endif
+    }
 }
 
 void
 js::GC(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason reason)
 {
     Collect(rt, false, SliceBudget::Unlimited, gckind, reason);
 }
 
@@ -5401,47 +5407,52 @@ js::PurgeJITCaches(Zone *zone)
         JSScript *script = i.get<JSScript>();
 
         /* Discard Ion caches. */
         jit::PurgeCaches(script, zone);
     }
 #endif
 }
 
+void
+ArenaLists::normalizeBackgroundFinalizeState(AllocKind thingKind)
+{
+    volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind];
+    switch (*bfs) {
+      case BFS_DONE:
+        break;
+      case BFS_JUST_FINISHED:
+        // No allocations between end of last sweep and now.
+        // Transfering over arenas is a kind of allocation.
+        *bfs = BFS_DONE;
+        break;
+      default:
+        JS_ASSERT(!"Background finalization in progress, but it should not be.");
+        break;
+    }
+}
 
 void
 ArenaLists::adoptArenas(JSRuntime *rt, ArenaLists *fromArenaLists)
 {
     // The other parallel threads have all completed now, and GC
     // should be inactive, but still take the lock as a kind of read
     // fence.
     AutoLockGC lock(rt);
 
     fromArenaLists->purge();
 
     for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
 #ifdef JS_THREADSAFE
         // When we enter a parallel section, we join the background
         // thread, and we do not run GC while in the parallel section,
         // so no finalizer should be active!
-        volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind];
-        switch (*bfs) {
-          case BFS_DONE:
-            break;
-          case BFS_JUST_FINISHED:
-            // No allocations between end of last sweep and now.
-            // Transfering over arenas is a kind of allocation.
-            *bfs = BFS_DONE;
-            break;
-          default:
-            JS_ASSERT(!"Background finalization in progress, but it should not be.");
-            break;
-        }
-#endif /* JS_THREADSAFE */
-
+        normalizeBackgroundFinalizeState(AllocKind(thingKind));
+        fromArenaLists->normalizeBackgroundFinalizeState(AllocKind(thingKind));
+#endif
         ArenaList *fromList = &fromArenaLists->arenaLists[thingKind];
         ArenaList *toList = &arenaLists[thingKind];
         while (fromList->head != nullptr) {
             ArenaHeader *fromHeader = fromList->head;
             fromList->head = fromHeader->next;
             fromHeader->next = nullptr;
 
             toList->insert(fromHeader);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -611,16 +611,18 @@ class ArenaLists
   private:
     inline void finalizeNow(FreeOp *fop, AllocKind thingKind);
     inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind);
     inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind);
 
     void *allocateFromArena(JS::Zone *zone, AllocKind thingKind);
     inline void *allocateFromArenaInline(JS::Zone *zone, AllocKind thingKind);
 
+    inline void normalizeBackgroundFinalizeState(AllocKind thingKind);
+
     friend class js::Nursery;
 };
 
 /*
  * Initial allocation size for data structures holding chunks is set to hold
  * chunks with total capacity of 16MB to avoid buffer resizes during browser
  * startup.
  */
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3843,16 +3843,23 @@ ExclusiveContext::getNewType(const Class
             AddTypeProperty(this, type, "ignoreCase", types::Type::BooleanType());
             AddTypeProperty(this, type, "multiline", types::Type::BooleanType());
             AddTypeProperty(this, type, "sticky", types::Type::BooleanType());
             AddTypeProperty(this, type, "lastIndex", types::Type::Int32Type());
         }
 
         if (obj->is<StringObject>())
             AddTypeProperty(this, type, "length", Type::Int32Type());
+
+        if (obj->is<ErrorObject>()) {
+            AddTypeProperty(this, type, "fileName", types::Type::StringType());
+            AddTypeProperty(this, type, "lineNumber", types::Type::Int32Type());
+            AddTypeProperty(this, type, "columnNumber", types::Type::Int32Type());
+            AddTypeProperty(this, type, "stack", types::Type::StringType());
+        }
     }
 
     /*
      * The new type is not present in any type sets, so mark the object as
      * unknown in all type sets it appears in. This allows the prototype of
      * such objects to mutate freely without triggering an expensive walk of
      * the compartment's type sets. (While scripts normally don't mutate
      * __proto__, the browser will for proxies and such, and we need to
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1465,28 +1465,28 @@ js::NumberValueToStringBuffer(JSContext 
     /*
      * Inflate to jschar string.  The input C-string characters are < 127, so
      * even if jschars are UTF-8, all chars should map to one jschar.
      */
     JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
     return sb.appendInflated(cstr, cstrlen);
 }
 
-static void
+static bool
 CharsToNumber(ThreadSafeContext *cx, const jschar *chars, size_t length, double *result)
 {
     if (length == 1) {
         jschar c = chars[0];
         if ('0' <= c && c <= '9')
             *result = c - '0';
         else if (unicode::IsSpace(c))
             *result = 0.0;
         else
             *result = GenericNaN();
-        return;
+        return true;
     }
 
     const jschar *end = chars + length;
     const jschar *bp = SkipSpace(chars, end);
 
     /* ECMA doesn't allow signed hex numbers (bug 273467). */
     if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) {
         /*
@@ -1499,42 +1499,49 @@ CharsToNumber(ThreadSafeContext *cx, con
         if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) ||
             endptr == bp + 2 ||
             SkipSpace(endptr, end) != end)
         {
             *result = GenericNaN();
         } else {
             *result = d;
         }
-        return;
+        return true;
     }
 
     /*
      * Note that ECMA doesn't treat a string beginning with a '0' as
      * an octal number here. This works because all such numbers will
      * be interpreted as decimal by js_strtod.  Also, any hex numbers
      * that have made it here (which can only be negative ones) will
      * be treated as 0 without consuming the 'x' by js_strtod.
      */
     const jschar *ep;
     double d;
-    if (!js_strtod(cx, bp, end, &ep, &d) || SkipSpace(ep, end) != end)
+    if (!js_strtod(cx, bp, end, &ep, &d)) {
+        *result = GenericNaN();
+        return false;
+    }
+
+    if (SkipSpace(ep, end) != end)
         *result = GenericNaN();
     else
         *result = d;
+
+    return true;
 }
 
 bool
 js::StringToNumber(ThreadSafeContext *cx, JSString *str, double *result)
 {
     ScopedThreadSafeStringInspector inspector(str);
     if (!inspector.ensureChars(cx))
         return false;
-    CharsToNumber(cx, inspector.chars(), str->length(), result);
-    return true;
+
+    return CharsToNumber(cx, inspector.chars(), str->length(), result);
 }
 
 bool
 js::NonObjectToNumberSlow(ThreadSafeContext *cx, Value v, double *out)
 {
     JS_ASSERT(!v.isNumber());
     JS_ASSERT(!v.isObject());
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3796,23 +3796,23 @@ static const JSFunctionSpec string_stati
     // This must be at the end because of bug 853075: functions listed after
     // self-hosted methods aren't available in self-hosted code.
 #if EXPOSE_INTL_API
     JS_SELF_HOSTED_FN("localeCompare", "String_static_localeCompare", 2,0),
 #endif
     JS_FS_END
 };
 
-Shape *
-StringObject::assignInitialShape(JSContext *cx)
+/* static */ Shape *
+StringObject::assignInitialShape(ExclusiveContext *cx, Handle<StringObject*> obj)
 {
-    JS_ASSERT(nativeEmpty());
-
-    return addDataProperty(cx, cx->names().length, LENGTH_SLOT,
-                           JSPROP_PERMANENT | JSPROP_READONLY);
+    JS_ASSERT(obj->nativeEmpty());
+
+    return obj->addDataProperty(cx, cx->names().length, LENGTH_SLOT,
+                                JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 JSObject *
 js_InitStringClass(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -171,41 +171,51 @@ js::CancelOffThreadIonCompile(JSCompartm
 static const JSClass workerGlobalClass = {
     "internal-worker-global", JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,  JS_DeletePropertyStub,
     JS_PropertyStub,  JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub,
     JS_ConvertStub,   nullptr
 };
 
-ParseTask::ParseTask(ExclusiveContext *cx, JSContext *initCx,
+ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
                      const jschar *chars, size_t length, JSObject *scopeChain,
                      JS::OffThreadCompileCallback callback, void *callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(scopeChain),
-    callback(callback), callbackData(callbackData), script(nullptr),
-    errors(cx), overRecursed(false)
+    exclusiveContextGlobal(exclusiveContextGlobal), callback(callback),
+    callbackData(callbackData), script(nullptr), errors(cx), overRecursed(false)
 {
     JSRuntime *rt = scopeChain->runtimeFromMainThread();
 
     if (!AddObjectRoot(rt, &this->scopeChain, "ParseTask::scopeChain"))
         MOZ_CRASH();
+    if (!AddObjectRoot(rt, &this->exclusiveContextGlobal, "ParseTask::exclusiveContextGlobal"))
+        MOZ_CRASH();
 }
 
 bool
 ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
 {
     return this->options.copy(cx, options);
 }
 
+void
+ParseTask::activate(JSRuntime *rt)
+{
+    rt->setUsedByExclusiveThread(exclusiveContextGlobal->zone());
+    cx->enterCompartment(exclusiveContextGlobal->compartment());
+}
+
 ParseTask::~ParseTask()
 {
     JSRuntime *rt = scopeChain->runtimeFromMainThread();
 
     JS_RemoveObjectRootRT(rt, &scopeChain);
+    JS_RemoveObjectRootRT(rt, &exclusiveContextGlobal);
 
     // ParseTask takes over ownership of its input exclusive context.
     js_delete(cx);
 
     for (size_t i = 0; i < errors.length(); i++)
         js_delete(errors[i]);
 }
 
@@ -252,46 +262,83 @@ js::StartOffThreadParseScript(JSContext 
             !js_GetClassObject(cx, global, JSProto_Array, &obj) ||
             !js_GetClassObject(cx, global, JSProto_RegExp, &obj) ||
             !js_GetClassObject(cx, global, JSProto_GeneratorFunction, &obj))
         {
             return false;
         }
     }
 
-    cx->runtime()->setUsedByExclusiveThread(global->zone());
-
     ScopedJSDeletePtr<ExclusiveContext> workercx(
         cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData *) nullptr,
                                    ThreadSafeContext::Context_Exclusive));
     if (!workercx)
         return false;
 
-    workercx->enterCompartment(global->compartment());
-
     ScopedJSDeletePtr<ParseTask> task(
-        cx->new_<ParseTask>(workercx.get(), cx, chars, length,
+        cx->new_<ParseTask>(workercx.get(), global, cx, chars, length,
                             scopeChain, callback, callbackData));
-    if (!task || !task->init(cx, options))
+    if (!task)
         return false;
 
     workercx.forget();
 
+    if (!task->init(cx, options))
+        return false;
+
     WorkerThreadState &state = *cx->runtime()->workerThreadState;
     JS_ASSERT(state.numThreads);
 
+    // Off thread parsing can't occur during incremental collections on the
+    // atoms compartment, to avoid triggering barriers. (Outside the atoms
+    // compartment, the compilation will use a new zone which doesn't require
+    // barriers itself.) If an atoms-zone GC is in progress, hold off on
+    // executing the parse task until the atoms-zone GC completes (see
+    // EnqueuePendingParseTasksAfterGC).
+    if (cx->runtime()->activeGCInAtomsZone()) {
+        if (!state.parseWaitingOnGC.append(task.get()))
+            return false;
+    } else {
+        task->activate(cx->runtime());
+
+        AutoLockWorkerThreadState lock(state);
+
+        if (!state.parseWorklist.append(task.get()))
+            return false;
+
+        state.notifyAll(WorkerThreadState::PRODUCER);
+    }
+
+    task.forget();
+
+    return true;
+}
+
+void
+js::EnqueuePendingParseTasksAfterGC(JSRuntime *rt)
+{
+    JS_ASSERT(!rt->activeGCInAtomsZone());
+
+    if (!rt->workerThreadState || rt->workerThreadState->parseWaitingOnGC.empty())
+        return;
+
+    // This logic should mirror the contents of the !activeGCInAtomsZone()
+    // branch in StartOffThreadParseScript:
+
+    WorkerThreadState &state = *rt->workerThreadState;
+
+    for (size_t i = 0; i < state.parseWaitingOnGC.length(); i++)
+        state.parseWaitingOnGC[i]->activate(rt);
+
     AutoLockWorkerThreadState lock(state);
 
-    if (!state.parseWorklist.append(task.get()))
-        return false;
-
-    task.forget();
+    JS_ASSERT(state.parseWorklist.empty());
+    state.parseWorklist.swap(state.parseWaitingOnGC);
 
     state.notifyAll(WorkerThreadState::PRODUCER);
-    return true;
 }
 
 void
 js::WaitForOffThreadParsingToFinish(JSRuntime *rt)
 {
     if (!rt->workerThreadState)
         return;
 
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -66,16 +66,19 @@ class WorkerThreadState
      * For now, only allow a single parallel asm.js compilation to happen at a
      * time. This avoids race conditions on asmJSWorklist/asmJSFinishedList/etc.
      */
     mozilla::Atomic<uint32_t> asmJSCompilationInProgress;
 
     /* Shared worklist for parsing/emitting scripts on worker threads. */
     Vector<ParseTask*, 0, SystemAllocPolicy> parseWorklist, parseFinishedList;
 
+    /* Main-thread-only list of parse tasks waiting for an atoms-zone GC to complete. */
+    Vector<ParseTask*, 0, SystemAllocPolicy> parseWaitingOnGC;
+
     /* Worklist for source compression worker threads. */
     Vector<SourceCompressionTask *, 0, SystemAllocPolicy> compressionWorklist;
 
     WorkerThreadState(JSRuntime *rt) {
         mozilla::PodZero(this);
         runtime = rt;
     }
     ~WorkerThreadState();
@@ -225,16 +228,23 @@ CancelOffThreadIonCompile(JSCompartment 
  * Start a parse/emit cycle for a stream of source. The characters must stay
  * alive until the compilation finishes.
  */
 bool
 StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
                           const jschar *chars, size_t length, HandleObject scopeChain,
                           JS::OffThreadCompileCallback callback, void *callbackData);
 
+/*
+ * Called at the end of GC to enqueue any Parse tasks that were waiting on an
+ * atoms-zone GC to finish.
+ */
+void
+EnqueuePendingParseTasksAfterGC(JSRuntime *rt);
+
 /* Block until in progress and pending off thread parse jobs have finished. */
 void
 WaitForOffThreadParsingToFinish(JSRuntime *rt);
 
 /* Start a compression job for the specified token. */
 bool
 StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task);
 
@@ -324,35 +334,40 @@ struct ParseTask
     size_t length;
     LifoAlloc alloc;
 
     // Rooted pointer to the scope in the target compartment which the
     // resulting script will be merged into. This is not safe to use off the
     // main thread.
     JSObject *scopeChain;
 
+    // Rooted pointer to the global object used by 'cx'.
+    JSObject *exclusiveContextGlobal;
+
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void *callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
     JSScript *script;
 
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<frontend::CompileError *> errors;
     bool overRecursed;
 
-    ParseTask(ExclusiveContext *cx, JSContext *initCx,
+    ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
               const jschar *chars, size_t length, JSObject *scopeChain,
               JS::OffThreadCompileCallback callback, void *callbackData);
     bool init(JSContext *cx, const ReadOnlyCompileOptions &options);
 
+    void activate(JSRuntime *rt);
+
     ~ParseTask();
 };
 
 // Compression tasks are allocated on the stack by their triggering thread,
 // which will block on the compression completing as the task goes out of scope
 // to ensure it completes at the required time.
 struct SourceCompressionTask
 {
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -144,22 +144,20 @@ js::TransparentObjectWrapper(JSContext *
     return Wrapper::New(cx, obj, wrappedProto, parent, &CrossCompartmentWrapper::singleton);
 }
 
 ErrorCopier::~ErrorCopier()
 {
     JSContext *cx = ac.ref().context()->asJSContext();
     if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) {
         RootedValue exc(cx, cx->getPendingException());
-        if (exc.isObject() && exc.toObject().is<ErrorObject>() &&
-            exc.toObject().as<ErrorObject>().getExnPrivate())
-        {
+        if (exc.isObject() && exc.toObject().is<ErrorObject>()) {
             cx->clearPendingException();
             ac.destroy();
-            Rooted<JSObject*> errObj(cx, &exc.toObject());
+            Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
             JSObject *copyobj = js_CopyErrorObject(cx, errObj, scope);
             if (copyobj)
                 cx->setPendingException(ObjectValue(*copyobj));
         }
     }
 }
 
 /* Cross compartment wrappers. */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -155,16 +155,17 @@ UNIFIED_SOURCES += [
     'jswrapper.cpp',
     'perf/jsperf.cpp',
     'prmjtime.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
+    'vm/ErrorObject.cpp',
     'vm/ForkJoin.cpp',
     'vm/GlobalObject.cpp',
     'vm/Id.cpp',
     'vm/Interpreter.cpp',
     'vm/MemoryMetrics.cpp',
     'vm/Monitor.cpp',
     'vm/ObjectImpl.cpp',
     'vm/OldDebugAPI.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/vm/ErrorObject.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/ErrorObject.h"
+
+#include "vm/GlobalObject.h"
+
+#include "jsobjinlines.h"
+
+#include "vm/Shape-inl.h"
+
+using namespace js;
+
+/* static */ Shape *
+js::ErrorObject::assignInitialShape(ExclusiveContext *cx, Handle<ErrorObject*> obj)
+{
+    MOZ_ASSERT(obj->nativeEmpty());
+
+    if (!obj->addDataProperty(cx, cx->names().fileName, FILENAME_SLOT, 0))
+        return nullptr;
+    if (!obj->addDataProperty(cx, cx->names().lineNumber, LINENUMBER_SLOT, 0))
+        return nullptr;
+    if (!obj->addDataProperty(cx, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0))
+        return nullptr;
+    return obj->addDataProperty(cx, cx->names().stack, STACK_SLOT, 0);
+}
+
+/* static */ bool
+js::ErrorObject::init(JSContext *cx, Handle<ErrorObject*> obj, JSExnType type,
+                      ScopedJSFreePtr<JSErrorReport> *errorReport, HandleString fileName,
+                      HandleString stack, uint32_t lineNumber, uint32_t columnNumber,
+                      HandleString message)
+{
+    // Null out early in case of error, for exn_finalize's sake.
+    obj->initReservedSlot(ERROR_REPORT_SLOT, PrivateValue(nullptr));
+
+    if (!EmptyShape::ensureInitialCustomShape<ErrorObject>(cx, obj))
+        return false;
+
+    // The .message property isn't part of the initial shape because it's
+    // present in some error objects -- |Error.prototype|, |new Error("f")|,
+    // |new Error("")| -- but not in others -- |new Error(undefined)|,
+    // |new Error()|.
+    RootedShape messageShape(cx);
+    if (message) {
+        messageShape = obj->addDataProperty(cx, cx->names().message, MESSAGE_SLOT, 0);
+        if (!messageShape)
+            return false;
+        MOZ_ASSERT(messageShape->slot() == MESSAGE_SLOT);
+    }
+
+    MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().fileName))->slot() == FILENAME_SLOT);
+    MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().lineNumber))->slot() == LINENUMBER_SLOT);
+    MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().columnNumber))->slot() ==
+               COLUMNNUMBER_SLOT);
+    MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().stack))->slot() == STACK_SLOT);
+    MOZ_ASSERT_IF(message,
+                  obj->nativeLookupPure(NameToId(cx->names().message))->slot() == MESSAGE_SLOT);
+
+    MOZ_ASSERT(JSEXN_ERR <= type && type < JSEXN_LIMIT);
+
+    JSErrorReport *report = errorReport ? errorReport->forget() : nullptr;
+    obj->initReservedSlot(EXNTYPE_SLOT, Int32Value(type));
+    obj->setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(report));
+    obj->initReservedSlot(FILENAME_SLOT, StringValue(fileName));
+    obj->initReservedSlot(LINENUMBER_SLOT, Int32Value(lineNumber));
+    obj->initReservedSlot(COLUMNNUMBER_SLOT, Int32Value(columnNumber));
+    obj->initReservedSlot(STACK_SLOT, StringValue(stack));
+    if (message)
+        obj->nativeSetSlotWithType(cx, messageShape, StringValue(message));
+
+    if (report && report->originPrincipals)
+        JS_HoldPrincipals(report->originPrincipals);
+
+    return true;
+}
+
+/* static */ ErrorObject *
+js::ErrorObject::create(JSContext *cx, JSExnType errorType, HandleString stack,
+                        HandleString fileName, uint32_t lineNumber, uint32_t columnNumber,
+                        ScopedJSFreePtr<JSErrorReport> *report, HandleString message)
+{
+    Rooted<JSObject*> proto(cx, cx->global()->getOrCreateCustomErrorPrototype(cx, errorType));
+    if (!proto)
+        return nullptr;
+
+    Rooted<ErrorObject*> errObject(cx);
+    {
+        JSObject* obj = NewObjectWithGivenProto(cx, &ErrorObject::class_, proto, nullptr);
+        if (!obj)
+            return nullptr;
+        errObject = &obj->as<ErrorObject>();
+    }
+
+    if (!ErrorObject::init(cx, errObject, errorType, report, fileName, stack,
+                           lineNumber, columnNumber, message))
+    {
+        return nullptr;
+    }
+
+    return errObject;
+}
--- a/js/src/vm/ErrorObject.h
+++ b/js/src/vm/ErrorObject.h
@@ -4,23 +4,105 @@
  * 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 vm_ErrorObject_h_
 #define vm_ErrorObject_h_
 
 #include "jsobj.h"
 
+#include "vm/Shape.h"
+
 struct JSExnPrivate;
 
+/*
+ * Initialize the exception constructor/prototype hierarchy.
+ */
+extern JSObject *
+js_InitExceptionClasses(JSContext *cx, JS::HandleObject obj);
+
 namespace js {
 
 class ErrorObject : public JSObject
 {
+    static ErrorObject *
+    createProto(JSContext *cx, JS::Handle<GlobalObject*> global, JSExnType type,
+                JS::HandleObject proto);
+
+    /* For access to createProto. */
+    friend JSObject *
+    ::js_InitExceptionClasses(JSContext *cx, JS::HandleObject global);
+
+    /* For access to assignInitialShape. */
+    friend bool
+    EmptyShape::ensureInitialCustomShape<ErrorObject>(ExclusiveContext *cx,
+                                                      Handle<ErrorObject*> obj);
+
+    /*
+     * Assign the initial error shape to the empty object.  (This shape does
+     * *not* include .message, which must be added separately if needed; see
+     * ErrorObject::init.)
+     */
+    static Shape *
+    assignInitialShape(ExclusiveContext *cx, Handle<ErrorObject*> obj);
+
+    static bool
+    init(JSContext *cx, Handle<ErrorObject*> obj, JSExnType type,
+         ScopedJSFreePtr<JSErrorReport> *errorReport, HandleString fileName, HandleString stack,
+         uint32_t lineNumber, uint32_t columnNumber, HandleString message);
+
+  protected:
+    static const uint32_t EXNTYPE_SLOT      = 0;
+    static const uint32_t ERROR_REPORT_SLOT = EXNTYPE_SLOT + 1;
+    static const uint32_t FILENAME_SLOT     = ERROR_REPORT_SLOT + 1;
+    static const uint32_t LINENUMBER_SLOT   = FILENAME_SLOT + 1;
+    static const uint32_t COLUMNNUMBER_SLOT = LINENUMBER_SLOT + 1;
+    static const uint32_t STACK_SLOT        = COLUMNNUMBER_SLOT + 1;
+    static const uint32_t MESSAGE_SLOT      = STACK_SLOT + 1;
+
+    static const uint32_t RESERVED_SLOTS = MESSAGE_SLOT + 1;
+
   public:
     static const Class class_;
 
-    JSExnPrivate *getExnPrivate() { return static_cast<JSExnPrivate*>(getPrivate()); }
+    // Create an error of the given type corresponding to the provided location
+    // info.  If |message| is non-null, then the error will have a .message
+    // property with that value; otherwise the error will have no .message
+    // property.
+    static ErrorObject *
+    create(JSContext *cx, JSExnType type, HandleString stack, HandleString fileName,
+           uint32_t lineNumber, uint32_t columnNumber, ScopedJSFreePtr<JSErrorReport> *report,
+           HandleString message);
+
+    JSExnType type() const {
+        return JSExnType(getReservedSlot(EXNTYPE_SLOT).toInt32());
+    }
+
+    JSErrorReport * getErrorReport() const {
+        void *priv = getReservedSlot(ERROR_REPORT_SLOT).toPrivate();
+        return static_cast<JSErrorReport*>(priv);
+    }
+
+    JSString * fileName() const {
+        return getReservedSlot(FILENAME_SLOT).toString();
+    }
+
+    uint32_t lineNumber() const {
+        return getReservedSlot(LINENUMBER_SLOT).toInt32();
+    }
+
+    uint32_t columnNumber() const {
+        return getReservedSlot(COLUMNNUMBER_SLOT).toInt32();
+    }
+
+    JSString * stack() const {
+        return getReservedSlot(STACK_SLOT).toString();
+    }
+
+    JSString * getMessage() const {
+        HeapSlot &slot = const_cast<ErrorObject*>(this)->getReservedSlotRef(MESSAGE_SLOT);
+        return slot.isString() ? slot.toString() : nullptr;
+    }
 };
 
 } // namespace js
 
 #endif // vm_ErrorObject_h_
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -555,17 +555,17 @@ GlobalObject::createConstructor(JSContex
 
 static JSObject *
 CreateBlankProto(JSContext *cx, const Class *clasp, JSObject &proto, GlobalObject &global)
 {
     JS_ASSERT(clasp != &JSObject::class_);
     JS_ASSERT(clasp != &JSFunction::class_);
 
     RootedObject blankProto(cx, NewObjectWithGivenProto(cx, clasp, &proto, &global, SingletonObject));
-    if (!blankProto)
+    if (!blankProto || !blankProto->setDelegate(cx))
         return nullptr;
 
     return blankProto;
 }
 
 JSObject *
 GlobalObject::createBlankPrototype(JSContext *cx, const Class *clasp)
 {
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -10,16 +10,17 @@
 #include "jsarray.h"
 #include "jsbool.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsnum.h"
 
 #include "builtin/RegExp.h"
 #include "js/Vector.h"
+#include "vm/ErrorObject.h"
 
 extern JSObject *
 js_InitObjectClass(JSContext *cx, js::HandleObject obj);
 
 extern JSObject *
 js_InitFunctionClass(JSContext *cx, js::HandleObject obj);
 
 extern JSObject *
@@ -392,17 +393,17 @@ class GlobalObject : public JSObject
         if (arrayBufferClassInitialized())
             return &getPrototype(JSProto_ArrayBuffer).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitTypedArrayClasses(cx, self))
             return nullptr;
         return &self->getPrototype(JSProto_ArrayBuffer).toObject();
     }
 
-    JSObject *getOrCreateCustomErrorPrototype(JSContext *cx, int exnType) {
+    JSObject *getOrCreateCustomErrorPrototype(JSContext *cx, JSExnType exnType) {
         JSProtoKey key = GetExceptionProtoKey(exnType);
         if (errorClassesInitialized())
             return &getPrototype(key).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitExceptionClasses(cx, self))
             return nullptr;
         return &self->getPrototype(key).toObject();
     }
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -12,16 +12,18 @@
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringBuffer.h"
 #include "vm/Xdr.h"
 #include "yarr/YarrSyntaxChecker.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/Shape-inl.h"
+
 using namespace js;
 using js::frontend::TokenStream;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
 JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
 JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
 
@@ -268,32 +270,29 @@ RegExpObject::createShared(ExclusiveCont
     if (!cx->compartment()->regExps.get(cx, getSource(), getFlags(), g))
         return false;
 
     self->setShared(cx, **g);
     return true;
 }
 
 Shape *
-RegExpObject::assignInitialShape(ExclusiveContext *cx)
+RegExpObject::assignInitialShape(ExclusiveContext *cx, Handle<RegExpObject*> self)
 {
-    JS_ASSERT(is<RegExpObject>());
-    JS_ASSERT(nativeEmpty());
+    JS_ASSERT(self->nativeEmpty());
 
     JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
     JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1);
     JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1);
     JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1);
     JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1);
     JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1);
 
-    RootedObject self(cx, this);
-
     /* The lastIndex property alone is writable but non-configurable. */
-    if (!addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT))
+    if (!self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT))
         return nullptr;
 
     /* Remaining instance properties are non-writable and non-configurable. */
     unsigned attrs = JSPROP_PERMANENT | JSPROP_READONLY;
     if (!self->addDataProperty(cx, cx->names().source, SOURCE_SLOT, attrs))
         return nullptr;
     if (!self->addDataProperty(cx, cx->names().global, GLOBAL_FLAG_SLOT, attrs))
         return nullptr;
@@ -304,29 +303,18 @@ RegExpObject::assignInitialShape(Exclusi
     return self->addDataProperty(cx, cx->names().sticky, STICKY_FLAG_SLOT, attrs);
 }
 
 bool
 RegExpObject::init(ExclusiveContext *cx, HandleAtom source, RegExpFlag flags)
 {
     Rooted<RegExpObject *> self(cx, this);
 
-    if (nativeEmpty()) {
-        if (isDelegate()) {
-            if (!assignInitialShape(cx))
-                return false;
-        } else {
-            RootedShape shape(cx, assignInitialShape(cx));
-            if (!shape)
-                return false;
-            RootedObject proto(cx, self->getProto());
-            EmptyShape::insertInitialShape(cx, shape, proto);
-        }
-        JS_ASSERT(!self->nativeEmpty());
-    }
+    if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, self))
+        return false;
 
     JS_ASSERT(self->nativeLookup(cx, NameToId(cx->names().lastIndex))->slot() ==
               LAST_INDEX_SLOT);
     JS_ASSERT(self->nativeLookup(cx, NameToId(cx->names().source))->slot() ==
               SOURCE_SLOT);
     JS_ASSERT(self->nativeLookup(cx, NameToId(cx->names().global))->slot() ==
               GLOBAL_FLAG_SLOT);
     JS_ASSERT(self->nativeLookup(cx, NameToId(cx->names().ignoreCase))->slot() ==
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -10,16 +10,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 #include "jsproxy.h"
 
 #include "gc/Marking.h"
 #include "gc/Zone.h"
+#include "vm/Shape.h"
 #if ENABLE_YARR_JIT
 #include "yarr/YarrJIT.h"
 #else
 #include "yarr/YarrInterpreter.h"
 #endif
 
 /*
  * JavaScript Regular Expressions
@@ -433,22 +434,28 @@ class RegExpObject : public JSObject
     void setShared(ExclusiveContext *cx, RegExpShared &shared) {
         shared.prepareForUse(cx);
         JSObject::setPrivate(&shared);
     }
 
   private:
     friend class RegExpObjectBuilder;
 
+    /* For access to assignInitialShape. */
+    friend bool
+    EmptyShape::ensureInitialCustomShape<RegExpObject>(ExclusiveContext *cx,
+                                                       Handle<RegExpObject*> obj);
+
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
-     * changing this regular expression object's last property to it.
+     * changing |obj|'s last property to it.
      */
-    Shape *assignInitialShape(ExclusiveContext *cx);
+    static Shape *
+    assignInitialShape(ExclusiveContext *cx, Handle<RegExpObject*> obj);
 
     bool init(ExclusiveContext *cx, HandleAtom source, RegExpFlag flags);
 
     /*
      * Precondition: the syntax for |source| has already been validated.
      * Side effect: sets the private field.
      */
     bool createShared(ExclusiveContext *cx, RegExpGuard *g);
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_Shape_inl_h
 #define vm_Shape_inl_h
 
 #include "vm/Shape.h"
 
+#include "mozilla/TypeTraits.h"
+
 #include "jsobj.h"
 
 #include "vm/Interpreter.h"
 #include "vm/ScopeObject.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
@@ -173,16 +175,49 @@ Shape::search(ExclusiveContext *cx, Shap
     for (Shape *shape = start; shape; shape = shape->parent) {
         if (shape->propidRef() == id)
             return shape;
     }
 
     return nullptr;
 }
 
+template<class ObjectSubclass>
+/* static */ inline bool
+EmptyShape::ensureInitialCustomShape(ExclusiveContext *cx, Handle<ObjectSubclass*> obj)
+{
+    static_assert(mozilla::IsBaseOf<JSObject, ObjectSubclass>::value,
+                  "ObjectSubclass must be a subclass of JSObject");
+
+    // If the provided object has a non-empty shape, it was given the cached
+    // initial shape when created: nothing to do.
+    if (!obj->nativeEmpty())
+        return true;
+
+    // If no initial shape was assigned, do so.
+    RootedShape shape(cx, ObjectSubclass::assignInitialShape(cx, obj));
+    if (!shape)
+        return false;
+    MOZ_ASSERT(!obj->nativeEmpty());
+
+    // If the object is a standard prototype -- |RegExp.prototype|,
+    // |String.prototype|, |RangeError.prototype|, &c. -- GlobalObject.cpp's
+    // |CreateBlankProto| marked it as a delegate.  These are the only objects
+    // of this class that won't use the standard prototype, and there's no
+    // reason to pollute the initial shape cache with entries for them.
+    if (obj->isDelegate())
+        return true;
+
+    // Cache the initial shape for non-prototype objects, however, so that
+    // future instances will begin life with that shape.
+    RootedObject proto(cx, obj->getProto());
+    EmptyShape::insertInitialShape(cx, shape, proto);
+    return true;
+}
+
 inline
 AutoRooterGetterSetter::Inner::Inner(ThreadSafeContext *cx, uint8_t attrs,
                                      PropertyOp *pgetter_, StrictPropertyOp *psetter_)
   : CustomAutoRooter(cx), attrs(attrs),
     pgetter(pgetter_), psetter(psetter_),
     getterRoot(cx, pgetter_), setterRoot(cx, psetter_)
 {
     JS_ASSERT_IF(attrs & JSPROP_GETTER, !IsPoisonedPtr(*pgetter));
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1377,16 +1377,29 @@ struct EmptyShape : public js::Shape
                                   JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0);
 
     /*
      * Reinsert an alternate initial shape, to be returned by future
      * getInitialShape calls, until the new shape becomes unreachable in a GC
      * and the table entry is purged.
      */
     static void insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto);
+
+    /*
+     * Some object subclasses are allocated with a built-in set of properties.
+     * The first time such an object is created, these built-in properties must
+     * be set manually, to compute an initial shape.  Afterward, that initial
+     * shape can be reused for newly-created objects that use the subclass's
+     * standard prototype.  This method should be used in a post-allocation
+     * init method, to ensure that objects of such subclasses compute and cache
+     * the initial shape, if it hasn't already been computed.
+     */
+    template<class ObjectSubclass>
+    static inline bool
+    ensureInitialCustomShape(ExclusiveContext *cx, Handle<ObjectSubclass*> obj);
 };
 
 /*
  * Entries for the per-compartment initialShapes set indexing initial shapes
  * for objects in the compartment and the associated types.
  */
 struct InitialShapeEntry
 {
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -828,24 +828,26 @@ AbstractFramePtr::popWith(JSContext *cx)
         MOZ_ASSUME_UNREACHABLE("Invalid frame");
 }
 
 Activation::Activation(JSContext *cx, Kind kind)
   : cx_(cx),
     compartment_(cx->compartment()),
     prev_(cx->mainThread().activation_),
     savedFrameChain_(0),
+    hideScriptedCallerCount_(0),
     kind_(kind)
 {
     cx->mainThread().activation_ = this;
 }
 
 Activation::~Activation()
 {
     JS_ASSERT(cx_->mainThread().activation_ == this);
+    JS_ASSERT(hideScriptedCallerCount_ == 0);
     cx_->mainThread().activation_ = prev_;
 }
 
 InterpreterActivation::InterpreterActivation(RunState &state, JSContext *cx, StackFrame *entryFrame)
   : Activation(cx, Interpreter),
     state_(state),
     entryFrame_(entryFrame),
     opMask_(0)
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1162,16 +1162,23 @@ class Activation
     Activation *prev_;
 
     // Counter incremented by JS_SaveFrameChain on the top-most activation and
     // decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop
     // iterating when it reaches this activation (if GO_THROUGH_SAVED is not
     // set).
     size_t savedFrameChain_;
 
+    // Counter incremented by JS::HideScriptedCaller and decremented by
+    // JS::UnhideScriptedCaller. If > 0 for the top activation,
+    // JS_DescribeScriptedCaller will return null instead of querying that
+    // activation, which should prompt the caller to consult embedding-specific
+    // data structures instead.
+    size_t hideScriptedCallerCount_;
+
     enum Kind { Interpreter, Jit, ForkJoin };
     Kind kind_;
 
     inline Activation(JSContext *cx, Kind kind_);
     inline ~Activation();
 
   public:
     JSContext *cx() const {
@@ -1213,16 +1220,27 @@ class Activation
     void restoreFrameChain() {
         JS_ASSERT(savedFrameChain_ > 0);
         savedFrameChain_--;
     }
     bool hasSavedFrameChain() const {
         return savedFrameChain_ > 0;
     }
 
+    void hideScriptedCaller() {
+        hideScriptedCallerCount_++;
+    }
+    void unhideScriptedCaller() {
+        JS_ASSERT(hideScriptedCallerCount_ > 0);
+        hideScriptedCallerCount_--;
+    }
+    bool scriptedCallerIsHidden() const {
+        return hideScriptedCallerCount_ > 0;
+    }
+
   private:
     Activation(const Activation &other) MOZ_DELETE;
     void operator=(const Activation &other) MOZ_DELETE;
 };
 
 // This variable holds a special opcode value which is greater than all normal
 // opcodes, and is chosen such that the bitwise or of this value with any
 // opcode is this value.
--- a/js/src/vm/StringObject-inl.h
+++ b/js/src/vm/StringObject-inl.h
@@ -6,37 +6,29 @@
 
 #ifndef vm_StringObject_inl_h
 #define vm_StringObject_inl_h
 
 #include "vm/StringObject.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/Shape-inl.h"
+
 namespace js {
 
 inline bool
 StringObject::init(JSContext *cx, HandleString str)
 {
     JS_ASSERT(numFixedSlots() == 2);
 
     Rooted<StringObject *> self(cx, this);
 
-    if (nativeEmpty()) {
-        if (isDelegate()) {
-            if (!assignInitialShape(cx))
-                return false;
-        } else {
-            RootedShape shape(cx, assignInitialShape(cx));
-            if (!shape)
-                return false;
-            RootedObject proto(cx, self->getProto());
-            EmptyShape::insertInitialShape(cx, shape, proto);
-        }
-    }
+    if (!EmptyShape::ensureInitialCustomShape<StringObject>(cx, self))
+        return false;
 
     JS_ASSERT(self->nativeLookup(cx, NameToId(cx->names().length))->slot() == LENGTH_SLOT);
 
     self->setStringThis(str);
 
     return true;
 }
 
--- a/js/src/vm/StringObject.h
+++ b/js/src/vm/StringObject.h
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_StringObject_h
 #define vm_StringObject_h
 
 #include "jsobj.h"
 #include "jsstr.h"
 
+#include "vm/Shape.h"
+
 namespace js {
 
 class StringObject : public JSObject
 {
     static const unsigned PRIMITIVE_VALUE_SLOT = 0;
     static const unsigned LENGTH_SLOT = 1;
 
   public:
@@ -52,19 +54,25 @@ class StringObject : public JSObject
         setFixedSlot(PRIMITIVE_VALUE_SLOT, StringValue(str));
         setFixedSlot(LENGTH_SLOT, Int32Value(int32_t(str->length())));
     }
 
     /* For access to init, as String.prototype is special. */
     friend JSObject *
     ::js_InitStringClass(JSContext *cx, js::HandleObject global);
 
+    /* For access to assignInitialShape. */
+    friend bool
+    EmptyShape::ensureInitialCustomShape<StringObject>(ExclusiveContext *cx,
+                                                       Handle<StringObject*> obj);
+
     /*
      * Compute the initial shape to associate with fresh String objects, which
      * encodes the initial length property. Return the shape after changing
-     * this String object's last property to it.
+     * |obj|'s last property to it.
      */
-    Shape *assignInitialShape(JSContext *cx);
+    static Shape *
+    assignInitialShape(ExclusiveContext *cx, Handle<StringObject*> obj);
 };
 
 } // namespace js
 
 #endif /* vm_StringObject_h */
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -115,17 +115,17 @@ interface nsIXPCComponents_utils_Sandbox
 interface ScheduledGCCallback : nsISupports
 {
     void callback();
 };
 
 /**
 * interface of Components.utils
 */
-[scriptable, uuid(ef621cac-c818-464a-9fb1-9a35731a7f32)]
+[scriptable, uuid(e14f588b-63aa-4091-be82-a459a52f8ca6)]
 interface nsIXPCComponents_Utils : nsISupports
 {
 
     /* reportError is designed to be called from JavaScript only.
      *
      * It will report a JS Error object to the JS console, and return. It
      * is meant for use in exception handler blocks which want to "eat"
      * an exception, but still want to report it to the console.
@@ -503,16 +503,27 @@ interface nsIXPCComponents_Utils : nsISu
 
     /**
      * Get a DOM classinfo for the given classname.  Only some class
      * names are supported.
      */
     nsIClassInfo getDOMClassInfo(in AString aClassName);
 
     /**
+     * Gets the incument global for the execution of this function. For internal
+     * and testing use only.
+     *
+     * If |callback| is passed, it is invoked with the incumbent global as its
+     * sole argument. This allows the incumbent global to be measured in callback
+     * environments with no scripted frames on the stack.
+     */
+    [implicit_jscontext]
+    jsval getIncumbentGlobal([optional] in jsval callback);
+
+    /**
       * Retrieve the last time, in microseconds since epoch, that a given
       * watchdog-related event occured.
       *
       * Valid categories:
       *   "RuntimeStateChange"      - Runtime switching between active and inactive states
       *   "WatchdogWakeup"          - Watchdog waking up from sleeping
       *   "WatchdogHibernateStart"  - Watchdog begins hibernating
       *   "WatchdogHibernateStop"   - Watchdog stops hibernating
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3483,16 +3483,43 @@ NS_IMETHODIMP
 nsXPCComponents_Utils::GetDOMClassInfo(const nsAString& aClassName,
                                        nsIClassInfo** aClassInfo)
 {
     *aClassInfo = nullptr;
     return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
+nsXPCComponents_Utils::GetIncumbentGlobal(const Value &aCallback,
+                                          JSContext *aCx, Value *aOut)
+{
+    nsCOMPtr<nsIGlobalObject> global = mozilla::dom::GetIncumbentGlobal();
+    RootedValue globalVal(aCx);
+
+    if (!global) {
+        globalVal = NullValue();
+    } else {
+        // Note: We rely on the wrap call for outerization.
+        globalVal = ObjectValue(*global->GetGlobalJSObject());
+        if (!JS_WrapValue(aCx, &globalVal))
+            return NS_ERROR_FAILURE;
+    }
+
+    // Invoke the callback, if passed.
+    if (aCallback.isObject()) {
+        Value ignored;
+        if (!JS_CallFunctionValue(aCx, nullptr, aCallback, 1, globalVal.address(), &ignored))
+            return NS_ERROR_FAILURE;
+    }
+
+    *aOut = globalVal;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsXPCComponents_Utils::GetWatchdogTimestamp(const nsAString& aCategory, PRTime *aOut)
 {
     WatchdogTimestampCategory category;
     if (aCategory.EqualsLiteral("RuntimeStateChange"))
         category = TimestampRuntimeStateChange;
     else if (aCategory.EqualsLiteral("WatchdogWakeup"))
         category = TimestampWatchdogWakeup;
     else if (aCategory.EqualsLiteral("WatchdogHibernateStart"))
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -62,16 +62,17 @@ support-files =
 [test_expandosharing.xul]
 [test_exposeInDerived.xul]
 [test_getweakmapkeys.xul]
 [test_mozMatchesSelector.xul]
 [test_nodelists.xul]
 [test_paris_weakmap_keys.xul]
 [test_precisegc.xul]
 [test_sandboxImport.xul]
+[test_scriptSettings.xul]
 [test_weakmap_keys_preserved.xul]
 [test_weakmap_keys_preserved2.xul]
 [test_weakref.xul]
 [test_wrappers.xul]
 [test_weakmaps.xul]
 skip-if = os == "win" # Bug 820471
 [test_wrappers-2.xul]
 # Disabled until this test gets updated to test the new proxy based wrappers.
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_scriptSettings.xul
@@ -0,0 +1,125 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=937317
+-->
+<window title="Mozilla Bug 937317"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=937317"
+     target="_blank">Mozilla Bug 937317</a>
+  </body>
+
+  <!-- test code goes here -->
+  <iframe></iframe>
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for the script settings stack. **/
+  SimpleTest.waitForExplicitFinish();
+  const Cu = Components.utils;
+  Cu.import("resource://gre/modules/Promise.jsm");
+  var iwin = window[0];
+
+  // Smoketest.
+  is(Cu.getIncumbentGlobal(), window, "smoketest");
+
+  // Calling a cross-compartment non-scripted function changes the
+  // compartment, but not the incumbent script settings object.
+  var sb = new Cu.Sandbox(window, { wantComponents: true });
+  is(sb.Components.utils.getIncumbentGlobal(), window, "cross-compartment sb non-scripted");
+  is(iwin.Components.utils.getIncumbentGlobal(), window, "cross-compartment win non-scripted");
+
+  // If we call a scripted function in the other compartment, that becomes
+  // the incumbent script.
+  function gib() { return Components.utils.getIncumbentGlobal(); };
+  Cu.evalInSandbox(gib.toSource(), sb);
+  iwin.eval(gib.toSource());
+  is(sb.gib(), sb, "cross-compartment sb scripted");
+  is(iwin.gib(), iwin, "cross-compartment win scripted");
+
+  // Eval-ing top-level script in another component makes that compartment the
+  // incumbent script.
+  is(Cu.evalInSandbox('Components.utils.getIncumbentGlobal()', sb), sb, 'eval sb');
+  is(iwin.eval('Components.utils.getIncumbentGlobal()'), iwin, 'eval iwin');
+
+  // Make sure that the callback mechanism works.
+  function makeCallback(expectedGlobal, deferred, kind) {
+    function cb(incumbentGlobal) {
+      is(incumbentGlobal, expectedGlobal, "Callback got the right incumbent global: " + kind);
+      if (deferred)
+        deferred.resolve();
+    }
+    info("Generated callback: " + kind);
+    return cb;
+  }
+
+  var bound = Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, undefined, "simple bound"));
+  is(bound(), window, "Bound method returns the right global");
+
+  // Callbacks grab the incumbent script at the time of invocation.
+  //
+  // Note - We avoid calling the initial defer |d| so that it's not in-scope for everything below.
+  let initialDefer = Promise.defer();
+  setTimeout(Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, initialDefer, "same-global setTimeout")), 0);
+  initialDefer.promise.then(function() {
+
+    // Try cross-global setTimeout where |window| is the incumbent script when the callback is created.
+    let d = Promise.defer();
+    iwin.setTimeout(Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, d, "cross-global setTimeout by |window|")), 0);
+    return d.promise;
+  }).then(function() {
+
+    // Try cross-global setTimeout where |iwin| is the incumbent script when the callback is created.
+    let d = Promise.defer();
+    iwin.wrappedJSObject.timeoutFun = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global setTimeout by |iwin|"));
+    iwin.eval('setTimeout(timeoutFun, 0);');
+    return d.promise;
+  }).then(function() {
+
+    // The incumbent script override doesn't take effect if the callback is scripted.
+    let d = Promise.defer();
+    iwin.wrappedJSObject.timeoutFun2 = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global setTimeout of scripted function"));
+    iwin.eval('let timeoutFun2Wrapper = function() { timeoutFun2(); }');
+    setTimeout(iwin.wrappedJSObject.timeoutFun2Wrapper, 0);
+    return d.promise;
+  }).then(function() {
+
+    // Try event listeners.
+    let d = Promise.defer();
+    let body = iwin.document.body;
+    body.addEventListener('click', Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, d, "cross-global event listener")));
+    body.dispatchEvent(new iwin.MouseEvent('click'));
+    return d.promise;
+
+  }).then(function() {
+
+    // Try an event handler set by |iwin|.
+    let d = Promise.defer();
+    let body = iwin.document.body;
+    iwin.wrappedJSObject.handler = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global event handler"));
+    iwin.eval('document.body.onmousemove = handler');
+    body.dispatchEvent(new iwin.MouseEvent('mousemove'));
+    return d.promise;
+
+  }).then(function() {
+
+    // Try an event handler compiled by a content attribute.
+    let d = Promise.defer();
+    let body = iwin.document.body;
+    iwin.wrappedJSObject.innerHandler = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global compiled event handler"));
+    iwin.eval("document.body.setAttribute('onmouseout', 'innerHandler()')");
+    body.dispatchEvent(new iwin.MouseEvent('mouseout'));
+    return d.promise;
+  }).then(function() {
+
+    SimpleTest.finish();
+  });
+
+  ]]>
+  </script>
+</window>
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -28,16 +28,19 @@
 #include "nsILinkHandler.h"
 #include "nsIDOMDocument.h"
 #include "nsISelectionListener.h"
 #include "nsISelectionPrivate.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsContentUtils.h"
 #include "nsLayoutStylesheetCache.h"
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/DocAccessible.h"
+#endif
 #include "mozilla/BasicEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/WeakPtr.h"
 
 #include "nsViewManager.h"
 #include "nsView.h"
 
@@ -1560,16 +1563,26 @@ nsDocumentViewer::Destroy()
       shEntry->SetContentViewer(this);
     }
 
     // Always sync the presentation state.  That way even if someone screws up
     // and shEntry has no window state at this point we'll be ok; we just won't
     // cache ourselves.
     shEntry->SyncPresentationState();
 
+    // Shut down accessibility for the document before we start to tear it down.
+#ifdef ACCESSIBILITY
+    if (mPresShell) {
+      a11y::DocAccessible* docAcc = mPresShell->GetDocAccessible();
+      if (docAcc) {
+        docAcc->Shutdown();
+      }
+    }
+#endif
+
     // Break the link from the document/presentation to the docshell, so that
     // link traversals cannot affect the currently-loaded document.
     // When the presentation is restored, Open() and InitInternal() will reset
     // these pointers to their original values.
 
     if (mDocument) {
       mDocument->SetContainer(nullptr);
     }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -30,17 +30,16 @@
 #include "nsBidiPresUtils.h"
 #include "imgIContainer.h"
 #include "ImageOps.h"
 #include "gfxRect.h"
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsCSSRendering.h"
-#include "nsCxPusher.h"
 #include "nsThemeConstants.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsIWidget.h"
 #include "gfxMatrix.h"
 #include "gfxPoint3D.h"
 #include "gfxTypes.h"
 #include "nsTArray.h"
@@ -4770,22 +4769,16 @@ nsLayoutUtils::SurfaceFromElement(nsIIma
   bool wantImageSurface = (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) != 0;
   bool premultAlpha = (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0;
 
   if (!premultAlpha) {
     forceCopy = true;
     wantImageSurface = true;
   }
 
-  // Push a null JSContext on the stack so that code that runs within
-  // the below code doesn't think it's being called by JS. See bug
-  // 604262.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   nsCOMPtr<imgIRequest> imgRequest;
   rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
                             getter_AddRefs(imgRequest));
   if (NS_FAILED(rv) || !imgRequest)
     return result;
 
   uint32_t status;
   imgRequest->GetImageStatus(&status);
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -32,27 +32,27 @@
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsComponentManagerUtils.h"
 #include "prlog.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
 #include "jsapi.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "mozilla/Preferences.h"
 #include "nsViewManager.h"
 #include "GeckoProfiler.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsPerformance.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "RestyleManager.h"
 #include "Layers.h"
 #include "imgIContainer.h"
 #include "nsIFrameRequestCallback.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *gLog = nullptr;
 #define LOG(...) PR_LOG(gLog, PR_LOG_NOTICE, (__VA_ARGS__))
 #else
@@ -717,18 +717,17 @@ nsRefreshDriver::AdvanceTimeAndRefresh(i
     mMostRecentRefresh = TimeStamp::Now();
 
     mTestControllingRefreshes = true;
   }
 
   mMostRecentRefreshEpochTime += aMilliseconds * 1000;
   mMostRecentRefresh += TimeDuration::FromMilliseconds((double) aMilliseconds);
 
-  nsCxPusher pusher;
-  pusher.PushNull();
+  mozilla::dom::AutoSystemCaller asc;
   DoTick();
 }
 
 void
 nsRefreshDriver::RestoreNormalRefresh()
 {
   mTestControllingRefreshes = false;
   EnsureTimerStarted(false);
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -10,16 +10,17 @@
     iframe {
       width: 600px;
       height: 600px;
     }
   </style>
 <script type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(2);
 
 var canvases = [];
 function callbackTestCanvas(canvas)
 {
   canvases.push(canvas);
 
   if (canvases.length != 2)
     return;
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -37,19 +37,19 @@
 #include "nsIDOMText.h" //for multiline getselection
 #include "nsFocusManager.h"
 #include "nsTextEditRules.h"
 #include "nsPresState.h"
 #include "nsContentList.h"
 #include "nsAttrValueInlines.h"
 #include "mozilla/Selection.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "nsTextNode.h"
 #include "nsStyleSet.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #define DEFAULT_COLUMN_WIDTH 20
 
 using namespace mozilla;
 
 nsIFrame*
 NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
@@ -269,18 +269,17 @@ nsTextControlFrame::EnsureEditorInitiali
 
   // Make sure that editor init doesn't do things that would kill us off
   // (especially off the script blockers it'll create for its DOM mutations).
   {
     nsAutoScriptBlocker scriptBlocker;
 
     // Time to mess with our security context... See comments in GetValue()
     // for why this is needed.
-    nsCxPusher pusher;
-    pusher.PushNull();
+    mozilla::dom::AutoSystemCaller asc;
 
     // Make sure that we try to focus the content even if the method fails
     class EnsureSetFocus {
     public:
       explicit EnsureSetFocus(nsTextControlFrame* aFrame)
         : mFrame(aFrame) {}
       ~EnsureSetFocus() {
         if (nsContentUtils::IsFocusedContent(mFrame->GetContent()))
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -2347,23 +2347,22 @@ SumLineCrossSizes(const nsTArray<FlexLin
   nscoord sum = 0;
   for (uint32_t lineIdx = 0; lineIdx < aLines.Length(); lineIdx++) {
     sum += aLines[lineIdx].GetLineCrossSize();
   }
   return sum;
 }
 
 nscoord
-nsFlexContainerFrame::ComputeFlexContainerCrossSize(
-  const nsHTMLReflowState& aReflowState,
-  const FlexboxAxisTracker& aAxisTracker,
-  const nsTArray<FlexLine>& aLines,
-  nscoord aAvailableHeightForContent,
-  bool* aIsDefinite,
-  nsReflowStatus& aStatus)
+nsFlexContainerFrame::ComputeCrossSize(const nsHTMLReflowState& aReflowState,
+                                       const FlexboxAxisTracker& aAxisTracker,
+                                       const nsTArray<FlexLine>& aLines,
+                                       nscoord aAvailableHeightForContent,
+                                       bool* aIsDefinite,
+                                       nsReflowStatus& aStatus)
 {
   MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null"); 
 
   if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
     // Cross axis is horizontal: our cross size is our computed width
     // (which is already resolved).
     *aIsDefinite = true;
     return aReflowState.ComputedWidth();
@@ -2686,20 +2685,18 @@ nsFlexContainerFrame::Reflow(nsPresConte
   // Calculate the cross size and (if necessary) baseline-alignment position
   // for each of our flex lines:
   for (uint32_t lineIdx = 0; lineIdx < lines.Length(); ++lineIdx) {
     lines[lineIdx].ComputeCrossSizeAndBaseline(axisTracker);
   }
 
   bool isCrossSizeDefinite;
   const nscoord contentBoxCrossSize =
-    ComputeFlexContainerCrossSize(aReflowState, axisTracker,
-                                  lines,
-                                  availableHeightForContent,
-                                  &isCrossSizeDefinite, aStatus);
+    ComputeCrossSize(aReflowState, axisTracker, lines,
+                     availableHeightForContent, &isCrossSizeDefinite, aStatus);
 
   // Set up state for cross-axis alignment, at a high level (outside the
   // scope of a particular flex line)
   CrossAxisPositionTracker
     crossAxisPosnTracker(lines, aReflowState.mStylePosition->mAlignContent,
                          contentBoxCrossSize, isCrossSizeDefinite, axisTracker);
 
   // Set the flex container's baseline, from the baseline-alignment position
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -99,22 +99,22 @@ protected:
                              nscoord aContentBoxMainSize,
                              nscoord aAvailableHeightForContent,
                              const FlexboxAxisTracker& aAxisTracker,
                              nsTArray<FlexLine>& aLines);
 
   nscoord GetMainSizeFromReflowState(const nsHTMLReflowState& aReflowState,
                                      const FlexboxAxisTracker& aAxisTracker);
 
-  nscoord ComputeFlexContainerCrossSize(const nsHTMLReflowState& aReflowState,
-                                        const FlexboxAxisTracker& aAxisTracker,
-                                        const nsTArray<FlexLine>& aLines,
-                                        nscoord aAvailableHeightForContent,
-                                        bool* aIsDefinite,
-                                        nsReflowStatus& aStatus);
+  nscoord ComputeCrossSize(const nsHTMLReflowState& aReflowState,
+                           const FlexboxAxisTracker& aAxisTracker,
+                           const nsTArray<FlexLine>& aLines,
+                           nscoord aAvailableHeightForContent,
+                           bool* aIsDefinite,
+                           nsReflowStatus& aStatus);
 
   nsresult SizeItemInCrossAxis(nsPresContext* aPresContext,
                                const FlexboxAxisTracker& aAxisTracker,
                                nsHTMLReflowState& aChildReflowState,
                                FlexItem& aItem);
 
   bool mChildrenHaveBeenReordered; // Have we ever had to reorder our kids
                                    // to satisfy their 'order' values?
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -18,17 +18,16 @@
 #include "nsGenericHTMLElement.h"
 #include "nsPresContext.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsBoxLayoutState.h"
 #include "nsBoxFrame.h"
 #include "nsImageFrame.h"
 #include "nsIImageLoadingContent.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "ImageContainer.h"
 #include "ImageLayers.h"
 #include "nsContentList.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
@@ -70,22 +69,16 @@ nsVideoFrame::CreateAnonymousContent(nsT
                                             nullptr,
                                             kNameSpaceID_XHTML,
                                             nsIDOMNode::ELEMENT_NODE);
     NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
     element = NS_NewHTMLImageElement(nodeInfo.forget());
     mPosterImage = element;
     NS_ENSURE_TRUE(mPosterImage, NS_ERROR_OUT_OF_MEMORY);
 
-    // Push a null JSContext on the stack so that code that runs
-    // within the below code doesn't think it's being called by
-    // JS. See bug 604262.
-    nsCxPusher pusher;
-    pusher.PushNull();
-
     // Set the nsImageLoadingContent::ImageState() to 0. This means that the
     // image will always report its state as 0, so it will never be reframed
     // to show frames for loading or the broken image icon. This is important,
     // as the image is native anonymous, and so can't be reframed (currently).
     nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
     NS_ENSURE_TRUE(imgContent, NS_ERROR_FAILURE);
 
     imgContent->ForceImageState(true, 0);
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-multi-line-horiz-1-ref.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-multi-line-horiz-1-ref.html
@@ -11,17 +11,16 @@
 <html>
 <head>
   <title>CSS Reftest Reference</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <meta charset="utf-8">
   <style>
     .flexContainer {
       display: inline-block;
-      flex-wrap: wrap;
       width: 40px;
       height: 40px;
       background: lightblue;
     }
     .flexContainer > * {
       width: 18px;
       display: inline-block;
     }
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-multi-line-horiz-2-ref.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-multi-line-horiz-2-ref.html
@@ -11,17 +11,16 @@
 <html>
 <head>
   <title>CSS Reftest Reference</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <meta charset="utf-8">
   <style>
     .flexContainer {
       display: inline-block;
-      flex-wrap: wrap;
       width: 40px;
       /* Split testcase's 40px height into 20px of padding-top and 20px of
          height, to set aside space for the testcase's (invisible) second line
          (which is above the first line, since this is wrap-reverse) */
       height: 20px;
       padding-top: 20px;
       background: lightblue;
     }
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-sizing-horiz-1-ref.xhtml
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-sizing-horiz-1-ref.xhtml
@@ -7,17 +7,16 @@
   <head>
     <title>CSS Reftest Reference</title>
     <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
     <style>
       div { height: 10px; }
       div.flexbox {
         border: 1px dashed blue;
         font-size: 10px;
-        flex-direction: column;
         margin-bottom: 2px;
       }
       div.a, div.b, div.c { float: left }
       div.a {
         width: 20px;
         background: lightgreen;
       }
       div.b {
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-sizing-vert-1-ref.xhtml
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-sizing-vert-1-ref.xhtml
@@ -8,17 +8,16 @@
     <title>CSS Reftest Reference</title>
     <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
     <style>
       div { width: 10px; }
       div.flexbox {
         float: left;
         border: 1px dashed blue;
         font-size: 10px;
-        flex-direction: column;
         margin-right: 2px;
       }
       div.a {
         height: 20px;
         background: lightgreen;
       }
       div.b {
         height: 40px;
deleted file mode 100644
--- a/media/libogg/src/moz.build
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-UNIFIED_SOURCES += [
-    'ogg_bitwise.c',
-    'ogg_framing.c',
-]
-
-LIBRARY_NAME = 'ogg'
-
-MSVC_ENABLE_PGO = True
-
-FORCE_STATIC_LIB = True
-
-if CONFIG['OS_TARGET'] == 'WINNT':
-    NO_VISIBILITY_FLAGS = True
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -224,17 +224,16 @@ static int nr_transport_addr_to_praddr(n
   PRNetAddr *naddr)
   {
     int _status;
 
     memset(naddr, 0, sizeof(*naddr));
 
     switch(addr->protocol){
       case IPPROTO_TCP:
-        ABORT(R_INTERNAL); /* Can't happen for now */
         break;
       case IPPROTO_UDP:
         break;
       default:
         ABORT(R_BAD_ARGS);
     }
 
     switch(addr->ip_version){
@@ -312,54 +311,55 @@ static int nr_transport_addr_to_netaddr(
   }
 
   _status = 0;
 abort:
   return(_status);
 }
 
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
-  nr_transport_addr *addr)
+                                 nr_transport_addr *addr, int protocol)
   {
     int _status;
     int r;
 
     switch(netaddr->raw.family) {
       case AF_INET:
         if ((r = nr_ip4_port_to_transport_addr(ntohl(netaddr->inet.ip),
                                                ntohs(netaddr->inet.port),
-                                               IPPROTO_UDP, addr)))
+                                               protocol, addr)))
           ABORT(r);
         break;
       case AF_INET6:
         ABORT(R_BAD_ARGS);
       default:
         MOZ_ASSERT(false);
         ABORT(R_BAD_ARGS);
     }
     _status=0;
   abort:
     return(_status);
   }
 
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
-  nr_transport_addr *addr, int keep)
+                                nr_transport_addr *addr, int protocol,
+                                int keep)
   {
     int _status;
     int r;
     struct sockaddr_in ip4;
 
     switch(praddr->raw.family) {
       case PR_AF_INET:
         ip4.sin_family = PF_INET;
         ip4.sin_addr.s_addr = praddr->inet.ip;
         ip4.sin_port = praddr->inet.port;
         if ((r = nr_sockaddr_to_transport_addr((sockaddr *)&ip4,
                                                sizeof(ip4),
-                                               IPPROTO_UDP, keep,
+                                               protocol, keep,
                                                addr)))
           ABORT(r);
         break;
       case PR_AF_INET6:
 #if 0
         r = nr_sockaddr_to_transport_addr((sockaddr *)&praddr->raw,
           sizeof(struct sockaddr_in6),IPPROTO_UDP,keep,addr);
         break;
@@ -415,19 +415,31 @@ int NrSocket::create(nr_transport_addr *
 
   if (!NS_SUCCEEDED(rv)) {
     ABORT(R_INTERNAL);
   }
 
   if((r=nr_transport_addr_to_praddr(addr, &naddr)))
     ABORT(r);
 
-  if (!(fd_ = PR_NewUDPSocket())) {
-    r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket");
-    ABORT(R_INTERNAL);
+  switch (addr->protocol) {
+    case IPPROTO_UDP:
+      if (!(fd_ = PR_NewUDPSocket())) {
+        r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket");
+        ABORT(R_INTERNAL);
+      }
+      break;
+    case IPPROTO_TCP:
+      if (!(fd_ = PR_NewTCPSocket())) {
+        r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket");
+        ABORT(R_INTERNAL);
+      }
+      break;
+    default:
+      ABORT(R_INTERNAL);
   }
 
   status = PR_Bind(fd_, &naddr);
   if (status != PR_SUCCESS) {
     r_log(LOG_GENERIC,LOG_CRIT,"Couldn't bind socket to address %s",
           addr->as_string);
     ABORT(R_INTERNAL);
   }
@@ -439,17 +451,17 @@ int NrSocket::create(nr_transport_addr *
   /* If we have a wildcard port, patch up the addr */
   if(nr_transport_addr_is_wildcard(addr)){
     status = PR_GetSockName(fd_, &naddr);
     if (status != PR_SUCCESS){
       r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
       ABORT(R_INTERNAL);
     }
 
-    if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,1)))
+    if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,addr->protocol,1)))
       ABORT(r);
   }
 
 
   // Set nonblocking
   PRSocketOptionData option;
   option.option = PR_SockOpt_Nonblocking;
   option.value.non_blocking = PR_TRUE;
@@ -527,31 +539,31 @@ int NrSocket::sendto(const void *msg, si
   }
 
   _status=0;
 abort:
   return(_status);
 }
 
 int NrSocket::recvfrom(void * buf, size_t maxlen,
-                                       size_t *len, int flags,
-                                       nr_transport_addr *from) {
+                       size_t *len, int flags,
+                       nr_transport_addr *from) {
   ASSERT_ON_THREAD(ststhread_);
   int r,_status;
   PRNetAddr nfrom;
   int32_t status;
 
   status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
   if (status <= 0) {
     r_log(LOG_GENERIC,LOG_ERR,"Error in recvfrom");
     ABORT(R_IO_ERROR);
   }
   *len=status;
 
-  if((r=nr_praddr_to_transport_addr(&nfrom,from,0)))
+  if((r=nr_praddr_to_transport_addr(&nfrom,from,my_addr_.protocol,0)))
     ABORT(r);
 
   //r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
 
   _status=0;
 abort:
   return(_status);
 }
@@ -562,16 +574,92 @@ int NrSocket::getaddr(nr_transport_addr 
 }
 
 // Close the socket so that the STS will detach and then kill it
 void NrSocket::close() {
   ASSERT_ON_THREAD(ststhread_);
   mCondition = NS_BASE_STREAM_CLOSED;
 }
 
+
+int NrSocket::connect(nr_transport_addr *addr) {
+  ASSERT_ON_THREAD(ststhread_);
+  int r,_status;
+  PRNetAddr naddr;
+  int32_t status;
+
+  if ((r=nr_transport_addr_to_praddr(addr, &naddr)))
+    ABORT(r);
+
+  if(!fd_)
+    ABORT(R_EOD);
+
+  // Note: this just means we tried to connect, not that we
+  // are actually live.
+  connect_invoked_ = true;
+  status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT);
+
+  if (status != PR_SUCCESS) {
+    if (PR_GetError() == PR_IN_PROGRESS_ERROR)
+      ABORT(R_WOULDBLOCK);
+
+    ABORT(R_IO_ERROR);
+  }
+
+  _status=0;
+abort:
+  return(_status);
+}
+
+
+int NrSocket::write(const void *msg, size_t len, size_t *written) {
+  ASSERT_ON_THREAD(ststhread_);
+  int _status;
+  int32_t status;
+
+  if (!connect_invoked_)
+    ABORT(R_FAILED);
+
+  status = PR_Write(fd_, msg, len);
+  if (status < 0) {
+    if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+      ABORT(R_WOULDBLOCK);
+    ABORT(R_IO_ERROR);
+  }
+
+  *written = status;
+
+  _status=0;
+abort:
+  return _status;
+}
+
+int NrSocket::read(void* buf, size_t maxlen, size_t *len) {
+  ASSERT_ON_THREAD(ststhread_);
+  int _status;
+  int32_t status;
+
+  if (!connect_invoked_)
+    ABORT(R_FAILED);
+
+  status = PR_Read(fd_, buf, maxlen);
+  if (status < 0) {
+    if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+      ABORT(R_WOULDBLOCK);
+    ABORT(R_IO_ERROR);
+  }
+  if (status == 0)
+    ABORT(R_EOD);
+
+  *len = (size_t)status;  // Guaranteed to be > 0
+  _status = 0;
+abort:
+  return(_status);
+}
+
 // NrSocketIpc Implementation
 NS_IMPL_ISUPPORTS1(NrSocketIpc, nsIUDPSocketInternal)
 
 NrSocketIpc::NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread)
     : err_(false),
       state_(NR_INIT),
       main_thread_(main_thread),
       monitor_("NrSocketIpc") {
@@ -671,17 +759,17 @@ NS_IMETHODIMP NrSocketIpc::CallListenerV
     }
 
     nr_transport_addr expected_addr;
     if(nr_transport_addr_copy(&expected_addr, &my_addr_)) {
       err_ = true;
       MOZ_ASSERT(false, "Failed to copy my_addr_");
     }
 
-    if (nr_praddr_to_transport_addr(&praddr, &my_addr_, 1)) {
+    if (nr_praddr_to_transport_addr(&praddr, &my_addr_, IPPROTO_UDP, 1)) {
       err_ = true;
       MOZ_ASSERT(false, "Failed to copy local host to my_addr_");
     }
 
     if (nr_transport_addr_cmp(&expected_addr, &my_addr_,
                               NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
       err_ = true;
       MOZ_ASSERT(false, "Address of opened socket is not expected");
@@ -846,17 +934,17 @@ int NrSocketIpc::recvfrom(void *buf, siz
     ABORT(R_WOULDBLOCK);
   }
 
   {
     RefPtr<nr_udp_message> msg(received_msgs_.front());
 
     received_msgs_.pop();
 
-    if ((r=nr_praddr_to_transport_addr(&msg->from, from, 0))) {
+    if ((r=nr_praddr_to_transport_addr(&msg->from, from, IPPROTO_UDP, 0))) {
       err_ = true;
       MOZ_ASSERT(false, "Get bogus address for received UDP packet");
       ABORT(r);
     }
 
     consumed_len = std::min(maxlen, msg->data->len());
     if (consumed_len < msg->data->len()) {
       r_log(LOG_GENERIC, LOG_DEBUG, "Partial received UDP packet will be discard");
@@ -878,16 +966,31 @@ int NrSocketIpc::getaddr(nr_transport_ad
 
   if (state_ != NR_CONNECTED) {
     return R_INTERNAL;
   }
 
   return nr_transport_addr_copy(addrp, &my_addr_);
 }
 
+int NrSocketIpc::connect(nr_transport_addr *addr) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrSocketIpc::write(const void *msg, size_t len, size_t *written) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
 // Main thread executors
 void NrSocketIpc::create_m(const nsACString &host, const uint16_t port) {
   ASSERT_ON_THREAD(main_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   nsresult rv;
   socket_child_ = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
@@ -954,23 +1057,32 @@ using namespace mozilla;
 static int nr_socket_local_destroy(void **objp);
 static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
                                   int flags, nr_transport_addr *to);
 static int nr_socket_local_recvfrom(void *obj,void * restrict buf,
   size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
 static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd);
 static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp);
 static int nr_socket_local_close(void *obj);
+static int nr_socket_local_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_local_write(void *obj,const void *msg, size_t len,
+                                 size_t *written);
+static int nr_socket_local_read(void *obj,void * restrict buf, size_t maxlen,
+                                size_t *len);
 
 static nr_socket_vtbl nr_socket_local_vtbl={
+  1,
   nr_socket_local_destroy,
   nr_socket_local_sendto,
   nr_socket_local_recvfrom,
   nr_socket_local_getfd,
   nr_socket_local_getaddr,
+  nr_socket_local_connect,
+  nr_socket_local_write,
+  nr_socket_local_read,
   nr_socket_local_close
 };
 
 int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) {
   NrSocketBase *sock = nullptr;
 
   // create IPC bridge for content process
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
@@ -982,24 +1094,25 @@ int nr_socket_local_create(nr_transport_
   }
 
   int r, _status;
 
   r = sock->create(addr);
   if (r)
     ABORT(r);
 
-  r = nr_socket_create_int(static_cast<void *>(sock), &nr_socket_local_vtbl, sockp);
+  r = nr_socket_create_int(static_cast<void *>(sock),
+                           sock->vtbl(), sockp);
   if (r)
     ABORT(r);
 
   // Add a reference so that we can delete it in destroy()
   sock->AddRef();
 
-  _status =0;
+  _status = 0;
 
 abort:
   if (_status) {
     delete sock;
   }
   return _status;
 }
 
@@ -1050,22 +1163,46 @@ static int nr_socket_local_getaddr(void 
 static int nr_socket_local_close(void *obj) {
   NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
 
   sock->close();
 
   return 0;
 }
 
+static int nr_socket_local_write(void *obj, const void *msg, size_t len,
+                                 size_t *written) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->write(msg, len, written);
+}
+
+static int nr_socket_local_read(void *obj, void * restrict buf, size_t maxlen,
+                                size_t *len) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->read(buf, maxlen, len);
+}
+
+static int nr_socket_local_connect(void *obj, nr_transport_addr *addr) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->connect(addr);
+}
+
 // Implement async api
 int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg,
                   char *function,int line) {
   NrSocketBase *s = static_cast<NrSocketBase *>(sock);
 
   return s->async_wait(how, cb, cb_arg, function, line);
 }
 
 int NR_async_cancel(NR_SOCKET sock,int how) {
   NrSocketBase *s = static_cast<NrSocketBase *>(sock);
 
   return s->cancel(how);
 }
 
+nr_socket_vtbl* NrSocketBase::vtbl() {
+  return &nr_socket_local_vtbl;
+}
+
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -59,56 +59,66 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #include "nsIEventTarget.h"
 #include "nsIUDPSocketChild.h"
 
 #include "databuffer.h"
 #include "m_cpp_utils.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 
+// Stub declaration for nICEr type
+typedef struct nr_socket_vtbl_ nr_socket_vtbl;
+
 namespace mozilla {
 
 namespace net {
   union NetAddr;
 }
 
 class NrSocketBase {
 public:
-  NrSocketBase() : poll_flags_(0) {
+  NrSocketBase() : connect_invoked_(false), poll_flags_(0) {
     memset(cbs_, 0, sizeof(cbs_));
     memset(cb_args_, 0, sizeof(cb_args_));
     memset(&my_addr_, 0, sizeof(my_addr_));
   }
   virtual ~NrSocketBase() {}
 
   // the nr_socket APIs
   virtual int create(nr_transport_addr *addr) = 0;
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to) = 0;
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from) = 0;
   virtual int getaddr(nr_transport_addr *addrp) = 0;
   virtual void close() = 0;
+  virtual int connect(nr_transport_addr *addr) = 0;
+  virtual int write(const void *msg, size_t len, size_t *written) = 0;
+  virtual int read(void* buf, size_t maxlen, size_t *len) = 0;
 
    // Implementations of the async_event APIs
   virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
                          char *function, int line);
   virtual int cancel(int how);
 
   // nsISupport reference counted interface
   NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
   NS_IMETHOD_(nsrefcnt) Release(void) = 0;
 
   uint32_t poll_flags() {
     return poll_flags_;
   }
 
+  virtual nr_socket_vtbl *vtbl();  // To access in test classes.
+
 protected:
   void fire_callback(int how);
+
+  bool connect_invoked_;
   nr_transport_addr my_addr_;
 
 private:
   NR_async_cb cbs_[NR_ASYNC_WAIT_WRITE + 1];
   void *cb_args_[NR_ASYNC_WAIT_WRITE + 1];
   uint32_t poll_flags_;
 };
 
@@ -140,16 +150,19 @@ public:
   virtual int create(nr_transport_addr *addr); // (really init, but it's called create)
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to);
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from);
   virtual int getaddr(nr_transport_addr *addrp);
   virtual void close();
+  virtual int connect(nr_transport_addr *addr);
+  virtual int write(const void *msg, size_t len, size_t *written);
+  virtual int read(void* buf, size_t maxlen, size_t *len);
 
 private:
   DISALLOW_COPY_ASSIGN(NrSocket);
 
   PRFileDesc *fd_;
   nsCOMPtr<nsIEventTarget> ststhread_;
 };
 
@@ -189,16 +202,19 @@ public:
   virtual int create(nr_transport_addr *addr);
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to);
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from);
   virtual int getaddr(nr_transport_addr *addrp);
   virtual void close();
+  virtual int connect(nr_transport_addr *addr);
+  virtual int write(const void *msg, size_t len, size_t *written);
+  virtual int read(void* buf, size_t maxlen, size_t *len);
 
 private:
   DISALLOW_COPY_ASSIGN(NrSocketIpc);
 
   // Main thread executors of the NrSocketBase APIs
   void create_m(const nsACString &host, const uint16_t port);
   void sendto_m(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
   void close_m();
@@ -211,15 +227,17 @@ private:
 
   nsCOMPtr<nsIUDPSocketChild> socket_child_;
   nsCOMPtr<nsIEventTarget> sts_thread_;
   const nsCOMPtr<nsIEventTarget> main_thread_;
   ReentrantMonitor monitor_;
 };
 
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
-                                 nr_transport_addr *addr);
+                                 nr_transport_addr *addr,
+                                 int protocol);
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
-                                nr_transport_addr *addr, int keep);
+                                nr_transport_addr *addr,
+                                int protocol, int keep);
 int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
                                               nsACString *host, int32_t *port);
 }  // close namespace
 #endif
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -178,23 +178,34 @@ static nr_ice_crypto_vtbl nr_ice_crypto_
   nr_crypto_nss_random_bytes,
   nr_crypto_nss_hmac,
   nr_crypto_nss_md5
 };
 
 
 
 
-nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server *server) const {
+nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server *server,
+                                            std::string transport) const {
   int r;
+  int transport_int;
 
   memset(server, 0, sizeof(nr_ice_stun_server));
+  if (transport == kNrIceTransportUdp) {
+    transport_int = IPPROTO_UDP;
+  } else if (transport == kNrIceTransportTcp) {
+    transport_int = IPPROTO_TCP;
+  } else {
+    MOZ_ASSERT(false);
+    return NS_ERROR_FAILURE;
+  }
 
   if (has_addr_) {
-    r = nr_praddr_to_transport_addr(&addr_, &server->u.addr, 0);
+    r = nr_praddr_to_transport_addr(&addr_, &server->u.addr,
+                                    transport_int, 0);
     if (r) {
       return NS_ERROR_FAILURE;
     }
     server->type=NR_ICE_STUN_SERVER_TYPE_ADDR;
   }
   else {
     MOZ_ASSERT(sizeof(server->u.dnsname.host) > host_.size());
     PL_strncpyz(server->u.dnsname.host, host_.c_str(),
@@ -205,20 +216,29 @@ nsresult NrIceStunServer::ToNicerStunStr
 
   return NS_OK;
 }
 
 
 nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server *server) const {
   memset(server, 0, sizeof(nr_ice_turn_server));
 
-  nsresult rv = ToNicerStunStruct(&server->turn_server);
+  nsresult rv = ToNicerStunStruct(&server->turn_server, transport_);
   if (NS_FAILED(rv))
     return rv;
 
+  if (transport_ == kNrIceTransportUdp) {
+    server->transport = IPPROTO_UDP;
+  } else if (transport_ == kNrIceTransportTcp) {
+    server->transport = IPPROTO_TCP;
+  } else {
+    MOZ_ASSERT(false);
+    return NS_ERROR_FAILURE;
+  }
+
   if (username_.empty())
     return NS_ERROR_INVALID_ARG;
   if (password_.empty())
     return NS_ERROR_INVALID_ARG;
 
   if (!(server->username=r_strdup(username_.c_str())))
     return NS_ERROR_OUT_OF_MEMORY;
 
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -97,17 +97,19 @@ class NrIceStunServer {
 
     nsresult rv = server->Init(addr, port);
     if (NS_FAILED(rv))
       return nullptr;
 
     return server.forget();
   }
 
-  nsresult ToNicerStunStruct(nr_ice_stun_server *server) const;
+  nsresult ToNicerStunStruct(nr_ice_stun_server *server,
+                             const std::string transport =
+                             kNrIceTransportUdp) const;
 
  protected:
   NrIceStunServer() : addr_() {}
 
   nsresult Init(const std::string& addr, uint16_t port) {
     PRStatus status = PR_StringToNetAddr(addr.c_str(), &addr_);
     if (status == PR_SUCCESS) {
       // Parseable as an address
@@ -134,36 +136,37 @@ class NrIceStunServer {
 };
 
 class NrIceTurnServer : public NrIceStunServer {
  public:
   static NrIceTurnServer *Create(const std::string& addr, uint16_t port,
                                  const std::string& username,
                                  const std::vector<unsigned char>& password,
                                  const std::string& transport = kNrIceTransportUdp) {
-    // TODO: Bug 906968 - Support TCP
     ScopedDeletePtr<NrIceTurnServer> server(
-        new NrIceTurnServer(username, password));
+        new NrIceTurnServer(username, password, transport));
 
     nsresult rv = server->Init(addr, port);
     if (NS_FAILED(rv))
       return nullptr;
 
     return server.forget();
   }
 
   nsresult ToNicerTurnStruct(nr_ice_turn_server *server) const;
 
  private:
   NrIceTurnServer(const std::string& username,
-                  const std::vector<unsigned char>& password) :
-      username_(username), password_(password) {}
+                  const std::vector<unsigned char>& password,
+                  const std::string& transport) :
+      username_(username), password_(password), transport_(transport) {}
 
   std::string username_;
   std::vector<unsigned char> password_;
+  std::string transport_;
 };
 
 class NrIceCtx {
  public:
   enum ConnectionState { ICE_CTX_INIT,
                          ICE_CTX_CHECKING,
                          ICE_CTX_OPEN,
                          ICE_CTX_FAILED
--- a/media/mtransport/nricemediastream.cpp
+++ b/media/mtransport/nricemediastream.cpp
@@ -68,32 +68,67 @@ extern "C" {
 // Local includes
 #include "nricectx.h"
 #include "nricemediastream.h"
 
 namespace mozilla {
 
 MOZ_MTLOG_MODULE("mtransport")
 
+static bool ToNrIceAddr(nr_transport_addr &addr,
+                        NrIceAddr *out) {
+  int r;
+  char addrstring[INET6_ADDRSTRLEN + 1];
+
+  r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring));
+  if (r)
+    return false;
+  out->host = addrstring;
+
+  int port;
+  r = nr_transport_addr_get_port(&addr, &port);
+  if (r)
+    return false;
+
+  out->port = port;
+
+  switch (addr.protocol) {
+    case IPPROTO_TCP:
+      out->transport = kNrIceTransportTcp;
+      break;
+    case IPPROTO_UDP:
+      out->transport = kNrIceTransportUdp;
+      break;
+    default:
+      MOZ_CRASH();
+      return false;
+  }
+
+  return true;
+}
+
 static bool ToNrIceCandidate(const nr_ice_candidate& candc,
                              NrIceCandidate* out) {
   MOZ_ASSERT(out);
   int r;
   // Const-cast because the internal nICEr code isn't const-correct.
   nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc);
-  char addr[INET6_ADDRSTRLEN + 1];
 
-  r = nr_transport_addr_get_addrstring(&cand->addr, addr, sizeof(addr));
-  if (r)
+  if (!ToNrIceAddr(cand->addr, &out->cand_addr))
     return false;
 
-  int port;
-  r = nr_transport_addr_get_port(&cand->addr, &port);
-  if (r)
-    return false;
+  if (cand->isock) {
+    nr_transport_addr addr;
+    r = nr_socket_getaddr(cand->isock->sock, &addr);
+    if (r)
+      return false;
+
+    if (!ToNrIceAddr(addr, &out->local_addr))
+      return false;
+  }
 
   NrIceCandidate::Type type;
 
   switch (cand->type) {
     case HOST:
       type = NrIceCandidate::ICE_HOST;
       break;
     case SERVER_REFLEXIVE:
@@ -104,18 +139,16 @@ static bool ToNrIceCandidate(const nr_ic
       break;
     case RELAYED:
       type = NrIceCandidate::ICE_RELAYED;
       break;
     default:
       return false;
   }
 
-  out->host = addr;
-  out->port = port;
   out->type = type;
   out->codeword = candc.codeword;
   return true;
 }
 
 // Make an NrIceCandidate from the candidate |cand|.
 // This is not a member fxn because we want to hide the
 // defn of nr_ice_candidate but we pass by reference.
--- a/media/mtransport/nricemediastream.h
+++ b/media/mtransport/nricemediastream.h
@@ -59,28 +59,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 
 namespace mozilla {
 
 typedef struct nr_ice_media_stream_ nr_ice_media_stream;
 
 class NrIceCtx;
 
+struct NrIceAddr {
+  std::string host;
+  uint16_t port;
+  std::string transport;
+};
+
 /* A summary of a candidate, for use in asking which candidate
    pair is active */
 struct NrIceCandidate {
   enum Type {
     ICE_HOST,
     ICE_SERVER_REFLEXIVE,
     ICE_PEER_REFLEXIVE,
     ICE_RELAYED
   };
 
-  std::string host;
-  uint16_t port;
+  NrIceAddr cand_addr;
+  NrIceAddr local_addr;
   Type type;
   std::string codeword;
 };
 
 struct NrIceCandidatePair {
 
   enum State {
     STATE_FROZEN,
--- a/media/mtransport/nriceresolver.cpp
+++ b/media/mtransport/nriceresolver.cpp
@@ -146,21 +146,26 @@ int NrIceResolver::resolve(nr_resolver_r
                            int (*cb)(void *cb_arg, nr_transport_addr *addr),
                            void *cb_arg,
                            void **handle) {
   int _status;
   MOZ_ASSERT(allocated_resolvers_ > 0);
   ASSERT_ON_THREAD(sts_thread_);
   nsCOMPtr<PendingResolution> pr;
 
-  if (resource->transport_protocol != IPPROTO_UDP) {
-    MOZ_MTLOG(ML_ERROR, "Only UDP is supported.");
+  if (resource->transport_protocol != IPPROTO_UDP &&
+      resource->transport_protocol != IPPROTO_TCP) {
+    MOZ_MTLOG(ML_ERROR, "Only UDP and TCP are is supported.");
     ABORT(R_NOT_FOUND);
   }
-  pr = new PendingResolution(sts_thread_, resource->port? resource->port : 3478,
+  pr = new PendingResolution(sts_thread_,
+                             resource->port? resource->port : 3478,
+                             resource->transport_protocol ?
+                             resource->transport_protocol :
+                             IPPROTO_UDP,
                              cb, cb_arg);
   if (NS_FAILED(dns_->AsyncResolve(nsAutoCString(resource->domain_name),
                                    nsIDNSService::RESOLVE_DISABLE_IPV6, pr,
                                    sts_thread_, getter_AddRefs(pr->request_)))) {
     MOZ_MTLOG(ML_ERROR, "AsyncResolve failed.");
     ABORT(R_NOT_FOUND);
   }
   // Because the C API offers no "finished" method to release the handle we
@@ -183,17 +188,18 @@ nsresult NrIceResolver::PendingResolutio
   // thread, but cancel() cannot guarantee this event isn't on the queue.
   if (!canceled_) {
     nr_transport_addr *cb_addr = nullptr;
     nr_transport_addr ta;
     // TODO(jib@mozilla.com): Revisit when we do TURN.
     if (NS_SUCCEEDED(status)) {
       net::NetAddr na;
       if (NS_SUCCEEDED(record->GetNextAddr(port_, &na))) {
-        MOZ_ALWAYS_TRUE (nr_netaddr_to_transport_addr(&na, &ta) == 0);
+        MOZ_ALWAYS_TRUE (nr_netaddr_to_transport_addr(&na, &ta,
+                                                      transport_) == 0);
         cb_addr = &ta;
       }
     }
     cb_(cb_arg_, cb_addr);
     Release();
   }
   return NS_OK;
 }
--- a/media/mtransport/nriceresolver.h
+++ b/media/mtransport/nriceresolver.h
@@ -81,33 +81,37 @@ class NrIceResolver
 
   int resolve(nr_resolver_resource *resource,
               int (*cb)(void *cb_arg, nr_transport_addr *addr),
               void *cb_arg, void **handle);
 
   class PendingResolution : public nsIDNSListener
   {
    public:
-    PendingResolution(nsIEventTarget *thread, uint16_t port,
+    PendingResolution(nsIEventTarget *thread,
+                      uint16_t port,
+                      int transport,
                       int (*cb)(void *cb_arg, nr_transport_addr *addr),
                       void *cb_arg) :
         thread_(thread),
         port_(port),
+        transport_(transport),
         cb_(cb), cb_arg_(cb_arg),
         canceled_ (false) {}
     virtual ~PendingResolution(){};
     NS_IMETHOD OnLookupComplete(nsICancelable *request, nsIDNSRecord *record,
                                 nsresult status);
     int cancel();
     nsCOMPtr<nsICancelable> request_;
     NS_DECL_THREADSAFE_ISUPPORTS
 
    private:
     nsCOMPtr<nsIEventTarget> thread_;
     uint16_t port_;
+    int transport_;
     int (*cb_)(void *cb_arg, nr_transport_addr *addr);
     void *cb_arg_;
     bool canceled_;
   };
 
   nr_resolver_vtbl* vtbl_;
   nsCOMPtr<nsIEventTarget> sts_thread_;
   nsCOMPtr<nsIDNSService> dns_;
--- a/media/mtransport/nriceresolverfake.cpp
+++ b/media/mtransport/nriceresolverfake.cpp
@@ -110,21 +110,24 @@ int NrIceResolverFake::resolve(void *obj
                                void **handle) {
   int r,_status;
 
   MOZ_ASSERT(obj);
   NrIceResolverFake *fake = static_cast<NrIceResolverFake *>(obj);
 
   MOZ_ASSERT(fake->allocated_resolvers_ > 0);
 
-  PendingResolution *pending = new PendingResolution(fake,
-                                                     resource->domain_name,
-                                                     resource->port ?
-                                                     resource->port : 3478,
-                                                     cb, cb_arg);
+  PendingResolution *pending =
+      new PendingResolution(fake,
+                            resource->domain_name,
+                            resource->port ? resource->port : 3478,
+                            resource->transport_protocol ?
+                            resource->transport_protocol :
+                            IPPROTO_UDP,
+                            cb, cb_arg);
 
   if ((r=NR_ASYNC_TIMER_SET(fake->delay_ms_,NrIceResolverFake::resolve_cb,
                             (void *)pending, &pending->timer_handle_))) {
     delete pending;
     ABORT(r);
   }
   *handle = pending;
 
@@ -137,17 +140,18 @@ void NrIceResolverFake::resolve_cb(NR_SO
   MOZ_ASSERT(cb_arg);
   PendingResolution *pending = static_cast<PendingResolution *>(cb_arg);
 
   const PRNetAddr *addr=pending->resolver_->Resolve(pending->hostname_);
 
   if (addr) {
     nr_transport_addr transport_addr;
 
-    int r = nr_praddr_to_transport_addr(addr, &transport_addr, 0);
+    int r = nr_praddr_to_transport_addr(addr, &transport_addr,
+                                        pending->transport_, 0);
     MOZ_ASSERT(!r);
     if (r)
       goto abort;
 
     r=nr_transport_addr_set_port(&transport_addr, pending->port_);
     MOZ_ASSERT(!r);
     if (r)
       goto abort;
--- a/media/mtransport/nriceresolverfake.h
+++ b/media/mtransport/nriceresolverfake.h
@@ -91,26 +91,29 @@ private:
     return &addrs_[hostname];
   }
 
 
   struct PendingResolution {
     PendingResolution(NrIceResolverFake *resolver,
                       const std::string& hostname,
                       uint16_t port,
+                      int transport,
                       int (*cb)(void *cb_arg, nr_transport_addr *addr),
                       void *cb_arg) :
         resolver_(resolver),
         hostname_(hostname),
         port_(port),
+        transport_(transport),
         cb_(cb), cb_arg_(cb_arg) {}
 
     NrIceResolverFake *resolver_;
     std::string hostname_;
     uint16_t port_;
+    int transport_;
     int (*cb_)(void *cb_arg, nr_transport_addr *addr);
     void *cb_arg_;
     void *timer_handle_;
   };
 
   nr_resolver_vtbl* vtbl_;
   std::map<std::string, PRNetAddr> addrs_;
   uint32_t delay_ms_;
--- a/media/mtransport/test/Makefile.in
+++ b/media/mtransport/test/Makefile.in
@@ -29,16 +29,17 @@ LOCAL_INCLUDES += \
  -I$(topsrcdir)/media/mtransport/third_party/ \
  -I$(topsrcdir)/media/mtransport/third_party/ \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/crypto \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/ice \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/net \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/stun \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/util \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/share \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/ \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/libekr \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/log \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/registry \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/stats \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/plugin \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/event \
  $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/buffered_stun_socket_unittest.cpp
@@ -0,0 +1,444 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include <iostream>
+
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+#include "mozilla/Scoped.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "nr_socket_buffered_stun.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "databuffer.h"
+#include "mtransport_test_utils.h"
+
+#include "nr_socket_prsock.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+MtransportTestUtils *test_utils;
+
+static uint8_t kStunMessage[] = {
+  0x00, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42,
+  0x9b, 0x90, 0xbe, 0x2c, 0xae, 0x1a, 0x0c, 0xa8,
+  0xa0, 0xd6, 0x8b, 0x08, 0x80, 0x28, 0x00, 0x04,
+  0xdb, 0x35, 0x5f, 0xaa
+};
+static size_t kStunMessageLen = sizeof(kStunMessage);
+
+class DummySocket : public NrSocketBase {
+ public:
+  DummySocket()
+      : writable_(UINT_MAX),
+        write_buffer_(nullptr),
+        readable_(UINT_MAX),
+        read_buffer_(nullptr),
+        cb_(nullptr),
+        cb_arg_(nullptr),
+        self_(nullptr) {}
+
+  // the nr_socket APIs
+  virtual int create(nr_transport_addr *addr) {
+    return 0;
+  }
+
+  virtual int sendto(const void *msg, size_t len,
+                     int flags, nr_transport_addr *to) {
+    MOZ_CRASH();
+    return 0;
+  }
+
+  virtual int recvfrom(void * buf, size_t maxlen,
+                       size_t *len, int flags,
+                       nr_transport_addr *from) {
+    MOZ_CRASH();
+    return 0;
+  }
+
+  virtual int getaddr(nr_transport_addr *addrp) {
+    MOZ_CRASH();
+    return 0;
+  }
+
+  virtual void close() {
+  }
+
+  virtual int connect(nr_transport_addr *addr) {
+    return 0;
+  }
+
+  virtual int write(const void *msg, size_t len, size_t *written) {
+    // Shouldn't be anything here.
+    EXPECT_EQ(nullptr, write_buffer_.get());
+
+    size_t to_write = std::min(len, writable_);
+
+    if (to_write) {
+      write_buffer_ = new DataBuffer(
+          static_cast<const uint8_t *>(msg), to_write);
+      *written = to_write;
+    }
+
+    return 0;
+  }
+
+  virtual int read(void* buf, size_t maxlen, size_t *len) {
+    if (!read_buffer_.get()) {
+      return R_WOULDBLOCK;
+    }
+
+    size_t to_read = std::min(read_buffer_->len(),
+                              std::min(maxlen, readable_));
+
+    memcpy(buf, read_buffer_->data(), to_read);
+    *len = to_read;
+
+    if (to_read < read_buffer_->len()) {
+      read_buffer_ = new DataBuffer(read_buffer_->data() + to_read,
+                                    read_buffer_->len() - to_read);
+    } else {
+      read_buffer_ = nullptr;
+    }
+
+    return 0;
+  }
+
+  // Implementations of the async_event APIs.
+  // These are no-ops because we handle scheduling manually
+  // for test purposes.
+  virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
+                         char *function, int line) {
+    EXPECT_EQ(nullptr, cb_);
+    cb_ = cb;
+    cb_arg_ = cb_arg;
+
+    return 0;
+  }
+
+  virtual int cancel(int how) {
+    cb_ = nullptr;
+    cb_arg_ = nullptr;
+
+    return 0;
+  }
+
+
+  // Read/Manipulate the current state.
+  void CheckWriteBuffer(uint8_t *data, size_t len) {
+    if (!len) {
+      EXPECT_EQ(nullptr, write_buffer_.get());
+    } else {
+      EXPECT_NE(nullptr, write_buffer_.get());
+      ASSERT_EQ(len, write_buffer_->len());
+      ASSERT_EQ(0, memcmp(data, write_buffer_->data(), len));
+    }
+  }
+
+  void ClearWriteBuffer() {
+    write_buffer_ = nullptr;
+  }
+
+  void SetWritable(size_t val) {
+    writable_ = val;
+  }
+
+  void FireWritableCb() {
+    NR_async_cb cb = cb_;
+    void *cb_arg = cb_arg_;
+
+    cb_ = nullptr;
+    cb_arg_ = nullptr;
+
+    cb(this, NR_ASYNC_WAIT_WRITE, cb_arg);
+  }
+
+  void SetReadBuffer(uint8_t *data, size_t len) {
+    EXPECT_EQ(nullptr, write_buffer_.get());
+    read_buffer_ = new DataBuffer(data, len);
+  }
+
+  void ClearReadBuffer() {
+    read_buffer_ = nullptr;
+  }
+
+  void SetReadable(size_t val) {
+    readable_ = val;
+  }
+
+  nr_socket *get_nr_socket() {
+    if (!self_) {
+      int r = nr_socket_create_int(this, vtbl(), &self_);
+      AddRef();
+      if (r)
+        return nullptr;
+    }
+
+    return self_;
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket);
+
+ private:
+  DISALLOW_COPY_ASSIGN(DummySocket);
+
+  size_t writable_;  // Amount we allow someone to write.
+  ScopedDeletePtr<DataBuffer> write_buffer_;
+  size_t readable_;   // Amount we allow someone to read.
+  ScopedDeletePtr<DataBuffer> read_buffer_;
+
+  NR_async_cb cb_;
+  void *cb_arg_;
+  nr_socket *self_;
+};
+
+class BufferedStunSocketTest : public ::testing::Test {
+ public:
+  BufferedStunSocketTest()
+      : dummy_(nullptr),
+        test_socket_(nullptr) {}
+
+  ~BufferedStunSocketTest() {
+    nr_socket_destroy(&test_socket_);
+  }
+
+  void SetUp() {
+    ScopedDeletePtr<DummySocket> dummy(new DummySocket());
+
+    int r = nr_socket_buffered_stun_create(
+        dummy->get_nr_socket(),
+        kStunMessageLen,
+        &test_socket_);
+    ASSERT_EQ(0, r);
+    dummy_ = dummy.forget();  // Now owned by test_socket_.
+
+    r = nr_ip4_str_port_to_transport_addr(
+        (char *)"192.0.2.133", 3333, IPPROTO_TCP, &remote_addr_);
+    ASSERT_EQ(0, r);
+
+    r = nr_socket_connect(test_socket_,
+                          &remote_addr_);
+    ASSERT_EQ(0, r);
+  }
+
+  nr_socket *socket() { return test_socket_; }
+
+ protected:
+  DummySocket *dummy_;
+  nr_socket *test_socket_;
+  nr_transport_addr remote_addr_;
+};
+
+
+TEST_F(BufferedStunSocketTest, TestCreate) {
+}
+
+TEST_F(BufferedStunSocketTest, TestSendTo) {
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToBuffered) {
+  dummy_->SetWritable(0);
+
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(nullptr, 0);
+
+  dummy_->SetWritable(kStunMessageLen);
+  dummy_->FireWritableCb();
+  dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToPartialBuffered) {
+  dummy_->SetWritable(10);
+
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(kStunMessage, 10);
+  dummy_->ClearWriteBuffer();
+
+  dummy_->SetWritable(kStunMessageLen);
+  dummy_->FireWritableCb();
+  dummy_->CheckWriteBuffer(kStunMessage + 10, kStunMessageLen - 10);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToReject) {
+  dummy_->SetWritable(0);
+
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(nullptr, 0);
+
+  r = nr_socket_sendto(test_socket_,
+                       kStunMessage,
+                       kStunMessageLen,
+                       0, &remote_addr_);
+  ASSERT_EQ(R_WOULDBLOCK, r);
+
+  dummy_->CheckWriteBuffer(nullptr, 0);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToWrongAddr) {
+  nr_transport_addr addr;
+
+  int r = nr_ip4_str_port_to_transport_addr(
+      (char *)"192.0.2.134", 3333, IPPROTO_TCP, &addr);
+  ASSERT_EQ(0, r);
+
+  r = nr_socket_sendto(test_socket_,
+                       kStunMessage,
+                       kStunMessageLen,
+                       0, &addr);
+  ASSERT_EQ(R_BAD_DATA, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFrom) {
+  dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, sizeof(tmp), &len, 0,
+                             &addr);
+  ASSERT_EQ(0, r);
+  ASSERT_EQ(kStunMessageLen, len);
+  ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
+  ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
+                                     NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromPartial) {
+  dummy_->SetReadBuffer(kStunMessage, 15);
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, sizeof(tmp), &len, 0,
+                             &addr);
+  ASSERT_EQ(R_WOULDBLOCK, r);
+
+
+  dummy_->SetReadBuffer(kStunMessage + 15, kStunMessageLen - 15);
+
+  r = nr_socket_recvfrom(test_socket_,
+                         tmp, sizeof(tmp), &len, 0,
+                         &addr);
+  ASSERT_EQ(0, r);
+  ASSERT_EQ(kStunMessageLen, len);
+  ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
+  ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
+                                     NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+
+  r = nr_socket_recvfrom(test_socket_,
+                         tmp, sizeof(tmp), &len, 0,
+                         &addr);
+  ASSERT_EQ(R_WOULDBLOCK, r);
+}
+
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromGarbage) {
+  uint8_t garbage[50];
+  memset(garbage, 0xff, sizeof(garbage));
+
+  dummy_->SetReadBuffer(garbage, sizeof(garbage));
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, sizeof(tmp), &len, 0,
+                             &addr);
+  ASSERT_EQ(R_BAD_DATA, r);
+
+  r = nr_socket_recvfrom(test_socket_,
+                         tmp, sizeof(tmp), &len, 0,
+                         &addr);
+  ASSERT_EQ(R_FAILED, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromTooShort) {
+  dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, kStunMessageLen - 1, &len, 0,
+                             &addr);
+  ASSERT_EQ(R_BAD_ARGS, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromReallyLong) {
+  uint8_t garbage[4096];
+  memset(garbage, 0xff, sizeof(garbage));
+  memcpy(garbage, kStunMessage, kStunMessageLen);
+  nr_stun_message_header *hdr = reinterpret_cast<nr_stun_message_header *>
+      (garbage);
+  hdr->length = htons(3000);
+
+  dummy_->SetReadBuffer(garbage, sizeof(garbage));
+
+  unsigned char tmp[4096];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, kStunMessageLen - 1, &len, 0,
+                             &addr);
+  ASSERT_EQ(R_BAD_DATA, r);
+}
+
+
+
+int main(int argc, char **argv)
+{
+  test_utils = new MtransportTestUtils();
+  NSS_NoDB_Init(nullptr);
+  NSS_SetDomesticPolicy();
+
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  int rv = RUN_ALL_TESTS();
+
+  delete test_utils;
+  return rv;
+}
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -82,34 +82,31 @@ bool ContainsSucceededPair(const std::ve
   }
   return false;
 }
 
 // Note: Does not correspond to any notion of prioritization; this is just
 // so we can use stl containers/algorithms that need a comparator
 bool operator<(const NrIceCandidate& lhs,
                const NrIceCandidate& rhs) {
-  if (lhs.host == rhs.host) {
-    if (lhs.port == rhs.port) {
-      if (lhs.type == rhs.type) {
-        return lhs.codeword < rhs.codeword;
+  if (lhs.cand_addr.host == rhs.cand_addr.host) {
+    if (lhs.cand_addr.port == rhs.cand_addr.port) {
+      if (lhs.cand_addr.transport == rhs.cand_addr.transport) {
+        return lhs.type < rhs.type;
       }
-      return lhs.type < rhs.type;
+      return lhs.cand_addr.transport < rhs.cand_addr.transport;
     }
-    return lhs.port < rhs.port;
+    return lhs.cand_addr.port < rhs.cand_addr.port;
   }
-  return lhs.host < rhs.host;
+  return lhs.cand_addr.host < rhs.cand_addr.host;
 }
 
 bool operator==(const NrIceCandidate& lhs,
                 const NrIceCandidate& rhs) {
-  return lhs.host == rhs.host &&
-         lhs.port == rhs.port &&
-         lhs.type == rhs.type &&
-         lhs.codeword == rhs.codeword;
+  return !((lhs < rhs) || (rhs < lhs));
 }
 
 class IceCandidatePairCompare {
   public:
     bool operator()(const NrIceCandidatePair& lhs,
                     const NrIceCandidatePair& rhs) const {
       if (lhs.priority == rhs.priority) {
         if (lhs.local == rhs.local) {
@@ -137,16 +134,17 @@ class IceTestPeer : public sigslot::has_
       ice_complete_(false),
       received_(0),
       sent_(0),
       fake_resolver_(),
       dns_resolver_(new NrIceResolver()),
       remote_(nullptr),
       candidate_filter_(nullptr),
       expected_local_type_(NrIceCandidate::ICE_HOST),
+      expected_local_transport_(kNrIceTransportUdp),
       expected_remote_type_(NrIceCandidate::ICE_HOST),
       trickle_mode_(TRICKLE_NONE),
       trickled_(0) {
     ice_ctx_->SignalGatheringStateChange.connect(
         this,
         &IceTestPeer::GatheringStateChange);
     ice_ctx_->SignalConnectionStateChange.connect(
         this,
@@ -184,32 +182,38 @@ class IceTestPeer : public sigslot::has_
     ScopedDeletePtr<NrIceStunServer> server(NrIceStunServer::Create(addr,
                                                                     port));
     stun_servers.push_back(*server);
     ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
   }
 
   void SetTurnServer(const std::string addr, uint16_t port,
                      const std::string username,
-                     const std::string password) {
+                     const std::string password,
+                     const std::string transport) {
     std::vector<unsigned char> password_vec(password.begin(), password.end());
-    SetTurnServer(addr, port, username, password_vec);
+    SetTurnServer(addr, port, username, password_vec, transport);
   }
 
 
   void SetTurnServer(const std::string addr, uint16_t port,
                      const std::string username,
-                     const std::vector<unsigned char> password) {
+                     const std::vector<unsigned char> password,
+                     const std::string transport) {
     std::vector<NrIceTurnServer> turn_servers;
     ScopedDeletePtr<NrIceTurnServer> server(NrIceTurnServer::Create(
-        addr, port, username, password));
+        addr, port, username, password, transport));
     turn_servers.push_back(*server);
     ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetTurnServers(turn_servers)));
   }
 
+  void SetTurnServers(const std::vector<NrIceTurnServer> servers) {
+    ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetTurnServers(servers)));
+  }
+
   void SetFakeResolver() {
     ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
     PRNetAddr addr;
     PRStatus status = PR_StringToNetAddr(g_stun_server_address.c_str(),
                                           &addr);
     addr.inet.port = kDefaultStunServerPort;
     ASSERT_EQ(PR_SUCCESS, status);
     fake_resolver_.SetAddr(g_stun_server_hostname, addr);
@@ -265,18 +269,20 @@ class IceTestPeer : public sigslot::has_
         candidates.push_back(candidates_in[i]);
       }
     }
 
     return candidates;
   }
 
   void SetExpectedTypes(NrIceCandidate::Type local,
-                        NrIceCandidate::Type remote) {
+                        NrIceCandidate::Type remote,
+                        std::string local_transport = kNrIceTransportUdp) {
     expected_local_type_ = local;
+    expected_local_transport_ = local_transport;
     expected_remote_type_ = remote;
   }
 
   bool gathering_complete() { return gathering_complete_; }
   int ready_ct() { return ready_ct_; }
   bool is_ready(size_t stream) {
     return streams_[stream]->state() == NrIceMediaStream::ICE_OPEN;
   }
@@ -363,58 +369,68 @@ class IceTestPeer : public sigslot::has_
       case NrIceCandidate::ICE_SERVER_REFLEXIVE:
         type = "srflx";
         break;
       case NrIceCandidate::ICE_PEER_REFLEXIVE:
         type = "prflx";
         break;
       case NrIceCandidate::ICE_RELAYED:
         type = "relay";
+        if (which.find("Local") != std::string::npos) {
+          type += "(" + cand.local_addr.transport + ")";
+        }
         break;
       default:
         FAIL();
     };
 
     std::cerr << which
               << " --> "
               << type
               << " "
-              << cand.host
+              << cand.local_addr.host
               << ":"
-              << cand.port
+              << cand.local_addr.port
               << " codeword="
               << cand.codeword
               << std::endl;
   }
 
-  void DumpAndCheckActiveCandidates() {
+  void DumpAndCheckActiveCandidates_s() {
     std::cerr << "Active candidates:" << std::endl;
     for (size_t i=0; i < streams_.size(); ++i) {
       for (int j=0; j < streams_[i]->components(); ++j) {
         std::cerr << "Stream " << i << " component " << j+1 << std::endl;
 
         NrIceCandidate *local;
         NrIceCandidate *remote;
 
         nsresult res = streams_[i]->GetActivePair(j+1, &local, &remote);
         if (res == NS_ERROR_NOT_AVAILABLE) {
           std::cerr << "Component unpaired or disabled." << std::endl;
         } else {
           ASSERT_TRUE(NS_SUCCEEDED(res));
           DumpCandidate("Local  ", *local);
           ASSERT_EQ(expected_local_type_, local->type);
+          ASSERT_EQ(expected_local_transport_, local->local_addr.transport);
           DumpCandidate("Remote ", *remote);
           ASSERT_EQ(expected_remote_type_, remote->type);
           delete local;
           delete remote;
         }
       }
     }
   }
 
+  void DumpAndCheckActiveCandidates() {
+    test_utils->sts_target()->Dispatch(
+      WrapRunnable(this, &IceTestPeer::DumpAndCheckActiveCandidates_s),
+      NS_DISPATCH_SYNC);
+  }
+
   void Close() {
     test_utils->sts_target()->Dispatch(
       WrapRunnable(ice_ctx_, &NrIceCtx::destroy_peer_ctx),
       NS_DISPATCH_SYNC);
   }
 
   void Shutdown() {
     ice_ctx_ = nullptr;
@@ -663,16 +679,17 @@ class IceTestPeer : public sigslot::has_
   bool ice_complete_;
   size_t received_;
   size_t sent_;
   NrIceResolverFake fake_resolver_;
   nsRefPtr<NrIceResolver> dns_resolver_;
   IceTestPeer *remote_;
   CandidateFilter candidate_filter_;
   NrIceCandidate::Type expected_local_type_;
+  std::string expected_local_transport_;
   NrIceCandidate::Type expected_remote_type_;
   TrickleMode trickle_mode_;
   int trickled_;
 };
 
 class IceGatherTest : public ::testing::Test {
  public:
   void SetUp() {
@@ -759,19 +776,25 @@ class IceConnectTest : public ::testing:
       if (!p2_->gathering_complete())
         return false;
     }
     return true;
   }
 
   void SetTurnServer(const std::string addr, uint16_t port,
                      const std::string username,
-                     const std::string password) {
-    p1_->SetTurnServer(addr, port, username, password);
-    p2_->SetTurnServer(addr, port, username, password);
+                     const std::string password,
+                     const std::string transport = kNrIceTransportUdp) {
+    p1_->SetTurnServer(addr, port, username, password, transport);
+    p2_->SetTurnServer(addr, port, username, password, transport);
+  }
+
+  void SetTurnServers(const std::vector<NrIceTurnServer>& servers) {
+    p1_->SetTurnServers(servers);
+    p2_->SetTurnServers(servers);
   }
 
   void SetCandidateFilter(CandidateFilter filter, bool both=true) {
     p1_->SetCandidateFilter(filter);
     if (both) {
       p2_->SetCandidateFilter(filter);
     }
   }
@@ -782,19 +805,20 @@ class IceConnectTest : public ::testing:
 
     ASSERT_TRUE_WAIT(p1_->ready_ct() == 1 && p2_->ready_ct() == 1, 5000);
     ASSERT_TRUE_WAIT(p1_->ice_complete() && p2_->ice_complete(), 5000);
 
     p1_->DumpAndCheckActiveCandidates();
     p2_->DumpAndCheckActiveCandidates();
   }
 
-  void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote) {
-    p1_->SetExpectedTypes(local, remote);
-    p2_->SetExpectedTypes(local, remote);
+  void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote,
+                        std::string transport = kNrIceTransportUdp) {
+    p1_->SetExpectedTypes(local, remote, transport);
+    p2_->SetExpectedTypes(local, remote, transport);
   }
 
   void SetExpectedTypes(NrIceCandidate::Type local1, NrIceCandidate::Type remote1,
                         NrIceCandidate::Type local2, NrIceCandidate::Type remote2) {
     p1_->SetExpectedTypes(local1, remote1);
     p2_->SetExpectedTypes(local2, remote2);
   }
 
@@ -1011,17 +1035,25 @@ TEST_F(IceGatherTest, TestGatherDNSStunB
   peer_->SetDNSResolver();
   Gather();
 }
 
 TEST_F(IceGatherTest, TestGatherTurn) {
   if (g_turn_server.empty())
     return;
   peer_->SetTurnServer(g_turn_server, kDefaultStunServerPort,
-                       g_turn_user, g_turn_password);
+                       g_turn_user, g_turn_password, kNrIceTransportUdp);
+  Gather();
+}
+
+TEST_F(IceGatherTest, TestGatherTurnTcp) {
+  if (g_turn_server.empty())
+    return;
+  peer_->SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                       g_turn_user, g_turn_password, kNrIceTransportTcp);
   Gather();
 }
 
 TEST_F(IceGatherTest, TestGatherDisableComponent) {
   peer_->SetStunServer(kDefaultStunServerHostname, kDefaultStunServerPort);
   peer_->AddStream(2);
   peer_->DisableComponent(1, 2);
   Gather();
@@ -1193,45 +1225,112 @@ TEST_F(IceConnectTest, TestConnectTurn) 
 
   AddStream("first", 1);
   SetTurnServer(g_turn_server, kDefaultStunServerPort,
                 g_turn_user, g_turn_password);
   ASSERT_TRUE(Gather(true));
   Connect();
 }
 
+TEST_F(IceConnectTest, TestConnectTurnTcp) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                g_turn_user, g_turn_password, kNrIceTransportTcp);
+  ASSERT_TRUE(Gather(true));
+  Connect();
+}
+
 TEST_F(IceConnectTest, TestConnectTurnOnly) {
   if (g_turn_server.empty())
     return;
 
   AddStream("first", 1);
   SetTurnServer(g_turn_server, kDefaultStunServerPort,
                 g_turn_user, g_turn_password);
   ASSERT_TRUE(Gather(true));
   SetCandidateFilter(IsRelayCandidate);
   SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
                    NrIceCandidate::Type::ICE_RELAYED);
   Connect();
 }
 
+TEST_F(IceConnectTest, TestConnectTurnTcpOnly) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                g_turn_user, g_turn_password, kNrIceTransportTcp);
+  ASSERT_TRUE(Gather(true));
+  SetCandidateFilter(IsRelayCandidate);
+  SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+                   NrIceCandidate::Type::ICE_RELAYED,
+                   kNrIceTransportTcp);
+  Connect();
+}
+
 TEST_F(IceConnectTest, TestSendReceiveTurnOnly) {
   if (g_turn_server.empty())
     return;
 
   AddStream("first", 1);
   SetTurnServer(g_turn_server, kDefaultStunServerPort,
                 g_turn_user, g_turn_password);
   ASSERT_TRUE(Gather(true));
   SetCandidateFilter(IsRelayCandidate);
   SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
                    NrIceCandidate::Type::ICE_RELAYED);
   Connect();
   SendReceive();
 }
 
+TEST_F(IceConnectTest, TestSendReceiveTurnTcpOnly) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                g_turn_user, g_turn_password, kNrIceTransportTcp);
+  ASSERT_TRUE(Gather(true));
+  SetCandidateFilter(IsRelayCandidate);
+  SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+                   NrIceCandidate::Type::ICE_RELAYED,
+                   kNrIceTransportTcp);
+  Connect();
+  SendReceive();
+}
+
+TEST_F(IceConnectTest, TestSendReceiveTurnBothOnly) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  std::vector<NrIceTurnServer> turn_servers;
+  std::vector<unsigned char> password_vec(g_turn_password.begin(),
+                                          g_turn_password.end());
+  turn_servers.push_back(*NrIceTurnServer::Create(
+                           g_turn_server, kDefaultStunServerPort,
+                           g_turn_user, password_vec, kNrIceTransportTcp));
+  turn_servers.push_back(*NrIceTurnServer::Create(
+                           g_turn_server, kDefaultStunServerPort,
+                           g_turn_user, password_vec, kNrIceTransportUdp));
+  SetTurnServers(turn_servers);
+  ASSERT_TRUE(Gather(true));
+  SetCandidateFilter(IsRelayCandidate);
+  // UDP is preferred.
+  SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+                   NrIceCandidate::Type::ICE_RELAYED,
+                   kNrIceTransportUdp);
+  Connect();
+  SendReceive();
+}
+
 TEST_F(IceConnectTest, TestConnectShutdownOneSide) {
   AddStream("first", 1);
   ASSERT_TRUE(Gather(true));
   ConnectThenDelete();
 }
 
 TEST_F(IceConnectTest, TestPollCandPairsBeforeConnect) {
   AddStream("first", 1);
--- a/media/mtransport/test/moz.build
+++ b/media/mtransport/test/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     CPP_UNIT_TESTS += [
+        'buffered_stun_socket_unittest.cpp',
         'ice_unittest.cpp',
         'nrappkit_unittest.cpp',
         'rlogringbuffer_unittest.cpp',
         'runnable_utils_unittest.cpp',
         'simpletokenbucket_unittest.cpp',
         'sockettransportservice_unittest.cpp',
         'TestSyncRunnable.cpp',
         'transport_unittests.cpp',
--- a/media/mtransport/test/mtransport_test_utils.h
+++ b/media/mtransport/test/mtransport_test_utils.h
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Original author: ekr@rtfm.com
 
 #ifndef mtransport_test_utils_h__
 #define mtransport_test_utils_h__
 
+#include <iostream>
+
 #include "nspr.h"
 #include "nsCOMPtr.h"
 #include "nsNetCID.h"
 #include "nsXPCOMGlue.h"
 #include "nsXPCOM.h"
 
 #include "nsIComponentManager.h"
 #include "nsIComponentRegistrar.h"
--- a/media/mtransport/test/stunserver.cpp
+++ b/media/mtransport/test/stunserver.cpp
@@ -149,21 +149,25 @@ static int nr_socket_wrapped_close(void 
 
 static int nr_socket_wrapped_set_send_addr(nr_socket *sock, nr_transport_addr *addr) {
   nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(sock->obj);
 
   return nr_transport_addr_copy(&wrapped->addr_, addr);
 }
 
 static nr_socket_vtbl nr_socket_wrapped_vtbl = {
+  1,
   nr_socket_wrapped_destroy,
   nr_socket_wrapped_sendto,
   nr_socket_wrapped_recvfrom,
   nr_socket_wrapped_getfd,
   nr_socket_wrapped_getaddr,
+  0,
+  0,
+  0,
   nr_socket_wrapped_close
 };
 
 int nr_socket_wrapped_create(nr_socket *inner, nr_socket **outp) {
   ScopedDeletePtr<nr_socket_wrapped> wrapped(new nr_socket_wrapped());
 
   wrapped->sock_ = inner;
 
--- a/media/mtransport/test/turn_unittest.cpp
+++ b/media/mtransport/test/turn_unittest.cpp
@@ -66,16 +66,17 @@ extern "C" {
 #include "registry.h"
 #include "async_timer.h"
 #include "r_crc32.h"
 #include "ice_util.h"
 #include "transport_addr.h"
 #include "nr_crypto.h"
 #include "nr_socket.h"
 #include "nr_socket_local.h"
+#include "nr_socket_buffered_stun.h"
 #include "stun_client_ctx.h"
 #include "turn_client_ctx.h"
 }
 
 #include "nricemediastream.h"
 #include "nricectx.h"
 
 
@@ -88,72 +89,91 @@ std::string g_turn_user;
 std::string g_turn_password;
 
 std::string kDummyTurnServer("192.0.2.1");  // From RFC 5737
 
 class TurnClient : public ::testing::Test {
  public:
   TurnClient()
       : turn_server_(g_turn_server),
+        real_socket_(nullptr),
         net_socket_(nullptr),
+        buffered_socket_(nullptr),
+        net_fd_(nullptr),
         turn_ctx_(nullptr),
         allocated_(false),
-        received_(0) {}
+        received_(0),
+        protocol_(IPPROTO_UDP) {
+  }
 
   ~TurnClient() {
   }
 
+  void SetTcp() {
+    protocol_ = IPPROTO_TCP;
+  }
+
   void Init_s() {
     int r;
+    nr_transport_addr addr;
+    r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr);
+    ASSERT_EQ(0, r);
 
-    nr_transport_addr addr;
-    r = nr_ip4_port_to_transport_addr(0, 0, IPPROTO_UDP, &addr);
+    r = nr_socket_local_create(&addr, &real_socket_);
     ASSERT_EQ(0, r);
 
-    r = nr_socket_local_create(&addr, &net_socket_);
-    ASSERT_EQ(0, r);
+    if (protocol_ == IPPROTO_TCP) {
+      int r =
+          nr_socket_buffered_stun_create(real_socket_, 100000,
+                                         &buffered_socket_);
+      ASSERT_EQ(0, r);
+      net_socket_ = buffered_socket_;
+    } else {
+      net_socket_ = real_socket_;
+    }
 
     r = nr_ip4_str_port_to_transport_addr(turn_server_.c_str(), 3478,
-      IPPROTO_UDP, &addr);
+      protocol_, &addr);
     ASSERT_EQ(0, r);
 
-
     std::vector<unsigned char> password_vec(
         g_turn_password.begin(), g_turn_password.end());
     Data password;
     INIT_DATA(password, &password_vec[0], password_vec.size());
     r = nr_turn_client_ctx_create("test", net_socket_,
                                   g_turn_user.c_str(),
                                   &password,
                                   &addr, &turn_ctx_);
     ASSERT_EQ(0, r);
 
     r = nr_socket_getfd(net_socket_, &net_fd_);
     ASSERT_EQ(0, r);
 
     NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb,
-                  (void *)this);
+        (void *)this);
   }
 
   void TearDown_s() {
     nr_turn_client_ctx_destroy(&turn_ctx_);
-    if (net_socket_) {
+    if (net_fd_) {
       NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ);
     }
-    nr_socket_destroy(&net_socket_);
+
+    nr_socket_destroy(&buffered_socket_);
   }
 
   void TearDown() {
     RUN_ON_THREAD(test_utils->sts_target(),
                   WrapRunnable(this, &TurnClient::TearDown_s),
                   NS_DISPATCH_SYNC);
   }
 
   void Allocate_s() {
     Init_s();
+    ASSERT_TRUE(turn_ctx_);
 
     int r = nr_turn_client_allocate(turn_ctx_,
                                     allocate_success_cb,
                                     this);
     ASSERT_EQ(0, r);
   }
 
   void Allocate(bool expect_success=true) {
@@ -288,26 +308,33 @@ class TurnClient : public ::testing::Tes
   }
 
   static void allocate_success_cb(NR_SOCKET s, int how, void *arg){
     static_cast<TurnClient *>(arg)->Allocated();
   }
 
  protected:
   std::string turn_server_;
+  nr_socket *real_socket_;
   nr_socket *net_socket_;
+  nr_socket *buffered_socket_;
   NR_SOCKET net_fd_;
   nr_turn_client_ctx *turn_ctx_;
   std::string relay_addr_;
   bool allocated_;
   int received_;
+  int protocol_;
 };
 
+TEST_F(TurnClient, Allocate) {
+  Allocate();
+}
 
-TEST_F(TurnClient, Allocate) {
+TEST_F(TurnClient, AllocateTcp) {
+  SetTcp();
   Allocate();
 }
 
 TEST_F(TurnClient, AllocateAndHold) {
   Allocate();
   PR_Sleep(20000);
 }
 
@@ -316,16 +343,28 @@ TEST_F(TurnClient, SendToSelf) {
   SendTo(relay_addr_);
   ASSERT_TRUE_WAIT(received() == 100, 1000);
   PR_Sleep(10000); // Wait 10 seconds to make sure the
                    // CreatePermission has time to complete/fail.
   SendTo(relay_addr_);
   ASSERT_TRUE_WAIT(received() == 200, 1000);
 }
 
+
+TEST_F(TurnClient, SendToSelfTcp) {
+  SetTcp();
+  Allocate();
+  SendTo(relay_addr_);
+  ASSERT_TRUE_WAIT(received() == 100, 1000);
+  PR_Sleep(10000); // Wait 10 seconds to make sure the
+                   // CreatePermission has time to complete/fail.
+  SendTo(relay_addr_);
+  ASSERT_TRUE_WAIT(received() == 200, 1000);
+}
+
 TEST_F(TurnClient, AllocateDummyServer) {
   turn_server_ = kDummyTurnServer;
   Allocate(false);
 }
 
 static std::string get_environment(const char *name) {
   char *value = getenv(name);
 
--- a/media/mtransport/third_party/nICEr/nicer.gyp
+++ b/media/mtransport/third_party/nICEr/nicer.gyp
@@ -82,16 +82,18 @@
                 "./src/net/nr_interface_prioritizer.c",
                 "./src/net/nr_interface_prioritizer.h",
 
                 # STUN
                 "./src/stun/addrs.c",
                 "./src/stun/addrs.h",
                 "./src/stun/nr_socket_turn.c",
                 "./src/stun/nr_socket_turn.h",
+                "./src/stun/nr_socket_buffered_stun.c",
+                "./src/stun/nr_socket_buffered_stun.h",
                 "./src/stun/stun.h",
                 "./src/stun/stun_build.c",
                 "./src/stun/stun_build.h",
                 "./src/stun/stun_client_ctx.c",
                 "./src/stun/stun_client_ctx.h",
                 "./src/stun/stun_codec.c",
                 "./src/stun/stun_codec.h",
                 "./src/stun/stun_hint.c",
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
@@ -437,16 +437,17 @@ static void nr_ice_candidate_fire_ready_
     cand->ready_cb_timer = 0;
     cand->ready_cb(0, 0, cand->ready_cb_arg);
   }
 
 int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg)
   {
     int r,_status;
     int protocol=NR_RESOLVE_PROTOCOL_STUN;
+    int transport=IPPROTO_UDP;
     cand->done_cb=ready_cb;
     cand->cb_arg=cb_arg;
 
     switch(cand->type){
       case HOST:
         if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr))
           ABORT(r);
         cand->osock=cand->isock->sock;
@@ -454,16 +455,17 @@ int nr_ice_candidate_initialize(nr_ice_c
         // Post this so that it doesn't happen in-line
         cand->ready_cb = ready_cb;
         cand->ready_cb_arg = cb_arg;
         NR_ASYNC_TIMER_SET(0, nr_ice_candidate_fire_ready_cb, (void *)cand, &cand->ready_cb_timer);
         break;
 #ifdef USE_TURN
       case RELAYED:
         protocol=NR_RESOLVE_PROTOCOL_TURN;
+        transport=cand->u.relayed.server->transport;
         /* Fall through */
 #endif
       case SERVER_REFLEXIVE:
         cand->state=NR_ICE_CAND_STATE_INITIALIZING;
 
         if(cand->stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
           /* Just copy the address */
           if (r=nr_transport_addr_copy(&cand->stun_server_addr,
@@ -475,17 +477,17 @@ int nr_ice_candidate_initialize(nr_ice_c
           if(r=nr_ice_candidate_initialize2(cand))
             ABORT(r);
         }
         else {
           nr_resolver_resource resource;
           resource.domain_name=cand->stun_server->u.dnsname.host;
           resource.port=cand->stun_server->u.dnsname.port;
           resource.stun_turn=protocol;
-          resource.transport_protocol=IPPROTO_UDP;  /* We don't support TCP yet */
+          resource.transport_protocol=transport;
 
           /* Try to resolve */
           if(!cand->ctx->resolver) {
             r_log(LOG_ICE, LOG_ERR, "ICE-CANDIDATE(%s): Can't use DNS names without a resolver", cand->label);
             ABORT(R_BAD_ARGS);
           }
 
           if(r=nr_resolver_resolve(cand->ctx->resolver,
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -39,16 +39,17 @@ static char *RCSSTRING __UNUSED__="$Id: 
 #include <nr_api.h>
 #include <registry.h>
 #include <async_timer.h>
 #include "ice_ctx.h"
 #include "ice_codeword.h"
 #include "stun.h"
 #include "nr_socket_local.h"
 #include "nr_socket_turn.h"
+#include "nr_socket_buffered_stun.h"
 #include "ice_reg.h"
 
 static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error);
 static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp);
 
 /* This function takes ownership of the contents of req (but not req itself) */
 static int nr_ice_pre_answer_request_create(nr_socket *sock, nr_stun_server_request *req, nr_ice_pre_answer_request **parp)
   {
@@ -165,62 +166,46 @@ int nr_ice_component_destroy(nr_ice_comp
     if(component->keepalive_timer)
       NR_async_timer_cancel(component->keepalive_timer);
     nr_stun_client_ctx_destroy(&component->keepalive_ctx);
 
     RFREE(component);
     return(0);
   }
 
-/* Make all the candidates we can make at the beginning */
-int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component)
+static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
   {
-    int r,_status;
-    nr_local_addr *addrs=ctx->local_addrs;
     nr_socket *sock;
     nr_ice_socket *isock=0;
     nr_ice_candidate *cand=0;
-    char *lufrag;
-    char *lpwd;
-    Data pwd;
-    int addr_ct=ctx->local_addr_ct;
     int i;
     int j;
     char label[256];
-
-    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/COMP(%d): initializing component",ctx->label,component->component_id);
-
-    if(addr_ct==0){
-      r_log(LOG_ICE,LOG_ERR,"ICE(%s)/COMP(%d): no local addresses available",ctx->label, component->component_id);
-      ABORT(R_NOT_FOUND);
-    }
+    int r,_status;
 
     /* Now one ice_socket for each address */
     for(i=0;i<addr_ct;i++){
       char suppress;
 
       if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
         if(r!=R_NOT_FOUND)
           ABORT(r);
       }
       else{
         if(suppress)
           continue;
       }
-
-
       r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string);
       if(r=nr_socket_local_create(&addrs[i].addr,&sock)){
         r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string);
         continue;
       }
 
       if(r=nr_ice_socket_create(ctx,component,sock,&isock))
         ABORT(r);
-
       /* Create one host candidate */
       if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,
         component->component_id,&cand))
         ABORT(r);
 
       TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
       component->candidate_ct++;
       cand=0;
@@ -237,16 +222,20 @@ int nr_ice_component_initialize(struct n
       }
 
 #ifdef USE_TURN
       /* And both a srvrflx and relayed candidate for each TURN server */
       for(j=0;j<ctx->turn_server_ct;j++){
         nr_socket *turn_sock;
         nr_ice_candidate *srvflx_cand;
 
+        /* Skip non-UDP */
+        if (ctx->turn_servers[j].transport != IPPROTO_UDP)
+          continue;
+
         /* srvrflx */
         if(r=nr_ice_candidate_create(ctx,component,
           isock,sock,SERVER_REFLEXIVE,
           &ctx->turn_servers[j].turn_server,component->component_id,&cand))
           ABORT(r);
         cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
         cand->done_cb=nr_ice_initialize_finished_cb;
         cand->cb_arg=cand;
@@ -272,42 +261,160 @@ int nr_ice_component_initialize(struct n
 #endif /* USE_TURN */
 
       /* Create a STUN server context for this socket */
       snprintf(label, sizeof(label), "server(%s)", addrs[i].addr.as_string);
       if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server))
         ABORT(r);
       if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
         ABORT(r);
+
       /* Add the default STUN credentials so that we can respond before
-         we hear about the peer. Note: we need to recompute these because
-         we have not yet computed the values in the peer media stream.*/
-      lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag;
-      assert(lufrag);
-      if (!lufrag)
-        ABORT(R_INTERNAL);
-      lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
-      assert(lpwd);
-      if (!lpwd)
-        ABORT(R_INTERNAL);
-
-      INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
-
-      if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, &pwd, nr_ice_component_stun_server_default_cb, component))
+         we hear about the peer. */
+      if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component))
         ABORT(r);
 
       STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
     }
-    isock=0;
+
+    _status = 0;
+ abort:
+    return(_status);
+  }
+
+static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
+  {
+    nr_ice_socket *isock=0;
+    nr_ice_candidate *cand=0;
+    int i;
+    int j;
+    char label[256];
+    int r,_status;
+
+    /* Create a new relayed candidate for each addr/TURN server pair */
+    for(i=0;i<addr_ct;i++){
+      char suppress;
+
+      if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
+        if(r!=R_NOT_FOUND)
+          ABORT(r);
+      }
+      else{
+        if(suppress)
+          continue;
+      }
+
+#ifdef USE_TURN
+      for(j=0;j<ctx->turn_server_ct;j++){
+        nr_transport_addr addr;
+        nr_socket *sock;
+        nr_socket *buffered_sock;
+        nr_socket *turn_sock;
+
+        /* Skip non-TCP */
+        if (ctx->turn_servers[j].transport != IPPROTO_TCP)
+          continue;
+
+        /* Create a local socket */
+        if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr)))
+          ABORT(r);
+        addr.protocol = IPPROTO_TCP;
+        if ((r=nr_transport_addr_fmt_addr_string(&addr)))
+          ABORT(r);
+        if((r=nr_socket_local_create(&addr, &sock))){
+          r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string);
+          continue;
+        }
+        /* Wrap it */
+        if((r=nr_socket_buffered_stun_create(sock, NR_STUN_MAX_MESSAGE_SIZE, &buffered_sock)))
+          ABORT(r);
+
+        /* The TURN socket */
+        if(r=nr_socket_turn_create(buffered_sock, &turn_sock))
+          ABORT(r);
+
+        /* Create an ICE socket */
+        if((r=nr_ice_socket_create(ctx, component, buffered_sock, &isock)))
+          ABORT(r);
 
+        /* Attach ourselves to it */
+        if(r=nr_ice_candidate_create(ctx,component,
+          isock,turn_sock,RELAYED,
+          &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+          ABORT(r);
+        cand->u.relayed.srvflx_candidate=NULL;
+        cand->u.relayed.server=&ctx->turn_servers[j];
+        TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+        component->candidate_ct++;
+        cand=0;
+
+        /* Create a STUN server context for this socket */
+        snprintf(label, sizeof(label), "server(%s)", addr.as_string);
+        if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server))
+          ABORT(r);
+        if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
+          ABORT(r);
+
+       /* Add the default STUN credentials so that we can respond before
+          we hear about the peer.*/
+        if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component))
+          ABORT(r);
+
+        STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
+      }
+    }
+#endif
+
+    _status = 0;
+ abort:
+    return(_status);
+  }
+
+
+/* Make all the candidates we can make at the beginning */
+int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component)
+  {
+    int r,_status;
+    nr_local_addr *addrs=ctx->local_addrs;
+    int addr_ct=ctx->local_addr_ct;
+    char *lufrag;
+    char *lpwd;
+    Data pwd;
+    nr_ice_candidate *cand;
+
+    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id);
+
+    if(addr_ct==0){
+      r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label);
+      ABORT(R_NOT_FOUND);
+    }
+
+    /* Note: we need to recompute these because
+       we have not yet computed the values in the peer media stream.*/
+    lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag;
+    assert(lufrag);
+    if (!lufrag)
+      ABORT(R_INTERNAL);
+    lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
+    assert(lpwd);
+    if (!lpwd)
+      ABORT(R_INTERNAL);
+    INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
+
+    /* Initialize the UDP candidates */
+    if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd))
+      ABORT(r);
+    /* And the TCP candidates */
+    if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd))
+      ABORT(r);
 
     /* count the candidates that will be initialized */
     cand=TAILQ_FIRST(&component->candidates);
     if(!cand){
-      r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create any valid candidates",ctx->label);
+      r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create any valid candidates",ctx->label);
       ABORT(R_NOT_FOUND);
     }
 
     while(cand){
       ctx->uninitialized_candidates++;
       cand=TAILQ_NEXT(cand,entry_comp);
     }
 
@@ -319,19 +426,18 @@ int nr_ice_component_initialize(struct n
           if(r!=R_WOULDBLOCK){
             ctx->uninitialized_candidates--;
             cand->state=NR_ICE_CAND_STATE_FAILED;
           }
         }
       }
       cand=TAILQ_NEXT(cand,entry_comp);
     }
-
     _status=0;
-  abort:
+ abort:
     return(_status);
   }
 
 /*
   Compare this newly initialized candidate against the other initialized
   candidates and discard the lower-priority one if they are redundant.
 
    This algorithm combined with the other algorithms, favors
@@ -635,18 +741,17 @@ int nr_ice_component_service_pre_answer_
         }
         (*serviced)++;
         STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
         nr_ice_pre_answer_request_destroy(&r1);
       }
     }
 
     _status=0;
- abort:
-    return(_status);
+     return(_status);
   }
 
 int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
   {
     int r, _status;
     nr_ice_candidate *pcand;
     nr_ice_cand_pair *pair=0;
     char codeword[5];
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -60,16 +60,17 @@ typedef struct nr_ice_stun_server_ {
       UINT2 port;
     } dnsname;
   } u;
   int index;
 } nr_ice_stun_server;
 
 typedef struct nr_ice_turn_server_ {
     nr_ice_stun_server    turn_server;
+    int                   transport;
     char                 *username;
     Data                 *password;
 } nr_ice_turn_server;
 
 typedef struct nr_ice_foundation_ {
   int index;
 
   nr_transport_addr addr;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
@@ -30,16 +30,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
 
 static char *RCSSTRING __UNUSED__="$Id: ice_socket.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
 
 #include <assert.h>
+#include <string.h>
 #include "nr_api.h"
 #include "ice_ctx.h"
 #include "stun.h"
 
 static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg)
   {
     int r;
     nr_ice_stun_ctx *sc1,*sc2;
@@ -57,17 +58,21 @@ static void nr_ice_socket_readable_cb(NR
     nr_socket *stun_srv_sock=sock->sock;
 
     r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label);
 
     /* Re-arm first! */
     NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg);
 
     if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){
-      r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error reading from socket",sock->ctx->label);
+      if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) {
+        /* Report this error upward. Bug 946423 */
+        r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error on reliable socket. Abandoning.",sock->ctx->label);
+        NR_ASYNC_CANCEL(s, NR_ASYNC_WAIT_READ);
+      }
       return;
     }
 
     /* Deal with the fact that sizeof(int) and sizeof(size_t) may not
        be the same */
     if (len_s > (size_t)INT_MAX)
       return;
 
@@ -184,29 +189,43 @@ static void nr_ice_socket_readable_cb(NR
 
     return;
   }
 
 int nr_ice_socket_create(nr_ice_ctx *ctx,nr_ice_component *comp, nr_socket *nsock, nr_ice_socket **sockp)
   {
     nr_ice_socket *sock=0;
     NR_SOCKET fd;
-    int _status;
+    nr_transport_addr addr;
+    int r,_status;
 
     if(!(sock=RCALLOC(sizeof(nr_ice_socket))))
       ABORT(R_NO_MEMORY);
 
     sock->sock=nsock;
     sock->ctx=ctx;
     sock->component=comp;
 
+    if(r=nr_socket_getaddr(nsock, &addr))
+      ABORT(r);
+
+    if (addr.protocol == IPPROTO_UDP) {
+      sock->type = NR_ICE_SOCKET_TYPE_DGRAM;
+    }
+    else {
+      assert(addr.protocol == IPPROTO_TCP);
+      sock->type = NR_ICE_SOCKET_TYPE_STREAM;
+    }
+
     TAILQ_INIT(&sock->candidates);
     TAILQ_INIT(&sock->stun_ctxs);
 
-    nr_socket_getfd(nsock,&fd);
+    if(r=nr_socket_getfd(nsock,&fd))
+      ABORT(r);
+
     NR_ASYNC_WAIT(fd,NR_ASYNC_WAIT_READ,nr_ice_socket_readable_cb,sock);
 
     *sockp=sock;
 
     _status=0;
   abort:
     if(_status) RFREE(sock);
     return(_status);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_socket.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.h
@@ -57,16 +57,20 @@ typedef struct nr_ice_stun_ctx_ {
   } u;
 
   TAILQ_ENTRY(nr_ice_stun_ctx_) entry;
 } nr_ice_stun_ctx;
 
 
 
 typedef struct nr_ice_socket_ {
+  int type;
+#define NR_ICE_SOCKET_TYPE_DGRAM  1
+#define NR_ICE_SOCKET_TYPE_STREAM 2
+
   nr_socket *sock;
   nr_ice_ctx *ctx;
 
   nr_ice_candidate_head candidates;
   nr_ice_component *component;
 
   TAILQ_HEAD(nr_ice_stun_ctx_head_,nr_ice_stun_ctx_) stun_ctxs;
 
--- a/media/mtransport/third_party/nICEr/src/net/nr_socket.c
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.c
@@ -34,24 +34,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 static char *RCSSTRING __UNUSED__="$Id: nr_socket.c,v 1.2 2008/04/28 17:59:02 ekr Exp $";
 
 #include <assert.h>
 #include <nr_api.h>
 #include "nr_socket.h"
 #include "local_addr.h"
 
+#define CHECK_DEFINED(f) assert(sock->vtbl->f); if (!sock->vtbl->f) ERETURN(R_INTERNAL);
 int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp)
   {
     int _status;
     nr_socket *sock=0;
 
     if(!(sock=RCALLOC(sizeof(nr_socket))))
       ABORT(R_NO_MEMORY);
 
+    assert(vtbl->version == 1);
+    if (vtbl->version != 1)
+       ABORT(R_INTERNAL);
+
     sock->obj=obj;
     sock->vtbl=vtbl;
 
     *sockp=sock;
 
     _status=0;
   abort:
     return(_status);
@@ -59,48 +64,74 @@ int nr_socket_create_int(void *obj, nr_s
 
 int nr_socket_destroy(nr_socket **sockp)
   {
     nr_socket *sock;
 
     if(!sockp || !*sockp)
       return(0);
 
+
     sock=*sockp;
     *sockp=0;
 
+    CHECK_DEFINED(destroy);
+
     assert(sock->vtbl);
     if (sock->vtbl)
       sock->vtbl->destroy(&sock->obj);
 
     RFREE(sock);
 
     return(0);
   }
 
 int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len, int flags,
   nr_transport_addr *addr)
   {
+    CHECK_DEFINED(ssendto);
     return sock->vtbl->ssendto(sock->obj,msg,len,flags,addr);
   }
 
-
 int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
   size_t *len, int flags, nr_transport_addr *addr)
   {
+    CHECK_DEFINED(srecvfrom);
     return sock->vtbl->srecvfrom(sock->obj, buf, maxlen, len, flags, addr);
   }
 
 int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd)
   {
+    CHECK_DEFINED(getfd);
     return sock->vtbl->getfd(sock->obj, fd);
   }
 
 int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp)
   {
+    CHECK_DEFINED(getaddr);
     return sock->vtbl->getaddr(sock->obj, addrp);
   }
 
 int nr_socket_close(nr_socket *sock)
   {
+    CHECK_DEFINED(close);
     return sock->vtbl->close(sock->obj);
   }
 
+int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr)
+  {
+    CHECK_DEFINED(connect);
+    return sock->vtbl->connect(sock->obj, addr);
+  }
+
+int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags)
+  {
+    CHECK_DEFINED(swrite);
+    return sock->vtbl->swrite(sock->obj, msg, len, written);
+  }
+
+
+int nr_socket_read(nr_socket *sock,void * restrict buf, size_t maxlen,
+  size_t *len, int flags)
+  {
+    CHECK_DEFINED(sread);
+    return sock->vtbl->sread(sock->obj, buf, maxlen, len);
+  }
--- a/media/mtransport/third_party/nICEr/src/net/nr_socket.h
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.h
@@ -47,23 +47,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 #ifdef __cplusplus
 #define restrict
 #elif defined(WIN32)
 #define restrict __restrict
 #endif
 
 typedef struct nr_socket_vtbl_ {
+  UINT4 version;   /* Currently 1 */
   int (*destroy)(void **obj);
   int (*ssendto)(void *obj,const void *msg, size_t len, int flags,
     nr_transport_addr *addr);
   int (*srecvfrom)(void *obj,void * restrict buf, size_t maxlen, size_t *len, int flags,
     nr_transport_addr *addr);
   int (*getfd)(void *obj, NR_SOCKET *fd);
   int (*getaddr)(void *obj, nr_transport_addr *addrp);
+  int (*connect)(void *obj, nr_transport_addr *addr);
+  int (*swrite)(void *obj,const void *msg, size_t len, size_t *written);
+  int (*sread)(void *obj,void * restrict buf, size_t maxlen, size_t *len);
   int (*close)(void *obj);
 } nr_socket_vtbl;
 
 typedef struct nr_socket_ {
   void *obj;
   nr_socket_vtbl *vtbl;
 } nr_socket;
 
@@ -73,11 +77,14 @@ int nr_socket_create_int(void *obj, nr_s
 int nr_socket_destroy(nr_socket **sockp);
 int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len,
   int flags,nr_transport_addr *addr);
 int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
   size_t *len, int flags, nr_transport_addr *addr);
 int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd);
 int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp);
 int nr_socket_close(nr_socket *sock);
+int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr);
+int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags);
+int nr_socket_read(nr_socket *sock, void * restrict buf, size_t maxlen, size_t *len, int flags);
 
 #endif
 
new file mode 100644
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
@@ -0,0 +1,395 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <nr_api.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <assert.h>
+
+#include "p_buf.h"
+#include "nr_socket.h"
+#include "stun.h"
+#include "nr_socket_buffered_stun.h"
+
+
+typedef struct nr_socket_buffered_stun_ {
+  nr_socket *inner;
+  nr_transport_addr remote_addr;
+
+  /* Read state */
+  int read_state;
+#define NR_ICE_SOCKET_READ_NONE 0
+#define NR_ICE_SOCKET_READ_HDR  1
+#define NR_ICE_SOCKET_READ_FAILED 2
+  UCHAR *buffer;
+  size_t buffer_size;
+  size_t bytes_needed;
+  size_t bytes_read;
+
+  /* Write state */
+  nr_p_buf_ctx *p_bufs;
+  nr_p_buf_head pending_writes;
+  size_t pending;
+  size_t max_pending;
+} nr_socket_buffered_stun;
+
+static int nr_socket_buffered_stun_destroy(void **objp);
+static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len,
+  int flags, nr_transport_addr *to);
+static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf,
+  size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
+static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_buffered_stun_close(void *obj);
+static int nr_socket_buffered_stun_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written);
+static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg);
+
+static nr_socket_vtbl nr_socket_buffered_stun_vtbl={
+  1,
+  nr_socket_buffered_stun_destroy,
+  nr_socket_buffered_stun_sendto,
+  nr_socket_buffered_stun_recvfrom,
+  nr_socket_buffered_stun_getfd,
+  nr_socket_buffered_stun_getaddr,
+  nr_socket_buffered_stun_connect,
+  0,
+  0,
+  nr_socket_buffered_stun_close
+};
+
+int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, nr_socket **sockp)
+{
+  int r, _status;
+  nr_socket_buffered_stun *sock = 0;
+
+  if (!(sock = RCALLOC(sizeof(nr_socket_buffered_stun))))
+    ABORT(R_NO_MEMORY);
+
+  sock->inner = inner;
+
+  if ((r=nr_ip4_port_to_transport_addr(INADDR_ANY, 0, NR_IPV4, &sock->remote_addr)))
+    ABORT(r);
+
+  /* TODO(ekr@rtfm.com): Check this */
+  if (!(sock->buffer = RMALLOC(NR_STUN_MAX_MESSAGE_SIZE)))
+    ABORT(R_NO_MEMORY);
+  sock->read_state = NR_ICE_SOCKET_READ_NONE;
+  sock->buffer_size = NR_STUN_MAX_MESSAGE_SIZE;
+  sock->bytes_needed = sizeof(nr_stun_message_header);
+
+  STAILQ_INIT(&sock->pending_writes);
+  if ((r=nr_p_buf_ctx_create(NR_STUN_MAX_MESSAGE_SIZE, &sock->p_bufs)))
+    ABORT(r);
+  sock->max_pending=max_pending;
+
+
+  if ((r=nr_socket_create_int(sock, &nr_socket_buffered_stun_vtbl, sockp)))
+    ABORT(r);
+
+  _status=0;
+abort:
+  if (_status) {
+    void *sock_v = sock;
+    sock->inner = 0;  /* Give up ownership so we don't destroy */
+    nr_socket_buffered_stun_destroy(&sock_v);
+  }
+  return(_status);
+}
+
+/* Note: This destroys the inner socket */
+int nr_socket_buffered_stun_destroy(void **objp)
+{
+  nr_socket_buffered_stun *sock;
+  NR_SOCKET fd;
+
+  if (!objp || !*objp)
+    return 0;
+
+  sock = (nr_socket_buffered_stun *)*objp;
+  *objp = 0;
+
+  /* Free the buffer if needed */
+  RFREE(sock->buffer);
+
+  /* Cancel waiting on the socket */
+  if (!nr_socket_getfd(sock->inner, &fd)) {
+    NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+  }
+
+  nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes);
+  nr_p_buf_ctx_destroy(&sock->p_bufs);
+  nr_socket_destroy(&sock->inner);
+  RFREE(sock);
+
+  return 0;
+}
+
+static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len,
+  int flags, nr_transport_addr *to)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  int r, _status;
+  size_t written;
+
+  /* Check that we are writing to the connected address if
+     connected */
+  if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) {
+    if (nr_transport_addr_cmp(&sock->remote_addr, to, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+      r_log(LOG_GENERIC, LOG_ERR, "Sendto on connected socket doesn't match");
+      ABORT(R_BAD_DATA);
+    }
+  }
+
+  if ((r=nr_socket_buffered_stun_write(obj, msg, len, &written)))
+    ABORT(r);
+
+  if (len != written)
+    ABORT(R_IO_ERROR);
+
+  _status=0;
+abort:
+  return _status;
+}
+
+static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock)
+  {
+    sock->read_state = NR_ICE_SOCKET_READ_FAILED;
+  }
+
+static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf,
+  size_t maxlen, size_t *len, int flags, nr_transport_addr *from)
+{
+  int r, _status;
+  size_t bytes_read;
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) {
+    ABORT(R_FAILED);
+  }
+
+reread:
+  /* Read all the expected bytes */
+  assert(sock->bytes_needed <= sock->buffer_size - sock->bytes_read);
+
+  if(r=nr_socket_read(sock->inner,
+                      sock->buffer + sock->bytes_read,
+                      sock->bytes_needed, &bytes_read, 0))
+    ABORT(r);
+
+  assert(bytes_read <= sock->bytes_needed);
+  sock->bytes_needed -= bytes_read;
+  sock->bytes_read += bytes_read;
+
+  /* Unfinished */
+  if (sock->bytes_needed)
+    ABORT(R_WOULDBLOCK);
+
+  /* No more bytes expeected */
+  if (sock->read_state == NR_ICE_SOCKET_READ_NONE) {
+    int tmp_length;
+    size_t remaining_length;
+
+    /* Parse the header */
+    if (r = nr_stun_message_length(sock->buffer, sock->bytes_read, &tmp_length))
+      ABORT(r);
+    assert(tmp_length >= 0);
+    if (tmp_length < 0)
+      ABORT(R_BAD_DATA);
+    remaining_length = tmp_length;
+
+    /* Check to see if we have enough room */
+    if ((sock->buffer_size - sock->bytes_read) < remaining_length)
+      ABORT(R_BAD_DATA);
+
+    /* Set ourselves up to read the rest of the data */
+    sock->read_state = NR_ICE_SOCKET_READ_HDR;
+    sock->bytes_needed = remaining_length;
+
+    goto reread;
+  }
+
+  if (maxlen < sock->bytes_read)
+    ABORT(R_BAD_ARGS);
+
+  memcpy(buf, sock->buffer, sock->bytes_read);
+  *len = sock->bytes_read;
+  sock->read_state = NR_ICE_SOCKET_READ_NONE;
+  sock->bytes_read = 0;
+  sock->bytes_needed = sizeof(nr_stun_message_header);
+
+  assert(!nr_transport_addr_is_wildcard(&sock->remote_addr));
+  if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) {
+    if ((r=nr_transport_addr_copy(from, &sock->remote_addr)))
+      ABORT(r);
+  }
+
+  _status=0;
+abort:
+  if (_status && (_status != R_WOULDBLOCK)) {
+    nr_socket_buffered_stun_failed(sock);
+  }
+
+  return(_status);
+}
+
+static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  return nr_socket_getfd(sock->inner, fd);
+}
+
+static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  return nr_socket_getaddr(sock->inner, addrp);
+}
+
+static int nr_socket_buffered_stun_close(void *obj)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  return nr_socket_close(sock->inner);
+}
+
+static int nr_socket_buffered_stun_connect(void *obj, nr_transport_addr *addr)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+  int r, _status;
+
+  if ((r=nr_transport_addr_copy(&sock->remote_addr, addr)))
+    ABORT(r);
+
+  if ((r=nr_socket_connect(sock->inner, addr)))
+    ABORT(r);
+
+  _status=0;
+abort:
+  return(_status);
+}
+
+static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+  int already_armed = 0;
+  int r,_status;
+  size_t written2 = 0;
+  size_t original_len = len;
+
+  /* Buffers are close to full, report error. Do this now so we never
+     get partial writes */
+  if ((sock->pending + len) > sock->max_pending) {
+    r_log(LOG_GENERIC, LOG_INFO, "Buffers full");
+    ABORT(R_WOULDBLOCK);
+  }
+
+
+  if (!sock->pending) {
+    r = nr_socket_write(sock->inner, msg, len, &written2, 0);
+    if (r) {
+      if (r != R_WOULDBLOCK)
+        ABORT(r);
+
+      written2=0;
+    }
+  } else {
+    already_armed = 1;
+  }
+
+  /* Buffer what's left */
+  len -= written2;
+
+  if (len) {
+    if ((r=nr_p_buf_write_to_chain(sock->p_bufs, &sock->pending_writes,
+                                     ((UCHAR *)msg) + written2, len)))
+      ABORT(r);
+
+    sock->pending += len;
+  }
+
+  if (sock->pending && !already_armed) {
+    NR_SOCKET fd;
+
+    if ((r=nr_socket_getfd(sock->inner, &fd)))
+      ABORT(r);
+
+    NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_writable_cb, sock);
+  }
+
+  *written = original_len;
+
+  _status=0;
+abort:
+  return _status;
+}
+
+static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg;
+  int r,_status;
+  nr_p_buf *n1, *n2;
+
+  /* Try to flush */
+  STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) {
+    size_t written = 0;
+
+    if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset,
+                           n1->length - n1->r_offset,
+                           &written, 0))) {
+
+      ABORT(r);
+    }
+
+    n1->r_offset += written;
+    if (n1->r_offset < n1->length) {
+      /* We wrote something, but not everything */
+      ABORT(R_WOULDBLOCK);
+    }
+
+    /* We are done with this p_buf */
+    STAILQ_REMOVE_HEAD(&sock->pending_writes, entry);
+    nr_p_buf_free(sock->p_bufs, n1);
+  }
+
+  _status=0;
+abort:
+  if (_status && _status != R_WOULDBLOCK) {
+    /* TODO(ekr@rtfm.com): Mark the socket as failed */
+  }
+}
copy from media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
copy to media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
@@ -1,10 +1,11 @@
 /*
 Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
 
 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
@@ -27,20 +28,25 @@ LIMITED TO, PROCUREMENT OF SUBSTITUTE GO
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
 
-#ifndef _nr_socket_turn_h
-#define _nr_socket_turn_h
+#ifndef _nr_socket_buffered_stun_h
+#define _nr_socket_buffered_stun_h
+
+#include "nr_socket.h"
+
+/* Wrapper socket which provides buffered STUN-oriented I/O
 
-/* This is a partial implementation of an nr_socket wrapped
-   around TURN. It implements only the nr_socket features
-   actually used by the ICE stack. You can't, for instance,
-   read off the socket */
-int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp);
-int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx);
+   1. Writes don't block and are automatically flushed when needed.
+   2. All reads are in units of STUN messages
+
+   This socket takes ownership of the inner socket |sock|.
+ */
+int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending,
+  nr_socket **sockp);
 
 #endif
 
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c
@@ -59,21 +59,25 @@ static int nr_socket_turn_sendto(void *o
   int flags, nr_transport_addr *to);
 static int nr_socket_turn_recvfrom(void *obj,void * restrict buf,
   size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
 static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd);
 static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp);
 static int nr_socket_turn_close(void *obj);
 
 static nr_socket_vtbl nr_socket_turn_vtbl={
+  1,
   nr_socket_turn_destroy,
   nr_socket_turn_sendto,
   nr_socket_turn_recvfrom,
   nr_socket_turn_getfd,
   nr_socket_turn_getaddr,
+  0,
+  0,
+  0,
   nr_socket_turn_close
 };
 
 int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp)
   {
     int r,_status;
     nr_socket_turn *sturn=0;
 
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
@@ -30,16 +30,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
 
 #ifndef _nr_socket_turn_h
 #define _nr_socket_turn_h
 
+#include "nr_socket.h"
+
 /* This is a partial implementation of an nr_socket