Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 04 Feb 2015 14:42:46 -0500
changeset 227485 3cda3997f45dce9a751ab555751ef762b909fbdf
parent 227407 35be56b517c8aaf280a85bc58b00ce2e53e026d7 (current diff)
parent 227484 0203370cd4dbb4055f7e80c6af615dd837596ffb (diff)
child 227495 a8d6037d4025e3d4cb75c9441a1b1df70479eb02
child 227520 7a0ec58272938394ff87e06e05860a5ea8306ba9
child 227575 03fed89b215df9aecdc112d933f703c4d794b91d
push id28230
push userryanvm@gmail.com
push dateWed, 04 Feb 2015 19:42:59 +0000
treeherdermozilla-central@3cda3997f45d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -688,60 +688,67 @@ pref("dom.ipc.processPriorityManager.tem
 // Number of different background levels for background processes.  We use
 // these different levels to force the low-memory killer to kill processes in
 // a LRU order.
 pref("dom.ipc.processPriorityManager.backgroundLRUPoolLevels", 5);
 
 // Kernel parameters for process priorities.  These affect how processes are
 // killed on low-memory and their relative CPU priorities.
 //
-// Note: The maximum nice value on Linux is 19, but the max value you should
-// use here is 18.  NSPR adds 1 to some threads' nice values, to mark
-// low-priority threads.  If the process priority manager were to renice a
-// process (and all its threads) to 19, all threads would have the same
-// niceness.  Then when we reniced the process to (say) 10, all threads would
-// /still/ have the same niceness; we'd effectively have erased NSPR's thread
-// priorities.
-
 // The kernel can only accept 6 (OomScoreAdjust, KillUnderKB) pairs. But it is
 // okay, kernel will still kill processes with larger OomScoreAdjust first even
 // its OomScoreAdjust don't have a corresponding KillUnderKB.
 
 pref("hal.processPriorityManager.gonk.MASTER.OomScoreAdjust", 0);
 pref("hal.processPriorityManager.gonk.MASTER.KillUnderKB", 4096);
-pref("hal.processPriorityManager.gonk.MASTER.Nice", 0);
+pref("hal.processPriorityManager.gonk.MASTER.cgroup", "");
 
 pref("hal.processPriorityManager.gonk.PREALLOC.OomScoreAdjust", 67);
-pref("hal.processPriorityManager.gonk.PREALLOC.Nice", 18);
+pref("hal.processPriorityManager.gonk.PREALLOC.cgroup", "apps/bg_non_interactive");
 
 pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.OomScoreAdjust", 67);
 pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.KillUnderKB", 5120);
-pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.Nice", 0);
+pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.cgroup", "apps/critical");
 
 pref("hal.processPriorityManager.gonk.FOREGROUND.OomScoreAdjust", 134);
 pref("hal.processPriorityManager.gonk.FOREGROUND.KillUnderKB", 6144);
-pref("hal.processPriorityManager.gonk.FOREGROUND.Nice", 1);
+pref("hal.processPriorityManager.gonk.FOREGROUND.cgroup", "apps");
 
 pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.OomScoreAdjust", 200);
-pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.Nice", 1);
+pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.cgroup", "apps");
 
 pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.OomScoreAdjust", 400);
 pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderKB", 7168);
-pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 7);
+pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.cgroup", "apps/bg_perceivable");
 
 pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.OomScoreAdjust", 534);
 pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.KillUnderKB", 8192);
-pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.Nice", 18);
+pref("hal.processPriorityManager.gonk.BACKGROUND_HOMESCREEN.cgroup", "apps/bg_non_interactive");
 
 pref("hal.processPriorityManager.gonk.BACKGROUND.OomScoreAdjust", 667);
 pref("hal.processPriorityManager.gonk.BACKGROUND.KillUnderKB", 20480);
-pref("hal.processPriorityManager.gonk.BACKGROUND.Nice", 18);
+pref("hal.processPriorityManager.gonk.BACKGROUND.cgroup", "apps/bg_non_interactive");
+
+// Control group definitions (i.e., CPU priority groups) for B2G processes.
+
+// Foreground apps
+pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_shares", 1024);
+pref("hal.processPriorityManager.gonk.cgroups.apps.cpu_notify_on_migrate", 0);
 
-// Processes get this niceness when they have low CPU priority.
-pref("hal.processPriorityManager.gonk.LowCPUNice", 18);
+// Foreground apps with high priority, 16x more CPU than foreground ones
+pref("hal.processPriorityManager.gonk.cgroups.apps/critical.cpu_shares", 16384);
+pref("hal.processPriorityManager.gonk.cgroups.apps/critical.cpu_notify_on_migrate", 0);
+
+// Background perceivable apps, ~10x less CPU than foreground ones
+pref("hal.processPriorityManager.gonk.cgroups.apps/bg_perceivable.cpu_shares", 103);
+pref("hal.processPriorityManager.gonk.cgroups.apps/bg_perceivable.cpu_notify_on_migrate", 0);
+
+// Background apps, ~20x less CPU than foreground ones and ~2x less than perceivable ones
+pref("hal.processPriorityManager.gonk.cgroups.apps/bg_non_interactive.cpu_shares", 52);
+pref("hal.processPriorityManager.gonk.cgroups.apps/bg_non_interactive.cpu_notify_on_migrate", 0);
 
 // By default the compositor thread on gonk runs without real-time priority.  RT
 // priority can be enabled by setting this pref to a value between 1 and 99.
 // Note that audio processing currently runs at RT priority 2 or 3 at most.
 //
 // If RT priority is disabled, then the compositor nice value is used. We prefer
 // to use a nice value of -4, which matches Android's preferences. Setting a preference
 // of RT priority 1 would mean it is higher than audio, which is -16. The compositor
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -261,17 +261,17 @@ function clearPluginPermissionTemplate()
   document.querySelector(".permPluginTemplateRadioAllow").removeAttribute("id");
   document.querySelector(".permPluginTemplateRadioBlock").removeAttribute("id");
 }
 
 function initPluginsRow() {
   let vulnerableLabel = document.getElementById("browserBundle").getString("pluginActivateVulnerable.label");
   let pluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
 
-  let permissionMap = Map();
+  let permissionMap = new Map();
 
   for (let plugin of pluginHost.getPluginTags()) {
     if (plugin.disabled) {
       continue;
     }
     for (let mimeType of plugin.getMimeTypes()) {
       let permString = pluginHost.getPermissionStringForType(mimeType);
       if (!permissionMap.has(permString)) {
--- a/browser/devtools/framework/ToolboxProcess.jsm
+++ b/browser/devtools/framework/ToolboxProcess.jsm
@@ -23,17 +23,17 @@ XPCOMUtils.defineLazyGetter(this, "Telem
 });
 XPCOMUtils.defineLazyGetter(this, "EventEmitter", function () {
   return devtools.require("devtools/toolkit/event-emitter");
 });
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
 
-let processes = Set();
+let processes = new Set();
 
 /**
  * Constructor for creating a process that will hold a chrome toolbox.
  *
  * @param function aOnClose [optional]
  *        A function called when the process stops running.
  * @param function aOnRun [optional]
  *        A function called when the process starts running.
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -2111,17 +2111,17 @@ Scope.prototype = {
   _title: null,
   _enum: null,
   _nonenum: null,
 };
 
 // Creating maps and arrays thousands of times for variables or properties
 // with a large number of children fills up a lot of memory. Make sure
 // these are instantiated only if needed.
-DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_store", Map);
+DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_store", () => new Map());
 DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_enumItems", Array);
 DevToolsUtils.defineLazyPrototypeGetter(Scope.prototype, "_nonEnumItems", Array);
 
 // An ellipsis symbol (usually "…") used for localization.
 XPCOMUtils.defineLazyGetter(Scope, "ellipsis", () =>
   Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data);
 
 /**
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -612,17 +612,17 @@ Item.prototype = {
   _target: null,
   _prebuiltNode: null,
   finalize: null,
   attachment: null
 };
 
 // Creating maps thousands of times for widgets with a large number of children
 // fills up a lot of memory. Make sure these are instantiated only if needed.
-DevToolsUtils.defineLazyPrototypeGetter(Item.prototype, "_itemsByElement", Map);
+DevToolsUtils.defineLazyPrototypeGetter(Item.prototype, "_itemsByElement", () => new Map());
 
 /**
  * Some generic Widget methods handling Item instances.
  * Iterable via "for (let childItem of wrappedView) { }".
  *
  * Usage:
  *   function MyView() {
  *     this.widget = new MyWidget(document.querySelector(".my-node"));
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -313,53 +313,16 @@ FindObjectClass(JSContext* cx, JSObject*
   do {
     obj = proto;
     js::GetObjectProto(cx, obj, &proto);
   } while (proto);
 
   sObjectClass = js::GetObjectJSClass(obj);
 }
 
-static inline nsresult
-WrapNative(JSContext *cx, nsISupports *native,
-           nsWrapperCache *cache, const nsIID* aIID, JS::MutableHandle<JS::Value> vp,
-           bool aAllowWrapping)
-{
-  if (!native) {
-    vp.setNull();
-
-    return NS_OK;
-  }
-
-  JSObject *wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
-  if (wrapper) {
-    return NS_OK;
-  }
-
-  JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
-  return nsDOMClassInfo::XPConnect()->WrapNativeToJSVal(cx, scope, native,
-                                                        cache, aIID,
-                                                        aAllowWrapping, vp);
-}
-
-static inline nsresult
-WrapNative(JSContext *cx, nsISupports *native, const nsIID* aIID,
-           bool aAllowWrapping, JS::MutableHandle<JS::Value> vp)
-{
-  return WrapNative(cx, native, nullptr, aIID, vp, aAllowWrapping);
-}
-
-// Same as the WrapNative above, but use these if aIID is nsISupports' IID.
-static inline nsresult
-WrapNative(JSContext *cx, nsISupports *native,
-           bool aAllowWrapping, JS::MutableHandle<JS::Value> vp)
-{
-  return WrapNative(cx, native, nullptr, nullptr, vp, aAllowWrapping);
-}
-
 // Helper to handle torn-down inner windows.
 static inline nsresult
 SetParentToWindow(nsGlobalWindow *win, JSObject **parent)
 {
   MOZ_ASSERT(win);
   MOZ_ASSERT(win->IsInnerWindow());
   *parent = win->FastGetGlobalJSObject();
 
@@ -1344,17 +1307,17 @@ BaseStubConstructor(nsIWeakReference* aW
     native = do_CreateInstance(*name_struct->mData->mConstructorCID, &rv);
   }
   if (NS_FAILED(rv)) {
     NS_ERROR("Failed to create the object");
     return rv;
   }
 
   js::AssertSameCompartment(cx, obj);
-  return WrapNative(cx, native, true, args.rval());
+  return nsContentUtils::WrapNative(cx, native, args.rval(), true);
 }
 
 static nsresult
 DefineInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj, const nsIID *aIID)
 {
   nsCOMPtr<nsIInterfaceInfoManager>
     iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
   NS_ENSURE_TRUE(iim, NS_ERROR_UNEXPECTED);
@@ -1787,17 +1750,17 @@ nsDOMConstructor::ToString(nsAString &aR
 
   return NS_OK;
 }
 
 
 static nsresult
 GetXPCProto(nsIXPConnect *aXPConnect, JSContext *cx, nsGlobalWindow *aWin,
             const nsGlobalNameStruct *aNameStruct,
-            nsIXPConnectJSObjectHolder **aProto)
+            JS::MutableHandle<JSObject*> aProto)
 {
   NS_ASSERTION(aNameStruct->mType ==
                  nsGlobalNameStruct::eTypeClassConstructor ||
                aNameStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfo,
                "Wrong type!");
 
   nsCOMPtr<nsIClassInfo> ci;
   if (aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
@@ -1808,28 +1771,24 @@ GetXPCProto(nsIXPConnect *aXPConnect, JS
 
     ci = NS_GetDOMClassInfoInstance(ci_id);
   }
   else {
     ci = nsDOMClassInfo::GetClassInfoInstance(aNameStruct->mData);
   }
   NS_ENSURE_TRUE(ci, NS_ERROR_UNEXPECTED);
 
+  nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
   nsresult rv =
     aXPConnect->GetWrappedNativePrototype(cx, aWin->GetGlobalJSObject(), ci,
-                                          aProto);
+                                          getter_AddRefs(proto_holder));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  JS::Rooted<JSObject*> proto_obj(cx, (*aProto)->GetJSObject());
-  if (!JS_WrapObject(cx, &proto_obj)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  NS_IF_RELEASE(*aProto);
-  return aXPConnect->HoldObject(cx, proto_obj, aProto);
+  aProto.set(proto_holder->GetJSObject());
+  return JS_WrapObject(cx, aProto) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 // Either ci_data must be non-null or name_struct must be non-null and of type
 // eTypeClassProto.
 static nsresult
 ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
                  JS::Handle<JSObject*> obj, const char16_t *name,
                  const nsDOMClassInfoData *ci_data,
@@ -1847,18 +1806,19 @@ ResolvePrototype(nsIXPConnect *aXPConnec
   nsRefPtr<nsDOMConstructor> constructor;
   nsresult rv = nsDOMConstructor::Create(name, ci_data, name_struct, aWin,
                                          getter_AddRefs(constructor));
   NS_ENSURE_SUCCESS(rv, rv);
 
   JS::Rooted<JS::Value> v(cx);
 
   js::AssertSameCompartment(cx, obj);
-  rv = WrapNative(cx, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
-                  false, &v);
+  rv = nsContentUtils::WrapNative(cx, constructor,
+                                  &NS_GET_IID(nsIDOMDOMConstructor), &v,
+                                  false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   FillPropertyDescriptor(ctorDesc, obj, 0, v);
   // And make sure we wrap the value into the right compartment.  Note that we
   // do this with ctorDesc.value(), not with v, because we need v to be in the
   // right compartment (that of the reflector of |constructor|) below.
   if (!JS_WrapValue(cx, ctorDesc.value())) {
     return NS_ERROR_UNEXPECTED;
@@ -2218,18 +2178,19 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
                                   nullptr,
                                   name_struct,
                                   static_cast<nsPIDOMWindow*>(aWin),
                                   getter_AddRefs(constructor));
     NS_ENSURE_SUCCESS(rv, rv);
 
     JS::Rooted<JS::Value> v(cx);
     js::AssertSameCompartment(cx, obj);
-    rv = WrapNative(cx, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
-                    false, &v);
+    rv = nsContentUtils::WrapNative(cx, constructor,
+                                    &NS_GET_IID(nsIDOMDOMConstructor), &v,
+                                    false);
     NS_ENSURE_SUCCESS(rv, rv);
 
     JS::Rooted<JSObject*> class_obj(cx, &v.toObject());
 
     // ... and define the constants from the DOM interface on that
     // constructor object.
 
     {
@@ -2250,33 +2211,32 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
       name_struct->mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
     if (!OldBindingConstructorEnabled(name_struct, aWin, cx)) {
       return NS_OK;
     }
 
     // Create the XPConnect prototype for our classinfo, PostCreateProto will
     // set up the prototype chain.  This will go ahead and define things on the
     // actual window's global.
-    nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
+    JS::Rooted<JSObject*> dot_prototype(cx);
     rv = GetXPCProto(nsDOMClassInfo::sXPConnect, cx, aWin, name_struct,
-                     getter_AddRefs(proto_holder));
+                     &dot_prototype);
     NS_ENSURE_SUCCESS(rv, rv);
+    MOZ_ASSERT(dot_prototype);
+
     bool isXray = xpc::WrapperFactory::IsXrayWrapper(obj);
     MOZ_ASSERT_IF(obj != aWin->GetGlobalJSObject(), isXray);
     if (!isXray) {
       // GetXPCProto already defined the property for us
       FillPropertyDescriptor(desc, obj, JS::UndefinedValue(), false);
       return NS_OK;
     }
 
     // This is the Xray case.  Look up the constructor object for this
     // prototype.
-    JS::Rooted<JSObject*> dot_prototype(cx, proto_holder->GetJSObject());
-    NS_ENSURE_STATE(dot_prototype);
-
     const nsDOMClassInfoData *ci_data;
     if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
       ci_data = &sClassInfoData[name_struct->mDOMClassInfoID];
     } else {
       ci_data = name_struct->mData;
     }
 
     return ResolvePrototype(nsDOMClassInfo::sXPConnect, aWin, cx, obj,
@@ -2296,23 +2256,21 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
   if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
     const nsGlobalNameStruct *alias_struct =
       nameSpaceManager->GetConstructorProto(name_struct);
     NS_ENSURE_TRUE(alias_struct, NS_ERROR_UNEXPECTED);
 
     // We need to use the XPConnect prototype for the DOM class that this
     // constructor is an alias for (for example for Image we need the prototype
     // for HTMLImageElement).
-    nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
+    JS::Rooted<JSObject*> dot_prototype(cx);
     rv = GetXPCProto(nsDOMClassInfo::sXPConnect, cx, aWin, alias_struct,
-                     getter_AddRefs(proto_holder));
+                     &dot_prototype);
     NS_ENSURE_SUCCESS(rv, rv);
-
-    JSObject* dot_prototype = proto_holder->GetJSObject();
-    NS_ENSURE_STATE(dot_prototype);
+    MOZ_ASSERT(dot_prototype);
 
     const nsDOMClassInfoData *ci_data;
     if (alias_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
       ci_data = &sClassInfoData[alias_struct->mDOMClassInfoID];
     } else if (alias_struct->mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
       ci_data = alias_struct->mData;
     } else {
       return NS_ERROR_UNEXPECTED;
@@ -2327,18 +2285,19 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
     nsRefPtr<nsDOMConstructor> constructor;
     rv = nsDOMConstructor::Create(class_name, nullptr, name_struct,
                                   static_cast<nsPIDOMWindow*>(aWin),
                                   getter_AddRefs(constructor));
     NS_ENSURE_SUCCESS(rv, rv);
 
     JS::Rooted<JS::Value> val(cx);
     js::AssertSameCompartment(cx, obj);
-    rv = WrapNative(cx, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
-                    true, &val);
+    rv = nsContentUtils::WrapNative(cx, constructor,
+                                    &NS_GET_IID(nsIDOMDOMConstructor), &val,
+                                    true);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(val.isObject(), "Why didn't we get a JSObject?");
 
     FillPropertyDescriptor(desc, obj, 0, val);
 
     return NS_OK;
   }
@@ -2365,17 +2324,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
     }
 
     if (prop_val.isPrimitive() && !prop_val.isNull()) {
       if (aWin->IsOuterWindow()) {
         nsGlobalWindow *inner = aWin->GetCurrentInnerWindowInternal();
         NS_ENSURE_TRUE(inner, NS_ERROR_UNEXPECTED);
       }
 
-      rv = WrapNative(cx, native, true, &prop_val);
+      rv = nsContentUtils::WrapNative(cx, native, &prop_val, true);
     }
 
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!JS_WrapValue(cx, &prop_val)) {
       return NS_ERROR_UNEXPECTED;
     }
 
--- a/dom/interfaces/settings/nsISettingsService.idl
+++ b/dom/interfaces/settings/nsISettingsService.idl
@@ -24,13 +24,14 @@ interface nsISettingsServiceLock : nsISu
   void set(in string aName,
            in jsval aValue,
            in nsISettingsServiceCallback aCallback,
            [optional] in string aMessage);
 
   void get(in string aName, in nsISettingsServiceCallback aCallback);
 };
 
-[scriptable, uuid(0505acf0-8e76-11e3-baa8-0800200c9a66)]
+[scriptable, uuid(d1ed155c-9f90-47bb-91c2-7eac54d69f4b)]
 interface nsISettingsService : nsISupports
 {
   nsISettingsServiceLock createLock([optional] in nsISettingsTransactionCompleteCallback aCallback);
+  void receiveMessage(in jsval aMessage);
 };
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -196,25 +196,16 @@ IsAppleAvailable()
 }
 
 static bool
 IsAndroidAvailable()
 {
 #ifndef MOZ_WIDGET_ANDROID
   return false;
 #else
-  // PowerVR is very slow at texture allocation for some reason, which causes poor performance.
-  nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
-
-  nsString vendor;
-  if (NS_FAILED(gfxInfo->GetAdapterVendorID(vendor)) ||
-      vendor.Find("Imagination") == 0) {
-    return nullptr;
-  }
-
   // We need android.media.MediaCodec which exists in API level 16 and higher.
   return AndroidBridge::Bridge()->GetAPIVersion() >= 16;
 #endif
 }
 
 static bool
 IsGonkMP4DecoderAvailable()
 {
--- a/dom/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp
@@ -65,36 +65,28 @@ public:
     mGLContext = nullptr;
   }
 
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
     mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample);
     return MediaCodecDataDecoder::Input(aSample);
   }
 
-  EGLImage CopySurface() {
-    if (!EnsureGLContext()) {
-      return nullptr;
-    }
+  bool WantCopy() {
+    // Allocating a texture is incredibly slow on PowerVR
+    return mGLContext->Vendor() != GLVendor::Imagination;
+  }
 
-    nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
-    layers::SurfaceTextureImage::Data data;
-    data.mSurfTex = mSurfaceTexture.get();
-    data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
-    data.mOriginPos = gl::OriginPos::BottomLeft;
-
-    layers::SurfaceTextureImage* stImg = static_cast<layers::SurfaceTextureImage*>(img.get());
-    stImg->SetData(data);
-
+  EGLImage CopySurface(layers::Image* img) {
     mGLContext->MakeCurrent();
 
-    GLuint tex = CreateTextureForOffscreen(mGLContext, mGLContext->GetGLFormats(), data.mSize);
+    GLuint tex = CreateTextureForOffscreen(mGLContext, mGLContext->GetGLFormats(), img->GetSize());
 
     GLBlitHelper helper(mGLContext);
-    if (!helper.BlitImageToTexture(img, data.mSize, tex, LOCAL_GL_TEXTURE_2D)) {
+    if (!helper.BlitImageToTexture(img, img->GetSize(), tex, LOCAL_GL_TEXTURE_2D)) {
       mGLContext->fDeleteTextures(1, &tex);
       return nullptr;
     }
 
     EGLint attribs[] = {
       LOCAL_EGL_IMAGE_PRESERVED_KHR, LOCAL_EGL_TRUE,
       LOCAL_EGL_NONE, LOCAL_EGL_NONE
     };
@@ -104,49 +96,63 @@ public:
                                                  LOCAL_EGL_GL_TEXTURE_2D_KHR,
                                                  (EGLClientBuffer)tex, attribs);
     mGLContext->fDeleteTextures(1, &tex);
 
     return eglImage;
   }
 
   virtual nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat, Microseconds aDuration) MOZ_OVERRIDE {
-    VideoInfo videoInfo;
-    videoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
-
-    EGLImage eglImage = CopySurface();
-    if (!eglImage) {
+    if (!EnsureGLContext()) {
       return NS_ERROR_FAILURE;
     }
 
-    EGLSync eglSync = nullptr;
-    if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
-        mGLContext->IsExtensionSupported(GLContext::OES_EGL_sync))
-    {
-      MOZ_ASSERT(mGLContext->IsCurrent());
-      eglSync = sEGLLibrary.fCreateSync(EGL_DISPLAY(),
-                                        LOCAL_EGL_SYNC_FENCE,
-                                        nullptr);
-      if (eglSync) {
-          mGLContext->fFlush();
-      }
-    } else {
-      NS_WARNING("No EGL fence support detected, rendering artifacts may occur!");
-    }
+    VideoInfo videoInfo;
+    videoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
 
-    nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::EGLIMAGE);
-    layers::EGLImageImage::Data data;
-    data.mImage = eglImage;
-    data.mSync = eglSync;
-    data.mOwns = true;
+    nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
+    layers::SurfaceTextureImage::Data data;
+    data.mSurfTex = mSurfaceTexture.get();
     data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
     data.mOriginPos = gl::OriginPos::BottomLeft;
 
-    layers::EGLImageImage* typedImg = static_cast<layers::EGLImageImage*>(img.get());
-    typedImg->SetData(data);
+    layers::SurfaceTextureImage* stImg = static_cast<layers::SurfaceTextureImage*>(img.get());
+    stImg->SetData(data);
+
+    if (WantCopy()) {
+      EGLImage eglImage = CopySurface(img);
+      if (!eglImage) {
+        return NS_ERROR_FAILURE;
+      }
+
+      EGLSync eglSync = nullptr;
+      if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
+          mGLContext->IsExtensionSupported(GLContext::OES_EGL_sync))
+      {
+        MOZ_ASSERT(mGLContext->IsCurrent());
+        eglSync = sEGLLibrary.fCreateSync(EGL_DISPLAY(),
+                                          LOCAL_EGL_SYNC_FENCE,
+                                          nullptr);
+        MOZ_ASSERT(eglSync);
+        mGLContext->fFlush();
+      } else {
+        NS_WARNING("No EGL fence support detected, rendering artifacts may occur!");
+      }
+
+      img = mImageContainer->CreateImage(ImageFormat::EGLIMAGE);
+      layers::EGLImageImage::Data data;
+      data.mImage = eglImage;
+      data.mSync = eglSync;
+      data.mOwns = true;
+      data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
+      data.mOriginPos = gl::OriginPos::BottomLeft;
+
+      layers::EGLImageImage* typedImg = static_cast<layers::EGLImageImage*>(img.get());
+      typedImg->SetData(data);
+    }
 
     nsresult rv;
     int32_t flags;
     NS_ENSURE_SUCCESS(rv = aInfo->Flags(&flags), rv);
 
     bool isSync = !!(flags & MediaCodec::BUFFER_FLAG_SYNC_FRAME);
 
     int32_t offset;
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -27,18 +27,17 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
 ContainerParser::ContainerParser()
-  : mInitData(new LargeDataBuffer())
-  , mHasInitData(false)
+  : mHasInitData(false)
 {
 }
 
 bool
 ContainerParser::IsInitSegmentPresent(LargeDataBuffer* aData)
 {
 MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
             this, aData->Length(),
@@ -76,20 +75,25 @@ ContainerParser::TimestampsFuzzyEqual(in
 
 int64_t
 ContainerParser::GetRoundingError()
 {
   NS_WARNING("Using default ContainerParser::GetRoundingError implementation");
   return 0;
 }
 
+bool
+ContainerParser::HasCompleteInitData()
+{
+  return mHasInitData && !!mInitData->Length();
+}
+
 LargeDataBuffer*
 ContainerParser::InitData()
 {
-  MOZ_ASSERT(mHasInitData);
   return mInitData;
 }
 
 class WebMContainerParser : public ContainerParser {
 public:
   WebMContainerParser()
     : mParser(0), mOffset(0)
   {}
@@ -102,17 +106,19 @@ public:
     ContainerParser::IsInitSegmentPresent(aData);
     // XXX: This is overly primitive, needs to collect data as it's appended
     // to the SB and handle, rather than assuming everything is present in a
     // single aData segment.
     // 0x1a45dfa3 // EBML
     // ...
     // DocType == "webm"
     // ...
-    // 0x18538067 // Segment (must be "unknown" size)
+    // 0x18538067 // Segment (must be "unknown" size or contain a value large
+                  // enough to include the Segment Information and Tracks
+                  // elements that follow)
     // 0x1549a966 // -> Segment Info
     // 0x1654ae6b // -> One or more Tracks
     if (aData->Length() >= 4 &&
         (*aData)[0] == 0x1a && (*aData)[1] == 0x45 && (*aData)[2] == 0xdf &&
         (*aData)[3] == 0xa3) {
       return true;
     }
     return false;
@@ -142,41 +148,48 @@ public:
   bool ParseStartAndEndTimestamps(LargeDataBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd)
   {
     bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
       mOffset = 0;
       mParser = WebMBufferedParser(0);
       mOverlappedMapping.Clear();
+      mInitData = new LargeDataBuffer();
+      mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/webm"));
     }
 
     // XXX if it only adds new mappings, overlapped but not available
     // (e.g. overlap < 0) frames are "lost" from the reported mappings here.
     nsTArray<WebMTimeDataOffset> mapping;
     mapping.AppendElements(mOverlappedMapping);
     mOverlappedMapping.Clear();
     ReentrantMonitor dummy("dummy");
     mParser.Append(aData->Elements(), aData->Length(), mapping, dummy);
+    if (mResource) {
+      mResource->AppendData(aData);
+    }
 
     // XXX This is a bit of a hack.  Assume if there are no timecodes
     // present and it's an init segment that it's _just_ an init segment.
     // We should be more precise.
-    if (initSegment) {
-      uint32_t length = aData->Length();
-      if (!mapping.IsEmpty()) {
-        length = mapping[0].mSyncOffset;
-        MOZ_ASSERT(length <= aData->Length());
-      }
-      MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                this, length);
-      if (!mInitData->ReplaceElementsAt(0, mInitData->Length(),
-                                        aData->Elements(), length)) {
-        // Unlikely OOM
-        return false;
+    if (initSegment || !HasCompleteInitData()) {
+      if (mParser.mInitEndOffset > 0) {
+        MOZ_ASSERT(mParser.mInitEndOffset <= mResource->GetLength());
+        if (!mInitData->SetLength(mParser.mInitEndOffset)) {
+          // Super unlikely OOM
+          return false;
+        }
+        char* buffer = reinterpret_cast<char*>(mInitData->Elements());
+        mResource->ReadFromCache(buffer, 0, mParser.mInitEndOffset);
+        MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
+                  this, mParser.mInitEndOffset);
+        mResource = nullptr;
+      } else {
+        MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Incomplete init found.");
       }
       mHasInitData = true;
     }
     mOffset += aData->Length();
 
     if (mapping.IsEmpty()) {
       return false;
     }
@@ -268,37 +281,42 @@ public:
     if (initSegment) {
       mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/mp4"));
       mStream = new MP4Stream(mResource);
       // We use a timestampOffset of 0 for ContainerParser, and require
       // consumers of ParseStartAndEndTimestamps to add their timestamp offset
       // manually. This allows the ContainerParser to be shared across different
       // timestampOffsets.
       mParser = new mp4_demuxer::MoofParser(mStream, 0, 0, &mMonitor);
+      mInitData = new LargeDataBuffer();
     } else if (!mStream || !mParser) {
       return false;
     }
 
     mResource->AppendData(aData);
     nsTArray<MediaByteRange> byteRanges;
     MediaByteRange mbr =
       MediaByteRange(mParser->mOffset, mResource->GetLength());
     byteRanges.AppendElement(mbr);
     mParser->RebuildFragmentedIndex(byteRanges);
 
-    if (initSegment) {
+    if (initSegment || !HasCompleteInitData()) {
       const MediaByteRange& range = mParser->mInitRange;
-      MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                this, range.mEnd - range.mStart);
-
-      if (!mInitData->ReplaceElementsAt(0, mInitData->Length(),
-                                        aData->Elements() + range.mStart,
-                                        range.mEnd - range.mStart)) {
-        // Super unlikely OOM
-        return false;
+      uint32_t length = range.mEnd - range.mStart;
+      if (length) {
+        if (!mInitData->SetLength(length)) {
+          // Super unlikely OOM
+          return false;
+        }
+        char* buffer = reinterpret_cast<char*>(mInitData->Elements());
+        mResource->ReadFromCache(buffer, range.mStart, length);
+        MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
+                  this, length);
+      } else {
+        MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Incomplete init found.");
       }
       mHasInitData = true;
     }
 
     mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
       mParser->GetCompositionRange(byteRanges);
     mResource->EvictData(mParser->mOffset, mParser->mOffset);
 
@@ -317,17 +335,16 @@ public:
   int64_t GetRoundingError()
   {
     return 20000;
   }
 
 private:
   nsRefPtr<MP4Stream> mStream;
   nsAutoPtr<mp4_demuxer::MoofParser> mParser;
-  nsRefPtr<SourceBufferResource> mResource;
   Monitor mMonitor;
 };
 #endif
 
 /*static*/ ContainerParser*
 ContainerParser::CreateForMIMEType(const nsACString& aType)
 {
   if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
--- a/dom/media/mediasource/ContainerParser.h
+++ b/dom/media/mediasource/ContainerParser.h
@@ -7,16 +7,17 @@
 #ifndef MOZILLA_CONTAINERPARSER_H_
 #define MOZILLA_CONTAINERPARSER_H_
 
 #include "nsRefPtr.h"
 
 namespace mozilla {
 
 class LargeDataBuffer;
+class SourceBufferResource;
 
 class ContainerParser {
 public:
   ContainerParser();
   virtual ~ContainerParser() {}
 
   // Return true if aData starts with an initialization segment.
   // The base implementation exists only for debug logging and is expected
@@ -43,17 +44,20 @@ public:
 
   LargeDataBuffer* InitData();
 
   bool HasInitData()
   {
     return mHasInitData;
   }
 
+  bool HasCompleteInitData();
+
   static ContainerParser* CreateForMIMEType(const nsACString& aType);
 
 protected:
   nsRefPtr<LargeDataBuffer> mInitData;
+  nsRefPtr<SourceBufferResource> mResource;
   bool mHasInitData;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_CONTAINERPARSER_H_ */
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -243,23 +243,21 @@ MediaSource::RemoveSourceBuffer(SourceBu
 {
   MOZ_ASSERT(NS_IsMainThread());
   SourceBuffer* sourceBuffer = &aSourceBuffer;
   MSE_API("MediaSource(%p)::RemoveSourceBuffer(aSourceBuffer=%p)", this, sourceBuffer);
   if (!mSourceBuffers->Contains(sourceBuffer)) {
     aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
     return;
   }
-  if (sourceBuffer->Updating()) {
-    // TODO:
-    // abort stream append loop (if running)
-    // set updating to false
-    // fire "abort" at sourceBuffer
-    // fire "updateend" at sourceBuffer
-  }
+
+  sourceBuffer->AbortBufferAppend();
+  // TODO:
+  // abort stream append loop (if running)
+
   // TODO:
   // For all sourceBuffer audioTracks, videoTracks, textTracks:
   //     set sourceBuffer to null
   //     remove sourceBuffer video, audio, text Tracks from MediaElement tracks
   //     remove sourceBuffer video, audio, text Tracks and fire "removetrack" at affected lists
   //     fire "removetrack" at modified MediaElement track lists
   // If removed enabled/selected, fire "change" at affected MediaElement list.
   if (mActiveSourceBuffers->Contains(sourceBuffer)) {
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -200,54 +200,48 @@ MediaSourceDecoder::DurationChanged(doub
   if (mMediaSource) {
     mMediaSource->DurationChange(aOldDuration, aNewDuration);
   }
   // Run the MediaElement duration changed algorithm
   MediaDecoder::DurationChanged();
 }
 
 void
-MediaSourceDecoder::SetDecodedDuration(int64_t aDuration)
+MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
 {
   // Only use the decoded duration if one wasn't already
   // set.
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (!mMediaSource || !IsNaN(mMediaSourceDuration)) {
     return;
   }
   double duration = aDuration;
   // A duration of -1 is +Infinity.
   if (aDuration >= 0) {
     duration /= USECS_PER_S;
   }
-  DoSetMediaSourceDuration(duration);
+  SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP);
 }
 
 void
 MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   double oldDuration = mMediaSourceDuration;
-  DoSetMediaSourceDuration(aDuration);
-  ScheduleDurationChange(oldDuration, aDuration, aAction);
-}
-
-void
-MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
-{
   if (aDuration >= 0) {
     mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
     mMediaSourceDuration = aDuration;
   } else {
     mDecoderStateMachine->SetDuration(INT64_MAX);
     mMediaSourceDuration = PositiveInfinity<double>();
   }
   if (mReader) {
     mReader->SetMediaSourceDuration(mMediaSourceDuration);
   }
+  ScheduleDurationChange(oldDuration, aDuration, aAction);
 }
 
 void
 MediaSourceDecoder::ScheduleDurationChange(double aOldDuration,
                                            double aNewDuration,
                                            MSRangeRemovalAction aAction)
 {
   if (aAction == MSRangeRemovalAction::SKIP) {
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -51,17 +51,17 @@ public:
                                                          int64_t aTimestampOffset /* microseconds */);
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
   void Ended();
   bool IsExpectingMoreData() MOZ_OVERRIDE;
 
-  void SetDecodedDuration(int64_t aDuration);
+  void SetInitialDuration(int64_t aDuration);
   void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
   double GetMediaSourceDuration();
   void DurationChanged(double aOldDuration, double aNewDuration);
 
   // Called whenever a TrackBuffer has new data appended or a new decoder
   // initializes.  Safe to call from any thread.
   void NotifyTimeRangesChanged();
 
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -892,50 +892,42 @@ MediaSourceReader::ReadMetadata(MediaInf
 
   mEssentialTrackBuffers.Clear();
   if (!mAudioTrack && !mVideoTrack) {
     MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata missing track: mAudioTrack=%p mVideoTrack=%p",
               this, mAudioTrack.get(), mVideoTrack.get());
     return NS_ERROR_FAILURE;
   }
 
-  int64_t maxDuration = -1;
-
   if (mAudioTrack) {
     MOZ_ASSERT(mAudioTrack->IsReady());
     mAudioReader = mAudioTrack->Decoders()[0]->GetReader();
 
     const MediaInfo& info = mAudioReader->GetMediaInfo();
     MOZ_ASSERT(info.HasAudio());
     mInfo.mAudio = info.mAudio;
     mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
-    maxDuration = std::max(maxDuration, mAudioReader->GetDecoder()->GetMediaDuration());
-    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld",
-              this, mAudioReader.get(), maxDuration);
+    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p duration=%lld",
+              this, mAudioReader.get(),
+              mAudioReader->GetDecoder()->GetMediaDuration());
   }
 
   if (mVideoTrack) {
     MOZ_ASSERT(mVideoTrack->IsReady());
     mVideoReader = mVideoTrack->Decoders()[0]->GetReader();
 
     const MediaInfo& info = mVideoReader->GetMediaInfo();
     MOZ_ASSERT(info.HasVideo());
     mInfo.mVideo = info.mVideo;
     mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
-    maxDuration = std::max(maxDuration, mVideoReader->GetDecoder()->GetMediaDuration());
-    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
-              this, mVideoReader.get(), maxDuration);
+    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p duration=%lld",
+              this, mVideoReader.get(),
+              mVideoReader->GetDecoder()->GetMediaDuration());
   }
 
-  if (!maxDuration) {
-    // Treat a duration of 0 as infinity
-    maxDuration = -1;
-  }
-  static_cast<MediaSourceDecoder*>(mDecoder)->SetDecodedDuration(maxDuration);
-
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
 }
 
 void
 MediaSourceReader::ReadUpdatedMetadata(MediaInfo* aInfo)
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -41,34 +41,37 @@ extern PRLogModuleInfo* GetMediaSourceAP
 namespace mozilla {
 
 namespace dom {
 
 class AppendDataRunnable : public nsRunnable {
 public:
   AppendDataRunnable(SourceBuffer* aSourceBuffer,
                      LargeDataBuffer* aData,
-                     double aTimestampOffset)
+                     double aTimestampOffset,
+                     uint32_t aUpdateID)
   : mSourceBuffer(aSourceBuffer)
   , mData(aData)
   , mTimestampOffset(aTimestampOffset)
+  , mUpdateID(aUpdateID)
   {
   }
 
   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
 
-    mSourceBuffer->AppendData(mData, mTimestampOffset);
+    mSourceBuffer->AppendData(mData, mTimestampOffset, mUpdateID);
 
     return NS_OK;
   }
 
 private:
   nsRefPtr<SourceBuffer> mSourceBuffer;
   nsRefPtr<LargeDataBuffer> mData;
   double mTimestampOffset;
+  uint32_t mUpdateID;
 };
 
 class RangeRemovalRunnable : public nsRunnable {
 public:
   RangeRemovalRunnable(SourceBuffer* aSourceBuffer,
                      double aStart,
                      double aEnd)
   : mSourceBuffer(aSourceBuffer)
@@ -209,30 +212,33 @@ SourceBuffer::Abort(ErrorResult& aRv)
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  Abort();
+  AbortBufferAppend();
   mTrackBuffer->ResetParserState();
   mAppendWindowStart = 0;
   mAppendWindowEnd = PositiveInfinity<double>();
-
+  // Discard the current decoder so no new data will be added to it.
   MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
-  mTrackBuffer->DiscardDecoder();
+  mTrackBuffer->DiscardCurrentDecoder();
 }
 
 void
-SourceBuffer::Abort()
+SourceBuffer::AbortBufferAppend()
 {
   if (mUpdating) {
-    // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
+    mPendingAppend.DisconnectIfExists();
+    // TODO: Abort segment parser loop, and stream append loop algorithms.
+    // cancel any pending buffer append.
+    mTrackBuffer->AbortAppendData();
     AbortUpdating();
   }
 }
 
 void
 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -264,16 +270,18 @@ SourceBuffer::RangeRemoval(double aStart
 
   nsRefPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd);
   NS_DispatchToMainThread(task);
 }
 
 void
 SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
 {
+  MSE_DEBUG("SourceBuffer(%p)::DoRangeRemoval (updating:%d)",
+            this, mUpdating);
   if (!mUpdating) {
     // abort was called in between.
     return;
   }
   if (mTrackBuffer && !IsInfinite(aStart)) {
     int64_t start = aStart * USECS_PER_S;
     int64_t end = IsInfinite(aEnd) ? INT64_MAX : (int64_t)(aEnd * USECS_PER_S);
     mTrackBuffer->RangeRemoval(start, end);
@@ -282,17 +290,17 @@ SourceBuffer::DoRangeRemoval(double aSta
   StopUpdating();
 }
 
 void
 SourceBuffer::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("SourceBuffer(%p)::Detach", this);
-  Abort();
+  AbortBufferAppend();
   if (mTrackBuffer) {
     mTrackBuffer->Detach();
   }
   mTrackBuffer = nullptr;
   mMediaSource = nullptr;
 }
 
 void
@@ -307,16 +315,17 @@ SourceBuffer::Ended()
 SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
   : DOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
   , mAppendWindowStart(0)
   , mAppendWindowEnd(PositiveInfinity<double>())
   , mTimestampOffset(0)
   , mAppendMode(SourceBufferAppendMode::Segments)
   , mUpdating(false)
+  , mUpdateID(0)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
   mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
                                             75 * (1 << 20));
   mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
   MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mTrackBuffer=%p",
             this, mTrackBuffer.get());
@@ -358,16 +367,17 @@ SourceBuffer::QueueAsyncSimpleEvent(cons
 }
 
 void
 SourceBuffer::StartUpdating()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mUpdating);
   mUpdating = true;
+  mUpdateID++;
   QueueAsyncSimpleEvent("updatestart");
 }
 
 void
 SourceBuffer::StopUpdating()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mUpdating) {
@@ -392,19 +402,21 @@ SourceBuffer::AbortUpdating()
   mUpdating = false;
   QueueAsyncSimpleEvent("abort");
   QueueAsyncSimpleEvent("updateend");
 }
 
 void
 SourceBuffer::CheckEndTime()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   // Check if we need to update mMediaSource duration
   double endTime = GetBufferedEnd();
-  if (endTime > mMediaSource->Duration()) {
+  double duration = mMediaSource->Duration();
+  if (endTime > duration) {
     mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP);
   }
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
@@ -413,50 +425,82 @@ SourceBuffer::AppendData(const uint8_t* 
   if (!data) {
     return;
   }
   StartUpdating();
 
   MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments,
              "We don't handle timestampOffset for sequence mode yet");
   nsRefPtr<nsIRunnable> task =
-    new AppendDataRunnable(this, data, mTimestampOffset);
+    new AppendDataRunnable(this, data, mTimestampOffset, mUpdateID);
   NS_DispatchToMainThread(task);
 }
 
 void
-SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset)
+SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset,
+                         uint32_t aUpdateID)
 {
-  if (!mUpdating) {
+  if (!mUpdating || aUpdateID != mUpdateID) {
     // The buffer append algorithm has been interrupted by abort().
     //
     // If the sequence appendBuffer(), abort(), appendBuffer() occurs before
     // the first StopUpdating() runnable runs, then a second StopUpdating()
     // runnable will be scheduled, but still only one (the first) will queue
     // events.
     return;
   }
 
   MOZ_ASSERT(mMediaSource);
+  MOZ_ASSERT(!mPendingAppend.Exists());
 
-  if (aData->Length()) {
-    if (!mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)) {
-      AppendError(true);
-      return;
-    }
+  if (!aData->Length()) {
+    StopUpdating();
+    return;
+  }
+
+  mPendingAppend.Begin(mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
+                       ->RefableThen(NS_GetCurrentThread(), __func__, this,
+                                     &SourceBuffer::AppendDataCompletedWithSuccess,
+                                     &SourceBuffer::AppendDataErrored));
+}
 
-    if (mTrackBuffer->HasInitSegment()) {
-      mMediaSource->QueueInitializationEvent();
-    }
+void
+SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
+{
+  mPendingAppend.Complete();
+  if (!mUpdating) {
+    // The buffer append algorithm has been interrupted by abort().
+    return;
+  }
 
+  if (mTrackBuffer->HasInitSegment()) {
+    mMediaSource->QueueInitializationEvent();
+  }
+
+  if (aGotMedia) {
     CheckEndTime();
   }
 
   StopUpdating();
- }
+}
+
+void
+SourceBuffer::AppendDataErrored(nsresult aError)
+{
+  mPendingAppend.Complete();
+  switch (aError) {
+    case NS_ERROR_ABORT:
+      // Nothing further to do as the trackbuffer has been shutdown.
+      // or append was aborted and abort() has handled all the events.
+      break;
+    default:
+      AppendError(true);
+      break;
+  }
+}
 
 void
 SourceBuffer::AppendError(bool aDecoderError)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mUpdating) {
     // The buffer append algorithm has been interrupted by abort().
     return;
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=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/. */
 
 #ifndef mozilla_dom_SourceBuffer_h_
 #define mozilla_dom_SourceBuffer_h_
 
+#include "MediaPromise.h"
 #include "MediaSource.h"
 #include "js/RootingAPI.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/SourceBufferBinding.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/mozalloc.h"
@@ -27,16 +28,17 @@ class JSObject;
 struct JSContext;
 
 namespace mozilla {
 
 class ErrorResult;
 class LargeDataBuffer;
 class TrackBuffer;
 template <typename T> class AsyncEventRunner;
+typedef MediaPromise<bool, nsresult, /* IsExclusive = */ true> TrackBufferAppendPromise;
 
 namespace dom {
 
 class TimeRanges;
 
 class SourceBuffer MOZ_FINAL : public DOMEventTargetHelper
 {
 public:
@@ -75,17 +77,17 @@ public:
   }
 
   void SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv);
 
   void AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv);
   void AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv);
 
   void Abort(ErrorResult& aRv);
-  void Abort();
+  void AbortBufferAppend();
 
   void Remove(double aStart, double aEnd, ErrorResult& aRv);
   /** End WebIDL Methods. */
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SourceBuffer, DOMEventTargetHelper)
 
   SourceBuffer(MediaSource* aMediaSource, const nsACString& aType);
@@ -134,41 +136,52 @@ private:
 
   // If the media segment contains data beyond the current duration,
   // then run the duration change algorithm with new duration set to the
   // maximum of the current duration and the group end timestamp.
   void CheckEndTime();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
-  void AppendData(LargeDataBuffer* aData, double aTimestampOffset);
+  void AppendData(LargeDataBuffer* aData, double aTimestampOffset,
+                  uint32_t aAppendID);
 
   // Implement the "Append Error Algorithm".
   // Will call endOfStream() with "decode" error if aDecodeError is true.
   // 3.5.3 Append Error Algorithm
   // http://w3c.github.io/media-source/#sourcebuffer-append-error
   void AppendError(bool aDecoderError);
 
   // Implements the "Prepare Append Algorithm". Returns LargeDataBuffer object
   // on success or nullptr (with aRv set) on error.
   already_AddRefed<LargeDataBuffer> PrepareAppend(const uint8_t* aData,
                                                 uint32_t aLength,
                                                 ErrorResult& aRv);
 
+  void AppendDataCompletedWithSuccess(bool aValue);
+  void AppendDataErrored(nsresult aError);
+
   nsRefPtr<MediaSource> mMediaSource;
 
   uint32_t mEvictionThreshold;
 
   nsRefPtr<TrackBuffer> mTrackBuffer;
 
   double mAppendWindowStart;
   double mAppendWindowEnd;
 
   double mTimestampOffset;
 
   SourceBufferAppendMode mAppendMode;
   bool mUpdating;
+
+  // Each time mUpdating is set to true, mUpdateID will be incremented.
+  // This allows for a queued AppendData task to identify if it was earlier
+  // aborted and another AppendData queued.
+  uint32_t mUpdateID;
+
+  MediaPromiseConsumerHolder<TrackBufferAppendPromise> mPendingAppend;
 };
 
 } // namespace dom
 
 } // namespace mozilla
 #endif /* mozilla_dom_SourceBuffer_h_ */
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -109,16 +109,21 @@ public:
                       MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   // Used by SourceBuffer.
   void AppendData(LargeDataBuffer* aData);
   void Ended();
+  bool IsEnded()
+  {
+    ReentrantMonitorAutoEnter mon(mMonitor);
+    return mEnded;
+  }
   // Remove data from resource if it holds more than the threshold
   // number of bytes. Returns amount evicted.
   uint32_t EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold);
 
   // Remove data from resource before the given offset.
   void EvictBefore(uint64_t aOffset);
 
   // Remove all data from the resource
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -97,26 +97,37 @@ public:
     nsRefPtr<SourceBufferDecoder> decoder = mOwner->NewDecoder(aTimestampOffset);
     if (!decoder) {
       return false;
     }
     mDecoders.AppendElement(decoder);
     return true;
   }
 
+  size_t Length()
+  {
+    return mDecoders.Length();
+  }
+
+  void AppendElement(SourceBufferDecoder* aDecoder)
+  {
+    mDecoders.AppendElement(aDecoder);
+  }
+
 private:
   TrackBuffer* mOwner;
-  nsAutoTArray<nsRefPtr<SourceBufferDecoder>,2> mDecoders;
+  nsAutoTArray<nsRefPtr<SourceBufferDecoder>,1> mDecoders;
 };
 
 nsRefPtr<ShutdownPromise>
 TrackBuffer::Shutdown()
 {
   mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   mShutdown = true;
+  mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
 
   MOZ_ASSERT(mShutdownPromise.IsEmpty());
   nsRefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
 
   RefPtr<MediaTaskQueue> queue = mTaskQueue;
   mTaskQueue = nullptr;
   queue->BeginShutdown()
        ->Then(mParentDecoder->GetReader()->GetTaskQueue(), __func__, this,
@@ -133,75 +144,119 @@ TrackBuffer::ContinueShutdown()
     mDecoders[0]->GetReader()->Shutdown()
                 ->Then(mParentDecoder->GetReader()->GetTaskQueue(), __func__, this,
                        &TrackBuffer::ContinueShutdown, &TrackBuffer::ContinueShutdown);
     mShutdownDecoders.AppendElement(mDecoders[0]);
     mDecoders.RemoveElementAt(0);
     return;
   }
 
+  mCurrentDecoder = nullptr;
   mInitializedDecoders.Clear();
   mParentDecoder = nullptr;
 
   mShutdownPromise.Resolve(true, __func__);
 }
 
-bool
+nsRefPtr<TrackBufferAppendPromise>
 TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mInitializationPromise.IsEmpty());
+
   DecodersToInitialize decoders(this);
+  nsRefPtr<TrackBufferAppendPromise> p = mInitializationPromise.Ensure(__func__);
+  bool hadInitData = mParser->HasInitData();
+  bool hadCompleteInitData = mParser->HasCompleteInitData();
+  nsRefPtr<LargeDataBuffer> oldInit = mParser->InitData();
+  bool newInitData = mParser->IsInitSegmentPresent(aData);
+
   // TODO: Run more of the buffer append algorithm asynchronously.
-  if (mParser->IsInitSegmentPresent(aData)) {
+  if (newInitData) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
-    if (!decoders.NewDecoder(aTimestampOffset)) {
-      return false;
-    }
-  } else if (!mParser->HasInitData()) {
+  } else if (!hadInitData) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
-    return false;
+    mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+    return p;
   }
 
-  int64_t start, end;
-  if (mParser->ParseStartAndEndTimestamps(aData, start, end)) {
+  int64_t start = 0, end = 0;
+  bool gotMedia = mParser->ParseStartAndEndTimestamps(aData, start, end);
+  bool gotInit = mParser->HasCompleteInitData();
+
+  if (newInitData) {
+    if (!gotInit) {
+      // We need a new decoder, but we can't initialize it yet.
+      nsRefPtr<SourceBufferDecoder> decoder = NewDecoder(aTimestampOffset);
+      // The new decoder is stored in mDecoders/mCurrentDecoder, so we
+      // don't need to do anything with 'decoder'. It's only a placeholder.
+      if (!decoder) {
+        mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+        return p;
+      }
+    } else {
+      if (!decoders.NewDecoder(aTimestampOffset)) {
+        mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+        return p;
+      }
+    }
+  } else if (!hadCompleteInitData && gotInit) {
+    MOZ_ASSERT(mCurrentDecoder);
+    // Queue pending decoder for initialization now that we have a full
+    // init segment.
+    decoders.AppendElement(mCurrentDecoder);
+  }
+
+  if (gotMedia) {
     start += aTimestampOffset;
     end += aTimestampOffset;
-    if (mParser->IsMediaSegmentPresent(aData) &&
-        mLastEndTimestamp &&
+    if (mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
          mLastTimestampOffset != aTimestampOffset ||
-         mDecoderPerSegment || (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
+         mDecoderPerSegment ||
+         (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
-      // This data is earlier in the timeline than data we have already
-      // processed, so we must create a new decoder to handle the decoding.
-      if (!decoders.NewDecoder(aTimestampOffset)) {
-        return false;
+      if (!newInitData) {
+        // This data is earlier in the timeline than data we have already
+        // processed or not continuous, so we must create a new decoder
+        // to handle the decoding.
+        if (!hadCompleteInitData || !decoders.NewDecoder(aTimestampOffset)) {
+          mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+          return p;
+        }
+        MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
+        AppendDataToCurrentResource(oldInit, 0);
       }
-      MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-      nsRefPtr<LargeDataBuffer> initData = mParser->InitData();
-      AppendDataToCurrentResource(initData, end - start);
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
   if (!AppendDataToCurrentResource(aData, end - start)) {
-    return false;
+    mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+    return p;
   }
 
-  // Tell our reader that we have more data to ensure that playback starts if
-  // required when data is appended.
-  mParentDecoder->GetReader()->MaybeNotifyHaveData();
-  return true;
+  if (decoders.Length()) {
+    // We're going to have to wait for the decoder to initialize, the promise
+    // will be resolved once initialization completes.
+    return p;
+  } else if (gotMedia) {
+    // Tell our reader that we have more data to ensure that playback starts if
+    // required when data is appended.
+    mParentDecoder->GetReader()->MaybeNotifyHaveData();
+  }
+  mInitializationPromise.Resolve(gotMedia, __func__);
+  return p;
 }
 
 bool
 TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mCurrentDecoder) {
     return false;
@@ -365,35 +420,35 @@ TrackBuffer::EvictBefore(double aTime)
 
 double
 TrackBuffer::Buffered(dom::TimeRanges* aRanges)
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
 
   double highestEndTime = 0;
 
-  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+  for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
     nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
-    mDecoders[i]->GetBuffered(r);
+    mInitializedDecoders[i]->GetBuffered(r);
     if (r->Length() > 0) {
       highestEndTime = std::max(highestEndTime, r->GetEndTime());
       aRanges->Union(r, double(mParser->GetRoundingError()) / USECS_PER_S);
     }
   }
 
   return highestEndTime;
 }
 
 already_AddRefed<SourceBufferDecoder>
 TrackBuffer::NewDecoder(int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mParentDecoder);
 
-  DiscardDecoder();
+  DiscardCurrentDecoder();
 
   nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType, aTimestampOffset);
   if (!decoder) {
     return nullptr;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   mCurrentDecoder = decoder;
   mDecoders.AppendElement(decoder);
@@ -405,62 +460,96 @@ TrackBuffer::NewDecoder(int64_t aTimesta
   decoder->SetTaskQueue(mTaskQueue);
   return decoder.forget();
 }
 
 bool
 TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
 {
   if (NS_WARN_IF(!mTaskQueue)) {
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return false;
   }
 
   RefPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
                                                       &TrackBuffer::InitializeDecoder,
                                                       aDecoder);
   if (NS_FAILED(mTaskQueue->Dispatch(task))) {
-    MSE_DEBUG("MediaSourceReader(%p): Failed to enqueue decoder initialization task", this);
+    MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
     RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return false;
   }
   return true;
 }
 
 void
 TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
 {
+  if (!mParentDecoder) {
+    MSE_DEBUG("TrackBuffer(%p) was shutdown. Aborting initialization.",
+              this);
+    return;
+  }
   // ReadMetadata may block the thread waiting on data, so we must be able
   // to leave the monitor while we call it. For the rest of this function
   // we want to hold the monitor though, since we run on a different task queue
   // from the reader and interact heavily with it.
   mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
 
+  if (mCurrentDecoder != aDecoder) {
+    MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
+              this);
+    // If we reached this point, the SourceBuffer would have disconnected
+    // the promise. So no need to reject it.
+    return;
+  }
+
   // We may be shut down at any time by the reader on another thread. So we need
   // to check for this each time we acquire the monitor. If that happens, we
   // need to abort immediately, because the reader has forgotten about us, and
   // important pieces of our state (like mTaskQueue) have also been torn down.
   if (mShutdown) {
     MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
+    RemoveDecoder(aDecoder);
     return;
   }
 
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   MediaDecoderReader* reader = aDecoder->GetReader();
   MSE_DEBUG("TrackBuffer(%p): Initializing subdecoder %p reader %p",
             this, aDecoder, reader);
 
   MediaInfo mi;
   nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
   nsresult rv;
+
+  // HACK WARNING:
+  // We only reach this point once we know that we have a complete init segment.
+  // We don't want the demuxer to do a blocking read as no more data can be
+  // appended while this routine is running. Marking the SourceBufferResource
+  // as ended will cause any incomplete reads to abort.
+  // As this decoder hasn't been initialized yet, the resource isn't yet in use
+  // and so it is safe to do so.
+  bool wasEnded = aDecoder->GetResource()->IsEnded();
+  if (!wasEnded) {
+    aDecoder->GetResource()->Ended();
+  }
   {
     ReentrantMonitorAutoExit mon(mParentDecoder->GetReentrantMonitor());
     rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
   }
+  if (!wasEnded) {
+    // Adding an empty buffer will reopen the SourceBufferResource
+    nsRefPtr<LargeDataBuffer> emptyBuffer = new LargeDataBuffer;
+    aDecoder->GetResource()->AppendData(emptyBuffer);
+  }
+  // HACK END.
 
   reader->SetIdle();
   if (mShutdown) {
     MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
     return;
   }
 
   if (NS_SUCCEEDED(rv) && reader->IsWaitingOnCDMResource()) {
@@ -470,40 +559,86 @@ TrackBuffer::InitializeDecoder(SourceBuf
 
   aDecoder->SetTaskQueue(nullptr);
 
   if (NS_FAILED(rv) || (!mi.HasVideo() && !mi.HasAudio())) {
     // XXX: Need to signal error back to owning SourceBuffer.
     MSE_DEBUG("TrackBuffer(%p): Reader %p failed to initialize rv=%x audio=%d video=%d",
               this, reader, rv, mi.HasAudio(), mi.HasVideo());
     RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return;
   }
 
   if (mi.HasVideo()) {
     MSE_DEBUG("TrackBuffer(%p): Reader %p video resolution=%dx%d",
               this, reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
   }
   if (mi.HasAudio()) {
     MSE_DEBUG("TrackBuffer(%p): Reader %p audio sampleRate=%d channels=%d",
               this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
   }
 
-  if (!RegisterDecoder(aDecoder)) {
-    // XXX: Need to signal error back to owning SourceBuffer.
-    MSE_DEBUG("TrackBuffer(%p): Reader %p not activated", this, reader);
+  RefPtr<nsIRunnable> task =
+    NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
+                                                      &TrackBuffer::CompleteInitializeDecoder,
+                                                      aDecoder);
+  if (NS_FAILED(NS_DispatchToMainThread(task))) {
+    MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
+    RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
+    return;
+  }
+}
+
+void
+TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
+{
+  if (!mParentDecoder) {
+    MSE_DEBUG("TrackBuffer(%p) was shutdown. Aborting initialization.",
+              this);
+    return;
+  }
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+  if (mCurrentDecoder != aDecoder) {
+    MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
+              this);
+    // If we reached this point, the SourceBuffer would have disconnected
+    // the promise. So no need to reject it.
+    return;
+  }
+
+  if (mShutdown) {
+    MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
     RemoveDecoder(aDecoder);
     return;
   }
 
+  if (!RegisterDecoder(aDecoder)) {
+    MSE_DEBUG("TrackBuffer(%p): Reader %p not activated",
+              this, aDecoder->GetReader());
+    RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
+    return;
+  }
+
+  int64_t duration = aDecoder->GetMediaDuration();
+  if (!duration) {
+    // Treat a duration of 0 as infinity
+    duration = -1;
+  }
+  mParentDecoder->SetInitialDuration(duration);
+
   // Tell our reader that we have more data to ensure that playback starts if
   // required when data is appended.
   mParentDecoder->GetReader()->MaybeNotifyHaveData();
 
-  MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
+  MSE_DEBUG("TrackBuffer(%p): Reader %p activated",
+            this, aDecoder->GetReader());
+  mInitializationPromise.ResolveIfExists(aDecoder->GetRealMediaDuration() > 0, __func__);
 }
 
 bool
 TrackBuffer::ValidateTrackFormats(const MediaInfo& aInfo)
 {
   if (mInfo.HasAudio() != aInfo.HasAudio() ||
       mInfo.HasVideo() != aInfo.HasVideo()) {
     MSE_DEBUG("TrackBuffer(%p)::ValidateTrackFormats audio/video track mismatch", this);
@@ -536,22 +671,20 @@ TrackBuffer::RegisterDecoder(SourceBuffe
     return false;
   }
   mInitializedDecoders.AppendElement(aDecoder);
   mParentDecoder->NotifyTimeRangesChanged();
   return true;
 }
 
 void
-TrackBuffer::DiscardDecoder()
+TrackBuffer::DiscardCurrentDecoder()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
-  if (mCurrentDecoder) {
-    mCurrentDecoder->GetResource()->Ended();
-  }
+  EndCurrentDecoder();
   mCurrentDecoder = nullptr;
 }
 
 void
 TrackBuffer::EndCurrentDecoder()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   if (mCurrentDecoder) {
@@ -559,33 +692,33 @@ TrackBuffer::EndCurrentDecoder()
   }
 }
 
 void
 TrackBuffer::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mCurrentDecoder) {
-    DiscardDecoder();
+    DiscardCurrentDecoder();
   }
 }
 
 bool
 TrackBuffer::HasInitSegment()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
-  return mParser->HasInitData();
+  return mParser->HasCompleteInitData();
 }
 
 bool
 TrackBuffer::IsReady()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   MOZ_ASSERT((mInfo.HasAudio() || mInfo.HasVideo()) || mInitializedDecoders.IsEmpty());
-  return mParser->HasInitData() && (mInfo.HasAudio() || mInfo.HasVideo());
+  return mInfo.HasAudio() || mInfo.HasVideo();
 }
 
 bool
 TrackBuffer::ContainsTime(int64_t aTime, int64_t aTolerance)
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
     nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
@@ -621,17 +754,34 @@ TrackBuffer::ResetDecode()
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
     mDecoders[i]->GetReader()->ResetDecode();
   }
 }
 
 void
 TrackBuffer::ResetParserState()
 {
-  // TODO
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mParser->HasInitData() && !mParser->HasCompleteInitData()) {
+    // We have an incomplete init segment pending. reset current parser and
+    // discard the current decoder.
+    mParser = ContainerParser::CreateForMIMEType(mType);
+    DiscardCurrentDecoder();
+  }
+}
+
+void
+TrackBuffer::AbortAppendData()
+{
+  DiscardCurrentDecoder();
+  // The SourceBuffer would have disconnected its promise.
+  // However we must ensure that the MediaPromiseHolder handle all pending
+  // promises.
+  mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
 }
 
 const nsTArray<nsRefPtr<SourceBufferDecoder>>&
 TrackBuffer::Decoders()
 {
   // XXX assert OnDecodeThread
   return mInitializedDecoders;
 }
@@ -712,17 +862,17 @@ TrackBuffer::RemoveDecoder(SourceBufferD
     ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
     // There should be no other references to the decoder. Assert that
     // we aren't using it in the MediaSourceReader.
     MOZ_ASSERT(!mParentDecoder->IsActiveReader(aDecoder->GetReader()));
     mInitializedDecoders.RemoveElement(aDecoder);
     mDecoders.RemoveElement(aDecoder);
 
     if (mCurrentDecoder == aDecoder) {
-      DiscardDecoder();
+      DiscardCurrentDecoder();
     }
   }
   aDecoder->GetReader()->GetTaskQueue()->Dispatch(task);
 }
 
 bool
 TrackBuffer::RangeRemoval(int64_t aStart, int64_t aEnd)
 {
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -2,18 +2,18 @@
 /* vim: set ts=8 sts=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/. */
 
 #ifndef MOZILLA_TRACKBUFFER_H_
 #define MOZILLA_TRACKBUFFER_H_
 
+#include "SourceBuffer.h"
 #include "SourceBufferDecoder.h"
-#include "MediaPromise.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/Maybe.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nscore.h"
 
@@ -35,17 +35,18 @@ public:
 
   TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
 
   nsRefPtr<ShutdownPromise> Shutdown();
 
   // Append data to the current decoder.  Also responsible for calling
   // NotifyDataArrived on the decoder to keep buffered range computation up
   // to date.  Returns false if the append failed.
-  bool AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset /* microseconds */);
+  nsRefPtr<TrackBufferAppendPromise> AppendData(LargeDataBuffer* aData,
+                                                int64_t aTimestampOffset /* microseconds */);
 
   // Evicts data held in the current decoders SourceBufferResource from the
   // start of the buffer through to aPlaybackTime. aThreshold is used to
   // bound the data being evicted. It will not evict more than aThreshold
   // bytes. aBufferStartTime contains the new start time of the current
   // decoders buffered data after the eviction. Returns true if data was
   // evicted.
   bool EvictData(double aPlaybackTime, uint32_t aThreshold, double* aBufferStartTime);
@@ -56,17 +57,17 @@ public:
 
   // Returns the highest end time of all of the buffered ranges in the
   // decoders managed by this TrackBuffer, and returns the union of the
   // decoders buffered ranges in aRanges. This may be called on any thread.
   double Buffered(dom::TimeRanges* aRanges);
 
   // Mark the current decoder's resource as ended, clear mCurrentDecoder and
   // reset mLast{Start,End}Timestamp.
-  void DiscardDecoder();
+  void DiscardCurrentDecoder();
   // Mark the current decoder's resource as ended.
   void EndCurrentDecoder();
 
   void Detach();
 
   // Returns true if an init segment has been appended.
   bool HasInitSegment();
 
@@ -95,16 +96,19 @@ public:
 
   // Runs MSE range removal algorithm.
   // http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
   // Implementation is only partial, we can only trim a buffer.
   // Returns true if data was evicted.
   // Times are in microseconds.
   bool RangeRemoval(int64_t aStart, int64_t aEnd);
 
+  // Abort any pending appendBuffer by rejecting any pending promises.
+  void AbortAppendData();
+
 #ifdef MOZ_EME
   nsresult SetCDMProxy(CDMProxy* aProxy);
 #endif
 
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
@@ -125,16 +129,21 @@ private:
                                    uint32_t aDuration /* microseconds */);
 
   // Queue execution of InitializeDecoder on mTaskQueue.
   bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder);
 
   // Runs decoder initialization including calling ReadMetadata.  Runs as an
   // event on the decode thread pool.
   void InitializeDecoder(SourceBufferDecoder* aDecoder);
+  // Once decoder has been initialized, set mediasource duration if required
+  // and resolve any pending InitializationPromise.
+  // Setting the mediasource duration must be done on the main thread.
+  // TODO: Why is that so?
+  void CompleteInitializeDecoder(SourceBufferDecoder* aDecoder);
 
   // Adds a successfully initialized decoder to mDecoders and (if it's the
   // first decoder initialized), initializes mHasAudio/mHasVideo.  Called
   // from the decode thread pool.  Return true if the decoder was
   // successfully registered.
   bool RegisterDecoder(SourceBufferDecoder* aDecoder);
 
   // Returns true if aInfo is considered a supported or the same format as
@@ -188,12 +197,15 @@ private:
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
   bool mDecoderPerSegment;
   bool mShutdown;
+
+  MediaPromiseHolder<TrackBufferAppendPromise> mInitializationPromise;
+
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_TRACKBUFFER_H_ */
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -450,29 +450,30 @@ skip-if = (toolkit == 'android' && proce
 [test_play_events_2.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_play_twice.html]
 # Seamonkey: Bug 598252
 skip-if = appname == "seamonkey"
 [test_playback.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_playback_errors.html]
+skip-if = toolkit == 'gonk' # bug 1128845
 [test_playback_rate.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #bug 845162
 [test_playback_rate_playpause.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_played.html]
 [test_preload_actions.html]
 [test_preload_attribute.html]
 [test_preload_suspend.html]
 skip-if = true # bug 493692
 [test_progress.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_reactivate.html]
-skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
+skip-if = toolkit == 'gonk' || (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 and bug 1128845 on gonk
 [test_readyState.html]
 [test_referer.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_replay_metadata.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_reset_events_async.html]
 [test_reset_src.html]
 [test_video_dimensions.html]
@@ -520,17 +521,17 @@ skip-if = (toolkit == 'android' && proce
 [test_texttrackcue.html]
 [test_texttracklist.html]
 [test_texttrackregion.html]
 [test_timeupdate_small_files.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_trackelementevent.html]
 [test_trackevent.html]
 [test_unseekable.html]
-skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
+skip-if = toolkit == 'gonk' || (toolkit == 'android' && processor == 'x86') #x86 only and bug 1128845 on gonk
 [test_video_to_canvas.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_video_in_audio_element.html]
 [test_videoDocumentTitle.html]
 [test_VideoPlaybackQuality.html]
 [test_VideoPlaybackQuality_disabled.html]
 [test_volume.html]
 [test_vttparser.html]
--- a/dom/media/test/test_bug686942.html
+++ b/dom/media/test/test_bug686942.html
@@ -18,33 +18,37 @@ https://bugzilla.mozilla.org/show_bug.cg
 if (navigator.platform.startsWith("Win")) {
   SimpleTest.expectAssertions(0, 2);
 }
 
 var manager = new MediaTestManager;
 
 function onloaded(event) {
   var v = event.target;
+  v.removeEventListener("loadedmetadata", onloaded);
   v.currentTime = v.duration;
   return;
 }
 
 function checkNotPlaying(v) {
   ok(v.currentTime == 0, "Should not be playing after seek to end and back to beginning");
   v._finished = true;
   manager.finished(v.token);
+  removeNodeAndSource(v);
 }
 
 function onseeked(event) {
   var v = event.target;
+  v.removeEventListener("seeked", onseeked);
   setTimeout(function() { checkNotPlaying(v); }, 500);
 }
 
 function onended(event) {
   var v = event.target;
+  v.removeEventListener("ended", onended);
   if (v._finished)
     return;
   v.addEventListener("seeked", onseeked, false);
   v.currentTime = 0;
 }
 
 function startTest(test, token) {
   var v = document.createElement('video');
--- a/dom/media/test/test_paused_after_ended.html
+++ b/dom/media/test/test_paused_after_ended.html
@@ -9,23 +9,26 @@
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var manager = new MediaTestManager;
 
 function ended(evt) {
   var v = evt.target;
+  v.removeEventListener("ended", ended);
   is(v.gotPause, true, "We should have received a \"pause\" event.")
   is(v.paused, true, v._name + " must be paused after end");
   manager.finished(v.token);
+  removeNodeAndSource(v);
 }
 
 function pause(evt) {
   var v = evt.target;
+  v.removeEventListener("pause", pause);
   v.gotPause = true;
 }
 
 function startTest(test, token) {
   var v = document.createElement('video');
   v.token = token;
   manager.started(v.token);
   v.src = test.name;
--- a/dom/media/test/test_streams_element_capture.html
+++ b/dom/media/test/test_streams_element_capture.html
@@ -42,16 +42,17 @@ function startTest(test, token) {
     }
     is(vout.readyState, vout.HAVE_CURRENT_DATA, test.name + " checking readyState");
     ok(vout.ended, test.name + " checking playback has ended");
     if (test.type.match(/^video/)) {
       checkDrawImage(vout);
     }
     vout.parentNode.removeChild(vout);
     manager.finished(vout.token);
+    removeNodeAndSource(v);
   }}(test, vout, stream);
   vout.addEventListener("ended", checkEnded, false);
 
   document.body.appendChild(vout);
   v.play();
   vout.play();
 
   // Log events for debugging.
--- a/dom/media/webm/WebMBufferedParser.cpp
+++ b/dom/media/webm/WebMBufferedParser.cpp
@@ -32,16 +32,17 @@ VIntLength(unsigned char aFirstByte, uin
 }
 
 void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
                                 nsTArray<WebMTimeDataOffset>& aMapping,
                                 ReentrantMonitor& aReentrantMonitor)
 {
   static const uint32_t SEGMENT_ID = 0x18538067;
   static const uint32_t SEGINFO_ID = 0x1549a966;
+  static const uint32_t TRACKS_ID = 0x1654AE6B;
   static const uint32_t CLUSTER_ID = 0x1f43b675;
   static const uint32_t TIMECODESCALE_ID = 0x2ad7b1;
   static const unsigned char TIMECODE_ID = 0xe7;
   static const unsigned char BLOCK_ID = 0xa1;
   static const unsigned char SIMPLEBLOCK_ID = 0xa3;
   static const uint32_t BLOCK_TIMECODE_LENGTH = 2;
 
   static const unsigned char CLUSTER_SYNC_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
@@ -110,16 +111,20 @@ void WebMBufferedParser::Append(const un
         mBlockSize = mElement.mSize.mValue;
         mBlockTimecode = 0;
         mBlockTimecodeLength = BLOCK_TIMECODE_LENGTH;
         mBlockOffset = mCurrentOffset + (p - aBuffer) -
                        (mElement.mID.mLength + mElement.mSize.mLength);
         mState = READ_VINT;
         mNextState = READ_BLOCK_TIMECODE;
         break;
+      case TRACKS_ID:
+        mSkipBytes = mElement.mSize.mValue;
+        mState = CHECK_INIT_FOUND;
+        break;
       default:
         mSkipBytes = mElement.mSize.mValue;
         mState = SKIP_DATA;
         mNextState = READ_ELEMENT_ID;
         break;
       }
       break;
     case READ_VINT: {
@@ -187,16 +192,30 @@ void WebMBufferedParser::Append(const un
         uint32_t left = aLength - (p - aBuffer);
         left = std::min(left, mSkipBytes);
         p += left;
         mSkipBytes -= left;
       } else {
         mState = mNextState;
       }
       break;
+    case CHECK_INIT_FOUND:
+      if (mSkipBytes) {
+        uint32_t left = aLength - (p - aBuffer);
+        left = std::min(left, mSkipBytes);
+        p += left;
+        mSkipBytes -= left;
+      }
+      if (!mSkipBytes) {
+        if (mInitEndOffset < 0) {
+          mInitEndOffset = mCurrentOffset + (p - aBuffer);
+        }
+        mState = READ_ELEMENT_ID;
+      }
+      break;
     }
   }
 
   NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data.");
   mCurrentOffset += aLength;
 }
 
 // SyncOffsetComparator and TimeComparator are slightly confusing, in that
@@ -316,17 +335,25 @@ void WebMBufferedState::NotifyDataArrive
                             mTimeMapping,
                             mReentrantMonitor);
 
   // Merge parsers with overlapping regions and clean up the remnants.
   uint32_t i = 0;
   while (i + 1 < mRangeParsers.Length()) {
     if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) {
       mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset;
+      mRangeParsers[i + 1].mInitEndOffset = mRangeParsers[i].mInitEndOffset;
       mRangeParsers.RemoveElementAt(i);
     } else {
       i += 1;
     }
   }
 }
 
+int64_t WebMBufferedState::GetInitEndOffset()
+{
+  if (mRangeParsers.IsEmpty()) {
+    return -1;
+  }
+  return mRangeParsers[0].mInitEndOffset;
+}
+
 } // namespace mozilla
-
--- a/dom/media/webm/WebMBufferedParser.h
+++ b/dom/media/webm/WebMBufferedParser.h
@@ -45,19 +45,19 @@ struct WebMTimeDataOffset
 // consumes blocks.  A new parser is created for each distinct range of data
 // received and begins parsing from the first WebM cluster within that
 // range.  Old parsers are destroyed when their range merges with a later
 // parser or an already parsed range.  The parser may start at any position
 // within the stream.
 struct WebMBufferedParser
 {
   explicit WebMBufferedParser(int64_t aOffset)
-    : mStartOffset(aOffset), mCurrentOffset(aOffset), mState(READ_ELEMENT_ID),
-      mVIntRaw(false), mClusterSyncPos(0), mTimecodeScale(1000000),
-      mGotTimecodeScale(false)
+    : mStartOffset(aOffset), mCurrentOffset(aOffset), mInitEndOffset(-1),
+      mState(READ_ELEMENT_ID), mVIntRaw(false), mClusterSyncPos(0),
+      mTimecodeScale(1000000), mGotTimecodeScale(false)
   {
     if (mStartOffset != 0) {
       mState = FIND_CLUSTER_SYNC;
     }
   }
 
   uint32_t GetTimecodeScale() {
     MOZ_ASSERT(mGotTimecodeScale);
@@ -90,16 +90,20 @@ struct WebMBufferedParser
   // adjacent parsers, in which case the later parser adopts the earlier
   // parser's mStartOffset.
   int64_t mStartOffset;
 
   // Current offset with the stream.  Updated in chunks as Append() consumes
   // data.
   int64_t mCurrentOffset;
 
+  // Tracks element's end offset. This indicates the end of the init segment.
+  // Will only be set if a Segment Information has been found.
+  int64_t mInitEndOffset;
+
 private:
   enum State {
     // Parser start state.  Expects to begin at a valid EBML element.  Move
     // to READ_VINT with mVIntRaw true, then return to READ_ELEMENT_SIZE.
     READ_ELEMENT_ID,
 
     // Store element ID read into mVInt into mElement.mID.  Move to
     // READ_VINT with mVIntRaw false, then return to PARSE_ELEMENT.
@@ -135,16 +139,21 @@ private:
     // mBlockTimecodeLength holds the remaining length of the block timecode
     // left to read.  Read each byte of the timecode into mBlockTimecode.
     // Once complete, calculate the scaled timecode from the cluster
     // timecode, block timecode, and timecode scale, and insert a
     // WebMTimeDataOffset entry into aMapping if one is not already present
     // for this offset.
     READ_BLOCK_TIMECODE,
 
+    // Will skip the current tracks element and set mInitEndOffset if an init
+    // segment has been found.
+    // Currently, only assumes it's the end of the tracks element.
+    CHECK_INIT_FOUND,
+
     // Skip mSkipBytes of data before resuming parse at mNextState.
     SKIP_DATA,
   };
 
   // Current state machine action.
   State mState;
 
   // Next state machine action.  SKIP_DATA and READ_VINT_REST advance to
@@ -227,16 +236,19 @@ public:
   bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
                                  uint64_t* aStartTime, uint64_t* aEndTime);
 
   // Returns true if aTime is is present in mTimeMapping and sets aOffset to
   // the latest offset for which decoding can resume without data
   // dependencies to arrive at aTime.
   bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);
 
+  // Returns end offset of init segment or -1 if none found.
+  int64_t GetInitEndOffset();
+
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~WebMBufferedState() {
     MOZ_COUNT_DTOR(WebMBufferedState);
   }
 
   // Synchronizes access to the mTimeMapping array.
   ReentrantMonitor mReentrantMonitor;
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -332,17 +332,17 @@ nsresult WebMReader::ReadMetadata(MediaI
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   nestegg_io io;
   io.read = webm_read;
   io.seek = webm_seek;
   io.tell = webm_tell;
   io.userdata = mDecoder;
   int64_t maxOffset = mDecoder->HasInitializationData() ?
-    mDecoder->GetResource()->GetLength() : -1;
+    mBufferedState->GetInitEndOffset() : -1;
   int r = nestegg_init(&mContext, io, &webm_log, maxOffset);
   if (r == -1) {
     return NS_ERROR_FAILURE;
   }
 
   uint64_t duration = 0;
   r = nestegg_duration(mContext, &duration);
   if (r == 0) {
--- a/dom/settings/SettingsRequestManager.jsm
+++ b/dom/settings/SettingsRequestManager.jsm
@@ -3,17 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
-this.EXPORTED_SYMBOLS = [];
+this.EXPORTED_SYMBOLS = ["SettingsRequestManager"];
 
 Cu.import("resource://gre/modules/SettingsDB.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 let DEBUG = false;
 let VERBOSE = false;
@@ -59,16 +59,19 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/memory-reporter-manager;1",
                                    "nsIMemoryReporterManager");
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
+XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
+                                   "@mozilla.org/settingsService;1",
+                                   "nsISettingsService");
 
 let SettingsPermissions = {
   checkPermission: function(aPrincipal, aPerm) {
     if (!aPrincipal) {
       Cu.reportError("SettingsPermissions.checkPermission was passed a null principal. Denying all permissions.");
       return false;
     }
     if (aPrincipal.origin == "[System Principal]" ||
@@ -1031,20 +1034,28 @@ let SettingsRequestManager = {
 
   receiveMessage: function(aMessage) {
     if (VERBOSE) debug("receiveMessage " + aMessage.name + ": " + JSON.stringify(aMessage.data));
 
     let msg = aMessage.data;
     let mm = aMessage.target;
 
     function returnMessage(name, data) {
-      try {
-        mm.sendAsyncMessage(name, data);
-      } catch (e) {
-        if (DEBUG) debug("Return message failed, " + name);
+      if (mm) {
+        try {
+          mm.sendAsyncMessage(name, data);
+        } catch (e) {
+          if (DEBUG) debug("Return message failed, " + name + ": " + e);
+        }
+      } else {
+        try {
+          gSettingsService.receiveMessage({ name: name, data: data });
+	} catch (e) {
+          if (DEBUG) debug("Direct return message failed, " + name + ": " + e);
+	}
       }
     }
 
     // For all message types that expect a lockID, we check to make
     // sure that we're accessing a lock that's part of our process. If
     // not, consider it a security violation and kill the app. Killing
     // based on creating a colliding lock ID happens as part of
     // CreateLock check below.
@@ -1188,9 +1199,10 @@ let SettingsRequestManager = {
         this.startRunning(msg.lockID);
         break;
       default:
         if (DEBUG) debug("Wrong message: " + aMessage.name);
     }
   }
 };
 
+this.SettingsRequestManager = SettingsRequestManager;
 SettingsRequestManager.init();
--- a/dom/settings/SettingsService.js
+++ b/dom/settings/SettingsService.js
@@ -74,34 +74,40 @@ function SettingsServiceLock(aSettingsSe
   }
 
   let createLockPayload = {
     lockID: this._id,
     isServiceLock: true,
     windowID: undefined,
     lockStack: (new Error).stack
   };
-  cpmm.sendAsyncMessage("Settings:CreateLock",
-                        createLockPayload,
-                        undefined,
-                        Services.scriptSecurityManager.getSystemPrincipal());
+
+  this.returnMessage("Settings:CreateLock", createLockPayload);
   Services.tm.currentThread.dispatch(closeHelper, Ci.nsIThread.DISPATCH_NORMAL);
 }
 
 SettingsServiceLock.prototype = {
   get closed() {
     return !this._open;
   },
 
+  returnMessage: function(aMessage, aData) {
+    SettingsRequestManager.receiveMessage({
+      name: aMessage,
+      data: aData,
+      target: undefined,
+      principal: Services.scriptSecurityManager.getSystemPrincipal()
+    });
+  },
+
   runOrFinalizeQueries: function() {
     if (!this._requests || Object.keys(this._requests).length == 0) {
-      this._settingsService.unregisterLock(this._id);
-      cpmm.sendAsyncMessage("Settings:Finalize", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal());
+      this.returnMessage("Settings:Finalize", {lockID: this._id});
     } else {
-      cpmm.sendAsyncMessage("Settings:Run", {lockID: this._id}, undefined, Services.scriptSecurityManager.getSystemPrincipal());
+      this.returnMessage("Settings:Run", {lockID: this._id});
     }
   },
 
   receiveMessage: function(aMessage) {
 
     let msg = aMessage.data;
     // SettingsRequestManager broadcasts changes to all locks in the child. If
     // our lock isn't being addressed, just return.
@@ -119,31 +125,33 @@ SettingsServiceLock.prototype = {
           break;
         case "Settings:Finalize:KO":
           if (DEBUG) debug("Lock finalize failed!");
           this.callAbort();
           break;
         default:
           if (DEBUG) debug("Message type " + aMessage.name + " is missing a requestID");
       }
+
+      this._settingsService.unregisterLock(this._id);
       return;
     }
 
     let req = this._requests[msg.requestID];
     if (!req) {
       if (DEBUG) debug("Matching request not found.");
       return;
     }
     delete this._requests[msg.requestID];
     switch (aMessage.name) {
       case "Settings:Get:OK":
         this._open = true;
         let settings_names = Object.keys(msg.settings);
         if (settings_names.length > 0) {
-          let name = settings_names[0];        
+          let name = settings_names[0];
           if (DEBUG && settings_names.length > 1) {
             debug("Warning: overloaded setting:" + name);
           }
           let result = msg.settings[name];
           this.callHandle(req.callback, name, result);
         } else {
           this.callHandle(req.callback, req.name, null);
         }
@@ -169,37 +177,33 @@ SettingsServiceLock.prototype = {
   get: function get(aName, aCallback) {
     if (VERBOSE) debug("get (" + this._id + "): " + aName);
     if (!this._open) {
       if (DEBUG) debug("Settings lock not open!\n");
       throw Components.results.NS_ERROR_ABORT;
     }
     let reqID = uuidgen.generateUUID().toString();
     this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName);
-    cpmm.sendAsyncMessage("Settings:Get", {requestID: reqID,
-                                           lockID: this._id,
-                                           name: aName},
-                                           undefined,
-                                           Services.scriptSecurityManager.getSystemPrincipal());
+    this.returnMessage("Settings:Get", {requestID: reqID,
+                                        lockID: this._id,
+                                        name: aName});
   },
 
   set: function set(aName, aValue, aCallback) {
     if (VERBOSE) debug("set: " + aName + " " + aValue);
     if (!this._open) {
       throw "Settings lock not open";
     }
     let reqID = uuidgen.generateUUID().toString();
     this._requests[reqID] = makeSettingsServiceRequest(aCallback, aName, aValue);
     let settings = {};
     settings[aName] = aValue;
-    cpmm.sendAsyncMessage("Settings:Set", {requestID: reqID,
-                                           lockID: this._id,
-                                           settings: settings},
-                                           undefined,
-                                           Services.scriptSecurityManager.getSystemPrincipal());
+    this.returnMessage("Settings:Set", {requestID: reqID,
+                                        lockID: this._id,
+                                        settings: settings});
   },
 
   callHandle: function callHandle(aCallback, aName, aValue) {
     try {
         aCallback && aCallback.handle ? aCallback.handle(aName, aValue) : null;
     } catch (e) {
       if (DEBUG) debug("settings 'handle' callback threw an exception, dropping: " + e + "\n");
     }
@@ -234,16 +238,17 @@ SettingsServiceLock.prototype = {
 };
 
 const SETTINGSSERVICE_CID        = Components.ID("{f656f0c0-f776-11e1-a21f-0800200c9a66}");
 
 function SettingsService()
 {
   if (VERBOSE) debug("settingsService Constructor");
   this._locks = [];
+  this._serviceLocks = {};
   this._createdLocks = 0;
   this._unregisteredLocks = 0;
   this.init();
 }
 
 SettingsService.prototype = {
 
   init: function() {
@@ -258,32 +263,56 @@ SettingsService.prototype = {
 
   observe: function(aSubject, aTopic, aData) {
     if (VERBOSE) debug("observe: " + aTopic);
     if (aTopic === kXpcomShutdownObserverTopic) {
       this.uninit();
     }
   },
 
+  receiveMessage: function(aMessage) {
+    if (VERBOSE) debug("Entering receiveMessage");
+
+    let lockID = aMessage.data.lockID;
+    if (!lockID) {
+      if (DEBUG) debug("No lock ID");
+      return;
+    }
+
+    if (!(lockID in this._serviceLocks)) {
+      if (DEBUG) debug("Received message for lock " + lockID + " but no lock");
+      return;
+    }
+
+    if (VERBOSE) debug("Delivering message");
+    this._serviceLocks[lockID].receiveMessage(aMessage);
+  },
+
   createLock: function createLock(aCallback) {
+    if (VERBOSE) debug("Calling createLock");
     var lock = new SettingsServiceLock(this, aCallback);
-    this.registerLock(lock._id);
+    if (VERBOSE) debug("Created lock " + lock._id);
+    this.registerLock(lock);
     return lock;
   },
 
-  registerLock: function(aLockID) {
-    this._locks.push(aLockID);
+  registerLock: function(aLock) {
+    if (VERBOSE) debug("Registering lock " + aLock._id);
+    this._locks.push(aLock._id);
+    this._serviceLocks[aLock._id] = aLock;
     this._createdLocks++;
   },
 
   unregisterLock: function(aLockID) {
     let lock_index = this._locks.indexOf(aLockID);
     if (lock_index != -1) {
       if (VERBOSE) debug("Unregistering lock " + aLockID);
       this._locks.splice(lock_index, 1);
+      this._serviceLocks[aLockID] = null;
+      delete this._serviceLocks[aLockID];
       this._unregisteredLocks++;
     }
   },
 
   collectReports: function(aCallback, aData, aAnonymize) {
     aCallback.callback("",
                        "settings-service-locks/alive",
                        Ci.nsIMemoryReporter.KIND_OTHER,
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -5,19 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
 #include "LayerScope.h"
 
 #include "nsAppRunner.h"
 #include "Composer2D.h"
 #include "Effects.h"
-#include "mozilla/TimeStamp.h"
+#include "mozilla/Endian.h"
+#include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/Endian.h"
+#include "mozilla/TimeStamp.h"
+
 #include "TexturePoolOGL.h"
 #include "mozilla/layers/CompositorOGL.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/TextureHostOGL.h"
 
 #include "gfxColor.h"
 #include "gfxContext.h"
@@ -61,228 +63,74 @@ using namespace mozilla::Compression;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla;
 using namespace layerscope;
 
 class DebugDataSender;
 class DebugGLData;
 
-/* This class handle websocket protocol which included
+/*
+ * This class handle websocket protocol which included
  * handshake and data frame's header
  */
 class LayerScopeWebSocketHandler : public nsIInputStreamCallback {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
 
     enum SocketStateType {
         NoHandshake,
         HandshakeSuccess,
         HandshakeFailed
     };
 
     LayerScopeWebSocketHandler()
         : mState(NoHandshake)
+        , mConnected(false)
     { }
 
-private:
-    virtual ~LayerScopeWebSocketHandler()
-    {
-        if (mTransport) {
-            mTransport->Close(NS_OK);
-        }
-    }
-
-public:
-    void OpenStream(nsISocketTransport* aTransport) {
-        MOZ_ASSERT(aTransport);
-
-        mTransport = aTransport;
-        mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
-                                     0,
-                                     0,
-                                     getter_AddRefs(mOutputStream));
-
-        nsCOMPtr<nsIInputStream> debugInputStream;
-        mTransport->OpenInputStream(0,
-                                    0,
-                                    0,
-                                    getter_AddRefs(debugInputStream));
-        mInputStream = do_QueryInterface(debugInputStream);
-        mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
-    }
-
-    bool WriteToStream(void *ptr, uint32_t size) {
-        if (mState == NoHandshake) {
-            // Not yet handshake, just return true in case of
-            // LayerScope remove this handle
-            return true;
-        } else if (mState == HandshakeFailed) {
-            return false;
-        }
+    void OpenStream(nsISocketTransport* aTransport);
 
-        // Generate WebSocket header
-        uint8_t wsHeader[10];
-        int wsHeaderSize = 0;
-        const uint8_t opcode = 0x2;
-        wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
-        if (size <= 125) {
-            wsHeaderSize = 2;
-            wsHeader[1] = size;
-        } else if (size < 65536) {
-            wsHeaderSize = 4;
-            wsHeader[1] = 0x7E;
-            NetworkEndian::writeUint16(wsHeader + 2, size);
-        } else {
-            wsHeaderSize = 10;
-            wsHeader[1] = 0x7F;
-            NetworkEndian::writeUint64(wsHeader + 2, size);
-        }
-
-        // Send WebSocket header
-        nsresult rv;
-        uint32_t cnt;
-        rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
-                                 wsHeaderSize, &cnt);
-        if (NS_FAILED(rv))
-            return false;
-
-        uint32_t written = 0;
-        while (written < size) {
-            uint32_t cnt;
-            rv = mOutputStream->Write(reinterpret_cast<char*>(ptr) + written,
-                                     size - written, &cnt);
-            if (NS_FAILED(rv))
-                return false;
-
-            written += cnt;
-        }
-
-        return true;
-    }
+    bool WriteToStream(void *aPtr, uint32_t aSize);
 
     // nsIInputStreamCallback
-    NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *stream) MOZ_OVERRIDE
-    {
-        nsTArray<nsCString> protocolString;
-        ReadInputStreamData(protocolString);
+    NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *aStream) MOZ_OVERRIDE;
 
-        if (WebSocketHandshake(protocolString)) {
-            mState = HandshakeSuccess;
-        } else {
-            mState = HandshakeFailed;
-        }
-        return NS_OK;
-    }
 private:
-    void ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
-    {
-        nsLineBuffer<char> lineBuffer;
-        nsCString line;
-        bool more = true;
-        do {
-            NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
+    virtual ~LayerScopeWebSocketHandler() { CloseConnection(); }
 
-            if (line.Length() > 0) {
-                aProtocolString.AppendElement(line);
-            }
-        } while (more && line.Length() > 0);
-    }
+    void ReadInputStreamData(nsTArray<nsCString>& aProtocolString);
 
-    bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
-    {
-        nsresult rv;
-        bool isWebSocket = false;
-        nsCString version;
-        nsCString wsKey;
-        nsCString protocol;
+    bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString);
 
-        // Validate WebSocket client request.
-        if (aProtocolString.Length() == 0)
-            return false;
-
-        // Check that the HTTP method is GET
-        const char* HTTP_METHOD = "GET ";
-        if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
-            return false;
-        }
+    nsresult HandleSocketMessage(nsIAsyncInputStream *aStream);
 
-        for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
-            const char* line = aProtocolString[i].get();
-            const char* prop_pos = strchr(line, ':');
-            if (prop_pos != nullptr) {
-                nsCString key(line, prop_pos - line);
-                nsCString value(prop_pos + 2);
-                if (key.EqualsIgnoreCase("upgrade") &&
-                    value.EqualsIgnoreCase("websocket")) {
-                    isWebSocket = true;
-                } else if (key.EqualsIgnoreCase("sec-websocket-version")) {
-                    version = value;
-                } else if (key.EqualsIgnoreCase("sec-websocket-key")) {
-                    wsKey = value;
-                } else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
-                    protocol = value;
-                }
-            }
-        }
+    nsresult ProcessInput(uint8_t *aBuffer, uint32_t aCount);
 
-        if (!isWebSocket) {
-            return false;
-        }
-
-        if (!(version.EqualsLiteral("7") ||
-              version.EqualsLiteral("8") ||
-              version.EqualsLiteral("13"))) {
-            return false;
-        }
-
-        if (!(protocol.EqualsIgnoreCase("binary"))) {
-            return false;
-        }
+    // Copied from WebsocketChannel, helper function to decode data frame
+    void ApplyMask(uint32_t aMask, uint8_t *aData, uint64_t aLen);
 
-        // Client request is valid. Start to generate and send server response.
-        nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
-        nsAutoCString res;
-        SHA1Sum sha1;
-        nsCString combined(wsKey + guid);
-        sha1.update(combined.get(), combined.Length());
-        uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
-        sha1.finish(digest);
-        nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
-        Base64Encode(newString, res);
+    bool HandleDataFrame(uint8_t *aData, uint32_t aSize);
 
-        nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
-        response.AppendLiteral("Upgrade: websocket\r\n");
-        response.AppendLiteral("Connection: Upgrade\r\n");
-        response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
-        response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
-        uint32_t written = 0;
-        uint32_t size = response.Length();
-        while (written < size) {
-            uint32_t cnt;
-            rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
-                                     size - written, &cnt);
-            if (NS_FAILED(rv))
-                return false;
+    void CloseConnection();
 
-            written += cnt;
-        }
-        mOutputStream->Flush();
-
-        return true;
-    }
-
+private:
     nsCOMPtr<nsIOutputStream> mOutputStream;
     nsCOMPtr<nsIAsyncInputStream> mInputStream;
     nsCOMPtr<nsISocketTransport> mTransport;
     SocketStateType mState;
+    bool mConnected;
 };
 
 NS_IMPL_ISUPPORTS(LayerScopeWebSocketHandler, nsIInputStreamCallback);
 
+
+/*
+ * Manage Websocket connections
+ */
 class LayerScopeWebSocketManager {
 public:
     LayerScopeWebSocketManager();
     ~LayerScopeWebSocketManager();
 
     void AddConnection(nsISocketTransport *aTransport)
     {
         MOZ_ASSERT(aTransport);
@@ -715,34 +563,48 @@ public:
                           int aWidth,
                           int aHeight);
 
     static void SendEffectChain(gl::GLContext* aGLContext,
                                 const EffectChain& aEffectChain,
                                 int aWidth = 0,
                                 int aHeight = 0);
 
+    static void SetLayersTreeSendable(bool aSet) {sLayersTreeSendable = aSet;}
+
+    static void SetLayersBufferSendable(bool aSet) {sLayersBufferSendable = aSet;}
+
+    static bool GetLayersTreeSendable() {return sLayersTreeSendable;}
+
 // Sender private functions
 private:
     static void SendColor(void* aLayerRef,
                           const gfxRGBA& aColor,
                           int aWidth,
                           int aHeight);
     static void SendTextureSource(GLContext* aGLContext,
                                   void* aLayerRef,
                                   TextureSourceOGL* aSource,
                                   bool aFlipY);
     static void SendTexturedEffect(GLContext* aGLContext,
                                    void* aLayerRef,
                                    const TexturedEffect* aEffect);
     static void SendYCbCrEffect(GLContext* aGLContext,
                                 void* aLayerRef,
                                 const EffectYCbCr* aEffect);
+
+// Data fields
+private:
+    static bool sLayersTreeSendable;
+    static bool sLayersBufferSendable;
 };
 
+bool SenderHelper::sLayersTreeSendable = true;
+bool SenderHelper::sLayersBufferSendable = true;
+
 
 // ----------------------------------------------
 // SenderHelper implementation
 // ----------------------------------------------
 void
 SenderHelper::SendLayer(LayerComposite* aLayer,
                         int aWidth,
                         int aHeight)
@@ -869,16 +731,18 @@ SenderHelper::SendYCbCrEffect(GLContext*
 }
 
 void
 SenderHelper::SendEffectChain(GLContext* aGLContext,
                               const EffectChain& aEffectChain,
                               int aWidth,
                               int aHeight)
 {
+    if (!sLayersBufferSendable) return;
+
     const Effect* primaryEffect = aEffectChain.mPrimaryEffect;
     switch (primaryEffect->mType) {
         case EffectTypes::RGB: {
             const TexturedEffect* texturedEffect =
                 static_cast<const TexturedEffect*>(primaryEffect);
             SendTexturedEffect(aGLContext, aEffectChain.mLayerRef, texturedEffect);
             break;
         }
@@ -903,31 +767,456 @@ SenderHelper::SendEffectChain(GLContext*
         default:
             break;
     }
 
     //const Effect* secondaryEffect = aEffectChain.mSecondaryEffects[EffectTypes::MASK];
     // TODO:
 }
 
+
+// ----------------------------------------------
+// LayerScopeWebSocketHandler implementation
+// ----------------------------------------------
+void
+LayerScopeWebSocketHandler::OpenStream(nsISocketTransport* aTransport)
+{
+    MOZ_ASSERT(aTransport);
+
+    mTransport = aTransport;
+    mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
+                                 0,
+                                 0,
+                                 getter_AddRefs(mOutputStream));
+
+    nsCOMPtr<nsIInputStream> debugInputStream;
+    mTransport->OpenInputStream(0,
+                                0,
+                                0,
+                                getter_AddRefs(debugInputStream));
+    mInputStream = do_QueryInterface(debugInputStream);
+    mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+}
+
+bool
+LayerScopeWebSocketHandler::WriteToStream(void *aPtr,
+                                          uint32_t aSize)
+{
+    if (mState == NoHandshake) {
+        // Not yet handshake, just return true in case of
+        // LayerScope remove this handle
+        return true;
+    } else if (mState == HandshakeFailed) {
+        return false;
+    }
+
+    if (!mOutputStream) {
+        return false;
+    }
+
+    // Generate WebSocket header
+    uint8_t wsHeader[10];
+    int wsHeaderSize = 0;
+    const uint8_t opcode = 0x2;
+    wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
+    if (aSize <= 125) {
+        wsHeaderSize = 2;
+        wsHeader[1] = aSize;
+    } else if (aSize < 65536) {
+        wsHeaderSize = 4;
+        wsHeader[1] = 0x7E;
+        NetworkEndian::writeUint16(wsHeader + 2, aSize);
+    } else {
+        wsHeaderSize = 10;
+        wsHeader[1] = 0x7F;
+        NetworkEndian::writeUint64(wsHeader + 2, aSize);
+    }
+
+    // Send WebSocket header
+    nsresult rv;
+    uint32_t cnt;
+    rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
+                              wsHeaderSize, &cnt);
+    if (NS_FAILED(rv))
+        return false;
+
+    uint32_t written = 0;
+    while (written < aSize) {
+        uint32_t cnt;
+        rv = mOutputStream->Write(reinterpret_cast<char*>(aPtr) + written,
+                                  aSize - written, &cnt);
+        if (NS_FAILED(rv))
+            return false;
+
+        written += cnt;
+    }
+
+    return true;
+}
+
+NS_IMETHODIMP
+LayerScopeWebSocketHandler::OnInputStreamReady(nsIAsyncInputStream *aStream)
+{
+    MOZ_ASSERT(mInputStream);
+
+    if (!mInputStream) {
+        return NS_OK;
+    }
+
+    if (!mConnected) {
+        nsTArray<nsCString> protocolString;
+        ReadInputStreamData(protocolString);
+
+        if (WebSocketHandshake(protocolString)) {
+            mState = HandshakeSuccess;
+            mConnected = true;
+            mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+        } else {
+            mState = HandshakeFailed;
+        }
+        return NS_OK;
+    } else {
+        return HandleSocketMessage(aStream);
+    }
+}
+
+void
+LayerScopeWebSocketHandler::ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
+{
+    nsLineBuffer<char> lineBuffer;
+    nsCString line;
+    bool more = true;
+    do {
+        NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
+
+        if (line.Length() > 0) {
+            aProtocolString.AppendElement(line);
+        }
+    } while (more && line.Length() > 0);
+}
+
+bool
+LayerScopeWebSocketHandler::WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
+{
+    nsresult rv;
+    bool isWebSocket = false;
+    nsCString version;
+    nsCString wsKey;
+    nsCString protocol;
+
+    // Validate WebSocket client request.
+    if (aProtocolString.Length() == 0)
+        return false;
+
+    // Check that the HTTP method is GET
+    const char* HTTP_METHOD = "GET ";
+    if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
+        return false;
+    }
+
+    for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
+        const char* line = aProtocolString[i].get();
+        const char* prop_pos = strchr(line, ':');
+        if (prop_pos != nullptr) {
+            nsCString key(line, prop_pos - line);
+            nsCString value(prop_pos + 2);
+            if (key.EqualsIgnoreCase("upgrade") &&
+                value.EqualsIgnoreCase("websocket")) {
+                isWebSocket = true;
+            } else if (key.EqualsIgnoreCase("sec-websocket-version")) {
+                version = value;
+            } else if (key.EqualsIgnoreCase("sec-websocket-key")) {
+                wsKey = value;
+            } else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
+                protocol = value;
+            }
+        }
+    }
+
+    if (!isWebSocket) {
+        return false;
+    }
+
+    if (!(version.EqualsLiteral("7") ||
+          version.EqualsLiteral("8") ||
+          version.EqualsLiteral("13"))) {
+        return false;
+    }
+
+    if (!(protocol.EqualsIgnoreCase("binary"))) {
+        return false;
+    }
+
+    if (!mOutputStream) {
+        return false;
+    }
+
+    // Client request is valid. Start to generate and send server response.
+    nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+    nsAutoCString res;
+    SHA1Sum sha1;
+    nsCString combined(wsKey + guid);
+    sha1.update(combined.get(), combined.Length());
+    uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
+    sha1.finish(digest);
+    nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
+    Base64Encode(newString, res);
+
+    nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
+    response.AppendLiteral("Upgrade: websocket\r\n");
+    response.AppendLiteral("Connection: Upgrade\r\n");
+    response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
+    response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
+    uint32_t written = 0;
+    uint32_t size = response.Length();
+    while (written < size) {
+        uint32_t cnt;
+        rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
+                                  size - written, &cnt);
+        if (NS_FAILED(rv))
+            return false;
+
+        written += cnt;
+    }
+    mOutputStream->Flush();
+
+    return true;
+}
+
+nsresult
+LayerScopeWebSocketHandler::HandleSocketMessage(nsIAsyncInputStream *aStream)
+{
+    // The reading and parsing of this input stream is customized for layer viewer.
+    const uint32_t cPacketSize = 1024;
+    char buffer[cPacketSize];
+    uint32_t count = 0;
+    nsresult rv = NS_OK;
+
+    do {
+        rv = mInputStream->Read((char *)buffer, cPacketSize, &count);
+
+        // TODO: combine packets if we have to read more than once
+
+        if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+            mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+            return NS_OK;
+        }
+
+        if (NS_FAILED(rv)) {
+            break;
+        }
+
+        if (count == 0) {
+            // NS_BASE_STREAM_CLOSED
+            CloseConnection();
+            break;
+        }
+
+        rv = ProcessInput(reinterpret_cast<uint8_t *>(buffer), count);
+    } while (NS_SUCCEEDED(rv) && mInputStream);
+    return rv;
+}
+
+nsresult
+LayerScopeWebSocketHandler::ProcessInput(uint8_t *aBuffer,
+                                         uint32_t aCount)
+{
+    uint32_t avail = aCount;
+
+    // Decode Websocket data frame
+    if (avail <= 2) {
+        NS_WARNING("Packet size is less than 2 bytes");
+        return NS_OK;
+    }
+
+    // First byte, data type, only care the opcode
+    // rsvBits: aBuffer[0] & 0x70 (0111 0000)
+    uint8_t finBit = aBuffer[0] & 0x80; // 1000 0000
+    uint8_t opcode = aBuffer[0] & 0x0F; // 0000 1111
+
+    if (!finBit) {
+        NS_WARNING("We cannot handle multi-fragments messages in Layerscope websocket parser.");
+        return NS_OK;
+    }
+
+    // Second byte, data length
+    uint8_t maskBit = aBuffer[1] & 0x80; // 1000 0000
+    int64_t payloadLength64 = aBuffer[1] & 0x7F; // 0111 1111
+
+    if (!maskBit) {
+        NS_WARNING("Client to Server should set the mask bit");
+        return NS_OK;
+    }
+
+    uint32_t framingLength = 2 + 4; // 4 for masks
+
+    if (payloadLength64 < 126) {
+        if (avail < framingLength)
+            return NS_OK;
+    } else if (payloadLength64 == 126) {
+        // 16 bit length field
+        framingLength += 2;
+        if (avail < framingLength) {
+            return NS_OK;
+        }
+
+        payloadLength64 = aBuffer[2] << 8 | aBuffer[3];
+    } else {
+        // 64 bit length
+        framingLength += 8;
+        if (avail < framingLength) {
+            return NS_OK;
+        }
+
+        if (aBuffer[2] & 0x80) {
+            // Section 4.2 says that the most significant bit MUST be
+            // 0. (i.e. this is really a 63 bit value)
+            NS_WARNING("High bit of 64 bit length set");
+            return NS_ERROR_ILLEGAL_VALUE;
+        }
+
+        // copy this in case it is unaligned
+        payloadLength64 = NetworkEndian::readInt64(aBuffer + 2);
+    }
+
+    uint8_t *payload = aBuffer + framingLength;
+    avail -= framingLength;
+
+    uint32_t payloadLength = static_cast<uint32_t>(payloadLength64);
+    if (avail < payloadLength) {
+        NS_WARNING("Packet size mismatch the payload length");
+        return NS_OK;
+    }
+
+    // Apply mask
+    uint32_t mask = NetworkEndian::readUint32(payload - 4);
+    ApplyMask(mask, payload, payloadLength);
+
+    if (opcode == 0x8) {
+        // opcode == 0x8 means connection close
+        CloseConnection();
+        return NS_BASE_STREAM_CLOSED;
+    }
+
+    if (!HandleDataFrame(payload, payloadLength)) {
+        NS_WARNING("Cannot decode payload data by the protocol buffer");
+    }
+
+    return NS_OK;
+}
+
+void
+LayerScopeWebSocketHandler::ApplyMask(uint32_t aMask,
+                                      uint8_t *aData,
+                                      uint64_t aLen)
+{
+    if (!aData || aLen == 0) {
+        return;
+    }
+
+    // Optimally we want to apply the mask 32 bits at a time,
+    // but the buffer might not be alligned. So we first deal with
+    // 0 to 3 bytes of preamble individually
+    while (aLen && (reinterpret_cast<uintptr_t>(aData) & 3)) {
+        *aData ^= aMask >> 24;
+        aMask = RotateLeft(aMask, 8);
+        aData++;
+        aLen--;
+    }
+
+    // perform mask on full words of data
+    uint32_t *iData = reinterpret_cast<uint32_t *>(aData);
+    uint32_t *end = iData + (aLen >> 2);
+    NetworkEndian::writeUint32(&aMask, aMask);
+    for (; iData < end; iData++) {
+        *iData ^= aMask;
+    }
+    aMask = NetworkEndian::readUint32(&aMask);
+    aData = (uint8_t *)iData;
+    aLen  = aLen % 4;
+
+    // There maybe up to 3 trailing bytes that need to be dealt with
+    // individually
+    while (aLen) {
+        *aData ^= aMask >> 24;
+        aMask = RotateLeft(aMask, 8);
+        aData++;
+        aLen--;
+    }
+}
+
+bool
+LayerScopeWebSocketHandler::HandleDataFrame(uint8_t *aData,
+                                            uint32_t aSize)
+{
+    // Handle payload data by protocol buffer
+    auto p = MakeUnique<CommandPacket>();
+    p->ParseFromArray(static_cast<void*>(aData), aSize);
+
+    if (!p->has_type()) {
+        MOZ_ASSERT(false, "Protocol buffer decoding failed or cannot recongize it");
+        return false;
+    }
+
+    switch (p->type()) {
+        case CommandPacket::LAYERS_TREE:
+            if (p->has_value()) {
+                SenderHelper::SetLayersTreeSendable(p->value());
+            }
+            break;
+
+        case CommandPacket::LAYERS_BUFFER:
+            if (p->has_value()) {
+                SenderHelper::SetLayersBufferSendable(p->value());
+            }
+            break;
+
+        case CommandPacket::NO_OP:
+        default:
+            NS_WARNING("Invalid message type");
+            break;
+    }
+    return true;
+}
+
+void
+LayerScopeWebSocketHandler::CloseConnection()
+{
+    WebSocketHelper::GetSocketManager()->CleanDebugData();
+    if (mInputStream) {
+        mInputStream->AsyncWait(nullptr, 0, 0, nullptr);
+        mInputStream = nullptr;
+    }
+    if (mOutputStream) {
+        mOutputStream = nullptr;
+    }
+    if (mTransport) {
+        mTransport->Close(NS_BASE_STREAM_CLOSED);
+        mTransport = nullptr;
+    }
+    mConnected = false;
+}
+
+
 // ----------------------------------------------
 // LayerScopeWebSocketManager implementation
 // ----------------------------------------------
 LayerScopeWebSocketManager::LayerScopeWebSocketManager()
 {
     NS_NewThread(getter_AddRefs(mDebugSenderThread));
 
     mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
     int port = gfxPrefs::LayerScopePort();
     mServerSocket->Init(port, false, -1);
     mServerSocket->AsyncListen(new DebugListener);
 }
 
 LayerScopeWebSocketManager::~LayerScopeWebSocketManager()
 {
+    mServerSocket->Close();
 }
 
 void
 LayerScopeWebSocketManager::AppendDebugData(DebugGLData *aDebugData)
 {
     if (!mCurrentSender) {
         mCurrentSender = new DebugDataSender();
     }
@@ -1000,17 +1289,17 @@ LayerScope::SendLayer(LayerComposite* aL
     }
     SenderHelper::SendLayer(aLayer, aWidth, aHeight);
 }
 
 void
 LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
 {
     // Protect this public function
-    if (!CheckSendable()) {
+    if (!CheckSendable() || !SenderHelper::GetLayersTreeSendable()) {
         return;
     }
     WebSocketHelper::GetSocketManager()->AppendDebugData(
         new DebugGLLayersData(Move(aPacket)));
 }
 
 bool
 LayerScope::CheckSendable()
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -541,25 +541,25 @@ TiledLayerBuffer<Derived, Tile>::Update(
       newRetainedTiles[index] = newTile;
 
       y += height;
     }
 
     x += width;
   }
 
+  mRetainedTiles = newRetainedTiles;
   AsDerived().PostValidate(aPaintRegion);
-  for (unsigned int i = 0; i < newRetainedTiles.Length(); ++i) {
-    AsDerived().UnlockTile(newRetainedTiles[i]);
+  for (unsigned int i = 0; i < mRetainedTiles.Length(); ++i) {
+    AsDerived().UnlockTile(mRetainedTiles[i]);
   }
 
   // At this point, oldTileCount should be zero
   NS_ABORT_IF_FALSE(oldTileCount == 0, "Failed to release old tiles");
 
-  mRetainedTiles = newRetainedTiles;
   mValidRegion = aNewValidRegion;
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
 } // layers
 } // mozilla
 
 #endif // GFX_TILEDLAYERBUFFER_H
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -328,16 +328,19 @@ TexClientFromReadback(SharedSurface* src
 }
 
 ////////////////////////////////////////
 
 static TemporaryRef<gl::ShSurfHandle>
 CloneSurface(gl::SharedSurface* src, gl::SurfaceFactory* factory)
 {
     RefPtr<gl::ShSurfHandle> dest = factory->NewShSurfHandle(src->mSize);
+    if (!dest) {
+        return nullptr;
+    }
     SharedSurface::ProdCopy(src, dest->Surf(), factory);
     return dest.forget();
 }
 
 void
 CanvasClientSharedSurface::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   if (mFront) {
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -512,16 +512,17 @@ TileClient::TileClient(const TileClient&
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
+  mOrigin = o.mOrigin;
 }
 
 TileClient&
 TileClient::operator=(const TileClient& o)
 {
   if (this == &o) return *this;
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
@@ -531,16 +532,17 @@ TileClient::operator=(const TileClient& 
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
+  mOrigin = o.mOrigin;
   return *this;
 }
 
 
 void
 TileClient::Flip()
 {
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
@@ -1051,16 +1053,41 @@ ClientTiledLayerBuffer::PostValidate(con
       ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
 
     mCallback(mPaintedLayer, ctx, aPaintRegion, DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
     mMoz2DTiles.clear();
     // Reset:
     mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
                              std::numeric_limits<int32_t>::max());
   }
+
+  for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
+    TileClient& tile = mRetainedTiles[i];
+    if (tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
+      // Only worry about padding when not doing low-res because it simplifies
+      // the math and the artifacts won't be noticable
+      // Edge padding prevents sampling artifacts when compositing.
+      if (mResolution == 1) {
+        nsIntRect unscaledTile = nsIntRect(tile.mOrigin.x, tile.mOrigin.y,
+                                           GetTileSize().width, GetTileSize().height);
+        nsIntRegion tileValidRegion = GetValidRegion();
+        tileValidRegion.OrWith(aPaintRegion);
+
+        // We only need to pad out if the tile has area that's not valid
+        if (!tileValidRegion.Contains(unscaledTile)) {
+          tileValidRegion = tileValidRegion.Intersect(unscaledTile);
+          // translate the region into tile space and pad
+          tileValidRegion.MoveBy(-nsIntPoint(unscaledTile.x, unscaledTile.y));
+          RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
+          PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
+        }
+      }
+    }
+  }
+
 }
 
 void
 ClientTiledLayerBuffer::UnlockTile(TileClient aTile)
 {
   // We locked the back buffer, and flipped so we now need to unlock the front
   if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
     aTile.mFrontBuffer->Unlock();
@@ -1139,16 +1166,18 @@ ClientTiledLayerBuffer::ValidateTile(Til
     if (!backBufferOnWhite->Lock(OpenMode::OPEN_READ_WRITE)) {
       NS_WARNING("Failed to lock tile TextureClient for updating.");
       aTile.DiscardBackBuffer();
       aTile.DiscardFrontBuffer();
       return TileClient();
     }
   }
 
+  aTile.mOrigin = gfx::ToIntPoint(aTileOrigin);
+
   if (usingTiledDrawTarget) {
     if (createdTextureClient) {
       if (!mCompositableClient->AddTextureClient(backBuffer)) {
         NS_WARNING("Failed to add tile TextureClient.");
         aTile.DiscardFrontBuffer();
         aTile.DiscardBackBuffer();
         return aTile;
       }
@@ -1242,36 +1271,16 @@ ClientTiledLayerBuffer::ValidateTile(Til
                           drawRect.height);
     gfx::IntPoint copyTarget(NS_lroundf(drawRect.x), NS_lroundf(drawRect.y));
     drawTarget->CopySurface(source, copyRect, copyTarget);
 
     // Mark the newly updated area as invalid in the front buffer
     aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
   }
 
-  // only worry about padding when not doing low-res
-  // because it simplifies the math and the artifacts
-  // won't be noticable
-  if (mResolution == 1) {
-    nsIntRect unscaledTile = nsIntRect(aTileOrigin.x,
-                                       aTileOrigin.y,
-                                       GetTileSize().width,
-                                       GetTileSize().height);
-
-    nsIntRegion tileValidRegion = GetValidRegion();
-    tileValidRegion.Or(tileValidRegion, aDirtyRegion);
-    // We only need to pad out if the tile has area that's not valid
-    if (!tileValidRegion.Contains(unscaledTile)) {
-      tileValidRegion = tileValidRegion.Intersect(unscaledTile);
-      // translate the region into tile space and pad
-      tileValidRegion.MoveBy(-nsIntPoint(unscaledTile.x, unscaledTile.y));
-      PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
-    }
-  }
-
   // The new buffer is now validated, remove the dirty region from it.
   aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
 
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution,
                    aTileOrigin.y * GetPresShellResolution(), GetTileLength(), GetTileLength());
 #endif
 
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -269,16 +269,18 @@ struct TileClient
   CompositableClient* mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   TimeStamp        mLastUpdate;
 #endif
   nsIntRegion mInvalidFront;
   nsIntRegion mInvalidBack;
   nsExpirationState mExpirationState;
 
+  gfx::IntPoint mOrigin;
+
 private:
   // Copies dirty pixels from the front buffer into the back buffer,
   // and records the copied region in aAddPaintedRegion.
   void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
                                    nsIntRegion& aAddPaintedRegion);
 };
 
 /**
--- a/gfx/layers/protobuf/LayerScopePacket.pb.cc
+++ b/gfx/layers/protobuf/LayerScopePacket.pb.cc
@@ -22,16 +22,17 @@ void protobuf_ShutdownFile_LayerScopePac
   delete LayersPacket_Layer::default_instance_;
   delete LayersPacket_Layer_Size::default_instance_;
   delete LayersPacket_Layer_Rect::default_instance_;
   delete LayersPacket_Layer_Region::default_instance_;
   delete LayersPacket_Layer_Matrix::default_instance_;
   delete LayersPacket_Layer_Shadow::default_instance_;
   delete MetaPacket::default_instance_;
   delete Packet::default_instance_;
+  delete CommandPacket::default_instance_;
 }
 
 void protobuf_AddDesc_LayerScopePacket_2eproto() {
   static bool already_here = false;
   if (already_here) return;
   already_here = true;
   GOOGLE_PROTOBUF_VERIFY_VERSION;
 
@@ -42,28 +43,30 @@ void protobuf_AddDesc_LayerScopePacket_2
   LayersPacket_Layer::default_instance_ = new LayersPacket_Layer();
   LayersPacket_Layer_Size::default_instance_ = new LayersPacket_Layer_Size();
   LayersPacket_Layer_Rect::default_instance_ = new LayersPacket_Layer_Rect();
   LayersPacket_Layer_Region::default_instance_ = new LayersPacket_Layer_Region();
   LayersPacket_Layer_Matrix::default_instance_ = new LayersPacket_Layer_Matrix();
   LayersPacket_Layer_Shadow::default_instance_ = new LayersPacket_Layer_Shadow();
   MetaPacket::default_instance_ = new MetaPacket();
   Packet::default_instance_ = new Packet();
+  CommandPacket::default_instance_ = new CommandPacket();
   FramePacket::default_instance_->InitAsDefaultInstance();
   ColorPacket::default_instance_->InitAsDefaultInstance();
   TexturePacket::default_instance_->InitAsDefaultInstance();
   LayersPacket::default_instance_->InitAsDefaultInstance();
   LayersPacket_Layer::default_instance_->InitAsDefaultInstance();
   LayersPacket_Layer_Size::default_instance_->InitAsDefaultInstance();
   LayersPacket_Layer_Rect::default_instance_->InitAsDefaultInstance();
   LayersPacket_Layer_Region::default_instance_->InitAsDefaultInstance();
   LayersPacket_Layer_Matrix::default_instance_->InitAsDefaultInstance();
   LayersPacket_Layer_Shadow::default_instance_->InitAsDefaultInstance();
   MetaPacket::default_instance_->InitAsDefaultInstance();
   Packet::default_instance_->InitAsDefaultInstance();
+  CommandPacket::default_instance_->InitAsDefaultInstance();
   ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_LayerScopePacket_2eproto);
 }
 
 // Force AddDescriptors() to be called at static initialization time.
 struct StaticDescriptorInitializer_LayerScopePacket_2eproto {
   StaticDescriptorInitializer_LayerScopePacket_2eproto() {
     protobuf_AddDesc_LayerScopePacket_2eproto();
   }
@@ -3568,15 +3571,229 @@ void Packet::Swap(Packet* other) {
   }
 }
 
 ::std::string Packet::GetTypeName() const {
   return "mozilla.layers.layerscope.Packet";
 }
 
 
+// ===================================================================
+
+bool CommandPacket_CmdType_IsValid(int value) {
+  switch(value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#ifndef _MSC_VER
+const CommandPacket_CmdType CommandPacket::NO_OP;
+const CommandPacket_CmdType CommandPacket::LAYERS_TREE;
+const CommandPacket_CmdType CommandPacket::LAYERS_BUFFER;
+const CommandPacket_CmdType CommandPacket::CmdType_MIN;
+const CommandPacket_CmdType CommandPacket::CmdType_MAX;
+const int CommandPacket::CmdType_ARRAYSIZE;
+#endif  // _MSC_VER
+#ifndef _MSC_VER
+const int CommandPacket::kTypeFieldNumber;
+const int CommandPacket::kValueFieldNumber;
+#endif  // !_MSC_VER
+
+CommandPacket::CommandPacket()
+  : ::google::protobuf::MessageLite() {
+  SharedCtor();
+}
+
+void CommandPacket::InitAsDefaultInstance() {
+}
+
+CommandPacket::CommandPacket(const CommandPacket& from)
+  : ::google::protobuf::MessageLite() {
+  SharedCtor();
+  MergeFrom(from);
+}
+
+void CommandPacket::SharedCtor() {
+  _cached_size_ = 0;
+  type_ = 0;
+  value_ = false;
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+CommandPacket::~CommandPacket() {
+  SharedDtor();
+}
+
+void CommandPacket::SharedDtor() {
+  if (this != default_instance_) {
+  }
+}
+
+void CommandPacket::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const CommandPacket& CommandPacket::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();  return *default_instance_;
+}
+
+CommandPacket* CommandPacket::default_instance_ = NULL;
+
+CommandPacket* CommandPacket::New() const {
+  return new CommandPacket;
+}
+
+void CommandPacket::Clear() {
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    type_ = 0;
+    value_ = false;
+  }
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+bool CommandPacket::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+  ::google::protobuf::uint32 tag;
+  while ((tag = input->ReadTag()) != 0) {
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+      case 1: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+          int value;
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+                 input, &value)));
+          if (::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value)) {
+            set_type(static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(value));
+          }
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(16)) goto parse_value;
+        break;
+      }
+      
+      // optional bool value = 2;
+      case 2: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_value:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+                 input, &value_)));
+          set_has_value();
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectAtEnd()) return true;
+        break;
+      }
+      
+      default: {
+      handle_uninterpreted:
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          return true;
+        }
+        DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+        break;
+      }
+    }
+  }
+  return true;
+#undef DO_
+}
+
+void CommandPacket::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+  if (has_type()) {
+    ::google::protobuf::internal::WireFormatLite::WriteEnum(
+      1, this->type(), output);
+  }
+  
+  // optional bool value = 2;
+  if (has_value()) {
+    ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->value(), output);
+  }
+  
+}
+
+int CommandPacket::ByteSize() const {
+  int total_size = 0;
+  
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+    if (has_type()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
+    }
+    
+    // optional bool value = 2;
+    if (has_value()) {
+      total_size += 1 + 1;
+    }
+    
+  }
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void CommandPacket::CheckTypeAndMergeFrom(
+    const ::google::protobuf::MessageLite& from) {
+  MergeFrom(*::google::protobuf::down_cast<const CommandPacket*>(&from));
+}
+
+void CommandPacket::MergeFrom(const CommandPacket& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    if (from.has_type()) {
+      set_type(from.type());
+    }
+    if (from.has_value()) {
+      set_value(from.value());
+    }
+  }
+}
+
+void CommandPacket::CopyFrom(const CommandPacket& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool CommandPacket::IsInitialized() const {
+  if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+  
+  return true;
+}
+
+void CommandPacket::Swap(CommandPacket* other) {
+  if (other != this) {
+    std::swap(type_, other->type_);
+    std::swap(value_, other->value_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
+::std::string CommandPacket::GetTypeName() const {
+  return "mozilla.layers.layerscope.CommandPacket";
+}
+
+
 // @@protoc_insertion_point(namespace_scope)
 
 }  // namespace layerscope
 }  // namespace layers
 }  // namespace mozilla
 
 // @@protoc_insertion_point(global_scope)
--- a/gfx/layers/protobuf/LayerScopePacket.pb.h
+++ b/gfx/layers/protobuf/LayerScopePacket.pb.h
@@ -40,16 +40,17 @@ class LayersPacket;
 class LayersPacket_Layer;
 class LayersPacket_Layer_Size;
 class LayersPacket_Layer_Rect;
 class LayersPacket_Layer_Region;
 class LayersPacket_Layer_Matrix;
 class LayersPacket_Layer_Shadow;
 class MetaPacket;
 class Packet;
+class CommandPacket;
 
 enum LayersPacket_Layer_LayerType {
   LayersPacket_Layer_LayerType_UnknownLayer = 0,
   LayersPacket_Layer_LayerType_LayerManager = 1,
   LayersPacket_Layer_LayerType_ContainerLayer = 2,
   LayersPacket_Layer_LayerType_PaintedLayer = 3,
   LayersPacket_Layer_LayerType_CanvasLayer = 4,
   LayersPacket_Layer_LayerType_ImageLayer = 5,
@@ -93,16 +94,26 @@ enum Packet_DataType {
   Packet_DataType_LAYERS = 5,
   Packet_DataType_META = 6
 };
 bool Packet_DataType_IsValid(int value);
 const Packet_DataType Packet_DataType_DataType_MIN = Packet_DataType_FRAMESTART;
 const Packet_DataType Packet_DataType_DataType_MAX = Packet_DataType_META;
 const int Packet_DataType_DataType_ARRAYSIZE = Packet_DataType_DataType_MAX + 1;
 
+enum CommandPacket_CmdType {
+  CommandPacket_CmdType_NO_OP = 0,
+  CommandPacket_CmdType_LAYERS_TREE = 1,
+  CommandPacket_CmdType_LAYERS_BUFFER = 2
+};
+bool CommandPacket_CmdType_IsValid(int value);
+const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MIN = CommandPacket_CmdType_NO_OP;
+const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MAX = CommandPacket_CmdType_LAYERS_BUFFER;
+const int CommandPacket_CmdType_CmdType_ARRAYSIZE = CommandPacket_CmdType_CmdType_MAX + 1;
+
 // ===================================================================
 
 class FramePacket : public ::google::protobuf::MessageLite {
  public:
   FramePacket();
   virtual ~FramePacket();
   
   FramePacket(const FramePacket& from);
@@ -1434,16 +1445,109 @@ class Packet : public ::google::protobuf
   
   friend void  protobuf_AddDesc_LayerScopePacket_2eproto();
   friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
   friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
   
   void InitAsDefaultInstance();
   static Packet* default_instance_;
 };
+// -------------------------------------------------------------------
+
+class CommandPacket : public ::google::protobuf::MessageLite {
+ public:
+  CommandPacket();
+  virtual ~CommandPacket();
+  
+  CommandPacket(const CommandPacket& from);
+  
+  inline CommandPacket& operator=(const CommandPacket& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  
+  static const CommandPacket& default_instance();
+  
+  void Swap(CommandPacket* other);
+  
+  // implements Message ----------------------------------------------
+  
+  CommandPacket* New() const;
+  void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+  void CopyFrom(const CommandPacket& from);
+  void MergeFrom(const CommandPacket& from);
+  void Clear();
+  bool IsInitialized() const;
+  
+  int ByteSize() const;
+  bool MergePartialFromCodedStream(
+      ::google::protobuf::io::CodedInputStream* input);
+  void SerializeWithCachedSizes(
+      ::google::protobuf::io::CodedOutputStream* output) const;
+  int GetCachedSize() const { return _cached_size_; }
+  private:
+  void SharedCtor();
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  public:
+  
+  ::std::string GetTypeName() const;
+  
+  // nested types ----------------------------------------------------
+  
+  typedef CommandPacket_CmdType CmdType;
+  static const CmdType NO_OP = CommandPacket_CmdType_NO_OP;
+  static const CmdType LAYERS_TREE = CommandPacket_CmdType_LAYERS_TREE;
+  static const CmdType LAYERS_BUFFER = CommandPacket_CmdType_LAYERS_BUFFER;
+  static inline bool CmdType_IsValid(int value) {
+    return CommandPacket_CmdType_IsValid(value);
+  }
+  static const CmdType CmdType_MIN =
+    CommandPacket_CmdType_CmdType_MIN;
+  static const CmdType CmdType_MAX =
+    CommandPacket_CmdType_CmdType_MAX;
+  static const int CmdType_ARRAYSIZE =
+    CommandPacket_CmdType_CmdType_ARRAYSIZE;
+  
+  // accessors -------------------------------------------------------
+  
+  // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+  inline bool has_type() const;
+  inline void clear_type();
+  static const int kTypeFieldNumber = 1;
+  inline ::mozilla::layers::layerscope::CommandPacket_CmdType type() const;
+  inline void set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value);
+  
+  // optional bool value = 2;
+  inline bool has_value() const;
+  inline void clear_value();
+  static const int kValueFieldNumber = 2;
+  inline bool value() const;
+  inline void set_value(bool value);
+  
+  // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.CommandPacket)
+ private:
+  inline void set_has_type();
+  inline void clear_has_type();
+  inline void set_has_value();
+  inline void clear_has_value();
+  
+  int type_;
+  bool value_;
+  
+  mutable int _cached_size_;
+  ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
+  
+  friend void  protobuf_AddDesc_LayerScopePacket_2eproto();
+  friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+  friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+  
+  void InitAsDefaultInstance();
+  static CommandPacket* default_instance_;
+};
 // ===================================================================
 
 
 // ===================================================================
 
 // FramePacket
 
 // optional uint64 value = 1;
@@ -2798,16 +2902,65 @@ inline ::mozilla::layers::layerscope::Me
 }
 inline ::mozilla::layers::layerscope::MetaPacket* Packet::release_meta() {
   clear_has_meta();
   ::mozilla::layers::layerscope::MetaPacket* temp = meta_;
   meta_ = NULL;
   return temp;
 }
 
+// -------------------------------------------------------------------
+
+// CommandPacket
+
+// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+inline bool CommandPacket::has_type() const {
+  return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void CommandPacket::set_has_type() {
+  _has_bits_[0] |= 0x00000001u;
+}
+inline void CommandPacket::clear_has_type() {
+  _has_bits_[0] &= ~0x00000001u;
+}
+inline void CommandPacket::clear_type() {
+  type_ = 0;
+  clear_has_type();
+}
+inline ::mozilla::layers::layerscope::CommandPacket_CmdType CommandPacket::type() const {
+  return static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(type_);
+}
+inline void CommandPacket::set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value) {
+  GOOGLE_DCHECK(::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value));
+  set_has_type();
+  type_ = value;
+}
+
+// optional bool value = 2;
+inline bool CommandPacket::has_value() const {
+  return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void CommandPacket::set_has_value() {
+  _has_bits_[0] |= 0x00000002u;
+}
+inline void CommandPacket::clear_has_value() {
+  _has_bits_[0] &= ~0x00000002u;
+}
+inline void CommandPacket::clear_value() {
+  value_ = false;
+  clear_has_value();
+}
+inline bool CommandPacket::value() const {
+  return value_;
+}
+inline void CommandPacket::set_value(bool value) {
+  set_has_value();
+  value_ = value;
+}
+
 
 // @@protoc_insertion_point(namespace_scope)
 
 }  // namespace layerscope
 }  // namespace layers
 }  // namespace mozilla
 
 // @@protoc_insertion_point(global_scope)
--- a/gfx/layers/protobuf/LayerScopePacket.proto
+++ b/gfx/layers/protobuf/LayerScopePacket.proto
@@ -1,14 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 
 option optimize_for = LITE_RUNTIME;
 
 package mozilla.layers.layerscope;
 
+// ===============================
+// Server to Client messages
+// ===============================
 message FramePacket {
   optional uint64 value = 1;
 }
 
 message ColorPacket {
   required uint64 layerref = 1;
   optional uint32 width = 2;
   optional uint32 height = 3;
@@ -128,8 +131,22 @@ message Packet {
   required DataType type = 1;
 
   optional FramePacket frame = 2;
   optional ColorPacket color = 3;
   optional TexturePacket texture = 4;
   optional LayersPacket layers = 5;
   optional MetaPacket meta = 6;
 }
+
+
+// ===============================
+// Client to Server messages
+// ===============================
+message CommandPacket {
+  enum CmdType {
+    NO_OP = 0;
+    LAYERS_TREE = 1;
+    LAYERS_BUFFER = 2;
+  }
+  required CmdType type = 1;
+  optional bool value = 2;
+}
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -19,16 +19,17 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/android_alarm.h>
 #include <math.h>
 #include <regex.h>
 #include <sched.h>
 #include <stdio.h>
 #include <sys/klog.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/resource.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "mozilla/DebugOnly.h"
 
 #include "android/log.h"
@@ -42,16 +43,17 @@
 #include "utils/threads.h"
 
 #include "base/message_loop.h"
 
 #include "Hal.h"
 #include "HalImpl.h"
 #include "HalLog.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/battery/Constants.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Preferences.h"
@@ -623,38 +625,16 @@ GetCurrentBatteryInformation(hal::Batter
   }
 }
 
 namespace {
 
 /**
  * RAII class to help us remember to close file descriptors.
  */
-const char *wakeLockFilename = "/sys/power/wake_lock";
-const char *wakeUnlockFilename = "/sys/power/wake_unlock";
-
-template<ssize_t n>
-bool ReadFromFile(const char *filename, char (&buf)[n])
-{
-  int fd = open(filename, O_RDONLY);
-  ScopedClose autoClose(fd);
-  if (fd < 0) {
-    HAL_LOG("Unable to open file %s.", filename);
-    return false;
-  }
-
-  ssize_t numRead = read(fd, buf, n);
-  if (numRead < 0) {
-    HAL_LOG("Error reading from file %s.", filename);
-    return false;
-  }
-
-  buf[std::min(numRead, n - 1)] = '\0';
-  return true;
-}
 
 bool WriteToFile(const char *filename, const char *toWrite)
 {
   int fd = open(filename, O_WRONLY);
   ScopedClose autoClose(fd);
   if (fd < 0) {
     HAL_LOG("Unable to open file %s.", filename);
     return false;
@@ -770,16 +750,19 @@ SetScreenBrightness(double brightness)
   }
 }
 
 static Monitor* sInternalLockCpuMonitor = nullptr;
 
 static void
 UpdateCpuSleepState()
 {
+  const char *wakeLockFilename = "/sys/power/wake_lock";
+  const char *wakeUnlockFilename = "/sys/power/wake_unlock";
+
   sInternalLockCpuMonitor->AssertCurrentThreadOwns();
   bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount;
   WriteToFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
 }
 
 static void
 InternalLockCpu() {
   MonitorAutoLock monitor(*sInternalLockCpuMonitor);
@@ -1281,17 +1264,16 @@ OomVictimLogger::Observe(
          after_float == ']') {
       if (lineTimestamp <= mLastLineChecked) {
         continue;
       }
 
       lineTimestampFound = true;
       mLastLineChecked = lineTimestamp;
     }
-      
 
     // Log interesting lines
     for (size_t i = 0; i < regex_count; i++) {
       int matching = !regexec(&(mRegexes[i]), line_begin, 0, NULL, 0);
       if (matching) {
         // Log content of kernel message. We try to skip the ], but if for
         // some reason (most likely due to buffer overflow/wraparound), we
         // can't find the ] then we just log the entire line.
@@ -1307,16 +1289,271 @@ OomVictimLogger::Observe(
         break;
       }
     }
   }
 
   return NS_OK;
 }
 
+/**
+ * Wraps a particular ProcessPriority, giving us easy access to the prefs that
+ * are relevant to it.
+ *
+ * Creating a PriorityClass also ensures that the control group is created.
+ */
+class PriorityClass
+{
+public:
+  /**
+   * Create a PriorityClass for the given ProcessPriority.  This implicitly
+   * reads the relevant prefs and opens the cgroup.procs file of the relevant
+   * control group caching its file descriptor for later use.
+   */
+  PriorityClass(ProcessPriority aPriority);
+
+  /**
+   * Closes the file descriptor for the cgroup.procs file of the associated
+   * control group.
+   */
+  ~PriorityClass();
+
+  PriorityClass(const PriorityClass& aOther);
+  PriorityClass& operator=(const PriorityClass& aOther);
+
+  ProcessPriority Priority()
+  {
+    return mPriority;
+  }
+
+  int32_t OomScoreAdj()
+  {
+    return clamped<int32_t>(mOomScoreAdj, OOM_SCORE_ADJ_MIN, OOM_SCORE_ADJ_MAX);
+  }
+
+  int32_t KillUnderKB()
+  {
+    return mKillUnderKB;
+  }
+
+  nsCString CGroup()
+  {
+    return mGroup;
+  }
+
+  /**
+   * Adds a process to this priority class, this moves the process' PID into
+   * the associated control group.
+   *
+   * @param aPid The PID of the process to be added.
+   */
+  void AddProcess(int aPid);
+
+private:
+  ProcessPriority mPriority;
+  int32_t mOomScoreAdj;
+  int32_t mKillUnderKB;
+  int mCGroupProcsFd;
+  nsCString mGroup;
+
+  /**
+   * Return a string that identifies where we can find the value of aPref
+   * that's specific to mPriority.  For example, we might return
+   * "hal.processPriorityManager.gonk.FOREGROUND_HIGH.oomScoreAdjust".
+   */
+  nsCString PriorityPrefName(const char* aPref)
+  {
+    return nsPrintfCString("hal.processPriorityManager.gonk.%s.%s",
+                           ProcessPriorityToString(mPriority), aPref);
+  }
+
+  /**
+   * Get the full path of the cgroup.procs file associated with the group.
+   */
+  nsCString CGroupProcsFilename()
+  {
+    nsCString cgroupName = mGroup;
+
+    /* If mGroup is empty, our cgroup.procs file is the root procs file,
+     * located at /dev/cpuctl/cgroup.procs.  Otherwise our procs file is
+     * /dev/cpuctl/NAME/cgroup.procs. */
+
+    if (!mGroup.IsEmpty()) {
+      cgroupName.AppendLiteral("/");
+    }
+
+    return NS_LITERAL_CSTRING("/dev/cpuctl/") + cgroupName +
+           NS_LITERAL_CSTRING("cgroup.procs");
+  }
+
+  int OpenCGroupProcs()
+  {
+    return open(CGroupProcsFilename().get(), O_WRONLY);
+  }
+};
+
+/**
+ * Try to create the cgroup for the given PriorityClass, if it doesn't already
+ * exist.  This essentially implements mkdir -p; that is, we create parent
+ * cgroups as necessary.  The group parameters are also set according to
+ * the corresponding preferences.
+ *
+ * @param aGroup The name of the group.
+ * @return true if we successfully created the cgroup, or if it already
+ * exists.  Otherwise, return false.
+ */
+static bool
+EnsureCGroupExists(const nsACString &aGroup)
+{
+  NS_NAMED_LITERAL_CSTRING(kDevCpuCtl, "/dev/cpuctl/");
+  NS_NAMED_LITERAL_CSTRING(kSlash, "/");
+
+  nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
+
+  /* If cgroup is not empty, append the cgroup name and a dot to obtain the
+   * group specific preferences. */
+  if (!aGroup.IsEmpty()) {
+    prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
+  }
+
+  nsAutoCString cpuSharesPref(prefPrefix + NS_LITERAL_CSTRING("cpu_shares"));
+  int cpuShares = Preferences::GetInt(cpuSharesPref.get());
+
+  nsAutoCString cpuNotifyOnMigratePref(prefPrefix
+    + NS_LITERAL_CSTRING("cpu_notify_on_migrate"));
+  int cpuNotifyOnMigrate = Preferences::GetInt(cpuNotifyOnMigratePref.get());
+
+  // Create mCGroup and its parent directories, as necessary.
+  nsCString cgroupIter = aGroup + kSlash;
+
+  int32_t offset = 0;
+  while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
+    nsAutoCString path = kDevCpuCtl + Substring(cgroupIter, 0, offset);
+    int rv = mkdir(path.get(), 0744);
+
+    if (rv == -1 && errno != EEXIST) {
+      HAL_LOG("Could not create the %s control group.", path.get());
+      return false;
+    }
+
+    offset++;
+  }
+
+  nsAutoCString pathPrefix(kDevCpuCtl + aGroup + kSlash);
+  nsAutoCString cpuSharesPath(pathPrefix + NS_LITERAL_CSTRING("cpu.shares"));
+  if (cpuShares && !WriteToFile(cpuSharesPath.get(),
+                                nsPrintfCString("%d", cpuShares).get())) {
+    HAL_LOG("Could not set the cpu share for group %s", cpuSharesPath.get());
+    return false;
+  }
+
+  nsAutoCString notifyOnMigratePath(pathPrefix
+    + NS_LITERAL_CSTRING("cpu.notify_on_migrate"));
+  if (!WriteToFile(notifyOnMigratePath.get(),
+                   nsPrintfCString("%d", cpuNotifyOnMigrate).get())) {
+    HAL_LOG("Could not set the cpu migration notification flag for group %s",
+            notifyOnMigratePath.get());
+    return false;
+  }
+
+  return true;
+}
+
+PriorityClass::PriorityClass(ProcessPriority aPriority)
+  : mPriority(aPriority)
+  , mOomScoreAdj(0)
+  , mKillUnderKB(0)
+  , mCGroupProcsFd(-1)
+{
+  DebugOnly<nsresult> rv;
+
+  rv = Preferences::GetInt(PriorityPrefName("OomScoreAdjust").get(),
+                           &mOomScoreAdj);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing oom_score_adj preference");
+
+  rv = Preferences::GetInt(PriorityPrefName("KillUnderKB").get(),
+                           &mKillUnderKB);
+
+  rv = Preferences::GetCString(PriorityPrefName("cgroup").get(), &mGroup);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing control group preference");
+
+  if (EnsureCGroupExists(mGroup)) {
+    mCGroupProcsFd = OpenCGroupProcs();
+  }
+}
+
+PriorityClass::~PriorityClass()
+{
+  close(mCGroupProcsFd);
+}
+
+PriorityClass::PriorityClass(const PriorityClass& aOther)
+  : mPriority(aOther.mPriority)
+  , mOomScoreAdj(aOther.mOomScoreAdj)
+  , mKillUnderKB(aOther.mKillUnderKB)
+  , mGroup(aOther.mGroup)
+{
+  mCGroupProcsFd = OpenCGroupProcs();
+}
+
+PriorityClass& PriorityClass::operator=(const PriorityClass& aOther)
+{
+  mPriority = aOther.mPriority;
+  mOomScoreAdj = aOther.mOomScoreAdj;
+  mKillUnderKB = aOther.mKillUnderKB;
+  mGroup = aOther.mGroup;
+  mCGroupProcsFd = OpenCGroupProcs();
+  return *this;
+}
+
+void PriorityClass::AddProcess(int aPid)
+{
+  if (mCGroupProcsFd < 0) {
+    return;
+  }
+
+  nsPrintfCString str("%d", aPid);
+
+  if (write(mCGroupProcsFd, str.get(), strlen(str.get())) < 0) {
+    HAL_ERR("Couldn't add PID %d to the %s control group", aPid, mGroup.get());
+  }
+}
+
+/**
+ * Get the PriorityClass associated with the given ProcessPriority.
+ *
+ * If you pass an invalid ProcessPriority value, we return null.
+ *
+ * The pointers returned here are owned by GetPriorityClass (don't free them
+ * yourself).  They are guaranteed to stick around until shutdown.
+ */
+PriorityClass*
+GetPriorityClass(ProcessPriority aPriority)
+{
+  static StaticAutoPtr<nsTArray<PriorityClass>> priorityClasses;
+
+  // Initialize priorityClasses if this is the first time we're running this
+  // method.
+  if (!priorityClasses) {
+    priorityClasses = new nsTArray<PriorityClass>();
+    ClearOnShutdown(&priorityClasses);
+
+    for (int32_t i = 0; i < NUM_PROCESS_PRIORITY; i++) {
+      priorityClasses->AppendElement(PriorityClass(ProcessPriority(i)));
+    }
+  }
+
+  if (aPriority < 0 ||
+      static_cast<uint32_t>(aPriority) >= priorityClasses->Length()) {
+    return nullptr;
+  }
+
+  return &(*priorityClasses)[aPriority];
+}
+
 static void
 EnsureKernelLowMemKillerParamsSet()
 {
   static bool kernelLowMemKillerParamsSet;
   if (kernelLowMemKillerParamsSet) {
     return;
   }
   kernelLowMemKillerParamsSet = true;
@@ -1347,31 +1584,22 @@ EnsureKernelLowMemKillerParamsSet()
   int32_t countOfLowmemorykillerParametersSets = 0;
 
   long page_size = sysconf(_SC_PAGESIZE);
 
   for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) {
     // The system doesn't function correctly if we're missing these prefs, so
     // crash loudly.
 
-    ProcessPriority priority = static_cast<ProcessPriority>(i);
+    PriorityClass* pc = GetPriorityClass(static_cast<ProcessPriority>(i));
 
-    int32_t oomScoreAdj;
-    if (!NS_SUCCEEDED(Preferences::GetInt(
-          nsPrintfCString("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
-                          ProcessPriorityToString(priority)).get(),
-          &oomScoreAdj))) {
-      MOZ_CRASH();
-    }
+    int32_t oomScoreAdj = pc->OomScoreAdj();
+    int32_t killUnderKB = pc->KillUnderKB();
 
-    int32_t killUnderKB;
-    if (!NS_SUCCEEDED(Preferences::GetInt(
-          nsPrintfCString("hal.processPriorityManager.gonk.%s.KillUnderKB",
-                          ProcessPriorityToString(priority)).get(),
-          &killUnderKB))) {
+    if (killUnderKB == 0) {
       // ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
       // which has only OomScoreAdjust but lacks KillUnderMB value, will not
       // create new LMK parameters.
       continue;
     }
 
     // The LMK in kernel silently malfunctions if we assign the parameters
     // in non-increasing order, so we add this assertion here. See bug 887192.
@@ -1392,17 +1620,18 @@ EnsureKernelLowMemKillerParamsSet()
     countOfLowmemorykillerParametersSets++;
   }
 
   // Strip off trailing commas.
   adjParams.Cut(adjParams.Length() - 1, 1);
   minfreeParams.Cut(minfreeParams.Length() - 1, 1);
   if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) {
     WriteToFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get());
-    WriteToFile("/sys/module/lowmemorykiller/parameters/minfree", minfreeParams.get());
+    WriteToFile("/sys/module/lowmemorykiller/parameters/minfree",
+                minfreeParams.get());
   }
 
   // Set the low-memory-notification threshold.
   int32_t lowMemNotifyThresholdKB;
   if (NS_SUCCEEDED(Preferences::GetInt(
         "hal.processPriorityManager.gonk.notifyLowMemUnderKB",
         &lowMemNotifyThresholdKB))) {
 
@@ -1414,158 +1643,16 @@ EnsureKernelLowMemKillerParamsSet()
   // Ensure OOM events appear in logcat
   nsRefPtr<OomVictimLogger> oomLogger = new OomVictimLogger();
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->AddObserver(oomLogger, "ipc:content-shutdown", false);
   }
 }
 
-static void
-SetNiceForPid(int aPid, int aNice)
-{
-  errno = 0;
-  int origProcPriority = getpriority(PRIO_PROCESS, aPid);
-  if (errno) {
-    HAL_LOG("Unable to get nice for pid=%d; error %d.  SetNiceForPid bailing.",
-            aPid, errno);
-    return;
-  }
-
-  int rv = setpriority(PRIO_PROCESS, aPid, aNice);
-  if (rv) {
-    HAL_LOG("Unable to set nice for pid=%d; error %d.  SetNiceForPid bailing.",
-            aPid, errno);
-    return;
-  }
-
-  // On Linux, setpriority(aPid) modifies the priority only of the main
-  // thread of that process.  We have to modify the priorities of all of the
-  // process's threads as well, so iterate over all the threads and increase
-  // each of their priorites by aNice - origProcPriority (and also ensure that
-  // none of the tasks has a lower priority than the main thread).
-  //
-  // This is horribly racy.
-
-  DIR* tasksDir = opendir(nsPrintfCString("/proc/%d/task/", aPid).get());
-  if (!tasksDir) {
-    HAL_LOG("Unable to open /proc/%d/task.  SetNiceForPid bailing.", aPid);
-    return;
-  }
-
-  // Be careful not to leak tasksDir; after this point, we must call closedir().
-
-  while (struct dirent* de = readdir(tasksDir)) {
-    char* endptr = nullptr;
-    long tidlong = strtol(de->d_name, &endptr, /* base */ 10);
-    if (*endptr || tidlong < 0 || tidlong > INT32_MAX || tidlong == aPid) {
-      // if dp->d_name was not an integer, was negative (?!) or too large, or
-      // was the same as aPid, we're not interested.
-      //
-      // (The |tidlong == aPid| check is very important; without it, we'll
-      // renice aPid twice, and the second renice will be relative to the
-      // priority set by the first renice.)
-      continue;
-    }
-
-    int tid = static_cast<int>(tidlong);
-
-    // Do not set the priority of threads running with a real-time policy
-    // as part of the bulk process adjustment.  These threads need to run
-    // at their specified priority in order to meet timing guarantees.
-    int schedPolicy = sched_getscheduler(tid);
-    if (schedPolicy == SCHED_FIFO || schedPolicy == SCHED_RR) {
-      continue;
-    }
-
-    errno = 0;
-    // Get and set the task's new priority.
-    int origtaskpriority = getpriority(PRIO_PROCESS, tid);
-    if (errno) {
-      HAL_LOG("Unable to get nice for tid=%d (pid=%d); error %d.  This isn't "
-              "necessarily a problem; it could be a benign race condition.",
-              tid, aPid, errno);
-      continue;
-    }
-
-    int newtaskpriority =
-      std::max(origtaskpriority - origProcPriority + aNice, aNice);
-
-    // Do not reduce priority of threads already running at priorities greater
-    // than normal.  These threads are likely special service threads that need
-    // elevated priorities to process audio, display composition, etc.
-    if (newtaskpriority > origtaskpriority &&
-        origtaskpriority < ANDROID_PRIORITY_NORMAL) {
-      continue;
-    }
-
-    rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
-
-    if (rv) {
-      HAL_LOG("Unable to set nice for tid=%d (pid=%d); error %d.  This isn't "
-              "necessarily a problem; it could be a benign race condition.",
-              tid, aPid, errno);
-      continue;
-    }
-  }
-
-  HAL_LOG("Changed nice for pid %d from %d to %d.",
-          aPid, origProcPriority, aNice);
-
-  closedir(tasksDir);
-}
-
-/*
- * Used to store the nice value adjustments and oom_adj values for the various
- * process priority levels.
- */
-struct ProcessPriorityPrefs {
-  bool initialized;
-  int lowPriorityNice;
-  struct {
-    int nice;
-    int oomScoreAdj;
-  } priorities[NUM_PROCESS_PRIORITY];
-};
-
-/*
- * Reads the preferences for the various process priority levels and sets up
- * watchers so that if they're dynamically changed the change is reflected on
- * the appropriate variables.
- */
-void
-EnsureProcessPriorityPrefs(ProcessPriorityPrefs* prefs)
-{
-  if (prefs->initialized) {
-    return;
-  }
-
-  // Read the preferences for process priority levels
-  for (int i = PROCESS_PRIORITY_BACKGROUND; i < NUM_PROCESS_PRIORITY; i++) {
-    ProcessPriority priority = static_cast<ProcessPriority>(i);
-
-    // Read the nice values
-    const char* processPriorityStr = ProcessPriorityToString(priority);
-    nsPrintfCString niceStr("hal.processPriorityManager.gonk.%s.Nice",
-                            processPriorityStr);
-    Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
-
-    // Read the oom_adj scores
-    nsPrintfCString oomStr("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
-                           processPriorityStr);
-    Preferences::AddIntVarCache(&prefs->priorities[i].oomScoreAdj,
-                                oomStr.get());
-  }
-
-  Preferences::AddIntVarCache(&prefs->lowPriorityNice,
-                              "hal.processPriorityManager.gonk.LowCPUNice");
-
-  prefs->initialized = true;
-}
-
 void
 SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
                    ProcessCPUPriority aCPUPriority,
                    uint32_t aBackgroundLRU)
 {
   HAL_LOG("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d, LRU=%u)",
           aPid, aPriority, aCPUPriority, aBackgroundLRU);
@@ -1574,59 +1661,33 @@ SetProcessPriority(int aPid,
   // OOM parameters according to our prefs.
   //
   // We could/should do this on startup instead of waiting for the first
   // SetProcessPriorityCall.  But in practice, the master process needs to set
   // its priority early in the game, so we can reasonably rely on
   // SetProcessPriority being called early in startup.
   EnsureKernelLowMemKillerParamsSet();
 
-  static ProcessPriorityPrefs prefs = { 0 };
-  EnsureProcessPriorityPrefs(&prefs);
+  PriorityClass* pc = GetPriorityClass(aPriority);
 
-  int oomScoreAdj = prefs.priorities[aPriority].oomScoreAdj;
+  int oomScoreAdj = pc->OomScoreAdj();
 
   RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
 
-  int clampedOomScoreAdj = clamped<int>(oomScoreAdj, OOM_SCORE_ADJ_MIN,
-                                                     OOM_SCORE_ADJ_MAX);
-  if (clampedOomScoreAdj != oomScoreAdj) {
-    HAL_LOG("Clamping OOM adjustment for pid %d to %d", aPid,
-            clampedOomScoreAdj);
-  } else {
-    HAL_LOG("Setting OOM adjustment for pid %d to %d", aPid,
-            clampedOomScoreAdj);
-  }
-
   // We try the newer interface first, and fall back to the older interface
   // on failure.
-
   if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
-                   nsPrintfCString("%d", clampedOomScoreAdj).get()))
+                   nsPrintfCString("%d", oomScoreAdj).get()))
   {
-    int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj);
-
     WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
-                nsPrintfCString("%d", oomAdj).get());
+                nsPrintfCString("%d", OomAdjOfOomScoreAdj(oomScoreAdj)).get());
   }
 
-  int nice = 0;
-
-  if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
-    nice = prefs.priorities[aPriority].nice;
-  } else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
-    nice = prefs.lowPriorityNice;
-  } else {
-    HAL_ERR("Unknown aCPUPriority value %d", aCPUPriority);
-    MOZ_ASSERT(false);
-    return;
-  }
-
-  HAL_LOG("Setting nice for pid %d to %d", aPid, nice);
-  SetNiceForPid(aPid, nice);
+  HAL_LOG("Assigning pid %d to cgroup %s", aPid, pc->CGroup().get());
+  pc->AddProcess(aPid);
 }
 
 static bool
 IsValidRealTimePriority(int aValue, int aSchedulePolicy)
 {
   return (aValue >= sched_get_priority_min(aSchedulePolicy)) &&
          (aValue <= sched_get_priority_max(aSchedulePolicy));
 }
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -804,26 +804,34 @@ js::atomics_futexWait(JSContext *cx, uns
     bool retval = true;
     switch (fx->wait(timeout)) {
       case JS::PerRuntimeFutexAPI::Woken:
         r.setInt32(AtomicsObject::FutexOK);
         break;
       case JS::PerRuntimeFutexAPI::Timedout:
         r.setInt32(AtomicsObject::FutexTimedout);
         break;
+      case JS::PerRuntimeFutexAPI::ErrorException:
+        MOZ_ASSERT(JS_IsExceptionPending(cx));
+        retval = false;
+        break;
+      case JS::PerRuntimeFutexAPI::InterruptForTerminate:
+        JS_ClearPendingException(cx);
+        retval = false;
+        break;
+      case JS::PerRuntimeFutexAPI::WaitingNotAllowed:
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
+        retval = false;
+        break;
       case JS::PerRuntimeFutexAPI::ErrorTooLong:
-        // This is a hack, but it's serviceable.
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_TOO_LONG);
         retval = false;
         break;
-      case JS::PerRuntimeFutexAPI::InterruptForTerminate:
-        // Throw an uncatchable exception.
-        JS_ClearPendingException(cx);
-        retval = false;
-        break;
+      default:
+        MOZ_CRASH();
     }
 
     if (w.lower_pri == &w) {
         sarb->setWaiters(nullptr);
     } else {
         w.lower_pri->back = w.back;
         w.back->lower_pri = w.lower_pri;
         if (sarb->waiters() == &w)
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -186,35 +186,35 @@ ParseEvalStringAsJSON(JSContext *cx, con
     JSONParser<CharT> parser(cx, jsonChars, JSONParserBase::NoError);
     if (!parser.parse(rval))
         return EvalJSON_Failure;
 
     return rval.isUndefined() ? EvalJSON_NotJSON : EvalJSON_Success;
 }
 
 static EvalJSONResult
-TryEvalJSON(JSContext *cx, JSFlatString *str, MutableHandleValue rval)
+TryEvalJSON(JSContext *cx, JSLinearString *str, MutableHandleValue rval)
 {
     if (str->hasLatin1Chars()) {
         AutoCheckCannotGC nogc;
         if (!EvalStringMightBeJSON(str->latin1Range(nogc)))
             return EvalJSON_NotJSON;
     } else {
         AutoCheckCannotGC nogc;
         if (!EvalStringMightBeJSON(str->twoByteRange(nogc)))
             return EvalJSON_NotJSON;
     }
 
-    AutoStableStringChars flatChars(cx);
-    if (!flatChars.init(cx, str))
+    AutoStableStringChars linearChars(cx);
+    if (!linearChars.init(cx, str))
         return EvalJSON_Failure;
 
-    return flatChars.isLatin1()
-           ? ParseEvalStringAsJSON(cx, flatChars.latin1Range(), rval)
-           : ParseEvalStringAsJSON(cx, flatChars.twoByteRange(), rval);
+    return linearChars.isLatin1()
+           ? ParseEvalStringAsJSON(cx, linearChars.latin1Range(), rval)
+           : ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
 }
 
 // Define subset of ExecuteType so that casting performs the injection.
 enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
 
 // Common code implementing direct and indirect eval.
 //
 // Evaluate call.argv[2], if it is a string, in the context of the given calling
@@ -272,29 +272,29 @@ EvalKernel(JSContext *cx, const CallArgs
 
         // Use the global as 'this', modulo outerization.
         JSObject *thisobj = GetThisObject(cx, scopeobj);
         if (!thisobj)
             return false;
         thisv = ObjectValue(*thisobj);
     }
 
-    Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
-    if (!flatStr)
+    RootedLinearString linearStr(cx, str->ensureLinear(cx));
+    if (!linearStr)
         return false;
 
     RootedScript callerScript(cx, caller ? caller.script() : nullptr);
-    EvalJSONResult ejr = TryEvalJSON(cx, flatStr, args.rval());
+    EvalJSONResult ejr = TryEvalJSON(cx, linearStr, args.rval());
     if (ejr != EvalJSON_NotJSON)
         return ejr == EvalJSON_Success;
 
     EvalScriptGuard esg(cx);
 
     if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
-        esg.lookupInEvalCache(flatStr, callerScript, pc);
+        esg.lookupInEvalCache(linearStr, callerScript, pc);
 
     if (!esg.foundScript()) {
         RootedScript maybeScript(cx);
         unsigned lineno;
         const char *filename;
         bool mutedErrors;
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
@@ -318,28 +318,28 @@ EvalKernel(JSContext *cx, const CallArgs
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc));
 
-        AutoStableStringChars flatChars(cx);
-        if (!flatChars.initTwoByte(cx, flatStr))
+        AutoStableStringChars linearChars(cx);
+        if (!linearChars.initTwoByte(cx, linearStr))
             return false;
 
-        const char16_t *chars = flatChars.twoByteRange().start().get();
-        SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
+        const char16_t *chars = linearChars.twoByteRange().start().get();
+        SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
-        SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
+        SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, staticScope,
-                                                     options, srcBuf, flatStr, staticLevel);
+                                                     options, srcBuf, linearStr, staticLevel);
         if (!compiled)
             return false;
 
         if (compiled->strict())
             staticScope->setStrict();
 
         esg.setNewScript(compiled);
     }
@@ -361,27 +361,27 @@ js::DirectEvalStringFromIon(JSContext *c
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL);
         return false;
     }
 
     // ES5 15.1.2.1 steps 2-8.
 
     unsigned staticLevel = callerScript->staticLevel() + 1;
 
-    Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
-    if (!flatStr)
+    RootedLinearString linearStr(cx, str->ensureLinear(cx));
+    if (!linearStr)
         return false;
 
-    EvalJSONResult ejr = TryEvalJSON(cx, flatStr, vp);
+    EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp);
     if (ejr != EvalJSON_NotJSON)
         return ejr == EvalJSON_Success;
 
     EvalScriptGuard esg(cx);
 
-    esg.lookupInEvalCache(flatStr, callerScript, pc);
+    esg.lookupInEvalCache(linearStr, callerScript, pc);
 
     if (!esg.foundScript()) {
         RootedScript maybeScript(cx);
         const char *filename;
         unsigned lineno;
         bool mutedErrors;
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
@@ -400,28 +400,28 @@ js::DirectEvalStringFromIon(JSContext *c
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(IsStrictEvalPC(pc));
 
-        AutoStableStringChars flatChars(cx);
-        if (!flatChars.initTwoByte(cx, flatStr))
+        AutoStableStringChars linearChars(cx);
+        if (!linearChars.initTwoByte(cx, linearStr))
             return false;
 
-        const char16_t *chars = flatChars.twoByteRange().start().get();
-        SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
+        const char16_t *chars = linearChars.twoByteRange().start().get();
+        SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
-        SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
+        SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, staticScope,
-                                                     options, srcBuf, flatStr, staticLevel);
+                                                     options, srcBuf, linearStr, staticLevel);
         if (!compiled)
             return false;
 
         if (compiled->strict())
             staticScope->setStrict();
 
         esg.setNewScript(compiled);
     }
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -1221,16 +1221,21 @@ MapObject::finalize(FreeOp *fop, JSObjec
 bool
 MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
 {
     Rooted<MapObject*> obj(cx, MapObject::create(cx));
     if (!obj)
         return false;
 
     CallArgs args = CallArgsFromVp(argc, vp);
+
+    // FIXME: bug 1083752
+    if (!WarnIfNotConstructing(cx, args, "Map"))
+        return false;
+
     if (!args.get(0).isNullOrUndefined()) {
         RootedValue adderVal(cx);
         if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal))
             return false;
 
         if (!IsCallable(adderVal))
             return ReportIsNotFunction(cx, adderVal);
 
@@ -1865,16 +1870,21 @@ SetObject::finalize(FreeOp *fop, JSObjec
 bool
 SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
 {
     Rooted<SetObject*> obj(cx, SetObject::create(cx));
     if (!obj)
         return false;
 
     CallArgs args = CallArgsFromVp(argc, vp);
+
+    // FIXME: bug 1083752
+    if (!WarnIfNotConstructing(cx, args, "Set"))
+        return false;
+
     if (!args.get(0).isNullOrUndefined()) {
         RootedValue adderVal(cx);
         if (!GetProperty(cx, obj, obj, cx->names().add, &adderVal))
             return false;
 
         if (!IsCallable(adderVal))
             return ReportIsNotFunction(cx, adderVal);
 
--- a/js/src/devtools/rootAnalysis/computeGCTypes.js
+++ b/js/src/devtools/rootAnalysis/computeGCTypes.js
@@ -92,26 +92,26 @@ function markGCType(typeName, child, why
 
     if (depth == 0 && isRootedTypeName(typeName))
         return;
     if (depth == 1 && isRootedPointerTypeName(typeName))
         return;
 
     if (depth == 0) {
         if (!(typeName in gcTypes))
-            gcTypes[typeName] = Set();
+            gcTypes[typeName] = new Set();
         gcTypes[typeName].add(why);
     } else if (depth == 1) {
         if (!(typeName in gcPointers))
-            gcPointers[typeName] = Set();
+            gcPointers[typeName] = new Set();
         gcPointers[typeName].add(why);
     }
 
     if (!(typeName in gcFields))
-        gcFields[typeName] = Map();
+        gcFields[typeName] = new Map();
     gcFields[typeName].set(why, [ child, ptrdness ]);
 
     if (typeName in structureParents) {
         for (var field of structureParents[typeName]) {
             var [ holderType, fieldName ] = field;
             markGCType(holderType, typeName, fieldName, depth, 0);
         }
     }
@@ -143,17 +143,17 @@ addGCType('js::ion::IonCode');
 addGCPointer('JS::Value');
 addGCPointer('jsid');
 
 // AutoCheckCannotGC should also not be held live across a GC function.
 addGCPointer('JS::AutoCheckCannotGC');
 
 function explain(csu, indent, seen) {
     if (!seen)
-        seen = Set();
+        seen = new Set();
     seen.add(csu);
     if (!(csu in gcFields))
         return;
     if (gcFields[csu].has('<annotation>')) {
         print(indent + "which is a GCThing because I said so");
         return;
     }
     if (gcFields[csu].has('<pointer-annotation>')) {
--- a/js/src/jit-test/lib/census.js
+++ b/js/src/jit-test/lib/census.js
@@ -77,17 +77,17 @@ const Census = {};
   //   |prop| which is present in the basis with value |value|.
   //
   // - extra(prop): Called when the subject has a property named |prop|, but the
   //   basis has no such property. This should return a walker that can check
   //   the subject's value.
   function makeBasisChecker({compare, missing, extra}) {
     return function makeWalker(basis) {
       if (typeof basis === 'object') {
-        var unvisited = Set(Object.getOwnPropertyNames(basis));
+        var unvisited = new Set(Object.getOwnPropertyNames(basis));
         return {
           enter: prop => {
             unvisited.delete(prop);
             if (prop in basis) {
               return makeWalker(basis[prop]);
             } else {
               return extra(prop);
             }
--- a/js/src/jit-test/tests/auto-regress/bug771027.js
+++ b/js/src/jit-test/tests/auto-regress/bug771027.js
@@ -1,8 +1,9 @@
 // |jit-test| error:TypeError
 
 // Binary: cache/js-dbg-32-b6aa44d8f11f-linux
 // Flags:
 //
 
 Array.prototype.iterator = (function() { { while(0) function Uint8ClampedArray() {  } } });
-assertEq(Set(["testing", "testing", 123]).size(), 2);
+var s = new Set(["testing", "testing", 123]);
+assertEq(s.size(), 2);
--- a/js/src/jit-test/tests/basic/bug797496.js
+++ b/js/src/jit-test/tests/basic/bug797496.js
@@ -1,8 +1,8 @@
 // |jit-test| error: TypeError
-var set = Set(['a']);
+var set = new Set(['a']);
 var n = 5;
 for (let v of set) {
     if (n === 0)
         break;
-    let g = set(Set(0xffffffff, n), 1);
+    let g = set(new Set(0xffffffff, n), 1);
 }
--- a/js/src/jit-test/tests/basic/bug808483.js
+++ b/js/src/jit-test/tests/basic/bug808483.js
@@ -1,12 +1,12 @@
 pSandbox = newGlobal();
 evalcx("\
     x = ArrayBuffer;\
-    y = Map();\
+    y = new Map();\
     x += 1;\
     w = x;\
     x += '0';\
     z = x;\
 ", pSandbox);
 evalcx("\
     x + '0';\
 ", pSandbox);
--- a/js/src/jit-test/tests/basic/spread-array.js
+++ b/js/src/jit-test/tests/basic/spread-array.js
@@ -14,18 +14,18 @@ assertEqArray([,,...[1, 2, 3],,,,], [,,1
 assertEqArray([...[1, 2, 3],,,,...[]], [1,2,3,,,,]);
 
 assertEqArray([...[undefined]], [undefined]);
 
 // other iterable objects
 assertEqArray([...new Int32Array([1, 2, 3])], [1, 2, 3]);
 assertEqArray([..."abc"], ["a", "b", "c"]);
 assertEqArray([...[1, 2, 3][Symbol.iterator]()], [1, 2, 3]);
-assertEqArray([...Set([1, 2, 3])], [1, 2, 3]);
-assertEqArray([...Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
+assertEqArray([...new Set([1, 2, 3])], [1, 2, 3]);
+assertEqArray([...new Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
 let itr = {};
 itr[Symbol.iterator] = function () {
     return {
         i: 1,
         next: function() {
             if (this.i < 4)
                 return { value: this.i++, done: false };
             else
--- a/js/src/jit-test/tests/basic/spread-call-eval.js
+++ b/js/src/jit-test/tests/basic/spread-call-eval.js
@@ -20,17 +20,17 @@ let line0 = Error().lineNumber;
 try {             // line0 + 1
   eval(...["("]); // line0 + 2
 } catch (e) {
   assertEq(e.lineNumber, 1);
 }
 
 // other iterable objects
 assertEq(eval(...["a + b"][Symbol.iterator]()), 11);
-assertEq(eval(...Set(["a + b"])), 11);
+assertEq(eval(...new Set(["a + b"])), 11);
 let itr = {};
 itr[Symbol.iterator] = function() {
     return {
         i: 0,
         next: function() {
             this.i++;
             if (this.i == 1)
                 return { value: "a + b", done: false };
--- a/js/src/jit-test/tests/basic/spread-call-funapply.js
+++ b/js/src/jit-test/tests/basic/spread-call-funapply.js
@@ -4,17 +4,17 @@ load(libdir + "iteration.js");
 
 function checkCommon(f) {
   assertEqArray(f.apply(null, ...[[1, 2, 3]]), [1, 2, 3]);
   assertEqArray(f.apply(...[null], [1, 2, 3]), [1, 2, 3]);
   assertEqArray(f.apply(...[null], ...[[1, 2, 3]]), [1, 2, 3]);
   assertEqArray(f.apply(...[null, [1, 2, 3]]), [1, 2, 3]);
 
   // other iterable objects
-  assertEqArray(f.apply(...Set([null, [1, 2, 3]])), [1, 2, 3]);
+  assertEqArray(f.apply(...new Set([null, [1, 2, 3]])), [1, 2, 3]);
   assertEqArray(f.apply(...[null, [1, 2, 3]][Symbol.iterator]()), [1, 2, 3]);
   let itr = {};
   itr[Symbol.iterator] = function() {
       return {
           i: 0,
           next: function() {
               this.i++;
               if (this.i == 1)
@@ -76,13 +76,13 @@ function checkRest(f) {
   assertEqArray(f.apply(null, ...[[]]), []);
   assertEqArray(f.apply(null, ...[[1]]), [1]);
   assertEqArray(f.apply(null, ...[[1, 2]]), [1, 2]);
   assertEqArray(f.apply(null, ...[[1, 2, 3, 4]]), [1, 2, 3, 4]);
 
   assertEqArray(f.apply(null, ...[[undefined]]), [undefined]);
 
   // other iterable objects
-  assertEqArray(f.apply(null, ...Map([[["a", "A"], ["b", "B"]]])).map(([k, v]) => k + v), ["aA", "bB"]);
+  assertEqArray(f.apply(null, ...new Map([[["a", "A"], ["b", "B"]]])).map(([k, v]) => k + v), ["aA", "bB"]);
 }
 
 checkRest(function(...x) x);
 checkRest((...x) => x);
--- a/js/src/jit-test/tests/basic/spread-call-length.js
+++ b/js/src/jit-test/tests/basic/spread-call-length.js
@@ -17,18 +17,18 @@ function checkLength(f, makeFn) {
   assertEq(makeFn("1, ...[2, 3, 4], 5")(f), 5);
 
   assertEq(makeFn("...[undefined]")(f), 1);
 
   // other iterable objects
   assertEq(makeFn("...arg")(f, new Int32Array([1, 2, 3])), 3);
   assertEq(makeFn("...arg")(f, "abc"), 3);
   assertEq(makeFn("...arg")(f, [1, 2, 3][Symbol.iterator]()), 3);
-  assertEq(makeFn("...arg")(f, Set([1, 2, 3])), 3);
-  assertEq(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
+  assertEq(makeFn("...arg")(f, new Set([1, 2, 3])), 3);
+  assertEq(makeFn("...arg")(f, new Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
   let itr = {};
   itr[Symbol.iterator] = function() {
       return {
           i: 1,
           next: function() {
               if (this.i < 4)
                   return { value: this.i++, done: false };
               else
--- a/js/src/jit-test/tests/basic/spread-call.js
+++ b/js/src/jit-test/tests/basic/spread-call.js
@@ -12,18 +12,18 @@ function checkCommon(f, makeFn) {
   assertEqArray(makeFn("1, ...[2], ...[3]")(f), [1, 2, 3]);
   assertEqArray(makeFn("1, ...[2, 3]")(f), [1, 2, 3]);
   assertEqArray(makeFn("1, ...[], 2, 3")(f), [1, 2, 3]);
 
   // other iterable objects
   assertEqArray(makeFn("...arg")(f, new Int32Array([1, 2, 3])), [1, 2, 3]);
   assertEqArray(makeFn("...arg")(f, "abc"), ["a", "b", "c"]);
   assertEqArray(makeFn("...arg")(f, [1, 2, 3][Symbol.iterator]()), [1, 2, 3]);
-  assertEqArray(makeFn("...arg")(f, Set([1, 2, 3])), [1, 2, 3]);
-  assertEqArray(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
+  assertEqArray(makeFn("...arg")(f, new Set([1, 2, 3])), [1, 2, 3]);
+  assertEqArray(makeFn("...arg")(f, new Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
   let itr = {};
   itr[Symbol.iterator] = function() {
       return {
           i: 1,
           next: function() {
               if (this.i < 4)
                   return { value: this.i++, done: false };
               else
--- a/js/src/jit-test/tests/collections/Map-clear-1.js
+++ b/js/src/jit-test/tests/collections/Map-clear-1.js
@@ -1,8 +1,8 @@
 // Clearing an empty Map has no effect.
 
-var m = Map();
+var m = new Map();
 for (var i = 0; i < 2; i++) {
     m.clear();
     assertEq(m.size, 0);
     assertEq(m.has(undefined), false);
 }
--- a/js/src/jit-test/tests/collections/Map-clear-2.js
+++ b/js/src/jit-test/tests/collections/Map-clear-2.js
@@ -1,11 +1,11 @@
 // Clearing a Map removes its entries; the Map remains usable afterwards.
 
-var m = Map([["a", "b"], ["b", "c"]]);
+var m = new Map([["a", "b"], ["b", "c"]]);
 assertEq(m.size, 2);
 m.clear();
 assertEq(m.size, 0);
 assertEq(m.has("a"), false);
 assertEq(m.get("a"), undefined);
 assertEq(m.delete("a"), false);
 assertEq(m.has("b"), false);
 for (var pair of m)
--- a/js/src/jit-test/tests/collections/Map-clear-3.js
+++ b/js/src/jit-test/tests/collections/Map-clear-3.js
@@ -1,10 +1,10 @@
 // Clearing a Map with a nontrivial number of elements works.
 
-var m = Map();
+var m = new Map();
 for (var i = 0; i < 100; i++)
     m.set(i, i);
 assertEq(m.size, i);
 m.clear();
 assertEq(m.size, 0);
 m.set("a", 1);
 assertEq(m.get("a"), 1);
--- a/js/src/jit-test/tests/collections/Map-clear-4.js
+++ b/js/src/jit-test/tests/collections/Map-clear-4.js
@@ -1,10 +1,10 @@
 // Clearing a Map after deleting some entries works.
 
-var m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
+var m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
 for (var [k, v] of m)
     if (k !== "c")
         m.delete(k);
 m.clear();
 assertEq(m.size, 0);
 assertEq(m.has("c"), false);
 assertEq(m.has("d"), false);
--- a/js/src/jit-test/tests/collections/Map-clear-5.js
+++ b/js/src/jit-test/tests/collections/Map-clear-5.js
@@ -1,12 +1,12 @@
 // Map.clear is unaffected by deleting/monkeypatching Map.prototype.{delete,iterator}.
 
 var data = [["a", 1], ["b", 2]];
-var m1 = Map(data), m2 = Map(data);
+var m1 = new Map(data), m2 = new Map(data);
 
 delete Map.prototype.delete;
 delete Map.prototype.iterator;
 m1.clear();
 assertEq(m1.size, 0);
 
 Map.prototype.delete = function () { throw "FAIL"; };
 Map.prototype.iterator = function () { throw "FAIL"; };
--- a/js/src/jit-test/tests/collections/Map-clear-6.js
+++ b/js/src/jit-test/tests/collections/Map-clear-6.js
@@ -1,6 +1,6 @@
 // Clearing a Map doesn't affect expando properties.
 
-var m = Map();
+var m = new Map();
 m.x = 3;
 m.clear();
 assertEq(m.x, 3);
--- a/js/src/jit-test/tests/collections/Map-clear-gc.js
+++ b/js/src/jit-test/tests/collections/Map-clear-gc.js
@@ -1,13 +1,13 @@
 // Clearing a Map removes any strong references to its keys and values.
 
 load(libdir + "referencesVia.js");
 
-var m = Map();
+var m = new Map();
 var k = {}, v = {};
 m.set(k, v);
 assertEq(referencesVia(m, "key", k), true);
 assertEq(referencesVia(m, "value", v), true);
 m.clear();
 if (typeof findReferences == 'function') {
     assertEq(referencesVia(m, "key", k), false);
     assertEq(referencesVia(m, "value", v), false);
--- a/js/src/jit-test/tests/collections/Map-clear-iterators-1.js
+++ b/js/src/jit-test/tests/collections/Map-clear-iterators-1.js
@@ -1,23 +1,23 @@
 // A Map iterator does not visit entries removed by clear().
 
 load(libdir + "iteration.js");
 
-var m = Map();
+var m = new Map();
 var it = m[Symbol.iterator]();
 m.clear();
 assertIteratorDone(it, undefined);
 
-m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
+m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
 it = m[Symbol.iterator]();
 assertIteratorNext(it, ["a", 1]);
 m.clear();
 assertIteratorDone(it, undefined);
 
 var log = "";
-m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
+m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
 for (var [k, v] of m) {
     log += k + v;
     if (k == "b")
         m.clear();
 }
 assertEq(log, "a1b2");
--- a/js/src/jit-test/tests/collections/Map-clear-iterators-2.js
+++ b/js/src/jit-test/tests/collections/Map-clear-iterators-2.js
@@ -1,12 +1,12 @@
 // A Map iterator continues to visit entries added after a clear().
 
 load(libdir + "asserts.js");
 load(libdir + "iteration.js");
 
-var m = Map([["a", 1]]);
+var m = new Map([["a", 1]]);
 var it = m[Symbol.iterator]();
 assertIteratorNext(it, ["a", 1]);
 m.clear();
 m.set("b", 2);
 assertIteratorNext(it, ["b", 2]);
 assertIteratorDone(it, undefined);
--- a/js/src/jit-test/tests/collections/Map-constructor-1.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-1.js
@@ -1,7 +1,17 @@
 // The Map constructor creates an empty Map by default.
 
-assertEq(Map().size, 0);
-assertEq((new Map).size, 0);
-assertEq(Map(undefined).size, 0);
-assertEq(new Map(undefined).size, 0);
-assertEq(new Map(null).size, 0);
+load(libdir + "asserts.js");
+
+var m = new Map();
+assertEq(m.size, 0);
+m = new Map(undefined);
+assertEq(m.size, 0);
+m = new Map(null);
+assertEq(m.size, 0);
+
+// FIXME: bug 1083752
+options("werror");
+assertEq(evaluate("Map()", {catchTermination: true}), "terminated");
+// assertThrowsInstanceOf(() => Map(), TypeError);
+// assertThrowsInstanceOf(() => Map(undefined), TypeError);
+// assertThrowsInstanceOf(() => Map(null), TypeError);
--- a/js/src/jit-test/tests/collections/Map-constructor-2.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-2.js
@@ -1,6 +1,6 @@
 // The Map constructor can take an argument that is an array of pairs.
 
 var arr = [["zero", 0], ["one", 1], ["two", 2]];
-var m = Map(arr);
+var m = new Map(arr);
 for (var [k, v] of arr)
     assertEq(m.get(k), v);
--- a/js/src/jit-test/tests/collections/Map-constructor-3.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-3.js
@@ -1,9 +1,9 @@
 // Map can take an argument that is an array of singleton arrays.
 
 var arr = [["a"], ["b"], ["c"]];
-var m = Map(arr);
+var m = new Map(arr);
 assertEq(m.size, 3);
 for (var [k, _] of arr) {
     assertEq(m.has(k), true);
     assertEq(m.get(k), undefined);
 }
--- a/js/src/jit-test/tests/collections/Map-constructor-4.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-4.js
@@ -1,6 +1,6 @@
-// Map(x) throws if x is not iterable (unless x is undefined).
+// new Map(x) throws if x is not iterable (unless x is undefined).
 
 load(libdir + "asserts.js");
 var nonIterables = [true, 1, -0, 3.14, NaN, {}, Math, this];
 for (let k of nonIterables)
-    assertThrowsInstanceOf(function () { Map(k); }, TypeError);
+    assertThrowsInstanceOf(function () { new Map(k); }, TypeError);
--- a/js/src/jit-test/tests/collections/Map-constructor-5.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-5.js
@@ -1,15 +1,15 @@
-// Map(arr) throws if arr contains holes (or undefined values).
+// new Map(arr) throws if arr contains holes (or undefined values).
 
 load(libdir + "asserts.js");
-assertThrowsInstanceOf(function () { Map([undefined]); }, TypeError);
-assertThrowsInstanceOf(function () { Map([null]); }, TypeError);
-assertThrowsInstanceOf(function () { Map([[0, 0], [1, 1], , [3, 3]]); }, TypeError);
-assertThrowsInstanceOf(function () { Map([[0, 0], [1, 1], ,]); }, TypeError);
+assertThrowsInstanceOf(function () { new Map([undefined]); }, TypeError);
+assertThrowsInstanceOf(function () { new Map([null]); }, TypeError);
+assertThrowsInstanceOf(function () { new Map([[0, 0], [1, 1], , [3, 3]]); }, TypeError);
+assertThrowsInstanceOf(function () { new Map([[0, 0], [1, 1], ,]); }, TypeError);
 
-// Map(iterable) throws if iterable doesn't have array-like objects
+// new Map(iterable) throws if iterable doesn't have array-like objects
 
-assertThrowsInstanceOf(function () { Map([1, 2, 3]); }, TypeError);
+assertThrowsInstanceOf(function () { new Map([1, 2, 3]); }, TypeError);
 assertThrowsInstanceOf(function () {
 	let s = new Set([1, 2, "abc"]);
 	new Map(s);
 }, TypeError);
--- a/js/src/jit-test/tests/collections/Map-constructor-duplicates.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-duplicates.js
@@ -1,8 +1,8 @@
 // When the argument to Map contains a key multiple times, the last value is retained.
 
 var arg = [["zero", 7], ["one", 1], ["two", 4], ["zero", 8], ["two", 2], ["zero", 0]];
-var m = Map(arg);
+var m = new Map(arg);
 assertEq(m.get("zero"), 0);
 assertEq(m.get("one"), 1);
 assertEq(m.get("two"), 2);
 assertEq(m.size, 3);
--- a/js/src/jit-test/tests/collections/Map-constructor-generator-1.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-generator-1.js
@@ -5,15 +5,15 @@ function data(n) {
     var s = '';
     for (var i = 0; i < n; i++) {
         yield [s, i];
         s += '.';
     }
     done = true;
 }
 
-var m = Map(data(50));
+var m = new Map(data(50));
 assertEq(done, true);  // the constructor consumes the argument
 assertEq(m.size, 50);
 assertEq(m.get(""), 0);
 assertEq(m.get("....."), 5);
 assertEq(m.get(Array(49+1).join(".")), 49);
 assertEq(m.has(undefined), false);
--- a/js/src/jit-test/tests/collections/Map-constructor-generator-2.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-generator-2.js
@@ -1,8 +1,8 @@
 // The argument to Map can be a generator-expression.
 
 var arr = [1, 2, "green", "red"];
-var m = Map([v, v] for (v of arr));
+var m = new Map([v, v] for (v of arr));
 assertEq(m.size, 4);
 
 for (var i = 0; i < 4; i++)
     assertEq(m.get(arr[i]), arr[i]);
--- a/js/src/jit-test/tests/collections/Map-constructor-generator-3.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-generator-3.js
@@ -1,8 +1,10 @@
 // The argument to Map may be a generator-iterator that produces no values.
 
-assertEq(Map(x for (x of [])).size, 0);
+var m = new Map(x for (x of []));
+assertEq(m.size, 0);
 
 function none() {
     if (0) yield 0;
 }
-assertEq(Map(none()).size, 0);
+m = new Map(none());
+assertEq(m.size, 0);
--- a/js/src/jit-test/tests/collections/Map-constructor-generator-exception.js
+++ b/js/src/jit-test/tests/collections/Map-constructor-generator-exception.js
@@ -4,9 +4,9 @@ load(libdir + "asserts.js");
 
 function data2() {
     yield [{}, "XR22/Z"];
     yield [{}, "23D-BN"];
     throw "oops";
 }
 
 var it = data2();
-assertThrowsValue(function () { Map(it); }, "oops");
+assertThrowsValue(function () { new Map(it); }, "oops");
--- a/js/src/jit-test/tests/collections/Map-delete-size.js
+++ b/js/src/jit-test/tests/collections/Map-delete-size.js
@@ -1,11 +1,11 @@
 // map.delete(k) decrements the map size iff an entry was actually removed.
 
-var m = Map();
+var m = new Map();
 m.delete(3);
 assertEq(m.size, 0);
 m.set({}, 'ok');
 m.set(Math, 'ok');
 assertEq(m.size, 2);
 m.delete({});
 assertEq(m.size, 2);
 m.delete(Math);
--- a/js/src/jit-test/tests/collections/Map-iterator-1.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-1.js
@@ -1,11 +1,11 @@
 // for-of can be used to iterate over a Map twice.
 
-var map = Map([['a', 0], ['b', 1], ['c', 2]]);
+var map = new Map([['a', 0], ['b', 1], ['c', 2]]);
 var log = '';
 
 for (let i = 0; i < 2; i++) {
     for (let [k, v] of map)
         log += k + v;
     log += ';'
 }
 assertEq(log, 'a0b1c2;a0b1c2;');
--- a/js/src/jit-test/tests/collections/Map-iterator-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-2.js
@@ -1,11 +1,11 @@
 // Nested for-of loops can iterate over a Map.
 
-var map = Map([['a', 0], ['b', 1]]);
+var map = new Map([['a', 0], ['b', 1]]);
 var log = '';
 for (let [k0, v0] of map) {
     log += k0 + v0 + ':'
     for (let [k1, v1] of map)
         log += k1 + v1;
     log += ';'
 };
 assertEq(log, 'a0:a0b1;b1:a0b1;');
--- a/js/src/jit-test/tests/collections/Map-iterator-add-1.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-add-1.js
@@ -1,11 +1,11 @@
 // map.iterator() is live: entries added during iteration are visited.
 
-var map = Map();
+var map = new Map();
 function force(k) {
     if (!map.has(k) && k >= 0)
         map.set(k, k - 1);
 }
 force(5);
 var log = '';
 for (let [k, v] of map) {
     log += k + ';';
--- a/js/src/jit-test/tests/collections/Map-iterator-add-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-add-2.js
@@ -1,10 +1,10 @@
 // A Map iterator does not iterate over new entries added after it throws StopIteration.
 
 load(libdir + "iteration.js");
 
-var map = Map();
+var map = new Map();
 var iter0 = map[Symbol.iterator](), iter1 = map[Symbol.iterator]();
 assertIteratorDone(iter0, undefined);  // closes iter0
 map.set(1, 2);
 assertIteratorDone(iter0, undefined);  // already closed
 assertIteratorNext(iter1, [1, 2]);     // was not yet closed
--- a/js/src/jit-test/tests/collections/Map-iterator-add-remove.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-add-remove.js
@@ -1,11 +1,11 @@
 // Removing and re-adding entries while an iterator is live causes the iterator to visit them again.
 
-var map = Map([['a', 1]]);
+var map = new Map([['a', 1]]);
 var n = 5;
 for (let [k, v] of map) {
     assertEq(k, 'a');
     assertEq(v, 1);
     if (n === 0)
         break;
     map.delete('a');
     map.set('a', 1);
--- a/js/src/jit-test/tests/collections/Map-iterator-order.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-order.js
@@ -1,13 +1,13 @@
 // Map iterators produces entries in the order they were inserted.
 
 load(libdir + "eqArrayHelper.js");
 
-var map = Map();
+var map = new Map();
 for (var i = 7; i !== 1; i = i * 7 % 1117)
     map.set("" + i, i);
 assertEq(map.size, 557);
 
 i = 7;
 for (var pair of map) {
     assertEqArray(pair, ["" + i, i]);
     i = i * 7 % 1117;
--- a/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js
@@ -1,14 +1,14 @@
 // mapiter.next() returns an actual array.
 
 load(libdir + "iteration.js");
 
 var key = {};
-var map = Map([[key, 'value']]);
+var map = new Map([[key, 'value']]);
 var entry = map[Symbol.iterator]().next().value;
 assertEq(Array.isArray(entry), true);
 assertEq(Object.getPrototypeOf(entry), Array.prototype);
 assertEq(Object.isExtensible(entry), true);
 
 assertEq(entry.length, 2);
 var names = Object.getOwnPropertyNames(entry).sort();
 assertEq(names.length, 3);
--- a/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js
@@ -1,13 +1,13 @@
 // mapiter.next() returns a fresh array each time.
 
 load(libdir + "iteration.js");
 
-var map = Map([['a', 1], ['b', 2]]);
+var map = new Map([['a', 1], ['b', 2]]);
 var iter = map[Symbol.iterator]();
 var a = iter.next(), b = iter.next();
 assertIteratorResult(a, ['a', 1], false);
 assertIteratorResult(b, ['b', 2], false);
 assertEq(a.value !== b.value, true);
 var a1 = map[Symbol.iterator]();
 assertIteratorNext(a1, ['a', 1]);
 assertEq(a.value !== a1.value, true);
--- a/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js
@@ -1,13 +1,13 @@
 // Modifying an array returned by mapiter.next() does not modify the Map.
 
 load(libdir + "iteration.js");
 
-var map = Map([['a', 1]]);
+var map = new Map([['a', 1]]);
 var res = map[Symbol.iterator]().next();
 assertIteratorResult(res, ['a', 1], false);
 res.value[0] = 'b';
 res.value[1] = 2;
 assertIteratorResult(res, ['b', 2], false);
 assertEq(map.get('a'), 1);
 assertEq(map.has('b'), false);
 assertEq(map.size, 1);
--- a/js/src/jit-test/tests/collections/Map-iterator-proxies-1.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-proxies-1.js
@@ -1,8 +1,8 @@
 // for-of works on a cross-compartment wrapper of a Map.
 
 var g = newGlobal();
-var mw = g.eval("Map([['a', 1], ['b', 2]])");
+var mw = g.eval("new Map([['a', 1], ['b', 2]])");
 var log = '';
 for (let [k, v] of mw)
     log += k + v;
 assertEq(log, "a1b2");
--- a/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js
@@ -3,19 +3,19 @@
 load(libdir + "asserts.js");
 load(libdir + "eqArrayHelper.js");
 load(libdir + "iteration.js");
 
 var g = newGlobal();
 
 var iterator_fn = Map.prototype[Symbol.iterator];
 assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
-assertThrowsInstanceOf(function () { iterator_fn.call(Set()); }, TypeError);
-var mapw = g.eval("Map([['x', 1], ['y', 2]])");
+assertThrowsInstanceOf(function () { iterator_fn.call(new Set()); }, TypeError);
+var mapw = g.eval("new Map([['x', 1], ['y', 2]])");
 assertEqArray(iterator_fn.call(mapw).next().value, ["x", 1]);
 
-var next_fn = Map()[Symbol.iterator]().next;
+var next_fn = (new Map())[Symbol.iterator]().next;
 assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
-assertThrowsInstanceOf(function () { next_fn.call(Set()[Symbol.iterator]()); }, TypeError);
+assertThrowsInstanceOf(function () { next_fn.call((new Set())[Symbol.iterator]()); }, TypeError);
 var iterw = mapw[Symbol.iterator]();
 assertEqArray(next_fn.call(iterw).value, ["x", 1]);
 assertEqArray(next_fn.call(iterw).value, ["y", 2]);
 assertEq(next_fn.call(iterw).done, true);
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-1.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-1.js
@@ -1,13 +1,13 @@
 // A map iterator can cope with removing the current entry.
 
 function test(pairs) {
     print(uneval(pairs));
-    var map = Map(pairs);
+    var map = new Map(pairs);
     
     var all_keys = '';
     var false_keys = '';
     for (let [k, v] of map) {
         all_keys += k;
         if (!v)
             false_keys += k;
     }
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-2.js
@@ -1,13 +1,13 @@
 // A map iterator can cope with removing the next entry.
 
 load(libdir + "iteration.js");
 
-var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
+var map = new Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
 var iter = map[Symbol.iterator]();
 var log = '';
 for (let [k, v] of iter) {
     log += k + v;
     if (k === 'b')
         map.delete('c');
 }
 assertEq(log, 'a0b1d3');
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-3.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-3.js
@@ -1,13 +1,13 @@
 // A map iterator can cope with removing the next entry, then the current entry.
 
 load(libdir + "asserts.js");
 load(libdir + "iteration.js");
 
-var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
+var map = new Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
 var iter = map[Symbol.iterator]();
 assertIteratorNext(iter, ['a', 0]);
 assertIteratorNext(iter, ['b', 1]);
 map.delete('c');
 map.delete('b');
 assertIteratorNext(iter, ['d', 3]);
 assertIteratorDone(iter, undefined);
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-4.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-4.js
@@ -1,14 +1,14 @@
 // Multiple live iterators on the same Map can cope with removing entries.
 
 load(libdir + "iteration.js");
 
 // Make a map.
-var map = Map();
+var map = new Map();
 var SIZE = 7;
 for (var j = 0; j < SIZE; j++)
     map.set(j, j);
 
 // Make lots of iterators pointing to entry 2 of the map.
 var NITERS = 5;
 var iters = [];
 for (var i = 0; i < NITERS; i++) {
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-5.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-5.js
@@ -1,12 +1,12 @@
 // Removing a Map entry already visited by an iterator does not cause any
 // entries to be skipped.
 
-var map = Map();
+var map = new Map();
 for (var i = 0; i < 20; i++)
     map.set(String.fromCharCode('A'.charCodeAt(0) + i), i);
 
 var log = '';
 for (var [k, v] of map) {
     log += k;
     if (v % 5 === 4) {
         // Delete all entries preceding this one.
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-6.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-6.js
@@ -1,15 +1,15 @@
 // Removing many Map entries does not cause a live iterator to skip any of the
 // entries that were not removed. (Compacting a Map must not be observable to
 // script.)
 
 load(libdir + "iteration.js");
 
-var map = Map();
+var map = new Map();
 for (var i = 0; i < 32; i++)
     map.set(i, i);
 var iter = map[Symbol.iterator]();
 assertIteratorNext(iter, [0, 0]);
 for (var i = 0; i < 30; i++)
     map.delete(i);
 assertEq(map.size, 2);
 for (var i = 32; i < 100; i++)
--- a/js/src/jit-test/tests/collections/Map-iterators-3.js
+++ b/js/src/jit-test/tests/collections/Map-iterators-3.js
@@ -1,10 +1,10 @@
 // A closed Map iterator does not visit new entries added after a clear().
 
 load(libdir + "iteration.js");
 
-var m = Map();
+var m = new Map();
 var it = m[Symbol.iterator]();
 assertIteratorDone(it, undefined);  // close the iterator
 m.clear();
 m.set("a", 1);
 assertIteratorDone(it, undefined);  // iterator still closed
--- a/js/src/jit-test/tests/collections/Map-set-size.js
+++ b/js/src/jit-test/tests/collections/Map-set-size.js
@@ -1,11 +1,11 @@
 // map.set(k, v) increments the map size iff map didn't already have an entry for k.
 
-var m = Map();
+var m = new Map();
 m.set('a', 0);
 assertEq(m.size, 1);
 m.set('a', 0);
 assertEq(m.size, 1);
 m.set('a', undefined);
 assertEq(m.size, 1);
 
 m.set('b', 2);
--- a/js/src/jit-test/tests/collections/Map-size.js
+++ b/js/src/jit-test/tests/collections/Map-size.js
@@ -1,6 +1,6 @@
 // Each Map has its own size.
 
-var m1 = Map(), m2 = Map();
+var m1 = new Map(), m2 = new Map();
 m1.set("x", 3);
 assertEq(m1.size, 1);
 assertEq(m2.size, 0);
--- a/js/src/jit-test/tests/collections/Map-surfaces-1.js
+++ b/js/src/jit-test/tests/collections/Map-surfaces-1.js
@@ -9,18 +9,17 @@ assertEq(desc.writable, true);
 
 assertEq(typeof Map, 'function');
 assertEq(Object.keys(Map).length, 0);
 assertEq(Map.length, 1);
 assertEq(Map.name, "Map");
 
 assertEq(Object.getPrototypeOf(Map.prototype), Object.prototype);
 assertEq(Object.prototype.toString.call(Map.prototype), "[object Map]");
-assertEq(Object.prototype.toString.call(new Map), "[object Map]");
-assertEq(Object.prototype.toString.call(Map()), "[object Map]");
+assertEq(Object.prototype.toString.call(new Map()), "[object Map]");
 assertEq(Object.keys(Map.prototype).join(), "");
 assertEq(Map.prototype.constructor, Map);
 
 function checkMethod(name, arity) { 
     var desc = Object.getOwnPropertyDescriptor(Map.prototype, name);
     assertEq(desc.enumerable, false);
     assertEq(desc.configurable, true);
     assertEq(desc.writable, true);
--- a/js/src/jit-test/tests/collections/Map-values-1.js
+++ b/js/src/jit-test/tests/collections/Map-values-1.js
@@ -1,11 +1,11 @@
 // map.keys(), .values(), and .entries() on an empty map produce empty iterators.
 
-var m = Map();
+var m = new Map();
 var ki = m.keys(), vi = m.values(), ei = m.entries();
 var p = Object.getPrototypeOf(ki)
 assertEq(Object.getPrototypeOf(vi), p);
 assertEq(Object.getPrototypeOf(ei), p);
 
 for (let k of ki)
     throw "FAIL";
 for (let v of vi)
--- a/js/src/jit-test/tests/collections/Map-values-2.js
+++ b/js/src/jit-test/tests/collections/Map-values-2.js
@@ -1,15 +1,15 @@
 // map.keys() and map.values() return iterators over the key or the value,
 // respectively, of each key-value pair in the map.
 
 load(libdir + "iteration.js");
 
 var data = [["one", 1], ["two", 2], ["three", 3], ["four", 4]];
-var m = Map(data);
+var m = new Map(data);
 
 var ki = m.keys();
 assertIteratorNext(ki, "one");
 assertIteratorNext(ki, "two");
 assertIteratorNext(ki, "three");
 assertIteratorNext(ki, "four");
 assertIteratorDone(ki, undefined);
 
--- a/js/src/jit-test/tests/collections/Set-add-size.js
+++ b/js/src/jit-test/tests/collections/Set-add-size.js
@@ -1,11 +1,11 @@
 // set.add(v) increments set.size iff the set did not already contain v.
 
-var s = Set();
+var s = new Set();
 for (var i = 0; i < 10; i++) {
     assertEq(s.size, i);
     s.add(i);
 }
 for (var i = 0; i < 10; i++) {
     assertEq(s.size, 10);
     s.add(i);
 }
--- a/js/src/jit-test/tests/collections/Set-clear-1.js
+++ b/js/src/jit-test/tests/collections/Set-clear-1.js
@@ -1,8 +1,8 @@
 // Clearing an empty Set has no effect.
 
-var s = Set();
+var s = new Set();
 for (var i = 0; i < 2; i++) {
     s.clear();
     assertEq(s.size, 0);
     assertEq(s.has(undefined), false);
 }
--- a/js/src/jit-test/tests/collections/Set-clear-2.js
+++ b/js/src/jit-test/tests/collections/Set-clear-2.js
@@ -1,11 +1,11 @@
 // Clearing a Set removes its elements; the Set remains usable afterwards.
 
-var s = Set(["x", "y", "z", "z", "y"]);
+var s = new Set(["x", "y", "z", "z", "y"]);
 assertEq(s.size, 3);
 s.clear();
 assertEq(s.size, 0);
 assertEq(s.has("x"), false);
 assertEq(s.delete("x"), false);
 assertEq(s.has("z"), false);
 for (var v of s)
     throw "FAIL";  // shouldn't be any elements
--- a/js/src/jit-test/tests/collections/Set-clear-3.js
+++ b/js/src/jit-test/tests/collections/Set-clear-3.js
@@ -1,10 +1,10 @@
 // Clearing a Set with a nontrivial number of elements works.
 
-var s = Set();
+var s = new Set();
 for (var i = 0; i < 100; i++)
     s.add(i);
 assertEq(s.size, i);
 s.clear();
 assertEq(s.size, 0);
 s.add(12);
 assertEq(s.has(12), true);
--- a/js/src/jit-test/tests/collections/Set-clear-4.js
+++ b/js/src/jit-test/tests/collections/Set-clear-4.js
@@ -1,10 +1,10 @@
 // Clearing a Set after deleting some entries works.
 
-var s = Set(["a", "b", "c", "d"]);
+var s = new Set(["a", "b", "c", "d"]);
 for (var v of s)
     if (v !== "c")
         s.delete(v);
 s.clear();
 assertEq(s.size, 0);
 assertEq(s.has("c"), false);
 assertEq(s.has("d"), false);
--- a/js/src/jit-test/tests/collections/Set-clear-5.js
+++ b/js/src/jit-test/tests/collections/Set-clear-5.js
@@ -1,12 +1,12 @@
 // Set.clear is unaffected by deleting/monkeypatching Set.prototype.{delete,iterator}.
 
 var data = ["a", 1, {}];
-var s1 = Set(data), s2 = Set(data);
+var s1 = new Set(data), s2 = new Set(data);
 
 delete Set.prototype.delete;
 delete Set.prototype.iterator;
 s1.clear();
 assertEq(s1.size, 0);
 
 Set.prototype.delete = function () { throw "FAIL"; };
 Set.prototype.iterator = function () { throw "FAIL"; };
--- a/js/src/jit-test/tests/collections/Set-clear-6.js
+++ b/js/src/jit-test/tests/collections/Set-clear-6.js
@@ -1,6 +1,6 @@
 // Clearing a Set doesn't affect expando properties.
 
-var s = Set();
+var s = new Set();
 s.x = 3;
 s.clear();
 assertEq(s.x, 3);
--- a/js/src/jit-test/tests/collections/Set-clear-gc.js
+++ b/js/src/jit-test/tests/collections/Set-clear-gc.js
@@ -1,11 +1,11 @@
 // Clearing a Set removes any strong references to its elements.
 
 load(libdir + "referencesVia.js");
 
-var s = Set();
+var s = new Set();
 var obj = {};
 s.add(obj);
 assertEq(referencesVia(s, "key", obj), true);
 s.clear();
 if (typeof findReferences == 'function')
     assertEq(referencesVia(s, "key", obj), false);
--- a/js/src/jit-test/tests/collections/Set-clear-iterators-1.js
+++ b/js/src/jit-test/tests/collections/Set-clear-iterators-1.js
@@ -1,23 +1,23 @@
 // A Set iterator does not visit entries removed by clear().
 
 load(libdir + "iteration.js");
 
-var s = Set();
+var s = new Set();
 var it = s[Symbol.iterator]();
 s.clear();
 assertIteratorDone(it, undefined);
 
-s = Set(["a", "b", "c", "d"]);
+s = new Set(["a", "b", "c", "d"]);
 it = s[Symbol.iterator]();
 assertIteratorNext(it, "a");
 s.clear();
 assertIteratorDone(it, undefined);
 
 var log = "";
-s = Set(["a", "b", "c", "d"]);
+s = new Set(["a", "b", "c", "d"]);
 for (var v of s) {
     log += v;
     if (v == "b")
         s.clear();
 }
 assertEq(log, "ab");
--- a/js/src/jit-test/tests/collections/Set-clear-iterators-2.js
+++ b/js/src/jit-test/tests/collections/Set-clear-iterators-2.js
@@ -1,11 +1,11 @@
 // A Set iterator continues to visit entries added after a clear().
 
 load(libdir + "iteration.js");
 
-var s = Set(["a"]);
+var s = new Set(["a"]);
 var it = s[Symbol.iterator]();
 assertIteratorNext(it, "a");
 s.clear();
 s.add("b");
 assertIteratorNext(it, "b");
 assertIteratorDone(it, undefined);
--- a/js/src/jit-test/tests/collections/Set-clear-iterators-3.js
+++ b/js/src/jit-test/tests/collections/Set-clear-iterators-3.js
@@ -1,10 +1,10 @@
 // A closed Set iterator does not visit new entries added after a clear().
 
 load(libdir + "iteration.js");
 
-var s = Set();
+var s = new Set();
 var it = s[Symbol.iterator]();
 assertIteratorDone(it, undefined);  // close the iterator
 s.clear();
 s.add("a");
 assertIteratorDone(it, undefined);
--- a/js/src/jit-test/tests/collections/Set-constructor-1.js
+++ b/js/src/jit-test/tests/collections/Set-constructor-1.js
@@ -1,7 +1,17 @@
 // The Set constructor creates an empty Set by default.
 
-assertEq(Set().size, 0);
-assertEq((new Set).size, 0);
-assertEq(Set(undefined).size, 0);
-assertEq(new Set(undefined).size, 0);
-assertEq(new Set(null).size, 0);
+load(libdir + "asserts.js");
+
+var s = new Set();
+assertEq(s.size, 0);
+s = new Set(undefined);
+assertEq(s.size, 0);
+s = new Set(null);
+assertEq(s.size, 0);
+
+// FIXME: bug 1083752
+options("werror");
+assertEq(evaluate("Set()", {catchTermination: true}), "terminated");
+// assertThrowsInstanceOf(() => Set(), TypeError);
+// assertThrowsInstanceOf(() => Set(undefined), TypeError);
+// assertThrowsInstanceOf(() => Set(null), TypeError);
--- a/js/src/jit-test/tests/collections/Set-constructor-2.js
+++ b/js/src/jit-test/tests/collections/Set-constructor-2.js
@@ -1,18 +1,18 @@
 // The Set constructor can take an argument that is an array.
 
-var s = Set([]);
+var s = new Set([]);
 assertEq(s.size, 0);
 assertEq(s.has(undefined), false);
 
-s = Set(["one", "two", "three"]);
+s = new Set(["one", "two", "three"]);
 assertEq(s.size, 3);
 assertEq(s.has("one"), true);
 assertEq(s.has("eleventeen"), false);
 
 var a = [{}, {}, {}];
-s = Set(a);
+s = new Set(a);
 assertEq(s.size, 3);
 for (let obj of a)
     assertEq(s.has(obj), true);
 assertEq(s.has({}), false);
 assertEq(s.has("three"), false);
--- a/js/src/jit-test/tests/collections/Set-constructor-3.js
+++ b/js/src/jit-test/tests/collections/Set-constructor-3.js
@@ -1,11 +1,12 @@
 // The argument to Set may contain a value multiple times. Duplicates are discarded.
 
-assertEq(Set(["testing", "testing", 123]).size, 2);
+var s = new Set(["testing", "testing", 123]);
+assertEq(s.size, 2);
 
 var values = [undefined, null, false, NaN, 0, -0, 6.022e23, -Infinity, "", "xyzzy", {}, Math.sin];
 for (let v of values) {
     var a = [v, {}, {}, {}, v, {}, v, v];
-    var s = Set(a);
+    s = new Set(a);
     assertEq(s.size, 5);
     assertEq(s.has(v), true);
 }
--- a/js/src/jit-test/tests/collections/Set-constructor-generator-1.js
+++ b/js/src/jit-test/tests/collections/Set-constructor-generator-1.js
@@ -1,12 +1,12 @@
 // The argument to Set can be a generator.
 
 function hexData(n) {
     for (var i = 0; i < n; i++)
         yield i.toString(16);
 }
 
-var s = Set(hexData(256));
+var s = new Set(hexData(256));
 assertEq(s.size, 256);
 assertEq(s.has("0"), true);
 assertEq(s.has(0), false);
 assertEq(s.has("ff"), true);
--- a/js/src/jit-test/tests/collections/Set-constructor-generator-2.js
+++ b/js/src/jit-test/tests/collections/Set-constructor-generator-2.js
@@ -1,8 +1,8 @@
 // The argument to Set can be a generator-expression.
 
-var s = Set(k * k for (k of [1, 2, 3, 4]));
+var s = new Set(k * k for (k of [1, 2, 3, 4]));
 assertEq(s.size, 4);
 assertEq(s.has(1), true);
 assertEq(s.has(4), true);
 assertEq(s.has(9), true);
 assertEq(s.has(16), true);
--- a/js/src/jit-test/tests/collections/Set-delete-size.js
+++ b/js/src/jit-test/tests/collections/Set-delete-size.js
@@ -1,11 +1,11 @@
 // set.delete(v) decrements set.size iff the set contained v.
 
-var s = Set();
+var s = new Set();
 for (var i = 0; i < 10; i++)
     s.add(i);
 
 for (var i = 10; i > 0; i--) {
     assertEq(s.size, i);
     assertEq(s.delete(i), false);
     assertEq(s.size, i);
     assertEq(s.delete(i - 1), true);
--- a/js/src/jit-test/tests/collections/Set-iterator-1.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-1.js
@@ -1,11 +1,11 @@
 // for-of can be used to iterate over a Set twice.
 
-var set = Set(['a', 'b', 'c']);
+var set = new Set(['a', 'b', 'c']);
 var log = '';
 
 for (let i = 0; i < 2; i++) {
     for (let x of set)
         log += x;
     log += ';'
 }
 assertEq(log, 'abc;abc;');
--- a/js/src/jit-test/tests/collections/Set-iterator-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-2.js
@@ -1,11 +1,11 @@
 // Nested for-of loops can iterate over a Set.
 
-var map = Set(['a', 'b']);
+var map = new Set(['a', 'b']);
 var log = '';
 for (let x of map) {
     log += x + ':'
     for (let y of map)
         log += y;
     log += ';'
 };
 assertEq(log, 'a:ab;b:ab;');
--- a/js/src/jit-test/tests/collections/Set-iterator-3.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-3.js
@@ -1,11 +1,11 @@
 // Iterating over a set of objects yields those exact objects.
 
 var arr = [{}, {}, {}, [], /xyz/, new Date];
-var set = Set(arr);
+var set = new Set(arr);
 assertEq(set.size, arr.length);
 
 var i = 0;
 for (var x of set)
     assertEq(x, arr[i++]);
 assertEq(i, arr.length);
 
--- a/js/src/jit-test/tests/collections/Set-iterator-add-1.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-add-1.js
@@ -1,11 +1,11 @@
 // set.iterator() is live: entries added during iteration are visited.
 
-var set = Set([5]);
+var set = new Set([5]);
 var log = '';
 for (let x of set) {
     log += x + ';';
     if (x > 0)
         set.add(x - 1);
 }
 assertEq(log, '5;4;3;2;1;0;');
 assertEq(set.size, 6);
--- a/js/src/jit-test/tests/collections/Set-iterator-add-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-add-2.js
@@ -1,10 +1,10 @@
 // A Set iterator does not iterate over new entries added after it throws StopIteration.
 
 load(libdir + "iteration.js");
 
-var set = Set();
+var set = new Set();
 var iter0 = set[Symbol.iterator](), iter1 = set[Symbol.iterator]();
 assertIteratorDone(iter0, undefined);  // closes iter0
 set.add("x");
 assertIteratorDone(iter0, undefined);  // already closed
 assertIteratorNext(iter1, "x");  // was not yet closed
--- a/js/src/jit-test/tests/collections/Set-iterator-add-remove.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-add-remove.js
@@ -1,11 +1,11 @@
 // Removing and re-adding entries while an iterator is live causes the iterator to visit them again.
 
-var set = Set(['a']);
+var set = new Set(['a']);
 var n = 5;
 for (let v of set) {
     assertEq(v, 'a');
     if (n === 0)
         break;
     set.delete('a');
     set.add('a');
     n--;
--- a/js/src/jit-test/tests/collections/Set-iterator-gc-1.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-gc-1.js
@@ -1,10 +1,10 @@
 // A Set iterator keeps the data alive.
 
 load(libdir + "referencesVia.js");
 load(libdir + "iteration.js");
 
 var key = {};
-var set = Set([key]);
+var set = new Set([key]);
 var iter = set[Symbol.iterator]();
 referencesVia(iter, "**UNKNOWN SLOT 0**", set);
 referencesVia(set, "key", key);
--- a/js/src/jit-test/tests/collections/Set-iterator-gc-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-gc-2.js
@@ -1,8 +1,8 @@
 // GC-ing during a for-of loop doesn't crash.
 
 var i = 0;
-for (var x of Set(Object.getOwnPropertyNames(this))) {
+for (var x of new Set(Object.getOwnPropertyNames(this))) {
     gc();
     if (++i >= 20)
         break;
 }
--- a/js/src/jit-test/tests/collections/Set-iterator-gc-3.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-gc-3.js
@@ -1,20 +1,20 @@
 // GC in nested for-loops is safe.
 
 var x;
-for (x of Set([1]))
-    for (x of Set([1]))
-        for (x of Set([1]))
-            for (x of Set([1]))
-                for (x of Set([1]))
-                    for (x of Set([1]))
-                        for (x of Set([1]))
-                            for (x of Set([1]))
-                                for (x of Set([1]))
-                                    for (x of Set([1]))
-                                        for (x of Set([1]))
-                                            for (x of Set([1]))
-                                                for (x of Set([1]))
-                                                    for (x of Set([1]))
-                                                        for (x of Set([1]))
-                                                            for (x of Set([1]))
+for (x of new Set([1]))
+    for (x of new Set([1]))
+        for (x of new Set([1]))
+            for (x of new Set([1]))
+                for (x of new Set([1]))
+                    for (x of new Set([1]))
+                        for (x of new Set([1]))
+                            for (x of new Set([1]))
+                                for (x of new Set([1]))
+                                    for (x of new Set([1]))
+                                        for (x of new Set([1]))
+                                            for (x of new Set([1]))
+                                                for (x of new Set([1]))
+                                                    for (x of new Set([1]))
+                                                        for (x of new Set([1]))
+                                                            for (x of new Set([1]))
                                                                 gc();
--- a/js/src/jit-test/tests/collections/Set-iterator-order.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-order.js
@@ -1,11 +1,11 @@
 // Set iterators produces entries in the order they were inserted.
 
-var set = Set();
+var set = new Set();
 var i;
 for (i = 7; i !== 1; i = i * 7 % 1117)
     set.add(i);
 assertEq(set.size, 557);
 
 i = 7;
 for (var v of set) {
     assertEq(v, i);
--- a/js/src/jit-test/tests/collections/Set-iterator-proxies-1.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-proxies-1.js
@@ -1,8 +1,8 @@
 // for-of works on a cross-compartment wrapper of a Set.
 
 var g = newGlobal();
-var mw = g.eval("Set(['a', 'b', 1, 2])");
+var mw = g.eval("new Set(['a', 'b', 1, 2])");
 var log = '';
 for (let x of mw)
     log += x;
 assertEq(log, "ab12");
--- a/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js
@@ -2,19 +2,19 @@
 
 load(libdir + "asserts.js");
 load(libdir + "iteration.js");
 
 var g = newGlobal();
 
 var iterator_fn = Set.prototype[Symbol.iterator];
 assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
-assertThrowsInstanceOf(function () { iterator_fn.call(Map()); }, TypeError);
-var setw = g.eval("Set(['x', 'y'])");
+assertThrowsInstanceOf(function () { iterator_fn.call(new Map()); }, TypeError);
+var setw = g.eval("new Set(['x', 'y'])");
 assertIteratorNext(iterator_fn.call(setw), "x");
 
-var next_fn = Set()[Symbol.iterator]().next;
+var next_fn = (new Set())[Symbol.iterator]().next;
 assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
-assertThrowsInstanceOf(function () { next_fn.call(Map()[Symbol.iterator]()); }, TypeError);
+assertThrowsInstanceOf(function () { next_fn.call((new Map())[Symbol.iterator]()); }, TypeError);
 var iterw = setw[Symbol.iterator]();
 assertIteratorResult(next_fn.call(iterw), "x", false);
 assertIteratorResult(next_fn.call(iterw), "y", false);
 assertIteratorResult(next_fn.call(iterw), undefined, true);
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-1.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-1.js
@@ -1,13 +1,13 @@
 // A set iterator can cope with removing the current entry.
 
 function test(letters, toRemove) {
-    var set = Set(letters);
-    toRemove = Set(toRemove);
+    var set = new Set(letters);
+    toRemove = new Set(toRemove);
 
     var leftovers = [x for (x of set) if (!toRemove.has(x))].join("");
 
     var log = "";
     for (let x of set) {
         log += x;
         if (toRemove.has(x))
             set.delete(x);
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-2.js
@@ -1,13 +1,13 @@
 // A map iterator can cope with removing the next entry.
 
 load(libdir + "iteration.js");
 
-var set = Set("abcd");
+var set = new Set("abcd");
 var iter = set[Symbol.iterator]();
 var log = "";
 for (let x of iter) {
     log += x;
     if (x === "b")
         set.delete("c");
 }
 assertEq(log, "abd");
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-3.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-3.js
@@ -1,12 +1,12 @@
 // A set iterator can cope with removing the next entry, then the current entry.
 
 load(libdir + "iteration.js");
 
-var set = Set("abcd");
+var set = new Set("abcd");
 var iter = set[Symbol.iterator]();
 assertIteratorNext(iter, "a");
 assertIteratorNext(iter, "b");
 set.delete("c");
 set.delete("b");
 assertIteratorNext(iter, "d");
 assertIteratorDone(iter, undefined);
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-4.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-4.js
@@ -1,14 +1,14 @@
 // Multiple live iterators on the same Set can cope with removing entries.
 
 load(libdir + "iteration.js");
 
 // Make a set.
-var set = Set();
+var set = new Set();
 var SIZE = 7;
 for (var j = 0; j < SIZE; j++)
     set.add(j);
 
 // Make lots of iterators pointing to entry 2 of the set.
 var NITERS = 5;
 var iters = [];
 for (var i = 0; i < NITERS; i++) {
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-5.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-5.js
@@ -1,13 +1,13 @@
 // Removing a Set entry already visited by an iterator does not cause any
 // entries to be skipped.
 
 var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-var set = Set(str);
+var set = new Set(str);
 
 var log = '';
 var i = 0;
 for (var x of set) {
     log += x;
     if (i++ % 5 === 0) {
         // Delete all entries preceding this one.
         for (let y of set) {
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-6.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-6.js
@@ -1,15 +1,15 @@
 // Removing many Set entries does not cause a live iterator to skip any of the
 // entries that were not removed. (Compacting a Set must not be observable to
 // script.)
 
 load(libdir + "iteration.js");
 
-var set = Set();
+var set = new Set();
 for (var i = 0; i < 32; i++)
     set.add(i);
 var iter = set[Symbol.iterator]();
 assertIteratorNext(iter, 0);
 for (var i = 0; i < 30; i++)
     set.delete(i);
 assertEq(set.size, 2);
 for (var i = 32; i < 100; i++)
--- a/js/src/jit-test/tests/collections/Set-size.js
+++ b/js/src/jit-test/tests/collections/Set-size.js
@@ -1,7 +1,7 @@
 // Each Set has its own size.
 
-var s1 = Set(), s2 = Set();
+var s1 = new Set(), s2 = new Set();
 for (var i = 0; i < 30; i++)
     s1.add(i);
 assertEq(s1.size, 30);
 assertEq(s2.size, 0);
--- a/js/src/jit-test/tests/collections/Set-surfaces-1.js
+++ b/js/src/jit-test/tests/collections/Set-surfaces-1.js
@@ -9,18 +9,17 @@ assertEq(desc.writable, true);
 
 assertEq(typeof Set, 'function');
 assertEq(Object.keys(Set).length, 0);
 assertEq(Set.length, 1);
 assertEq(Set.name, "Set");
 
 assertEq(Object.getPrototypeOf(Set.prototype), Object.prototype);
 assertEq(Object.prototype.toString.call(Set.prototype), "[object Set]");
-assertEq(Object.prototype.toString.call(new Set), "[object Set]");
-assertEq(Object.prototype.toString.call(Set()), "[object Set]");
+assertEq(Object.prototype.toString.call(new Set()), "[object Set]");
 assertEq(Object.keys(Set.prototype).join(), "");
 assertEq(Set.prototype.constructor, Set);
 
 function checkMethod(name, arity) { 
     var desc = Object.getOwnPropertyDescriptor(Set.prototype, name);
     assertEq(desc.enumerable, false);
     assertEq(desc.configurable, true);
     assertEq(desc.writable, true);
--- a/js/src/jit-test/tests/collections/Set-values-1.js
+++ b/js/src/jit-test/tests/collections/Set-values-1.js
@@ -1,11 +1,11 @@
 // set.keys(), .values(), and .entries() on an empty set produce empty iterators
 
-var s = Set();
+var s = new Set();
 var ki = s.keys(), vi = s.values(), ei = s.entries();
 var p = Object.getPrototypeOf(ki);
 assertEq(Object.getPrototypeOf(vi), p);
 assertEq(Object.getPrototypeOf(ei), p);
 
 for (let k of ki)
 	throw "FAIL";
 for (let v of vi)
--- a/js/src/jit-test/tests/collections/Set-values-2.js
+++ b/js/src/jit-test/tests/collections/Set-values-2.js
@@ -1,15 +1,15 @@
 // set.keys() and set.values() return iterators over the elements
 // and set.entries() returns an iterator that yields pairs [e, e].
 
 load(libdir + "iteration.js");
 
 var data = [1, 2, 3, 4];
-var s = Set(data);
+var s = new Set(data);
 
 var ki = s.keys();
 assertIteratorNext(ki, 1);
 assertIteratorNext(ki, 2);
 assertIteratorNext(ki, 3);
 assertIteratorNext(ki, 4);
 assertIteratorDone(ki, undefined);
 
--- a/js/src/jit-test/tests/collections/WeakMap-clear.js
+++ b/js/src/jit-test/tests/collections/WeakMap-clear.js
@@ -1,10 +1,10 @@
 var key = {};
-var wm = WeakMap();
+var wm = new WeakMap();
 
 assertEq(wm.has(key), false);
 // Clearing an already empty WeakMap
 wm.clear();
 assertEq(wm.has(key), false);
 
 // Clearing a WeakMap with a live key
 wm.set(key, 42);
--- a/js/src/jit-test/tests/collections/WeakMap-constructor-1.js
+++ b/js/src/jit-test/tests/collections/WeakMap-constructor-1.js
@@ -1,12 +1,14 @@
 // The WeakMap constructor creates an empty WeakMap by default.
 
 load(libdir + "asserts.js");
 
 new WeakMap();
 new WeakMap(undefined);
 new WeakMap(null);
 
-// FIXME: bug 1062075
+// FIXME: bug 1083752
+options("werror");
+assertEq(evaluate("WeakMap()", {catchTermination: true}), "terminated");
 // assertThrowsInstanceOf(() => WeakMap(), TypeError);
 // assertThrowsInstanceOf(() => WeakMap(undefined), TypeError);
 // assertThrowsInstanceOf(() => WeakMap(null), TypeError);
--- a/js/src/jit-test/tests/collections/WeakMap-constructor-4.js
+++ b/js/src/jit-test/tests/collections/WeakMap-constructor-4.js
@@ -1,6 +1,6 @@
-// WeakMap(x) throws if x is not iterable (unless x is undefined).
+// new WeakMap(x) throws if x is not iterable (unless x is undefined).
 
 load(libdir + "asserts.js");
 var nonIterables = [true, 1, -0, 3.14, NaN, {}, Math, this];
 for (let k of nonIterables)
     assertThrowsInstanceOf(function () { new WeakMap(k); }, TypeError);
--- a/js/src/jit-test/tests/collections/WeakMap-constructor-5.js
+++ b/js/src/jit-test/tests/collections/WeakMap-constructor-5.js
@@ -1,23 +1,23 @@
-// WeakMap(arr) throws if arr contains holes (or undefined values).
+// new WeakMap(arr) throws if arr contains holes (or undefined values).
 
 load(libdir + "asserts.js");
 
 var k1 = {};
 var v1 = 42;
 var k2 = {};
 var v2 = 43;
 var k3 = {};
 var v3 = {};
 
 assertThrowsInstanceOf(function () { new WeakMap([undefined]); }, TypeError);
 assertThrowsInstanceOf(function () { new WeakMap([null]); }, TypeError);
 assertThrowsInstanceOf(function () { new WeakMap([[k1, v1], [k2, v2], , [k3, k3]]); }, TypeError);
 assertThrowsInstanceOf(function () { new WeakMap([[k1, v1], [k2, v2], ,]); }, TypeError);
 
-// WeakMap(iterable) throws if iterable doesn't have array-like objects
+// new WeakMap(iterable) throws if iterable doesn't have array-like objects
 
 assertThrowsInstanceOf(function () { new WeakMap([1, 2, 3]); }, TypeError);
 assertThrowsInstanceOf(function () {
   let s = new Set([1, 2, "abc"]);
   new WeakMap(s);
 }, TypeError);
--- a/js/src/jit-test/tests/collections/WeakMap-surfaces.js
+++ b/js/src/jit-test/tests/collections/WeakMap-surfaces.js
@@ -7,18 +7,17 @@ assertEq(desc.writable, true);
 
 assertEq(typeof WeakMap, 'function');
 assertEq(Object.keys(WeakMap).length, 0);
 assertEq(WeakMap.length, 1);
 assertEq(WeakMap.name, "WeakMap");
 
 assertEq(Object.getPrototypeOf(WeakMap.prototype), Object.prototype);
 assertEq(Object.prototype.toString.call(WeakMap.prototype), "[object WeakMap]");
-assertEq(Object.prototype.toString.call(new WeakMap), "[object WeakMap]");
-assertEq(Object.prototype.toString.call(WeakMap()), "[object WeakMap]");
+assertEq(Object.prototype.toString.call(new WeakMap()), "[object WeakMap]");
 assertEq(Object.keys(WeakMap.prototype).join(), "");
 assertEq(WeakMap.prototype.constructor, WeakMap);
 
 function checkMethod(name, arity) { 
     var desc = Object.getOwnPropertyDescriptor(WeakMap.prototype, name);
     assertEq(desc.enumerable, false);
     assertEq(desc.configurable, true);
     assertEq(desc.writable, true);
--- a/js/src/jit-test/tests/collections/WeakSet-constructor.js
+++ b/js/src/jit-test/tests/collections/WeakSet-constructor.js
@@ -1,9 +1,9 @@
 var list = [Number, Function];
 
 var ws = new WeakSet(list);
 assertEq(ws.has(Number), true);
 assertEq(ws.has(Function), true);
 
-ws = new WeakSet(Set(list));
+ws = new WeakSet(new Set(list));
 assertEq(ws.has(Number), true);
 assertEq(ws.has(Function), true);
--- a/js/src/jit-test/tests/collections/constructor-errors.js
+++ b/js/src/jit-test/tests/collections/constructor-errors.js
@@ -12,11 +12,11 @@ var misc = [
     new Boolean(true), new Number(0),
     {iterator: function () { return undefined; }},
     {iterator: function () { return null; }},
     {iterator: function () { return true; }},
     {iterator: function () { return 17; }},
 ];
 
 for (var v of misc) {
-    assertThrowsInstanceOf(function () { Set(v); }, TypeError);
-    assertThrowsInstanceOf(function () { Map(v); }, TypeError);
+    assertThrowsInstanceOf(function () { new Set(v); }, TypeError);
+    assertThrowsInstanceOf(function () { new Map(v); }, TypeError);
 }
\ No newline at end of file
--- a/js/src/jit-test/tests/collections/iterator-gc.js
+++ b/js/src/jit-test/tests/collections/iterator-gc.js
@@ -6,11 +6,11 @@ load(libdir + "referencesVia.js");
 var key = {};
 
 function test(obj, edgeName) {
     var iter = obj[Symbol.iterator]();
     referencesVia(iter, "**UNKNOWN SLOT 0**", obj);
     referencesVia(obj, edgeName, key);
 }
 
-test([key],                 "element[0]");
-test(Map([[key, 'value']]), "key");
-test(Set([key]),            "key");
+test([key],                     "element[0]");
+test(new Map([[key, 'value']]), "key");
+test(new Set([key]),            "key");
--- a/js/src/jit-test/tests/collections/iterator-proto-1.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-1.js
@@ -6,10 +6,10 @@ load(libdir + "iteration.js");
 function test(obj0, obj1) {
     var iter0 = obj0[Symbol.iterator](), iter1 = obj1[Symbol.iterator]();
     var proto = Object.getPrototypeOf(iter0);
     assertEq(Object.getPrototypeOf(iter1), proto);
     assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
 }
 
 test([], [1]);
-test(Map(), Map([[1, 1]]));
-test(Set(), Set([1]));
+test(new Map(), new Map([[1, 1]]));
+test(new Set(), new Set([1]));
--- a/js/src/jit-test/tests/collections/iterator-proto-2.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-2.js
@@ -1,13 +1,13 @@
 // Iterators of different collection types have different prototypes.
 
 load(libdir + "iteration.js");
 
 var aproto = Object.getPrototypeOf(Array()[Symbol.iterator]());
-var mproto = Object.getPrototypeOf(Map()[Symbol.iterator]());
-var sproto = Object.getPrototypeOf(Set()[Symbol.iterator]());
+var mproto = Object.getPrototypeOf((new Map())[Symbol.iterator]());
+var sproto = Object.getPrototypeOf((new Set())[Symbol.iterator]());
 assertEq(aproto !== mproto, true);
 assertEq(aproto !== sproto, true);
 assertEq(mproto !== sproto, true);
 assertEq(aproto.next !== mproto.next, true);
 assertEq(aproto.next !== sproto.next, true);
 assertEq(mproto.next !== sproto.next, true);
--- a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
@@ -1,15 +1,15 @@
 // Iterator prototype surfaces.
 
 load(libdir + "asserts.js");
 load(libdir + "iteration.js");
 
 function test(constructor) {
-    var proto = Object.getPrototypeOf(constructor()[Symbol.iterator]());
+    var proto = Object.getPrototypeOf(new constructor()[Symbol.iterator]());
     var names = Object.getOwnPropertyNames(proto);
     names.sort();
     assertDeepEq(names, ['next']);
     assertEq(proto.hasOwnProperty(Symbol.iterator), true);
 
     var desc = Object.getOwnPropertyDescriptor(proto, 'next');
     assertEq(desc.configurable, true);
     assertEq(desc.enumerable, false);
--- a/js/src/jit-test/tests/debug/onNewScript-01.js
+++ b/js/src/jit-test/tests/debug/onNewScript-01.js
@@ -1,13 +1,13 @@
 // Basic newScript hook tests.
 
 var g = newGlobal();
 var dbg = Debugger(g);
-var seen = WeakMap();
+var seen = new WeakMap();
 var hits = 0;
 dbg.onNewScript = function (s) {
     // Exceptions thrown from onNewScript are swept under the rug, but they
     // will at least prevent hits from being the expected number.
     assertEq(s instanceof Debugger.Script, true);
     assertEq(!seen.has(s), true);
     seen.set(s, true);
     hits++;
--- a/js/src/jit-test/tests/debug/onNewScript-02.js
+++ b/js/src/jit-test/tests/debug/onNewScript-02.js
@@ -1,13 +1,13 @@
 // Creating a new script with any number of subscripts triggers the newScript hook exactly once.
 
 var g = newGlobal();
 var dbg = Debugger(g);
-var seen = WeakMap();
+var seen = new WeakMap();
 var hits;
 dbg.onNewScript = function (s) {
     assertEq(s instanceof Debugger.Script, true);
     assertEq(!seen.has(s), true);
     seen.set(s, true);
     hits++;
 };
 
--- a/js/src/jit-test/tests/gc/bug-880816.js
+++ b/js/src/jit-test/tests/gc/bug-880816.js
@@ -4,17 +4,17 @@ lfcode.push("2");
 lfcode.push("{ function foo() {} }");
 lfcode.push("evaluate('\
 var INVALIDATE_MODES = INVALIDATE_MODE_STRINGS.map(s => ({mode: s}));\
 function range(n, m) {}\
 function seq_scan(array, f) {}\
 function assertStructuralEq(e1, e2) {}\
 for (var i = 0, l = a.length; i < l; i++) {}\
 ');");
-lfcode.push("for (var x of Set(Object.getOwnPropertyNames(this))) {}");
+lfcode.push("for (var x of new Set(Object.getOwnPropertyNames(this))) {}");
 var lfRunTypeId = -1;
 while (true) {
   var file = lfcode.shift(); if (file == undefined) { break; }
   loadFile(file)
 }
 function loadFile(lfVarx) {
     try {
         if (lfVarx.substr(-3) == ".js") {}
--- a/js/src/jit-test/tests/generators/bug908920.js
+++ b/js/src/jit-test/tests/generators/bug908920.js
@@ -1,9 +1,9 @@
 if (typeof schedulegc != 'undefined') {
     Function("\
         x = (function() { yield })();\
-        Set(x);\
+        new Set(x);\
         schedulegc(1);\
         print( /x/ );\
         for (p in x) {}\
     ")();
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1124485.js
@@ -0,0 +1,7 @@
+function f(x) {
+    var y = Math.fround(x);
+    assertEq(y, Math.pow(y, 1));
+}
+
+f(0);
+f(2147483647);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/proxy/delete-not-invoked-on-proto.js
@@ -0,0 +1,10 @@
+// Create Proxy that throws for everything.
+var {proxy, revoke} = Proxy.revocable({}, {});
+
+var obj = {__proto__: proxy, a: 1};
+// This revokes the proxy, so every operation on it THROWS.
+revoke();
+
+assertEq(delete obj.a, true);
+assertEq(delete obj.b, true);
+// Should not have invoked anything on [[Prototype]]
--- a/js/src/jit-test/tests/structured-clone/Map-Set-cross-compartment.js
+++ b/js/src/jit-test/tests/structured-clone/Map-Set-cross-compartment.js
@@ -1,8 +1,8 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 // Don't crash
-serialize(evalcx("Set(['x', 'y'])"));
-serialize(evalcx("Map([['x', 1]])"));
+serialize(evalcx("new Set(['x', 'y'])"));
+serialize(evalcx("new Map([['x', 1]])"));
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4590,20 +4590,26 @@ IonBuilder::specializeInlinedReturn(MDef
     // else use the result type.
 
     if (rdef->resultTypeSet()) {
         // Don't specialize if return typeset is a subset of the
         // observed typeset. The return typeset is already more specific.
         if (rdef->resultTypeSet()->isSubset(types))
             return rdef;
     } else {
+        MIRType observedType = types->getKnownMIRType();
+
+        // Don't specialize if type is MIRType_Float32 and TI reports
+        // MIRType_Double. Float is more specific than double.
+        if (observedType == MIRType_Double && rdef->type() == MIRType_Float32)
+            return rdef;
+
         // Don't specialize if types are inaccordance, except for MIRType_Value
         // and MIRType_Object (when not unknown object), since the typeset
         // contains more specific information.
-        MIRType observedType = types->getKnownMIRType();
         if (observedType == rdef->type() &&
             observedType != MIRType_Value &&
             (observedType != MIRType_Object || types->unknownObject()))
         {
             return rdef;
         }
     }
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -135,27 +135,37 @@ MacroAssemblerARM::convertDoubleToInt32(
 
 // Checks whether a float32 is representable as a 32-bit integer. If so, the
 // integer is written to the output register. Otherwise, a bailout is taken to
 // the given snapshot. This function overwrites the scratch float register.
 void
 MacroAssemblerARM::convertFloat32ToInt32(FloatRegister src, Register dest,
                                          Label *fail, bool negativeZeroCheck)
 {
-    // Convert the floating point value to an integer, if it did not fit, then
-    // when we convert it *back* to a float, it will have a different value,
-    // which we can test.
-    ma_vcvt_F32_I32(src, ScratchFloat32Reg.sintOverlay());
-    // Move the value into the dest register.
-    ma_vxfer(ScratchFloat32Reg, dest);
-    ma_vcvt_I32_F32(ScratchFloat32Reg.sintOverlay(), ScratchFloat32Reg);
-    ma_vcmp_f32(src, ScratchFloat32Reg);
+    // Converting the floating point value to an integer and then converting it
+    // back to a float32 would not work, as float to int32 conversions are
+    // clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX
+    // and then back to float(INT32_MAX + 1)).  If this ever happens, we just
+    // bail out.
+    FloatRegister ScratchSIntReg = ScratchFloat32Reg.sintOverlay();
+    ma_vcvt_F32_I32(src, ScratchSIntReg);
+
+    // Store the result
+    ma_vxfer(ScratchSIntReg, dest);
+
+    ma_vcvt_I32_F32(ScratchSIntReg, ScratchFloat32Reg);
+    ma_vcmp(src, ScratchFloat32Reg);
     as_vmrs(pc);
     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
 
+    // Bail out in the clamped cases.
+    ma_cmp(dest, Imm32(0x7fffffff));
+    ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    ma_b(fail, Assembler::Equal);
+
     if (negativeZeroCheck) {
         ma_cmp(dest, Imm32(0));
         // Test and bail for -0.0, when integer result is 0. Move the float into
         // the output reg, and if it is non-zero then the original value was
         // -0.0
         as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, Assembler::Equal, 0);
         ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
         ma_b(fail, Assembler::Equal);
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -347,16 +347,41 @@ class CachePage
     }
 
   private:
     char data_[kPageSize];   // The cached data.
     static const int kValidityMapSize = kPageSize >> kLineShift;
     char validity_map_[kValidityMapSize];  // One byte per line.
 };
 
+// Protects the icache() and redirection() properties of the
+// Simulator.
+class AutoLockSimulatorCache
+{
+  public:
+    explicit AutoLockSimulatorCache(Simulator *sim) : sim_(sim) {
+        PR_Lock(sim_->cacheLock_);
+        MOZ_ASSERT(!sim_->cacheLockHolder_);
+#ifdef DEBUG
+        sim_->cacheLockHolder_ = PR_GetCurrentThread();
+#endif
+    }
+
+    ~AutoLockSimulatorCache() {
+        MOZ_ASSERT(sim_->cacheLockHolder_);
+#ifdef DEBUG
+        sim_->cacheLockHolder_ = nullptr;
+#endif
+        PR_Unlock(sim_->cacheLock_);
+    }
+
+  private:
+    Simulator *const sim_;
+};
+
 bool Simulator::ICacheCheckingEnabled = false;
 
 int64_t Simulator::StopSimAt = -1L;
 
 Simulator *
 Simulator::Create()
 {
     Simulator *sim = js_new<Simulator>();
@@ -917,75 +942,75 @@ static bool
 AllOnOnePage(uintptr_t start, int size)
 {
     intptr_t start_page = (start & ~CachePage::kPageMask);
     intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
     return start_page == end_page;
 }
 
 static CachePage *
-GetCachePage(Simulator::ICacheMap &i_cache, void *page)
+GetCachePageLocked(Simulator::ICacheMap &i_cache, void *page)
 {
     MOZ_ASSERT(Simulator::ICacheCheckingEnabled);
 
     Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
     if (p)
         return p->value();
 
     CachePage *new_page = js_new<CachePage>();
     if (!i_cache.add(p, page, new_page))
         return nullptr;
     return new_page;
 }
 
 // Flush from start up to and not including start + size.
 static void
-FlushOnePage(Simulator::ICacheMap &i_cache, intptr_t start, int size)
+FlushOnePageLocked(Simulator::ICacheMap &i_cache, intptr_t start, int size)
 {
     MOZ_ASSERT(size <= CachePage::kPageSize);
     MOZ_ASSERT(AllOnOnePage(start, size - 1));
     MOZ_ASSERT((start & CachePage::kLineMask) == 0);
     MOZ_ASSERT((size & CachePage::kLineMask) == 0);
 
     void *page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
     int offset = (start & CachePage::kPageMask);
-    CachePage *cache_page = GetCachePage(i_cache, page);
+    CachePage *cache_page = GetCachePageLocked(i_cache, page);
     char *valid_bytemap = cache_page->validityByte(offset);
     memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
 }
 
 static void
-FlushICache(Simulator::ICacheMap &i_cache, void *start_addr, size_t size)
+FlushICacheLocked(Simulator::ICacheMap &i_cache, void *start_addr, size_t size)
 {
     intptr_t start = reinterpret_cast<intptr_t>(start_addr);
     int intra_line = (start & CachePage::kLineMask);
     start -= intra_line;
     size += intra_line;
     size = ((size - 1) | CachePage::kLineMask) + 1;
     int offset = (start & CachePage::kPageMask);
     while (!AllOnOnePage(start, size - 1)) {
         int bytes_to_flush = CachePage::kPageSize - offset;
-        FlushOnePage(i_cache, start, bytes_to_flush);
+        FlushOnePageLocked(i_cache, start, bytes_to_flush);
         start += bytes_to_flush;
         size -= bytes_to_flush;
         MOZ_ASSERT((start & CachePage::kPageMask) == 0);
         offset = 0;
     }
     if (size != 0)
-        FlushOnePage(i_cache, start, size);
+        FlushOnePageLocked(i_cache, start, size);
 }
 
 static void
-CheckICache(Simulator::ICacheMap &i_cache, SimInstruction *instr)
+CheckICacheLocked(Simulator::ICacheMap &i_cache, SimInstruction *instr)
 {
     intptr_t address = reinterpret_cast<intptr_t>(instr);
     void *page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
     void *line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
     int offset = (address & CachePage::kPageMask);
-    CachePage* cache_page = GetCachePage(i_cache, page);
+    CachePage* cache_page = GetCachePageLocked(i_cache, page);
     char *cache_valid_byte = cache_page->validityByte(offset);
     bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
     char *cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
     if (cache_hit) {
         // Check that the data in memory matches the contents of the I-cache.
         MOZ_ASSERT(memcmp(reinterpret_cast<void*>(instr),
                           cache_page->cachedData(offset),
                           SimInstruction::kInstrSize) == 0);
@@ -1016,19 +1041,23 @@ Simulator::setLastDebuggerInput(char *in
     js_free(lastDebuggerInput_);
     lastDebuggerInput_ = input;
 }
 
 void
 Simulator::FlushICache(void *start_addr, size_t size)
 {
     JitSpewCont(JitSpew_CacheFlush, "[%p %zx]", start_addr, size);
-    if (!Simulator::ICacheCheckingEnabled)
-        return;
-    js::jit::FlushICache(Simulator::Current()->icache(), start_addr, size);
+    if (Simulator::ICacheCheckingEnabled) {
+        Simulator *sim = Simulator::Current();
+
+        AutoLockSimulatorCache als(sim);
+
+        js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
+    }
 }
 
 Simulator::Simulator()
 {
     // Set up simulator support first. Some of this information is needed to
     // setup the architecture state.
 
     // Note, allocation and anything that depends on allocated memory is
@@ -1074,22 +1103,30 @@ Simulator::Simulator()
 
     // The lr and pc are initialized to a known bad value that will cause an
     // access violation if the simulator ever tries to execute it.
     registers_[pc] = bad_lr;
     registers_[lr] = bad_lr;
 
     lastDebuggerInput_ = nullptr;
 
+    cacheLock_ = nullptr;
+#ifdef DEBUG
+    cacheLockHolder_ = nullptr;
+#endif
     redirection_ = nullptr;
 }
 
 bool
 Simulator::init()
 {
+    cacheLock_ = PR_NewLock();
+    if (!cacheLock_)
+        return false;
+
     if (!icache_.init())
         return false;
 
     // Allocate 2MB for the stack. Note that we will only use 1MB, see below.
     static const size_t stackSize = 2 * 1024*1024;
     stack_ = reinterpret_cast<char*>(js_malloc(stackSize));
     if (!stack_)
         return false;
@@ -1111,35 +1148,39 @@ Simulator::init()
 // (because it's x86 code instead of arm code). We do that by redirecting the VM
 // call to a svc (Supervisor Call) instruction that is handled by the
 // simulator. We write the original destination of the jump just at a known
 // offset from the svc instruction so the simulator knows what to call.
 class Redirection
 {
     friend class Simulator;
 
+    // sim's lock must already be held.
     Redirection(void *nativeFunction, ABIFunctionType type, Simulator *sim)
       : nativeFunction_(nativeFunction),
         swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected),
         type_(type),
         next_(nullptr)
     {
         next_ = sim->redirection();
         if (Simulator::ICacheCheckingEnabled)
-            FlushICache(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
+            FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
         sim->setRedirection(this);
     }
 
   public:
     void *addressOfSwiInstruction() { return &swiInstruction_; }
     void *nativeFunction() const { return nativeFunction_; }
     ABIFunctionType type() const { return type_; }
 
     static Redirection *Get(void *nativeFunction, ABIFunctionType type) {
         Simulator *sim = Simulator::Current();
+
+        AutoLockSimulatorCache als(sim);
+
         Redirection *current = sim->redirection();
         for (; current != nullptr; current = current->next_) {
             if (current->nativeFunction_ == nativeFunction) {
                 MOZ_ASSERT(current->type() == type);
                 return current;
             }
         }
 
@@ -1164,16 +1205,17 @@ class Redirection
     uint32_t swiInstruction_;
     ABIFunctionType type_;
     Redirection *next_;
 };
 
 Simulator::~Simulator()
 {
     js_free(stack_);
+    PR_DestroyLock(cacheLock_);
     Redirection *r = redirection_;
     while (r) {
         Redirection *next = r->next_;
         js_delete(r);
         r = next;
     }
 }
 
@@ -4095,18 +4137,20 @@ Simulator::decodeSpecialCondition(SimIns
         MOZ_CRASH();
     }
 }
 
 // Executes the current instruction.
 void
 Simulator::instructionDecode(SimInstruction *instr)
 {
-    if (Simulator::ICacheCheckingEnabled)
-        CheckICache(icache(), instr);
+    if (Simulator::ICacheCheckingEnabled) {
+        AutoLockSimulatorCache als(this);
+        CheckICacheLocked(icache(), instr);
+    }
 
     pc_modified_ = false;
 
     static const uint32_t kSpecialCondition = 15 << 28;
     if (instr->conditionField() == kSpecialCondition) {
         decodeSpecialCondition(instr);
     } else if (conditionallyExecute(instr)) {
         switch (instr->typeValue()) {
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -26,25 +26,28 @@
 // (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 jit_arm_Simulator_arm_h
 #define jit_arm_Simulator_arm_h
 
 #ifdef JS_ARM_SIMULATOR
 
+#include "jslock.h"
+
 #include "jit/arm/Architecture-arm.h"
 #include "jit/IonTypes.h"
 
 namespace js {
 namespace jit {
 
 class Simulator;
 class Redirection;
 class CachePage;
+class AutoLockSimulator;
 
 // When the SingleStepCallback is called, the simulator is about to execute
 // sim->get_pc() and the current machine state represents the completed
 // execution of the previous pc.
 typedef void (*SingleStepCallback)(void *arg, Simulator *sim, void *pc);
 
 // VFP rounding modes. See ARM DDI 0406B Page A2-29.
 enum VFPRoundingMode {
@@ -63,16 +66,17 @@ enum VFPRoundingMode {
 const uint32_t kVFPRoundingModeMask = 3 << 22;
 
 typedef int32_t Instr;
 class SimInstruction;
 
 class Simulator
 {
     friend class Redirection;
+    friend class AutoLockSimulatorCache;
 
   public:
     friend class ArmDebugger;
     enum Register {
         no_reg = -1,
         r0 = 0, r1, r2, r3, r4, r5, r6, r7,
         r8, r9, r10, r11, r12, r13, r14, r15,
         num_registers,
@@ -378,42 +382,55 @@ class Simulator
     StopCountAndDesc watched_stops_[kNumOfWatchedStops];
 
   public:
     int64_t icount() {
         return icount_;
     }
 
   private:
-    Redirection *redirection_;
-
     // ICache checking.
     struct ICacheHasher {
         typedef void *Key;
         typedef void *Lookup;
         static HashNumber hash(const Lookup &l);
         static bool match(const Key &k, const Lookup &l);
     };
 
   public:
     typedef HashMap<void *, CachePage *, ICacheHasher, SystemAllocPolicy> ICacheMap;
 
-  protected:
+  private:
+    // This lock creates a critical section around 'redirection_' and
+    // 'icache_', which are referenced both by the execution engine
+    // and by the off-thread compiler (see Redirection::Get in the cpp file).
+    PRLock *cacheLock_;
+#ifdef DEBUG
+    PRThread *cacheLockHolder_;
+#endif
+
+    Redirection *redirection_;
     ICacheMap icache_;
 
   public:
     ICacheMap &icache() {
+        // Technically we need the lock to access the innards of the
+        // icache, not to take its address, but the latter condition
+        // serves as a useful complement to the former.
+        MOZ_ASSERT(cacheLockHolder_);
         return icache_;
     }
 
     Redirection *redirection() const {
+        MOZ_ASSERT(cacheLockHolder_);
         return redirection_;
     }
 
     void setRedirection(js::jit::Redirection *redirection) {
+        MOZ_ASSERT(cacheLockHolder_);
         redirection_ = redirection;
     }
 };
 
 #define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror)             \
     JS_BEGIN_MACRO                                                              \
         if (cx->runtime()->simulator()->overRecursedWithExtra(extra)) {         \
             js_ReportOverRecursed(cx);                                          \
--- a/js/src/jit/mips/Simulator-mips.cpp
+++ b/js/src/jit/mips/Simulator-mips.cpp
@@ -481,30 +481,55 @@ class CachePage {
     }
 
   private:
     char data_[kPageSize];   // The cached data.
     static const int kValidityMapSize = kPageSize >> kLineShift;
     char validity_map_[kValidityMapSize];  // One byte per line.
 };
 
+// Protects the icache() and redirection() properties of the
+// Simulator.
+class AutoLockSimulatorCache
+{
+  public:
+    explicit AutoLockSimulatorCache(Simulator *sim) : sim_(sim) {
+        PR_Lock(sim_->cacheLock_);
+        MOZ_ASSERT(!sim_->cacheLockHolder_);
+#ifdef DEBUG
+        sim_->cacheLockHolder_ = PR_GetCurrentThread();
+#endif
+    }
+
+    ~AutoLockSimulatorCache() {
+        MOZ_ASSERT(sim_->cacheLockHolder_);
+#ifdef DEBUG
+        sim_->cacheLockHolder_ = nullptr;
+#endif
+        PR_Unlock(sim_->cacheLock_);
+    }
+
+  private:
+    Simulator *const sim_;
+};
+
 bool Simulator::ICacheCheckingEnabled = false;
 
 int Simulator::StopSimAt = -1;
 
 Simulator *
 Simulator::Create()
 {
     Simulator *sim = js_new<Simulator>();
     if (!sim)
         return nullptr;
 
-    if (!sim->icache_.init()) {
+    if (!sim->init()) {
         js_delete(sim);
-        return false;
+        return nullptr;
     }
 
     if (getenv("MIPS_SIM_ICACHE_CHECKS"))
         Simulator::ICacheCheckingEnabled = true;
 
     char *stopAtStr = getenv("MIPS_SIM_STOP_AT");
     int64_t stopAt;
     if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) {
@@ -1112,73 +1137,73 @@ AllOnOnePage(uintptr_t start, int size)
 void
 Simulator::setLastDebuggerInput(char *input)
 {
     js_free(lastDebuggerInput_);
     lastDebuggerInput_ = input;
 }
 
 static CachePage *
-GetCachePage(Simulator::ICacheMap &i_cache, void *page)
+GetCachePageLocked(Simulator::ICacheMap &i_cache, void *page)
 {
     Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
     if (p)
         return p->value();
 
     CachePage *new_page = js_new<CachePage>();
     if (!i_cache.add(p, page, new_page))
         return nullptr;
     return new_page;
 }
 
 // Flush from start up to and not including start + size.
 static void
-FlushOnePage(Simulator::ICacheMap &i_cache, intptr_t start, int size)
+FlushOnePageLocked(Simulator::ICacheMap &i_cache, intptr_t start, int size)
 {
     MOZ_ASSERT(size <= CachePage::kPageSize);
     MOZ_ASSERT(AllOnOnePage(start, size - 1));
     MOZ_ASSERT((start & CachePage::kLineMask) == 0);
     MOZ_ASSERT((size & CachePage::kLineMask) == 0);
     void *page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
     int offset = (start & CachePage::kPageMask);
-    CachePage *cache_page = GetCachePage(i_cache, page);
+    CachePage *cache_page = GetCachePageLocked(i_cache, page);
     char *valid_bytemap = cache_page->validityByte(offset);
     memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
 }
 
 static void
-FlushICache(Simulator::ICacheMap &i_cache, void *start_addr, size_t size)
+FlushICacheLocked(Simulator::ICacheMap &i_cache, void *start_addr, size_t size)
 {
     intptr_t start = reinterpret_cast<intptr_t>(start_addr);
     int intra_line = (start & CachePage::kLineMask);
     start -= intra_line;
     size += intra_line;
     size = ((size - 1) | CachePage::kLineMask) + 1;
     int offset = (start & CachePage::kPageMask);
     while (!AllOnOnePage(start, size - 1)) {
         int bytes_to_flush = CachePage::kPageSize - offset;
-        FlushOnePage(i_cache, start, bytes_to_flush);
+        FlushOnePageLocked(i_cache, start, bytes_to_flush);
         start += bytes_to_flush;
         size -= bytes_to_flush;
         MOZ_ASSERT((start & CachePage::kPageMask) == 0);
         offset = 0;
     }
     if (size != 0) {
-        FlushOnePage(i_cache, start, size);
+        FlushOnePageLocked(i_cache, start, size);
     }
 }
 
 static void
-CheckICache(Simulator::ICacheMap &i_cache, SimInstruction *instr)
+CheckICacheLocked(Simulator::ICacheMap &i_cache, SimInstruction *instr)
 {
     intptr_t address = reinterpret_cast<intptr_t>(instr);
     void *page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
     void *line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
     int offset = (address & CachePage::kPageMask);
-    CachePage *cache_page = GetCachePage(i_cache, page);
+    CachePage *cache_page = GetCachePageLocked(i_cache, page);
     char *cache_valid_byte = cache_page->validityByte(offset);
     bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
     char *cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
     if (cache_hit) {
         // Check that the data in memory matches the contents of the I-cache.
         MOZ_ASSERT(memcmp(reinterpret_cast<void*>(instr),
                           cache_page->cachedData(offset),
                           SimInstruction::kInstrSize) == 0);
@@ -1201,34 +1226,33 @@ Simulator::ICacheHasher::match(const Key
     MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
     MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
     return k == l;
 }
 
 void
 Simulator::FlushICache(void *start_addr, size_t size)
 {
-    js::jit::FlushICache(Simulator::Current()->icache(), start_addr, size);
+    if (Simulator::ICacheCheckingEnabled) {
+        Simulator *sim = Simulator::Current();
+        AutoLockSimulatorCache als(sim);
+        js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
+    }
 }
 
 Simulator::Simulator()
 {
     // Set up simulator support first. Some of this information is needed to
     // setup the architecture state.
 
-    // Allocate 2MB for the stack. Note that we will only use 1MB, see below.
-    static const size_t stackSize = 2 * 1024 * 1024;
-    stack_ = static_cast<char*>(js_malloc(stackSize));
-    if (!stack_) {
-        MOZ_ReportAssertionFailure("[unhandlable oom] Simulator stack", __FILE__, __LINE__);
-        MOZ_CRASH();
-    }
-    // Leave a safety margin of 1MB to prevent overrunning the stack when
-    // pushing values (total stack size is 2MB).
-    stackLimit_ = reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024;
+    // Note, allocation and anything that depends on allocated memory is
+    // deferred until init(), in order to handle OOM properly.
+
+    stack_ = nullptr;
+    stackLimit_ = 0;
     pc_modified_ = false;
     icount_ = 0;
     break_count_ = 0;
     resume_pc_ = 0;
     break_pc_ = nullptr;
     break_instr_ = 0;
 
     // Set up architecture state.
@@ -1236,63 +1260,95 @@ Simulator::Simulator()
     for (int i = 0; i < Register::kNumSimuRegisters; i++) {
         registers_[i] = 0;
     }
     for (int i = 0; i < Simulator::FPURegister::kNumFPURegisters; i++) {
         FPUregisters_[i] = 0;
     }
     FCSR_ = 0;
 
-    // The sp is initialized to point to the bottom (high address) of the
-    // allocated stack area. To be safe in potential stack underflows we leave
-    // some buffer below.
-    registers_[sp] = reinterpret_cast<int32_t>(stack_) + stackSize - 64;
     // The ra and pc are initialized to a known bad value that will cause an
     // access violation if the simulator ever tries to execute it.
     registers_[pc] = bad_ra;
     registers_[ra] = bad_ra;
 
     for (int i = 0; i < kNumExceptions; i++)
         exceptions[i] = 0;
 
     lastDebuggerInput_ = nullptr;
 
+    cacheLock_ = nullptr;
+#ifdef DEBUG
+    cacheLockHolder_ = nullptr;
+#endif
     redirection_ = nullptr;
 }
 
+bool
+Simulator::init()
+{
+    cacheLock_ = PR_NewLock();
+    if (!cacheLock_)
+        return false;
+
+    if (!icache_.init())
+        return false;
+
+    // Allocate 2MB for the stack. Note that we will only use 1MB, see below.
+    static const size_t stackSize = 2 * 1024 * 1024;
+    stack_ = static_cast<char*>(js_malloc(stackSize));
+    if (!stack_)
+        return false;
+
+    // Leave a safety margin of 1MB to prevent overrunning the stack when
+    // pushing values (total stack size is 2MB).
+    stackLimit_ = reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024;
+
+    // The sp is initialized to point to the bottom (high address) of the
+    // allocated stack area. To be safe in potential stack underflows we leave
+    // some buffer below.
+    registers_[sp] = reinterpret_cast<int32_t>(stack_) + stackSize - 64;
+
+    return true;
+}
+
 // When the generated code calls an external reference we need to catch that in
 // the simulator.  The external reference will be a function compiled for the
 // host architecture.  We need to call that function instead of trying to
 // execute it with the simulator.  We do that by redirecting the external
 // reference to a swi (software-interrupt) instruction that is handled by
 // the simulator.  We write the original destination of the jump just at a known
 // offset from the swi instruction so the simulator knows what to call.
 class Redirection
 {
     friend class Simulator;
 
+    // sim's lock must already be held.
     Redirection(void* nativeFunction, ABIFunctionType type, Simulator *sim)
       : nativeFunction_(nativeFunction),
         swiInstruction_(kCallRedirInstr),
         type_(type),
         next_(nullptr)
     {
         next_ = sim->redirection();
 	if (Simulator::ICacheCheckingEnabled)
-	    FlushICache(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
+	    FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
         sim->setRedirection(this);
     }
 
   public:
     void *addressOfSwiInstruction() { return &swiInstruction_; }
     void *nativeFunction() const { return nativeFunction_; }
     ABIFunctionType type() const { return type_; }
 
     static Redirection *Get(void *nativeFunction, ABIFunctionType type) {
         Simulator *sim = Simulator::Current();
+
+        AutoLockSimulatorCache als(sim);
+
         Redirection *current = sim->redirection();
         for (; current != nullptr; current = current->next_) {
             if (current->nativeFunction_ == nativeFunction) {
                 MOZ_ASSERT(current->type() == type);
                 return current;
             }
         }
 
@@ -1317,16 +1373,17 @@ class Redirection
     uint32_t swiInstruction_;
     ABIFunctionType type_;
     Redirection *next_;
 };
 
 Simulator::~Simulator()
 {
     js_free(stack_);
+    PR_DestroyLock(cacheLock_);
     Redirection *r = redirection_;
     while (r) {
         Redirection *next = r->next_;
         js_delete(r);
         r = next;
     }
 }
 
@@ -3208,18 +3265,20 @@ Simulator::decodeTypeJump(SimInstruction
     set_pc(next_pc);
     pc_modified_ = true;
 }
 
 // Executes the current instruction.
 void
 Simulator::instructionDecode(SimInstruction *instr)
 {
-    if (Simulator::ICacheCheckingEnabled)
-        CheckICache(icache(), instr);
+    if (Simulator::ICacheCheckingEnabled) {
+        AutoLockSimulatorCache als(this);
+        CheckICacheLocked(icache(), instr);
+    }
     pc_modified_ = false;
 
     switch (instr->instructionType()) {
       case SimInstruction::kRegisterType:
         decodeTypeRegister(instr);
         break;
       case SimInstruction::kImmediateType:
         decodeTypeImmediate(instr);
--- a/js/src/jit/mips/Simulator-mips.h
+++ b/js/src/jit/mips/Simulator-mips.h
@@ -26,24 +26,27 @@
 // (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 jit_mips_Simulator_mips_h
 #define jit_mips_Simulator_mips_h
 
 #ifdef JS_MIPS_SIMULATOR
 
+#include "jslock.h"
+
 #include "jit/IonTypes.h"
 
 namespace js {
 namespace jit {
 
 class Simulator;
 class Redirection;
 class CachePage;
+class AutoLockSimulator;
 
 const intptr_t kPointerAlignment = 4;
 const intptr_t kPointerAlignmentMask = kPointerAlignment - 1;
 
 const intptr_t kDoubleAlignment = 8;
 const intptr_t kDoubleAlignmentMask = kDoubleAlignment - 1;
 
 
@@ -97,16 +100,17 @@ const uint32_t kMaxStopCode = 127;
 // Utility functions
 
 typedef uint32_t Instr;
 class SimInstruction;
 
 class Simulator {
     friend class Redirection;
     friend class MipsDebugger;
+    friend class AutoLockSimulatorCache;
   public:
 
     // Registers are declared in order. See "See MIPS Run Linux" chapter 2.
     enum Register {
         no_reg = -1,
         zero_reg = 0,
         at,
         v0, v1,
@@ -132,17 +136,19 @@ class Simulator {
     enum FPURegister {
         f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11,
         f12, f13, f14, f15,   // f12 and f14 are arguments FPURegisters.
         f16, f17, f18, f19, f20, f21, f22, f23, f24, f25,
         f26, f27, f28, f29, f30, f31,
         kNumFPURegisters
     };
 
+    // Returns nullptr on OOM.
     static Simulator *Create();
+
     static void Destroy(Simulator *simulator);
 
     // Constructor/destructor are for internal use only; use the static methods above.
     Simulator();
     ~Simulator();
 
     // The currently executing Simulator instance. Potentially there can be one
     // for each native thread.
@@ -219,16 +225,18 @@ class Simulator {
         // the ra is set to this value on transition from native C code to
         // simulated execution, so that the simulator can "return" to the native
         // C code.
         end_sim_pc = -2,
         // Unpredictable value.
         Unpredictable = 0xbadbeaf
     };
 
+    bool init();
+
     // Unsupported instructions use Format to print an error and stop execution.
     void format(SimInstruction* instr, const char* format);
 
     // Read and write memory.
     inline uint32_t readBU(uint32_t addr);
     inline int32_t readB(uint32_t addr);
     inline void writeB(uint32_t addr, uint8_t value);
     inline void writeB(uint32_t addr, int8_t value);
@@ -349,42 +357,55 @@ class Simulator {
     // the breakpoint was hit or gone through.
     struct StopCountAndDesc {
         uint32_t count_;
         char *desc_;
     };
     StopCountAndDesc watchedStops_[kNumOfWatchedStops];
 
   private:
-    Redirection *redirection_;
-
     // ICache checking.
     struct ICacheHasher {
         typedef void *Key;
         typedef void *Lookup;
         static HashNumber hash(const Lookup &l);
         static bool match(const Key &k, const Lookup &l);
     };
 
   public:
     typedef HashMap<void *, CachePage *, ICacheHasher, SystemAllocPolicy> ICacheMap;
 
-  protected:
+  private:
+    // This lock creates a critical section around 'redirection_' and
+    // 'icache_', which are referenced both by the execution engine
+    // and by the off-thread compiler (see Redirection::Get in the cpp file).
+    PRLock *cacheLock_;
+#ifdef DEBUG
+    PRThread *cacheLockHolder_;
+#endif
+
+    Redirection *redirection_;
     ICacheMap icache_;
 
   public:
     ICacheMap &icache() {
+        // Technically we need the lock to access the innards of the
+        // icache, not to take its address, but the latter condition
+        // serves as a useful complement to the former.
+        MOZ_ASSERT(cacheLockHolder_);
         return icache_;
     }
 
     Redirection *redirection() const {
+        MOZ_ASSERT(cacheLockHolder_);
         return redirection_;
     }
 
     void setRedirection(js::jit::Redirection *redirection) {
+        MOZ_ASSERT(cacheLockHolder_);
         redirection_ = redirection;
     }
 };
 
 #define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror)             \
     JS_BEGIN_MACRO                                                              \
         if (cx->mainThread().simulator()->overRecursedWithExtra(extra)) {       \
             js_ReportOverRecursed(cx);                                          \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -453,8 +453,9 @@ MSG_DEF(JSMSG_BAD_PARSE_NODE,          0
 MSG_DEF(JSMSG_BAD_SYMBOL,              1, JSEXN_TYPEERR, "{0} is not a well-known @@-symbol")
 MSG_DEF(JSMSG_SYMBOL_TO_STRING,        0, JSEXN_TYPEERR, "can't convert symbol to string")
 MSG_DEF(JSMSG_SYMBOL_TO_NUMBER,        0, JSEXN_TYPEERR, "can't convert symbol to number")
 
 // Atomics and futexes
 MSG_DEF(JSMSG_ATOMICS_NOT_INSTALLED,     0, JSEXN_ERR, "futex support is not installed")
 MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY,         0, JSEXN_TYPEERR, "invalid array type for the operation")
 MSG_DEF(JSMSG_ATOMICS_TOO_LONG,          0, JSEXN_RANGEERR, "timeout value too large")
+MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED,  0, JSEXN_ERR, "waiting is not allowed on this thread")
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -614,25 +614,23 @@ class PerRuntimeFutexAPI
     virtual ~PerRuntimeFutexAPI() {}
 
     // Acquire the GLOBAL lock for all futex resources in all domains.
     virtual void lock() = 0;
 
     // Release the GLOBAL lock.
     virtual void unlock() = 0;
 
-    // Return true iff the calling thread is a worker thread.  This must be
-    // used to guard calls to wait().  The lock need not be held.
-    virtual bool isOnWorkerThread() = 0;
-
     enum WakeResult {
         Woken,                  // Woken by futexWait
         Timedout,               // Woken by timeout
-        InterruptForTerminate,  // Woken by a request to terminate the worker
-        ErrorTooLong            // Implementation constraint on the timer (for now)
+        ErrorException,         // Propagate a pending exception
+        InterruptForTerminate,  // Woken by a request to terminate the worker, throw an uncatchable
+        WaitingNotAllowed,      // wait() was not allowed to block on this thread (permanently)
+        ErrorTooLong            // Implementation limit
     };
 
     // Block the thread.
     //
     // The lock must be held around this call, see lock() and unlock().
     virtual WakeResult wait(double timeout_ns) = 0;
 
     // Wake the thread represented by this PerRuntimeFutexAPI.
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -831,30 +831,30 @@ json_parse(JSContext *cx, unsigned argc,
 
     /* Step 1. */
     JSString *str = (args.length() >= 1)
                     ? ToString<CanGC>(cx, args[0])
                     : cx->names().undefined;
     if (!str)
         return false;
 
-    JSFlatString *flat = str->ensureFlat(cx);
-    if (!flat)
+    JSLinearString *linear = str->ensureLinear(cx);
+    if (!linear)
         return false;
 
-    AutoStableStringChars flatChars(cx);
-    if (!flatChars.init(cx, flat))
+    AutoStableStringChars linearChars(cx);
+    if (!linearChars.init(cx, linear))
         return false;
 
-    RootedValue reviver(cx, args.get(1));
+    HandleValue reviver = args.get(1);
 
     /* Steps 2-5. */
-    return flatChars.isLatin1()
-           ? ParseJSONWithReviver(cx, flatChars.latin1Range(), reviver, args.rval())
-           : ParseJSONWithReviver(cx, flatChars.twoByteRange(), reviver, args.rval());
+    return linearChars.isLatin1()
+           ? ParseJSONWithReviver(cx, linearChars.latin1Range(), reviver, args.rval())
+           : ParseJSONWithReviver(cx, linearChars.twoByteRange(), reviver, args.rval());
 }
 
 /* ES5 15.12.3. */
 bool
 json_stringify(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject replacer(cx, args.get(1).isObject() ? &args[1].toObject() : nullptr);
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3532,28 +3532,28 @@ reflect_parse(JSContext *cx, uint32_t ar
         }
     }
 
     /* Extract the builder methods first to report errors before parsing. */
     ASTSerializer serialize(cx, loc, filename, lineno);
     if (!serialize.init(builder))
         return false;
 
-    JSFlatString *flat = src->ensureFlat(cx);
-    if (!flat)
+    JSLinearString *linear = src->ensureLinear(cx);
+    if (!linear)
         return false;
 
-    AutoStableStringChars flatChars(cx);
-    if (!flatChars.initTwoByte(cx, flat))
+    AutoStableStringChars linearChars(cx);
+    if (!linearChars.initTwoByte(cx, linear))
         return false;
 
     CompileOptions options(cx);
     options.setFileAndLine(filename, lineno);
     options.setCanLazilyParse(false);
-    mozilla::Range<const char16_t> chars = flatChars.twoByteRange();
+    mozilla::Range<const char16_t> chars = linearChars.twoByteRange();
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars.start().get(),
                                     chars.length(), /* foldConstants = */ false, nullptr, nullptr);
     if (!parser.checkOptions())
         return false;
 
     serialize.setParser(&parser);
 
     ParseNode *pn = parser.parse(nullptr);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3060,42 +3060,42 @@ CopySubstringsToFatInline(JSFatInlineStr
         pos += ranges[i].length;
     }
 
     MOZ_ASSERT(pos == outputLen);
     buf[outputLen] = 0;
 }
 
 static inline JSFatInlineString *
-FlattenSubstrings(JSContext *cx, Handle<JSFlatString*> flatStr, const StringRange *ranges,
+FlattenSubstrings(JSContext *cx, HandleLinearString str, const StringRange *ranges,
                   size_t rangesLen, size_t outputLen)
 {
-    JSFatInlineString *str = NewGCFatInlineString<CanGC>(cx);
-    if (!str)
+    JSFatInlineString *result = NewGCFatInlineString<CanGC>(cx);
+    if (!result)
         return nullptr;
 
     AutoCheckCannotGC nogc;
-    if (flatStr->hasLatin1Chars())
-        CopySubstringsToFatInline(str, flatStr->latin1Chars(nogc), ranges, rangesLen, outputLen);
+    if (str->hasLatin1Chars())
+        CopySubstringsToFatInline(result, str->latin1Chars(nogc), ranges, rangesLen, outputLen);
     else
-        CopySubstringsToFatInline(str, flatStr->twoByteChars(nogc), ranges, rangesLen, outputLen);
-    return str;
+        CopySubstringsToFatInline(result, str->twoByteChars(nogc), ranges, rangesLen, outputLen);
+    return result;
 }
 
 static JSString *
-AppendSubstrings(JSContext *cx, Handle<JSFlatString*> flatStr,
-                 const StringRange *ranges, size_t rangesLen)
+AppendSubstrings(JSContext *cx, HandleLinearString str, const StringRange *ranges,
+                 size_t rangesLen)
 {
     MOZ_ASSERT(rangesLen);
 
     /* For single substrings, construct a dependent string. */
     if (rangesLen == 1)
-        return NewDependentString(cx, flatStr, ranges[0].start, ranges[0].length);
-
-    bool isLatin1 = flatStr->hasLatin1Chars();
+        return NewDependentString(cx, str, ranges[0].start, ranges[0].length);
+
+    bool isLatin1 = str->hasLatin1Chars();
     uint32_t fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
     if (isLatin1)
         fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_LATIN1;
 
     /* Collect substrings into a rope */
     size_t i = 0;
     RopeBuilder rope(cx);
     RootedString part(cx, nullptr);
@@ -3108,20 +3108,20 @@ AppendSubstrings(JSContext *cx, Handle<J
             if (substrLen + ranges[end].length > fatInlineMaxLength)
                 break;
             substrLen += ranges[end].length;
         }
 
         if (i == end) {
             /* Not even one range fits JSFatInlineString, use DependentString */
             const StringRange &sr = ranges[i++];
-            part = NewDependentString(cx, flatStr, sr.start, sr.length);
+            part = NewDependentString(cx, str, sr.start, sr.length);
         } else {
             /* Copy the ranges (linearly) into a JSFatInlineString */
-            part = FlattenSubstrings(cx, flatStr, ranges + i, end - i, substrLen);
+            part = FlattenSubstrings(cx, str, ranges + i, end - i, substrLen);
             i = end;
         }
 
         if (!part)
             return nullptr;
 
         /* Appending to the rope permanently roots the substring. */
         if (!rope.append(part))
@@ -3129,35 +3129,35 @@ AppendSubstrings(JSContext *cx, Handle<J
     }
 
     return rope.result();
 }
 
 static bool
 StrReplaceRegexpRemove(JSContext *cx, HandleString str, RegExpShared &re, MutableHandleValue rval)
 {
-    Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
-    if (!flatStr)
+    RootedLinearString linearStr(cx, str->ensureLinear(cx));
+    if (!linearStr)
         return false;
 
     Vector<StringRange, 16, SystemAllocPolicy> ranges;
 
-    size_t charsLen = flatStr->length();
+    size_t charsLen = linearStr->length();
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     size_t startIndex = 0; /* Index used for iterating through the string. */
     size_t lastIndex = 0;  /* Index after last successful match. */
     size_t lazyIndex = 0;  /* Index before last successful match. */
 
     /* Accumulate StringRanges for unmatched substrings. */
     while (startIndex <= charsLen) {
         if (!CheckForInterrupt(cx))
             return false;
 
-        RegExpRunStatus status = re.execute(cx, flatStr, startIndex, &matches);
+        RegExpRunStatus status = re.execute(cx, linearStr, startIndex, &matches);
         if (status == RegExpRunStatus_Error)
             return false;
         if (status == RegExpRunStatus_Success_NotFound)
             break;
         MatchPair &match = matches[0];
 
         /* Include the latest unmatched substring. */
         if (size_t(match.start) > lastIndex) {
@@ -3178,42 +3178,42 @@ StrReplaceRegexpRemove(JSContext *cx, Ha
     RegExpStatics *res;
 
     /* If unmatched, return the input string. */
     if (!lastIndex) {
         if (startIndex > 0) {
             res = cx->global()->getRegExpStatics(cx);
             if (!res)
                 return false;
-            res->updateLazily(cx, flatStr, &re, lazyIndex);
+            res->updateLazily(cx, linearStr, &re, lazyIndex);
         }
         rval.setString(str);
         return true;
     }
 
     /* The last successful match updates the RegExpStatics. */
     res = cx->global()->getRegExpStatics(cx);
     if (!res)
         return false;
 
-    res->updateLazily(cx, flatStr, &re, lazyIndex);
+    res->updateLazily(cx, linearStr, &re, lazyIndex);
 
     /* Include any remaining part of the string. */
     if (lastIndex < charsLen) {
         if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex)))
             return false;
     }
 
     /* Handle the empty string before calling .begin(). */
     if (ranges.empty()) {
         rval.setString(cx->runtime()->emptyString);
         return true;
     }
 
-    JSString *result = AppendSubstrings(cx, flatStr, ranges.begin(), ranges.length());
+    JSString *result = AppendSubstrings(cx, linearStr, ranges.begin(), ranges.length());
     if (!result)
         return false;
 
     rval.setString(result);
     return true;
 }
 
 static inline bool
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -15,16 +15,17 @@
 #include "jswrapper.h"
 
 #include "vm/GlobalObject.h"
 #include "vm/WeakMapObject.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/Interpreter-inl.h"
+#include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 WeakMapBase::WeakMapBase(JSObject *memOf, JSCompartment *c)
   : memberOf(memOf),
     compartment(c),
     next(WeakMapNotInList),
@@ -521,17 +522,22 @@ JS::SetWeakMapEntry(JSContext *cx, Handl
 static bool
 WeakMap_construct(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx, NewBuiltinClassInstance(cx, &WeakMapObject::class_));
     if (!obj)
         return false;
 
-    // ES6 23.3.1.1 steps 5-6, 11.
+    // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1.
+    // FIXME: bug 1083752
+    if (!WarnIfNotConstructing(cx, args, "WeakMap"))
+        return false;
+
+    // Steps 5-6, 11.
     if (!args.get(0).isNullOrUndefined()) {
         // Steps 7a-b.
         RootedValue adderVal(cx);
         if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal))
             return false;
 
         // Step 7c.
         if (!IsCallable(adderVal))
--- a/js/src/tests/ecma_6/shell.js
+++ b/js/src/tests/ecma_6/shell.js
@@ -145,18 +145,18 @@ if (typeof assertDeepEq === 'undefined')
                         if (Object_hasOwnProperty(db, "value"))
                             throw Error_("got accessor property, expected data property" + pmsg);
                         check(da.get, db.get, at(pmsg, ".[[Get]]"));
                         check(da.set, db.set, at(pmsg, ".[[Set]]"));
                     }
                 }
             };
 
-            var ab = Map_();
-            var bpath = Map_();
+            var ab = new Map_();
+            var bpath = new Map_();
 
             function check(a, b, path) {
                 if (typeof a === "symbol") {
                     // Symbols are primitives, but they have identity.
                     // Symbol("x") !== Symbol("x") but
                     // assertDeepEq(Symbol("x"), Symbol("x")) should pass.
                     if (typeof b !== "symbol") {
                         throw Error_("got " + uneval_(a) + ", expected " + uneval_(b) + " " + path);
--- a/js/src/tests/ecma_7/SIMD/select-bitselect.js
+++ b/js/src/tests/ecma_7/SIMD/select-bitselect.js
@@ -123,14 +123,14 @@ function test() {
         [float64x2(1,-2), float64x2(13.37,3.13)],
         [float64x2(1.5,2.75), float64x2(NaN,Infinity)],
         [float64x2(-NaN,-Infinity), float64x2(9.75,16.125)]
     ];
 
     testSelect(float64x2, inputs);
     testBitSelectSimple(float64x2, inputs);
     testBitSelectComplex(float64x2, inputs);
-
-    if (typeof reportCompare === "function")
-        reportCompare(true, true);
 }
 
-test();
+// TODO temporarily disabled because of bug 1123404
+//test();
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/js1_8_5/extensions/findReferences-01.js
+++ b/js/src/tests/js1_8_5/extensions/findReferences-01.js
@@ -6,17 +6,17 @@ if (typeof findReferences == "function")
     function C() {}
     var o = new C;
     o.x = {};               // via ordinary property
     o[42] = {};             // via numeric property
     o[123456789] = {};      // via ridiculous numeric property
     o.myself = o;           // self-references should be reported
     o.alsoMyself = o;       // multiple self-references should all be reported
 
-    assertEq(referencesVia(o, 'type; type_proto', C.prototype), true);
+    assertEq(referencesVia(o, 'group; group_proto', C.prototype), true);
     assertEq(referencesVia(o, 'shape; base; parent', this), true);
     assertEq(referencesVia(o, 'x', o.x), true);
     assertEq(referencesVia(o, 'objectElements[42]', o[42]), true);
     assertEq(referencesVia(o, '123456789', o[123456789]), true);
     assertEq(referencesVia(o, 'myself', o), true);
     assertEq(referencesVia(o, 'alsoMyself', o), true);
 
     function g() { return 42; }
--- a/js/src/tests/js1_8_5/extensions/weakmap.js
+++ b/js/src/tests/js1_8_5/extensions/weakmap.js
@@ -75,17 +75,17 @@ function test()
         } catch (x) {
             thrown = true;
         }
 
         check(function() thrown, todo);
     }
 
     var key = {};
-    var map = WeakMap();
+    var map = new WeakMap();
 
     check(function() !map.has(key));
     map.set(key, 42);
     check(function() map.get(key) == 42);
     check(function() typeof map.get({}) == "undefined");
     check(function() map.get({}, "foo") == "foo");
 
     gc(); gc(); gc();
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6011,18 +6011,18 @@ DebuggerGenericEval(JSContext *cx, const
     MOZ_ASSERT_IF(!iter, scope && scope->is<GlobalObject>());
 
     /* Check the first argument, the eval code string. */
     if (!code.isString()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
                              fullMethodName, "string", InformalValueTypeName(code));
         return false;
     }
-    Rooted<JSFlatString *> flat(cx, code.toString()->ensureFlat(cx));
-    if (!flat)
+    RootedLinearString linear(cx, code.toString()->ensureLinear(cx));
+    if (!linear)
         return false;
 
     /*
      * Gather keys and values of bindings, if any. This must be done in the
      * debugger compartment, since that is where any exceptions must be
      * thrown.
      */
     AutoIdVector keys(cx);
@@ -6123,17 +6123,17 @@ DebuggerGenericEval(JSContext *cx, const
         env = nenv;
     }
 
     /* Run the code and produce the completion value. */
     RootedValue rval(cx);
     AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
     jsbytecode *pc = iter ? iter->pc() : nullptr;
     AutoStableStringChars stableChars(cx);
-    if (!stableChars.initTwoByte(cx, flat))
+    if (!stableChars.initTwoByte(cx, linear))
         return false;
 
     mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
     bool ok = EvaluateInEnv(cx, env, thisv, frame, pc, chars, url ? url : "debugger eval code",
                             lineNumber, &rval);
     return dbg->receiveCompletionValue(ac, ok, rval, vp);
 }
 
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2214,16 +2214,63 @@ js::NativeSetElement(JSContext *cx, Hand
                      MutableHandleValue vp, bool strict)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return NativeSetProperty(cx, obj, receiver, id, Qualified, vp, strict);
 }
 
+/*** [[Delete]] **********************************************************************************/
+
+// ES6 draft rev31 9.1.10 [[Delete]]
+bool
+js::NativeDeleteProperty(JSContext *cx, HandleNativeObject obj, HandleId id, bool *succeeded)
+{
+    // Steps 2-3.
+    RootedShape shape(cx);
+    if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
+        return false;
+
+    // Step 4.
+    if (!shape) {
+        // If no property call the class's delProperty hook, passing succeeded
+        // as the result parameter. This always succeeds when there is no hook.
+        return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
+    }
+
+    cx->runtime()->gc.poke();
+
+    // Step 6. Non-configurable property.
+    if (GetShapeAttributes(obj, shape) & JSPROP_PERMANENT) {
+        *succeeded = false;
+        return true;
+    }
+
+    if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
+        return false;
+    if (!*succeeded)
+        return true;
+
+    // Step 5.
+    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        // Typed array elements are non-configurable.
+        MOZ_ASSERT(!IsAnyTypedArray(obj));
+
+        if (!obj->maybeCopyElementsForWrite(cx))
+            return false;
+
+        obj->setDenseElementHole(cx, JSID_TO_INT(id));
+    } else {
+        if (!obj->removeProperty(cx, id))
+            return false;
+    }
+
+    return SuppressDeletedProperty(cx, obj, id);
+}
 
 /* * */
 
 bool
 js::NativeSetPropertyAttributes(JSContext *cx, HandleNativeObject obj, HandleId id,
                                 unsigned *attrsp)
 {
     RootedObject nobj(cx);
@@ -2248,59 +2295,8 @@ js::NativeSetPropertyAttributes(JSContex
             return false;
         if (*attrsp & JSPROP_READONLY)
             types::MarkTypePropertyNonWritable(cx, nobj, id);
         return true;
     } else {
         return SetPropertyAttributes(cx, nobj, id, attrsp);
     }
 }
-
-bool
-js::NativeDeleteProperty(JSContext *cx, HandleNativeObject obj, HandleId id, bool *succeeded)
-{
-    RootedObject proto(cx);
-    RootedShape shape(cx);
-    if (!NativeLookupProperty<CanGC>(cx, obj, id, &proto, &shape))
-        return false;
-    if (!shape || proto != obj) {
-        /*
-         * If no property, or the property comes from a prototype, call the
-         * class's delProperty hook, passing succeeded as the result parameter.
-         */
-        return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
-    }
-
-    cx->runtime()->gc.poke();
-
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        if (IsAnyTypedArray(obj)) {
-            // Don't delete elements from typed arrays.
-            *succeeded = false;
-            return true;
-        }
-
-        if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
-            return false;
-        if (!*succeeded)
-            return true;
-
-        NativeObject *nobj = &obj->as<NativeObject>();
-        if (!nobj->maybeCopyElementsForWrite(cx))
-            return false;
-
-        nobj->setDenseElementHole(cx, JSID_TO_INT(id));
-        return SuppressDeletedProperty(cx, obj, id);
-    }
-
-    if (!shape->configurable()) {
-        *succeeded = false;
-        return true;
-    }
-
-    RootedId propid(cx, shape->propid());
-    if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded))
-        return false;
-    if (!*succeeded)
-        return true;
-
-    return obj->removeProperty(cx, id) && SuppressDeletedProperty(cx, obj, id);
-}
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 232;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 233;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 367,
+static_assert(JSErr_Limit == 368,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext *cx)
--- a/js/xpconnect/idl/nsIXPConnect.idl
+++ b/js/xpconnect/idl/nsIXPConnect.idl
@@ -262,17 +262,17 @@ interface nsIXPCFunctionThisTranslator :
 %{ C++
 // For use with the service manager
 // {CB6593E0-F9B2-11d2-BDD6-000064657374}
 #define NS_XPCONNECT_CID \
 { 0xcb6593e0, 0xf9b2, 0x11d2, \
     { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
 %}
 
-[noscript, uuid(a632b020-331f-11e4-8c21-0800200c9a66)]
+[noscript, uuid(f0966cb3-9171-4dc6-a446-37ea16611fb0)]
 interface nsIXPConnect : nsISupports
 {
 %{ C++
   NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
 %}
 
     /**
      * Creates a new global object using the given aCOMObj as the global
@@ -540,23 +540,16 @@ interface nsIXPConnect : nsISupports
      */
     virtual nsIPrincipal* GetPrincipal(JSObject* obj,
                                        bool allowShortCircuit) const = 0;
     virtual char* DebugPrintJSStack(bool showArgs,
                                     bool showLocals,
                                     bool showThisProps) = 0;
 %}
 
-    /**
-     * Creates a JS object holder around aObject that will hold the object
-     * alive for as long as the holder stays alive.
-     */
-    nsIXPConnectJSObjectHolder holdObject(in JSContextPtr aJSContext,
-                                          in JSObjectPtr aObject);
-
     [noscript] void writeScript(in nsIObjectOutputStream aStream,
                                 in JSContextPtr aJSContext,
                                 in JSScriptPtr aJSScript);
 
     [noscript] JSScriptPtr readScript(in nsIObjectInputStream aStream,
                                       in JSContextPtr aJSContext);
 
     [noscript] void writeFunction(in nsIObjectOutputStream aStream,
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1189,28 +1189,16 @@ nsXPConnect::GetPrincipal(JSObject* obj,
                 return result;
             }
         }
     }
 
     return nullptr;
 }
 
-NS_IMETHODIMP
-nsXPConnect::HoldObject(JSContext *aJSContext, JSObject *aObjectArg,
-                        nsIXPConnectJSObjectHolder **aHolder)
-{
-    RootedObject aObject(aJSContext, aObjectArg);
-    if (!aObject)
-        return NS_ERROR_FAILURE;
-    nsRefPtr<XPCJSObjectHolder> objHolder = new XPCJSObjectHolder(aObject);
-    objHolder.forget(aHolder);
-    return NS_OK;
-}
-
 namespace xpc {
 
 bool
 Base64Encode(JSContext *cx, HandleValue val, MutableHandleValue out)
 {
     MOZ_ASSERT(cx);
 
     nsAutoCString encodedString;
--- a/js/xpconnect/tests/chrome/test_getweakmapkeys.xul
+++ b/js/xpconnect/tests/chrome/test_getweakmapkeys.xul
@@ -25,22 +25,22 @@ https://bugzilla.mozilla.org/show_bug.cg
   is(Cu.nondeterministicGetWeakMapKeys(11), undefined,
     "nondeterministicGetWeakMapKeys should return undefined for non-objects");
   is(Cu.nondeterministicGetWeakMapKeys({}), undefined,
     "nondeterministicGetWeakMapKeys should return undefined for non-weakmap objects");
   is(Cu.nondeterministicGetWeakMapKeys(null), undefined,
     "nondeterministicGetWeakMapKeys should return undefined for null");
 
   /* return an empty array for an empty WeakMap */
-  let mempty = WeakMap();
+  let mempty = new WeakMap();
   is(Cu.nondeterministicGetWeakMapKeys(mempty).length, 0,
     "nondeterministicGetWeakMapKeys should return empty array for empty weakmap");
 
   /* Test freeing/nonfreeing. */
-  let m = WeakMap();
+  let m = new WeakMap();
   let liveKeys = new Array();
 
   let add_elements = function () {
     let k1 = {};
     m.set(k1, "live1");
     liveKeys.push(k1);
 
     let k2 = {};
--- a/js/xpconnect/tests/chrome/test_weakmap_keys_preserved.xul
+++ b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved.xul
@@ -20,17 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   /** Test for Bug 673468 **/
 
   let Cc = Components.classes;
   let Cu = Components.utils;
   let Ci = Components.interfaces;
 
   let system = Cc["@mozilla.org/systemprincipal;1"].createInstance();
   let sandbox = Cu.Sandbox(system);
-  let map = sandbox.WeakMap();
+  let map = new sandbox.WeakMap();
   let obj = {};
   map.set(obj, {});
 
   Cu.forceGC();
 
   ok(map.has(obj), "Weakmap still contains our wrapper!");
   ]]>
   </script>
--- a/js/xpconnect/tests/mochitest/test_bug655297-1.html
+++ b/js/xpconnect/tests/mochitest/test_bug655297-1.html
@@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
     <form>0</form> <form>1</form> <form>2</form> <form>3</form> <form>4</form>
     <form>5</form> <form>6</form> <form>7</form> <form>8</form> <form>9</form>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 655297 **/
 
-var map = WeakMap();
+var map = new WeakMap();
 function f() {
     var paras = document.getElementsByTagName("form");
     for (var i = 0; i < paras.length; i++)
         map.set(paras[i], "ok");
 }
 function g() {
     var paras = document.getElementsByTagName("form");
     for (var i = 0; i < paras.length; i++) {
--- a/js/xpconnect/tests/mochitest/test_bug655297-2.html
+++ b/js/xpconnect/tests/mochitest/test_bug655297-2.html
@@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
     <p>0</p> <p>1</p> <p>2</p> <p>3</p> <p>4</p>
     <p>5</p> <p>6</p> <p>7</p> <p>8</p> <p>9</p>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 655297 **/
 
-var map = WeakMap();
+var map = new WeakMap();
 function f() {
     var paras = document.getElementsByTagName("p");
     for (var i = 0; i < paras.length; i++)
         map.set(paras[i], "ok");
 }
 function g() {
     var paras = document.getElementsByTagName("p");
     for (var i = 0; i < paras.length; i++) {
--- a/js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html
+++ b/js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html
@@ -4,17 +4,17 @@
   <title>Test Cross-Compartment DOM WeakMaps</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <script type="application/javascript">
 
-var my_map = WeakMap();
+var my_map = new WeakMap();
 
 function setup() {
   var item = window.frames[0].document.querySelector("body");
 
   my_map.set(item, "success_string");
 
   var rule_fail = false;
   try {
--- a/js/xpconnect/tests/unit/test_bug856067.js
+++ b/js/xpconnect/tests/unit/test_bug856067.js
@@ -1,10 +1,10 @@
 const Cu = Components.utils;
 
 function run_test() {
   var sb = new Cu.Sandbox('http://www.example.com');
-  let w = Cu.evalInSandbox('var w = WeakMap(); w.__proto__ = new Set(); w.foopy = 12; w', sb);
+  let w = Cu.evalInSandbox('var w = new WeakMap(); w.__proto__ = new Set(); w.foopy = 12; w', sb);
   do_check_eq(Object.getPrototypeOf(w), sb.Object.prototype);
   do_check_eq(Object.getOwnPropertyNames(w).length, 0);
   do_check_eq(w.wrappedJSObject.foopy, 12);
   do_check_eq(w.foopy, undefined);
 }
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -423,16 +423,20 @@ nsBlockReflowContext::PlaceBlock(const n
                    mICoord, mBCoord - backupContainingBlockAdvance,
                    mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode),
                    mContainerWidth);
 
   WritingMode frameWM = mFrame->GetWritingMode();
   LogicalPoint logPos =
     LogicalPoint(mWritingMode, mICoord, mBCoord).
       ConvertTo(frameWM, mWritingMode, mContainerWidth - mMetrics.Width());
+
+  // ApplyRelativePositioning in right-to-left writing modes needs to
+  // know the updated frame width
+  mFrame->SetSize(mWritingMode, mMetrics.Size(mWritingMode));
   aReflowState.ApplyRelativePositioning(&logPos, mContainerWidth);
 
   // Now place the frame and complete the reflow process
   nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics,
                                       &aReflowState, frameWM, logPos,
                                       mContainerWidth, 0);
 
   aOverflowAreas = mMetrics.mOverflowAreas + mFrame->GetPosition();
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -3671,32 +3671,33 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
                                aContentBoxMainSize,
                                contentBoxCrossSize);
       // Adjust physicalPosn to be relative to the container's border-box
       // (i.e. its frame rect), instead of the container's content-box:
       physicalPosn += containerContentBoxOrigin;
 
       //XXX Can we calculate the logical position more directly instead
       //    of this double conversion?
+      nsSize finalFlexedPhysicalSize =
+        aAxisTracker.PhysicalSizeFromLogicalSizes(item->GetMainSize(),
+                                                  item->GetCrossSize());
       LogicalPoint framePos(outerWM, physicalPosn,
-                            containerWidth - item->Frame()->GetRect().width);
+                            containerWidth - finalFlexedPhysicalSize.width -
+                              item->GetBorderPadding().LeftRight());
 
       // (Intentionally snapshotting this before ApplyRelativePositioning, to
       // maybe use for setting the flex container's baseline.)
       const nscoord itemNormalBPos = framePos.B(outerWM);
 
       // Check if we actually need to reflow the item -- if we already reflowed
       // it with the right size, we can just reposition it as-needed.
       bool itemNeedsReflow = true; // (Start out assuming the worst.)
       if (item->HadMeasuringReflow()) {
         // We've already reflowed the child once. Was the size we gave it in
         // that reflow the same as its final (post-flexing/stretching) size?
-        nsSize finalFlexedPhysicalSize =
-          aAxisTracker.PhysicalSizeFromLogicalSizes(item->GetMainSize(),
-                                                    item->GetCrossSize());
         if (item->Frame()->GetSize() == finalFlexedPhysicalSize) {
           // It has the correct size --> no need to reflow! Just make sure it's
           // at the right position.
           itemNeedsReflow = false;
           MoveFlexItemToFinalPosition(aReflowState, *item, framePos,
                                       containerWidth);
         }
       }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1121748-1-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Bug 1121748-1</title>
+  <meta charset=utf-8>
+  <style>
+    html { width: 25%; }
+    ul {
+      margin: 0;
+      padding: 0;
+      border: 1px solid green;
+    }
+    ul li {
+      position: relative;
+      margin: 2px 10px;
+      background: #eee;
+      display: block;
+    }
+  </style>
+  <script>
+  function test() {
+    document.body.dir = 'rtl';
+  }
+  </script>
+</head>
+<body onload="test()">
+  <ul>
+    <li> item #1 </li>
+    <li> item #2 </li>
+    <li> item #3 </li>
+  </ul>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1121748-1.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Bug 1121748-1</title>
+  <meta charset=utf-8>
+  <style>
+    html { width: 25%; }
+    ul {
+      margin: 0;
+      padding: 0;
+      border: 1px solid green;
+    }
+    ul li {
+      position: relative;
+      margin: 2px 10px;
+      background: #eee;
+      display: block;
+    }
+    ul li[hidden] {
+      display: none;
+    }
+  </style>
+  <script>
+  function test() {
+    document.body.dir = 'rtl';
+    document.querySelector('#test').offsetHeight;
+    document.querySelector('#test').hidden = false;
+  }
+  </script>
+</head>
+<body onload="test()">
+  <ul>
+    <li id="test" hidden> item #1 </li>
+    <li> item #2 </li>
+    <li> item #3 </li>
+  </ul>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1121748-2-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body {
+  border: 1px solid gray;
+}
+img {
+  direction: rtl;
+}
+.statusbar {
+  height: 26px;
+  background: #eef;
+}
+#test1 {
+  border: 1px solid red;
+}
+#test2 {
+  border: 1px solid green;
+}
+</style>
+</head>
+<body onload="test1()">
+<div>
+ <div class="statusbar">
+  <img src="bar" id="test2"><img src="foo" id="test1">
+ </div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1121748-2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body {
+  border: 1px solid gray;
+}
+.statusbar {
+  height: 26px;
+  background: #eef;
+  display: flex;
+  justify-content: flex-end;
+}
+#test1 {
+  border: 1px solid red;
+}
+#test2 {
+  border: 1px solid green;
+}
+</style>
+<script>
+function test1() {
+  document.querySelector('#test1').hidden = false;
+  document.querySelector('#test2').hidden = false;
+}
+</script>
+</head>
+<body onload="test1()">
+<div dir="rtl">
+ <div class="statusbar">
+  <img src="foo" id="test1" hidden>
+  <img src="bar" id="test2" hidden>
+ </div>
+</div>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1853,12 +1853,14 @@ fuzzy-if(B2G,128,75) == 1081185-1.html 1
 == 1103258-1.html 1103258-1-ref.html # assertion crash test with layers culling test
 == 1105137-1.html 1105137-1-ref.html
 fuzzy-if(d2d,36,304) HTTP(..) == 1116480-1-fakeitalic-overflow.html 1116480-1-fakeitalic-overflow-ref.html
 == 1111753-1.html about:blank
 == 1119117-1a.html 1119117-1-ref.html
 == 1119117-1b.html 1119117-1-ref.html
 == 1120431-1.html 1120431-1-ref.html
 == 1120431-2.html 1120431-2-ref.html
+== 1121748-1.html 1121748-1-ref.html
+== 1121748-2.html 1121748-2-ref.html
 == 1127107-1a-nowrap.html 1127107-1-ref.html
 == 1127107-1b-pre.html 1127107-1-ref.html
 == 1127107-2-capitalize.html 1127107-2-capitalize-ref.html
 == 1127679-1a-inline-flex-relpos.html 1127679-1b-inline-flex-relpos.html
new file mode 100644
--- /dev/null
+++ b/mfbt/IntegerRange.h
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+/* Iterator over ranges of integers */
+
+#ifndef mozilla_IntegerRange_h
+#define mozilla_IntegerRange_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/ReverseIterator.h"
+
+namespace mozilla {
+
+namespace detail {
+
+template<typename IntTypeT>
+class IntegerIterator
+{
+public:
+  typedef const IntTypeT ValueType;
+  typedef typename MakeSigned<IntTypeT>::Type DifferenceType;
+
+  template<typename IntType>
+  explicit IntegerIterator(IntType aCurrent)
+    : mCurrent(aCurrent) { }
+
+  template<typename IntType>
+  IntegerIterator(const IntegerIterator<IntType>& aOther)
+    : mCurrent(aOther.mCurrent) { }
+
+  // Since operator* is required to return a reference, we return
+  // the reference of our member here.
+  const IntTypeT& operator*() const { return mCurrent; }
+
+  /* Increments and descrements operators */
+
+  IntegerIterator& operator++() { ++mCurrent; return *this; }
+  IntegerIterator& operator--() { --mCurrent; return *this; }
+  IntegerIterator operator++(int) { auto ret = *this; ++mCurrent; return ret; }
+  IntegerIterator operator--(int) { auto ret = *this; --mCurrent; return ret; }
+
+  IntegerIterator operator+(DifferenceType aN) const
+  {
+    return IntegerIterator(mCurrent + aN);
+  }
+  IntegerIterator operator-(DifferenceType aN) const
+  {
+    return IntegerIterator(mCurrent - aN);
+  }
+  IntegerIterator& operator+=(DifferenceType aN)
+  {
+    mCurrent += aN;
+    return *this;
+  }
+  IntegerIterator& operator-=(DifferenceType aN)
+  {
+    mCurrent -= aN;
+    return *this;
+  }
+
+  /* Comparison operators */
+
+  template<typename IntType1, typename IntType2>
+  friend bool operator==(const IntegerIterator<IntType1>& aIter1,
+                         const IntegerIterator<IntType2>& aIter2);
+  template<typename IntType1, typename IntType2>
+  friend bool operator!=(const IntegerIterator<IntType1>& aIter1,
+                         const IntegerIterator<IntType2>& aIter2);
+  template<typename IntType1, typename IntType2>
+  friend bool operator<(const IntegerIterator<IntType1>& aIter1,
+                        const IntegerIterator<IntType2>& aIter2);
+  template<typename IntType1, typename IntType2>
+  friend bool operator<=(const IntegerIterator<IntType1>& aIter1,
+                         const IntegerIterator<IntType2>& aIter2);
+  template<typename IntType1, typename IntType2>
+  friend bool operator>(const IntegerIterator<IntType1>& aIter1,
+                        const IntegerIterator<IntType2>& aIter2);
+  template<typename IntType1, typename IntType2>
+  friend bool operator>=(const IntegerIterator<IntType1>& aIter1,
+                         const IntegerIterator<IntType2>& aIter2);
+
+private:
+  IntTypeT mCurrent;
+};
+
+template<typename IntType1, typename IntType2>
+bool operator==(const IntegerIterator<IntType1>& aIter1,
+                const IntegerIterator<IntType2>& aIter2)
+{
+  return aIter1.mCurrent == aIter2.mCurrent;
+}
+
+template<typename IntType1, typename IntType2>
+bool operator!=(const IntegerIterator<IntType1>& aIter1,
+                const IntegerIterator<IntType2>& aIter2)
+{
+  return aIter1.mCurrent != aIter2.mCurrent;
+}
+
+template<typename IntType1, typename IntType2>
+bool operator<(const IntegerIterator<IntType1>& aIter1,
+               const IntegerIterator<IntType2>& aIter2)
+{
+  return aIter1.mCurrent < aIter2.mCurrent;
+}
+
+template<typename IntType1, typename IntType2>
+bool operator<=(const IntegerIterator<IntType1>& aIter1,
+                const IntegerIterator<IntType2>& aIter2)
+{
+  return aIter1.mCurrent <= aIter2.mCurrent;
+}
+
+template<typename IntType1, typename IntType2>
+bool operator>(const IntegerIterator<IntType1>& aIter1,
+               const IntegerIterator<IntType2>& aIter2)
+{
+  return aIter1.mCurrent > aIter2.mCurrent;
+}
+
+template<typename IntType1, typename IntType2>
+bool operator>=(const IntegerIterator<IntType1>& aIter1,
+                const IntegerIterator<IntType2>& aIter2)
+{
+  return aIter1.mCurrent >= aIter2.mCurrent;
+}
+
+template<typename IntTypeT>
+class IntegerRange
+{
+public:
+  typedef IntegerIterator<IntTypeT> iterator;
+  typedef IntegerIterator<IntTypeT> const_iterator;
+  typedef ReverseIterator<IntegerIterator<IntTypeT>> reverse_iterator;
+  typedef ReverseIterator<IntegerIterator<IntTypeT>> const_reverse_iterator;
+
+  template<typename IntType>
+  explicit IntegerRange(IntType aEnd)
+    : mBegin(0), mEnd(aEnd) { }
+
+  template<typename IntType1, typename IntType2>
+  IntegerRange(IntType1 aBegin, IntType2 aEnd)
+    : mBegin(aBegin), mEnd(aEnd) { }
+
+  iterator begin() const { return iterator(mBegin); }
+  const_iterator cbegin() const { return begin(); }
+  iterator end() const { return iterator(mEnd); }
+  const_iterator cend() const { return end(); }
+  reverse_iterator rbegin() const { return reverse_iterator(mEnd); }
+  const_reverse_iterator crbegin() const { return rbegin(); }
+  reverse_iterator rend() const { return reverse_iterator(mBegin); }
+  const_reverse_iterator crend() const { return rend(); }
+
+private:
+  IntTypeT mBegin;
+  IntTypeT mEnd;
+};
+
+template<typename T, bool = IsUnsigned<T>::value>
+struct GeqZero
+{
+  static bool check(T t) {
+    return t >= 0;
+  }
+};
+
+template<typename T>
+struct GeqZero<T, true>
+{
+  static bool check(T t) {
+    return true;
+  }
+};
+
+} // namespace detail
+
+template<typename IntType>
+detail::IntegerRange<IntType>
+MakeRange(IntType aEnd)
+{
+  MOZ_ASSERT(detail::GeqZero<IntType>::check(aEnd),
+             "Should never have negative value here");
+  return detail::IntegerRange<IntType>(aEnd);
+}
+
+template<typename IntType1, typename IntType2>
+detail::IntegerRange<IntType2>
+MakeRange(IntType1 aBegin, IntType2 aEnd)
+{
+  static_assert(IsSigned<IntType1>::value == IsSigned<IntType2>::value,
+                "signed/unsigned mismatch");
+  MOZ_ASSERT(aEnd >= aBegin, "End value should be larger than begin value");
+  return detail::IntegerRange<IntType2>(aBegin, aEnd);
+}
+
+} // namespace mozilla
+
+#endif // mozilla_IntegerRange_h
new file mode 100644
--- /dev/null
+++ b/mfbt/IteratorTraits.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+/* Iterator traits to expose a value type and a difference type */
+
+#ifndef mozilla_IteratorTraits_h
+#define mozilla_IteratorTraits_h
+
+#include <stddef.h>
+
+namespace mozilla {
+
+template<typename Iterator>
+struct IteratorTraits
+{
+  typedef typename Iterator::ValueType ValueType;
+  typedef typename Iterator::DifferenceType DifferenceType;
+};
+
+template<typename T>
+struct IteratorTraits<T*>
+{
+  typedef T ValueType;
+  typedef ptrdiff_t DifferenceType;
+};
+
+template<typename T>
+struct IteratorTraits<const T*>
+{
+  typedef const T ValueType;
+  typedef ptrdiff_t DifferenceType;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_IteratorTraits_h
new file mode 100644
--- /dev/null
+++ b/mfbt/ReverseIterator.h
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+/* An iterator that acts like another iterator, but iterating in
+ * the negative direction. (Note that not all iterators can iterate
+ * in the negative direction.) */
+
+#ifndef mozilla_ReverseIterator_h
+#define mozilla_ReverseIterator_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/IteratorTraits.h"
+
+namespace mozilla {
+
+template<typename IteratorT>
+class ReverseIterator
+{
+public:
+  typedef typename IteratorTraits<IteratorT>::ValueType ValueType;
+  typedef typename IteratorTraits<IteratorT>::DifferenceType DifferenceType;
+
+  template<typename Iterator>
+  explicit ReverseIterator(Iterator aIter)
+    : mCurrent(aIter) { }
+
+  template<typename Iterator>
+  MOZ_IMPLICIT ReverseIterator(const ReverseIterator<Iterator>& aOther)
+    : mCurrent(aOther.mCurrent) { }
+
+  ValueType& operator*() const
+  {
+    IteratorT tmp = mCurrent;
+    return *--tmp;
+  }
+
+  /* Increments and decrements operators */
+
+  ReverseIterator& operator++() { --mCurrent; return *this; }
+  ReverseIterator& operator--() { ++mCurrent; return *this; }
+  ReverseIterator operator++(int) { auto ret = *this; mCurrent--; return ret; }
+  ReverseIterator operator--(int) { auto ret = *this; mCurrent++; return ret; }
+
+  ReverseIterator operator+(DifferenceType aN) const
+  {
+    return ReverseIterator(mCurrent - aN);
+  }
+  ReverseIterator operator-(DifferenceType aN) const
+  {
+    return ReverseIterator(mCurrent + aN);
+  }
+  ReverseIterator& operator+=(DifferenceType aN)
+  {
+    mCurrent -= aN;
+    return *this;
+  }
+  ReverseIterator& operator-=(DifferenceType aN)
+  {
+    mCurrent += aN;
+    return *this;
+  }
+
+  /* Comparison operators */
+
+  template<typename Iterator1, typename Iterator2>
+  friend bool operator==(const ReverseIterator<Iterator1>& aIter1,
+                         const ReverseIterator<Iterator2>& aIter2);
+  template<typename Iterator1, typename Iterator2>
+  friend bool operator!=(const ReverseIterator<Iterator1>& aIter1,
+                         const ReverseIterator<Iterator2>& aIter2);
+  template<typename Iterator1, typename Iterator2>
+  friend bool operator<(const ReverseIterator<Iterator1>& aIter1,
+                        const ReverseIterator<Iterator2>& aIter2);
+  template<typename Iterator1, typename Iterator2>
+  friend bool operator<=(const ReverseIterator<Iterator1>& aIter1,
+                         const ReverseIterator<Iterator2>& aIter2);
+  template<typename Iterator1, typename Iterator2>
+  friend bool operator>(const ReverseIterator<Iterator1>& aIter1,
+                        const ReverseIterator<Iterator2>& aIter2);
+  template<typename Iterator1, typename Iterator2>
+  friend bool operator>=(const ReverseIterator<Iterator1>& aIter1,
+                         const ReverseIterator<Iterator2>& aIter2);
+
+private:
+  IteratorT mCurrent;
+};
+
+template<typename Iterator1, typename Iterator2>
+bool
+operator==(const ReverseIterator<Iterator1>& aIter1,
+           const ReverseIterator<Iterator2>& aIter2)
+{
+  return aIter1.mCurrent == aIter2.mCurrent;
+}
+
+template<typename Iterator1, typename Iterator2>
+bool
+operator!=(const ReverseIterator<Iterator1>& aIter1,
+           const ReverseIterator<Iterator2>& aIter2)
+{
+  return aIter1.mCurrent != aIter2.mCurrent;
+}
+
+template<typename Iterator1, typename Iterator2>
+bool
+operator<(const ReverseIterator<Iterator1>& aIter1,
+          const ReverseIterator<Iterator2>& aIter2)
+{
+  return aIter1.mCurrent > aIter2.mCurrent;
+}
+
+template<typename Iterator1, typename Iterator2>
+bool
+operator<=(const ReverseIterator<Iterator1>& aIter1,
+           const ReverseIterator<Iterator2>& aIter2)
+{
+  return aIter1.mCurrent >= aIter2.mCurrent;
+}
+
+template<typename Iterator1, typename Iterator2>
+bool
+operator>(const ReverseIterator<Iterator1>& aIter1,
+          const ReverseIterator<Iterator2>& aIter2)
+{
+  return aIter1.mCurrent < aIter2.mCurrent;
+}
+
+template<typename Iterator1, typename Iterator2>
+bool
+operator>=(const ReverseIterator<Iterator1>& aIter1,
+           const ReverseIterator<Iterator2>& aIter2)
+{
+  return aIter1.mCurrent <= aIter2.mCurrent;
+}
+
+namespace detail {
+
+template<typename IteratorT>
+class IteratorRange
+{
+public:
+  typedef IteratorT iterator;
+  typedef IteratorT const_iterator;
+  typedef ReverseIterator<IteratorT> reverse_iterator;
+  typedef ReverseIterator<IteratorT> const_reverse_iterator;
+
+  template<typename Iterator1, typename Iterator2>
+  IteratorRange(Iterator1 aIterBegin, Iterator2 aIterEnd)
+    : mIterBegin(aIterBegin), mIterEnd(aIterEnd) { }
+
+  template<typename Iterator>
+  IteratorRange(const IteratorRange<Iterator>& aOther)
+    : mIterBegin(aOther.mIterBegin), mIterEnd(aOther.mIterEnd) { }
+
+  iterator begin() const { return mIterBegin; }
+  const_iterator cbegin() const { return begin(); }
+  iterator end() const { return mIterEnd; }
+  const_iterator cend() const { return end(); }
+  reverse_iterator rbegin() const { return reverse_iterator(mIterEnd); }
+  const_reverse_iterator crbegin() const { return rbegin(); }
+  reverse_iterator rend() const { return reverse_iterator(mIterBegin); }
+  const_reverse_iterator crend() const { return rend(); }
+
+private:
+  IteratorT mIterBegin;
+  IteratorT mIterEnd;
+};
+
+} // namespace detail
+
+template<typename Range>
+detail::IteratorRange<typename Range::reverse_iterator>
+Reversed(Range& aRange)
+{
+  return {aRange.rbegin(), aRange.rend()};
+}
+
+template<typename Range>
+detail::IteratorRange<typename Range::const_reverse_iterator>
+Reversed(const Range& aRange)
+{
+  return {aRange.rbegin(), aRange.rend()};
+}
+
+} // namespace mozilla
+
+#endif // mozilla_ReverseIterator_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -32,17 +32,19 @@ EXPORTS.mozilla = [
     'double-conversion/utils.h',
     'Endian.h',
     'EnumeratedArray.h',
     'EnumSet.h',
     'FloatingPoint.h',
     'GuardObjects.h',
     'HashFunctions.h',
     'IntegerPrintfMacros.h',
+    'IntegerRange.h',
     'IntegerTypeTraits.h',
+    'IteratorTraits.h',
     'JSONWriter.h',
     'Likely.h',
     'LinkedList.h',
     'MacroArgs.h',
     'MacroForEach.h',
     'MathAlgorithms.h',
     'Maybe.h',
     'MaybeOneOf.h',
@@ -54,16 +56,17 @@ EXPORTS.mozilla = [
     'Pair.h',
     'PodOperations.h',
     'Poison.h',
     'Range.h',
     'RangedPtr.h',
     'RefCountType.h',
     'ReentrancyGuard.h',
     'RefPtr.h',
+    'ReverseIterator.h',
     'RollingMean.h',
     'Scoped.h',
     'SegmentedVector.h',
     'SHA1.h',
     'SizePrintfMacros.h',
     'SplayTree.h',
     'TaggedAnonymousMemory.h',
     'TemplateLib.h',
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -2455,37 +2455,31 @@ public class GeckoAppShell
         GeckoScreenOrientation.getInstance().unlock();
     }
 
     @WrapElementForJNI
     public static boolean pumpMessageLoop() {
         Handler geckoHandler = ThreadUtils.sGeckoHandler;
         Message msg = getNextMessageFromQueue(ThreadUtils.sGeckoQueue);
 
-        if (msg == null)
+        if (msg == null) {
             return false;
+        }
+
         if (msg.obj == geckoHandler && msg.getTarget() == geckoHandler) {
             // Our "queue is empty" message; see runGecko()
-            msg.recycle();
             return false;
         }
-        if (msg.getTarget() == null) 
-            Looper.myLooper().quit();
-        else
-            msg.getTarget().dispatchMessage(msg);
 
-        try {
-            // Bug 1055166 - this sometimes throws IllegalStateException on Android L.
-            // There appears to be no way to figure out if a message is in use or not, let
-            // alone receive a notification when it is no longer being used. Just catch
-            // the exception for now, and if a better solution comes along we can use it.
-            msg.recycle();
-        } catch (IllegalStateException e) {
-            // There is nothing we can do here so just eat it
+        if (msg.getTarget() == null) {
+            Looper.myLooper().quit();
+        } else {
+            msg.getTarget().dispatchMessage(msg);
         }
+
         return true;
     }
 
     @WrapElementForJNI
     public static void notifyWakeLockChanged(String topic, String state) {
         if (getGeckoInterface() != null)
             getGeckoInterface().notifyWakeLockChanged(topic, state);
     }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -414,20 +414,20 @@ pref("media.getusermedia.playout_delay",
 #endif
 #endif
 
 #if !defined(ANDROID)
 pref("media.getusermedia.screensharing.enabled", true);
 #endif
 
 #ifdef RELEASE_BUILD
-pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,collaborate.com,*.collaborate.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,example.com");
+pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,collaborate.com,*.collaborate.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,example.com");
 #else
  // temporary value, not intended for release - bug 1049087
-pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,collaborate.com,*.collaborate.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,example.com");
+pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,collaborate.com,*.collaborate.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,example.com");
 #endif
 // OS/X 10.6 and XP have screen/window sharing off by default due to various issues - Caveat emptor
 pref("media.getusermedia.screensharing.allow_on_old_platforms", false);
 
 // TextTrack support
 pref("media.webvtt.enabled", true);
 pref("media.webvtt.regions.enabled", false);
 
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -1125,17 +1125,17 @@ com.im
 ltd.co.im
 net.im
 org.im
 plc.co.im
 tt.im
 tv.im
 
 // in : http://en.wikipedia.org/wiki/.in
-// see also: http://www.inregistry.in/policies/
+// see also: https://registry.in/Policies
 // Please note, that nic.in is not an offical eTLD, but used by most
 // government institutions.
 in
 co.in
 firm.in
 net.in
 org.in
 gen.in
@@ -6775,17 +6775,17 @@ xxx
 
 // zm : http://en.wikipedia.org/wiki/.zm
 *.zm
 
 // zw : http://en.wikipedia.org/wiki/.zw
 *.zw
 
 
-// List of new gTLDs imported from https://newgtlds.icann.org/newgtlds.csv on 2014-12-22T18:02:07Z
+// List of new gTLDs imported from https://newgtlds.icann.org/newgtlds.csv on 2015-01-27T00:02:07Z
 
 // abb : 2014-10-24 ABB Ltd
 abb
 
 // abbott : 2014-07-24 Abbott Laboratories, Inc.
 abbott
 
 // abogado : 2014-04-24 Top Level Domain Holdings Limited
@@ -6798,16 +6798,19 @@ academy
 accenture
 
 // accountant : 2014-11-20 dot Accountant Limited
 accountant
 
 // accountants : 2014-03-20 Knob Town, LLC
 accountants
 
+// aco : 2015-01-08 ACO Severin Ahlmann GmbH & Co. KG
+aco
+
 // active : 2014-05-01 The Active Network, Inc
 active
 
 // actor : 2013-12-12 United TLD Holdco Ltd.
 actor
 
 // ads : 2014-12-04 Charleston Road Registry Inc.
 ads
@@ -6828,31 +6831,40 @@ agency
 aig
 
 // airforce : 2014-03-06 United TLD Holdco Ltd.
 airforce
 
 // airtel : 2014-10-24 Bharti Airtel Limited
 airtel
 
+// alibaba : 2015-01-15 Alibaba Group Holding Limited
+alibaba
+
+// alipay : 2015-01-15 Alibaba Group Holding Limited
+alipay
+
 // allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft
 allfinanz
 
 // alsace : 2014-07-02 REGION D ALSACE
 alsace
 
 // amsterdam : 2014-07-24 Gemeente Amsterdam
 amsterdam
 
 // analytics : 2014-12-18 Campus IP LLC
 analytics
 
 // android : 2014-08-07 Charleston Road Registry Inc.
 android
 
+// anquan : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+anquan
+
 // apartments : 2014-12-11 June Maple, LLC
 apartments
 
 // aquarelle : 2014-07-24 Aquarelle.com
 aquarelle
 
 // aramco : 2014-11-20 Aramco Services Company
 aramco
@@ -6882,22 +6894,28 @@ audio
 author
 
 // auto : 2014-11-13 Uniregistry, Corp.
 auto
 
 // autos : 2014-01-09 DERAutos, LLC
 autos
 
+// avianca : 2015-01-08 Aerovias del Continente Americano S.A. Avianca
+avianca
+
 // axa : 2013-12-19 AXA SA
 axa
 
 // azure : 2014-12-18 Microsoft Corporation
 azure
 
+// baidu : 2015-01-08 Baidu, Inc.
+baidu
+
 // band : 2014-06-12  
 band
 
 // bank : 2014-09-25 fTLD Registry Services LLC
 bank
 
 // bar : 2013-12-12 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
 bar
@@ -6993,28 +7011,34 @@ boats
 bom
 
 // bond : 2014-06-05 Bond University Limited
 bond
 
 // boo : 2014-01-30 Charleston Road Registry Inc.
 boo
 
+// boots : 2015-01-08 THE BOOTS COMPANY PLC
+boots
+
 // bot : 2014-12-18 Amazon EU S.à r.l.
 bot
 
 // boutique : 2013-11-14 Over Galley, LLC
 boutique
 
 // bradesco : 2014-12-18 Banco Bradesco S.A.
 bradesco
 
 // bridgestone : 2014-12-18 Bridgestone Corporation
 bridgestone
 
+// broadway : 2014-12-22 Celebrate Broadway, Inc.
+broadway
+
 // broker : 2014-12-11 IG Group Holdings PLC
 broker
 
 // brussels : 2014-02-06 DNS.be vzw
 brussels
 
 // budapest : 2013-11-21 Top Level Domain Holdings Limited
 budapest
@@ -7059,16 +7083,19 @@ cancerresearch
 canon
 
 // capetown : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
 capetown
 
 // capital : 2014-03-06 Delta Mill, LLC
 capital
 
+// car : 2015-01-22 Charleston Road Registry Inc.
+car
+
 // caravan : 2013-12-12 Caravan International, Inc.
 caravan
 
 // cards : 2013-12-05 Foggy Hollow, LLC
 cards
 
 // care : 2014-03-06 Goose Cross
 care
@@ -7137,16 +7164,19 @@ christmas
 chrome
 
 // church : 2014-02-06 Holly Fileds, LLC
 church
 
 // circle : 2014-12-18 Amazon EU S.à r.l.
 circle
 
+// cisco : 2014-12-22 Cisco Technology, Inc.
+cisco
+
 // citic : 2014-01-09 CITIC Group Corporation
 citic
 
 // city : 2014-05-29 Snow Sky, LLC
 city
 
 // cityeats : 2014-12-11 Lifestyle Domain Holdings, Inc.
 cityeats
@@ -7191,25 +7221,31 @@ commbank
 community
 
 // company : 2013-11-07 Silver Avenue, LLC
 company
 
 // computer : 2013-10-24 Pine Mill, LLC
 computer
 
+// comsec : 2015-01-08 VeriSign, Inc.
+comsec
+
 // condos : 2013-12-05 Pine House, LLC
 condos
 
 // construction : 2013-09-16 Fox Dynamite, LLC
 construction
 
 // consulting : 2013-12-05  
 consulting
 
+// contact : 2015-01-08 Top Level Spectrum, Inc.
+contact
+
 // contractors : 2013-09-10 Magic Woods, LLC
 contractors
 
 // cooking : 2013-11-21 Top Level Domain Holdings Limited
 cooking
 
 // cool : 2013-11-14 Koko Lake, LLC
 cool
@@ -7224,16 +7260,19 @@ country
 courses
 
 // credit : 2014-03-20 Snow Shadow, LLC
 credit
 
 // creditcard : 2014-03-20 Binky Frostbite, LLC
 creditcard
 
+// creditunion : 2015-01-22 CUNA Performance Resources, LLC
+creditunion
+
 // cricket : 2014-10-09 dot Cricket Limited
 cricket
 
 // crown : 2014-10-24 Crown Equipment Corporation
 crown
 
 // crs : 2014-04-03 Federated Co-operatives Limited
 crs
@@ -7245,16 +7284,19 @@ cruises
 csc
 
 // cuisinella : 2014-04-03 SALM S.A.S.
 cuisinella
 
 // cymru : 2014-05-08 Nominet UK
 cymru
 
+// cyou : 2015-01-22 Beijing Gamease Age Digital Technology Co., Ltd.
+cyou
+
 // dabur : 2014-02-06 Dabur India Limited
 dabur
 
 // dad : 2014-01-23 Charleston Road Registry Inc.
 dad
 
 // dance : 2013-10-24 United TLD Holdco Ltd.
 dance
@@ -7269,16 +7311,19 @@ dating
 datsun
 
 // day : 2014-01-30 Charleston Road Registry Inc.
 day
 
 // dclk : 2014-11-20 Charleston Road Registry Inc.
 dclk
 
+// dealer : 2014-12-22 Dealer Dot Com, Inc.
+dealer
+
 // deals : 2014-05-22 Sand Sunset, LLC
 deals
 
 // degree : 2014-03-06  
 degree
 
 // delivery : 2014-09-11 Steel Station, LLC
 delivery
@@ -7338,16 +7383,19 @@ doha
 domains
 
 // doosan : 2014-04-03 Doosan Corporation
 doosan
 
 // download : 2014-11-20 dot Support Limited
 download
 
+// dubai : 2015-01-01 Dubai Smart Government Department
+dubai
+
 // durban : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
 durban
 
 // dvag : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
 dvag
 
 // earth : 2014-12-04 Interlink Co., Ltd.
 earth
@@ -7443,16 +7491,19 @@ fashion
 fast
 
 // feedback : 2013-12-19 Top Level Spectrum, Inc.
 feedback
 
 // ferrero : 2014-12-18 Ferrero Trading Lux S.A.
 ferrero
 
+// film : 2015-01-08 Motion Picture Domain Registry Pty Ltd
+film
+
 // final : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
 final
 
 // finance : 2014-03-20 Cotton Cypress, LLC
 finance
 
 // financial : 2014-03-06 Just Cover, LLC
 financial
@@ -7575,16 +7626,19 @@ globo
 gmail
 
 // gmo : 2014-01-09 GMO Internet, Inc.
 gmo
 
 // gmx : 2014-04-24 1&1 Mail & Media GmbH
 gmx
 
+// gold : 2015-01-22 June Edge, LLC
+gold
+
 // goldpoint : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
 goldpoint
 
 // golf : 2014-12-18 Lone falls, LLC
 golf
 
 // goo : 2014-12-18 NTT Resonant Inc.
 goo
@@ -7695,16 +7749,19 @@ how
 hsbc
 
 // ibm : 2014-07-31 International Business Machines Corporation
 ibm
 
 // ice : 2014-10-30 IntercontinentalExchange, Inc.
 ice
 
+// icu : 2015-01-08 One.com A/S
+icu
+
 // ifm : 2014-01-30 ifm electronic gmbh
 ifm
 
 // iinet : 2014-07-03 Connect West Pty. Ltd.
 iinet
 
 // immo : 2014-07-10 Auburn Bloom, LLC
 immo
@@ -7803,16 +7860,22 @@ kinder
 kitchen
 
 // kiwi : 2013-09-20 DOT KIWI LIMITED
 kiwi
 
 // koeln : 2014-01-09 NetCologne Gesellschaft für Telekommunikation mbH
 koeln
 
+// komatsu : 2015-01-08 Komatsu Ltd.
+komatsu
+
+// kpn : 2015-01-08 Koninklijke KPN N.V.
+kpn
+
 // krd : 2013-12-05 KRG Department of Information Technology
 krd
 
 // kred : 2013-12-19 KredTLD Pty Ltd
 kred
 
 // kyoto : 2014-11-07 Academic Institution: Kyoto Jyoho Gakuen
 kyoto
@@ -7827,16 +7890,19 @@ land
 landrover
 
 // lat : 2014-10-16 ECOM-LAC Federaciòn de Latinoamèrica y el Caribe para Internet y el Comercio Electrònico
 lat
 
 // latrobe : 2014-06-16 La Trobe University
 latrobe
 
+// law : 2015-01-22 Minds + Machines Group Limited
+law
+
 // lawyer : 2014-03-20  
 lawyer
 
 // lds : 2014-03-20 IRI Domain Management, LLC (\
 lds
 
 // lease : 2014-03-06 Victor Trail, LLC
 lease
@@ -7854,16 +7920,19 @@ lgbt
 liaison
 
 // lidl : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
 lidl
 
 // life : 2014-02-06 Trixy Oaks, LLC
 life
 
+// lifeinsurance : 2015-01-15 American Council of Life Insurers
+lifeinsurance
+
 // lifestyle : 2014-12-11 Lifestyle Domain Holdings, Inc.
 lifestyle
 
 // lighting : 2013-08-27 John McCook, LLC
 lighting
 
 // like : 2014-12-18 Amazon EU S.à r.l.
 like
@@ -7896,16 +7965,19 @@ loans
 london
 
 // lotte : 2014-11-07 Lotte Holdings Co., Ltd.
 lotte
 
 // lotto : 2014-04-10 Afilias Limited
 lotto
 
+// love : 2014-12-22 Merchant Law Group LLP
+love
+
 // ltd : 2014-09-25 Over Corner, LLC
 ltd
 
 // ltda : 2014-04-17 DOMAIN ROBOT SERVICOS DE HOSPEDAGEM NA INTERNET LTDA
 ltda
 
 // lupin : 2014-11-07 LUPIN LIMITED
 lupin
@@ -7920,16 +7992,19 @@ luxury
 madrid
 
 // maif : 2014-10-02 Mutuelle Assurance Instituteur France (MAIF)
 maif
 
 // maison : 2013-12-05 Victor Frostbite, LLC
 maison
 
+// makeup : 2015-01-15 L'Oréal
+makeup
+
 // man : 2014-12-04 MAN SE
 man
 
 // management : 2013-11-07 John Goodbye, LLC
 management
 
 // mango : 2013-10-24 PUNTO FA S.L.
 mango
@@ -8028,16 +8103,19 @@ mtpc
 nadex
 
 // nagoya : 2013-10-24 GMO Registry, Inc.
 nagoya
 
 // navy : 2014-03-06 United TLD Holdco Ltd.
 navy
 
+// nec : 2015-01-08 NEC Corporation
+nec
+
 // netbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
 netbank
 
 // network : 2013-11-14 Trixy Manor, LLC
 network
 
 // neustar : 2013-12-05 NeuStar, Inc.
 neustar
@@ -8061,16 +8139,19 @@ nhk
 nico
 
 // ninja : 2013-11-07 United TLD Holdco Ltd.
 ninja
 
 // nissan : 2014-03-27 NISSAN MOTOR CO., LTD.
 nissan
 
+// nokia : 2015-01-08 Nokia Corporation
+nokia
+
 // norton : 2014-12-04 Symantec Corporation
 norton
 
 // nowruz : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
 nowruz
 
 // nra : 2014-05-22 NRA Holdings Company, INC.
 nra
@@ -8085,25 +8166,31 @@ ntt
 nyc
 
 // obi : 2014-09-25 OBI Group Holding SE & Co. KGaA
 obi
 
 // okinawa : 2013-12-05 BusinessRalliart Inc.
 okinawa
 
+// omega : 2015-01-08 The Swatch Group Ltd
+omega
+
 // one : 2014-11-07 One.com A/S
 one
 
 // ong : 2014-03-06 Public Interest Registry
 ong
 
 // onl : 2013-09-16 I-Registry Ltd.
 onl
 
+// online : 2015-01-15 DotOnline Inc.
+online
+
 // ooo : 2014-01-09 INFIBEAM INCORPORATION LIMITED
 ooo
 
 // oracle : 2014-06-19 Oracle Corporation
 oracle
 
 // organic : 2014-03-27 Afilias Limited
 organic
@@ -8163,16 +8250,19 @@ piaget
 pics
 
 // pictet : 2014-06-26 Pictet Europe S.A.
 pictet
 
 // pictures : 2014-03-06 Foggy Sky, LLC
 pictures
 
+// pid : 2015-01-08 Top Level Spectrum, Inc.
+pid
+
 // pin : 2014-12-18 Amazon EU S.à r.l.
 pin
 
 // pink : 2013-10-01 Afilias Limited
 pink
 
 // pizza : 2014-06-26 Foggy Moon, LLC
 pizza
@@ -8319,16 +8409,19 @@ ruhr
 ryukyu
 
 // saarland : 2013-12-12 dotSaarland GmbH
 saarland
 
 // safe : 2014-12-18 Amazon EU S.à r.l.
 safe
 
+// safety : 2015-01-08 Safety Registry Services, LLC.
+safety
+
 // sakura : 2014-12-18 SAKURA Internet Inc.
 sakura
 
 // sale : 2014-10-16  
 sale
 
 // salon : 2014-12-11 Outer Orchard, LLC
 salon
@@ -8418,22 +8511,31 @@ sharp
 shia
 
 // shiksha : 2013-11-14 Afilias Limited
 shiksha
 
 // shoes : 2013-10-02 Binky Galley, LLC
 shoes
 
+// shouji : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+shouji
+
 // shriram : 2014-01-23 Shriram Capital Ltd.
 shriram
 
 // singles : 2013-08-27 Fern Madison, LLC
 singles
 
+// site : 2015-01-15 DotSite Inc.
+site
+
+// skin : 2015-01-15 L'Oréal
+skin
+
 // sky : 2014-06-19 Sky IP International Ltd, a company incorporated in England and Wales, operating via its registered Swiss branch
 sky
 
 // skype : 2014-12-18 Microsoft Corporation
 skype
 
 // smile : 2014-12-18 Amazon EU S.à r.l.
 smile
@@ -8448,49 +8550,61 @@ software
 sohu
 
 // solar : 2013-11-07 Ruby Town, LLC
 solar
 
 // solutions : 2013-11-07 Silver Cover, LLC
 solutions
 
+// sony : 2015-01-08 Sony Corporation
+sony
+
 // soy : 2014-01-23 Charleston Road Registry Inc.
 soy
 
 // space : 2014-04-03 DotSpace Inc.
 space
 
 // spiegel : 2014-02-05 SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG
 spiegel
 
 // spreadbetting : 2014-12-11 IG Group Holdings PLC
 spreadbetting
 
 // stada : 2014-11-13 STADA Arzneimittel AG
 stada
 
+// star : 2015-01-08 Star India Private Limited
+star
+
 // statoil : 2014-12-04 Statoil ASA
 statoil
 
 // stc : 2014-10-09 Saudi Telecom Company
 stc
 
 // stcgroup : 2014-10-09 Saudi Telecom Company
 stcgroup
 
 // stockholm : 2014-12-18 Stockholms kommun
 stockholm
 
+// storage : 2014-12-22 Self Storage Company LLC
+storage
+
 // study : 2014-12-11 OPEN UNIVERSITIES AUSTRALIA PTY LTD
 study
 
 // style : 2014-12-04 Binky Moon, LLC
 style
 
+// sucks : 2014-12-22 Vox Populi Registry Inc.
+sucks
+
 // supplies : 2013-12-19 Atomic Fields, LLC
 supplies
 
 // supply : 2013-12-19 Half Falls, LLC
 supply
 
 // support : 2013-10-24 Grand Orchard, LLC
 support
@@ -8499,16 +8613,19 @@ support
 surf
 
 // surgery : 2014-03-20 Tin Avenue, LLC
 surgery
 
 // suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION
 suzuki
 
+// swatch : 2015-01-08 The Swatch Group Ltd
+swatch
+
 // swiss : 2014-10-16 Swiss Confederation
 swiss
 
 // sydney : 2014-09-18 State of New South Wales, Department of Premier and Cabinet
 sydney
 
 // symantec : 2014-12-04 Symantec Corporation
 symantec
@@ -8517,16 +8634,19 @@ symantec
 systems
 
 // tab : 2014-12-04 Tabcorp Holdings Limited
 tab
 
 // taipei : 2014-07-10 Taipei City Government
 taipei
 
+// taobao : 2015-01-15 Alibaba Group Holding Limited
+taobao
+
 // tatar : 2014-04-24 Limited Liability Company \
 tatar
 
 // tattoo : 2013-08-30 Uniregistry, Corp.
 tattoo
 
 // tax : 2014-03-20 Storm Orchard, LLC
 tax
@@ -8553,16 +8673,19 @@ tienda
 tips
 
 // tires : 2014-11-07 Dog Edge, LLC
 tires
 
 // tirol : 2014-04-24 punkt Tirol GmbH
 tirol
 
+// tmall : 2015-01-15 Alibaba Group Holding Limited
+tmall
+
 // today : 2013-09-20 Pearl Woods, LLC
 today
 
 // tokyo : 2013-11-13 GMO Registry, Inc.
 tokyo
 
 // tools : 2013-11-21 Pioneer North, LLC
 tools
@@ -8571,16 +8694,19 @@ tools
 top
 
 // toray : 2014-12-18 Toray Industries, Inc.
 toray
 
 // toshiba : 2014-04-10 TOSHIBA Corporation
 toshiba
 
+// tours : 2015-01-22 Sugar Station, LLC
+tours
+
 // town : 2014-03-06 Koko Moon, LLC
 town
 
 // toys : 2014-03-06 Pioneer Orchard, LLC
 toys
 
 // trade : 2014-01-23 Elite Registry Limited
 trade
@@ -8634,16 +8760,19 @@ vet
 viajes
 
 // video : 2014-10-16  
 video
 
 // villas : 2013-12-05 New Sky, LLC
 villas
 
+// vip : 2015-01-22 Minds + Machines Group Limited
+vip
+
 // virgin : 2014-09-25 Virgin Enterprises Limited
 virgin
 
 // vision : 2013-12-05 Koko Station, LLC
 vision
 
 // vista : 2014-09-18 Vistaprint Limited
 vista
@@ -8682,16 +8811,22 @@ walter
 wang
 
 // wanggou : 2014-12-18 Amazon EU S.à r.l.
 wanggou
 
 // watch : 2013-11-14 Sand Shadow, LLC
 watch
 
+// watches : 2014-12-22 Richemont DNS Inc.
+watches
+
+// weather : 2015-01-08 The Weather Channel, LLC
+weather
+
 // webcam : 2014-01-23 dot Webcam Limited
 webcam
 
 // website : 2014-04-03 DotWebsite Inc.
 website
 
 // wed : 2013-10-01 Atgron, Inc.
 wed
@@ -8736,67 +8871,88 @@ wtc
 wtf
 
 // xbox : 2014-12-18 Microsoft Corporation
 xbox
 
 // xerox : 2014-10-24 Xerox DNHC LLC
 xerox
 
+// xihuan : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+xihuan
+
 // xin : 2014-12-11 Elegant Leader Limited
 xin
 
+// xn--11b4c3d : 2015-01-15 VeriSign Sarl
+कॉम
+
 // xn--1qqw23a : 2014-01-09 Guangzhou YU Wei Information Technology Co., Ltd.
 佛山
 
 // xn--30rr7y : 2014-06-12 Excellent First Limited
 慈善
 
 // xn--3bst00m : 2013-09-13 Eagle Horizon Limited
 集团
 
 // xn--3ds443g : 2013-09-08 TLD REGISTRY LIMITED
 在线
 
+// xn--3pxu8k : 2015-01-15 VeriSign Sarl
+点看
+
+// xn--42c2d9a : 2015-01-15 VeriSign Sarl
+คอม
+
 // xn--45q11c : 2013-11-21 Zodiac Scorpio Limited
 八卦
 
 // xn--4gbrim : 2013-10-04 Suhub Electronic Establishment
 موقع
 
 // xn--55qw42g : 2013-11-08 China Organizational Name Administration Center
 公益
 
 // xn--55qx5d : 2013-11-14 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center)
 公司
 
+// xn--5tzm5g : 2014-12-22 Global Website TLD Asia Limited
+网站
+
 // xn--6frz82g : 2013-09-23 Afilias Limited
 移动
 
 // xn--6qq986b3xl : 2013-09-13 Tycoon Treasure Limited
 我爱你
 
 // xn--80adxhks : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
 москва
 
 // xn--80asehdb : 2013-07-14 CORE Association
 онлайн
 
 // xn--80aswg : 2013-07-14 CORE Association
 сайт
 
+// xn--9dbq2a : 2015-01-15 VeriSign Sarl
+קום
+
 // xn--9et52u : 2014-06-12 RISE VICTORY LIMITED
 时尚
 
 // xn--b4w605ferd : 2014-08-07 Temasek Holdings (Private) Limited
 淡马锡
 
 // xn--c1avg : 2013-11-14 Public Interest Registry
 орг
 
+// xn--c2br7g : 2015-01-15 VeriSign Sarl
+नेट
+
 // xn--cg4bki : 2013-09-27 SAMSUNG SDS CO., LTD
 삼성
 
 // xn--czr694b : 2014-01-16 HU YI GLOBAL INFORMATION RESOURCES(HOLDING) COMPANY.HONGKONG LIMITED
 商标
 
 // xn--czrs0t : 2013-12-19 Wild Island, LLC
 商店
@@ -8808,16 +8964,19 @@ xin
 дети
 
 // xn--eckvdtc9d : 2014-12-18 Amazon EU S.à r.l.
 ポイント
 
 // xn--efvy88h : 2014-08-22 Xinhua News Agency Guangdong Branch 新华通讯社广东分社
 新闻
 
+// xn--fhbei : 2015-01-15 VeriSign Sarl
+كوم
+
 // xn--fiq228c5hs : 2013-09-08 TLD REGISTRY LIMITED
 中文网
 
 // xn--fiq64b : 2013-10-14 CITIC Group Corporation
 中信
 
 // xn--fjq720a : 2014-05-22 Will Bloom, LLC
 娱乐
@@ -8832,34 +8991,46 @@ xin
 संगठन
 
 // xn--imr513n : 2014-12-11 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED
 餐厅
 
 // xn--io0a7i : 2013-11-14 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center)
 网络
 
+// xn--j1aef : 2015-01-15 VeriSign Sarl
+ком
+
+// xn--jlq61u9w7b : 2015-01-08 Nokia Corporation
+诺基亚
+
 // xn--kcrx77d1x4a : 2014-11-07 Koninklijke Philips N.V.
 飞利浦
 
+// xn--kpu716f : 2014-12-22 Richemont DNS Inc.
+手表
+
 // xn--kput3i : 2014-02-13 Beijing RITT-Net Technology Development Co., Ltd
 手机
 
 // xn--mgba3a3ejt : 2014-11-20 Aramco Services Company
 ارامكو
 
 // xn--mgbab2bd : 2013-10-31 CORE Association
 بازار
 
 // xn--mgbb9fbpob : 2014-12-18 GreenTech Consultancy Company W.L.L.
 موبايلي
 
 // xn--mgbt3dhd : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
 همراه
 
+// xn--mk1bu44c : 2015-01-15 VeriSign Sarl
+닷컴
+
 // xn--mxtq1m : 2014-03-06 Net-Chinese Co., Ltd.
 政府
 
 // xn--ngbc5azd : 2013-07-13 International Domain Registry Pty. Ltd.
 شبكة
 
 // xn--ngbe9e0a : 2014-12-04 Kuwait Finance House
 بيتك
@@ -8871,28 +9042,40 @@ xin
 组织机构
 
 // xn--nyqy26a : 2014-11-07 Stable Tone Limited
 健康
 
 // xn--p1acf : 2013-12-12 Rusnames Limited
 рус
 
+// xn--pbt977c : 2014-12-22 Richemont DNS Inc.
+珠宝
+
+// xn--pssy2u : 2015-01-15 VeriSign Sarl
+大拿
+
 // xn--q9jyb4c : 2013-09-17 Charleston Road Registry Inc.
 みんな
 
 // xn--qcka1pmc : 2014-07-31 Charleston Road Registry Inc.
 グーグル
 
 // xn--rhqv96g : 2013-09-11 Stable Tone Limited
 世界
 
-// xn--ses554g : 2014-01-16 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED
+// xn--ses554g : 2014-01-16
 网址
 
+// xn--t60b56a : 2015-01-15 VeriSign Sarl
+닷넷
+
+// xn--tckwe : 2015-01-15 VeriSign Sarl
+コム
+
 // xn--unup4y : 2013-07-14 Spring Fields, LLC
 游戏
 
 // xn--vermgensberater-ctb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
 vermögensberater
 
 // xn--vermgensberatung-pwb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
 vermögensberatung
@@ -8928,31 +9111,35 @@ yodobashi
 yoga
 
 // yokohama : 2013-12-12 GMO Registry, Inc.
 yokohama
 
 // youtube : 2014-05-01 Charleston Road Registry Inc.
 youtube
 
+// yun : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+yun
+
 // zara : 2014-11-07 Industria de Diseño Textil, S.A. (INDITEX, S.A.)
 zara
 
 // zero : 2014-12-18 Amazon EU S.à r.l.
 zero
 
 // zip : 2014-05-08 Charleston Road Registry Inc.
 zip
 
 // zone : 2013-11-14 Outer Falls, LLC
 zone
 
 // zuerich : 2014-11-07 Kanton Zürich (Canton of Zurich)
 zuerich
 
+
 // ===END ICANN DOMAINS===
 // ===BEGIN PRIVATE DOMAINS===
 
 // Amazon CloudFront : https://aws.amazon.com/cloudfront/
 // Submitted by Donavan Miller <donavanm@amazon.com> 2013-03-22
 cloudfront.net
 
 // Amazon Elastic Compute Cloud: https://aws.amazon.com/ec2/
@@ -9069,16 +9256,20 @@ cloudcontrolapp.com
 
 // co.ca : http://registry.co.ca/
 co.ca
 
 // CoDNS B.V.
 co.nl
 co.no
 
+// Commerce Guys, SAS
+// Submitted by Damien Tournoud <damien@commerceguys.com> 2015-01-22
+*.platform.sh
+
 // Cupcake : https://cupcake.io/
 // Submitted by Jonathan Rudenberg <jonathan@cupcake.io> 2013-10-08
 cupcake.is
 
 // DreamHost : http://www.dreamhost.com/
 // Submitted by Andrew Farmer <andrew.farmer@dreamhost.com> 2012-10-02
 dreamhosters.com
 
--- a/netwerk/protocol/http/SpdySession31.cpp
+++ b/netwerk/protocol/http/SpdySession31.cpp
@@ -728,24 +728,22 @@ SpdySession31::GenerateRstStream(uint32_
   packet[6] = 0;
   packet[7] = 8;                                  /* length */
 
   aID = PR_htonl(aID);
   memcpy(packet + 8, &aID, 4);
   aStatusCode = PR_htonl(aStatusCode);
   memcpy(packet + 12, &aStatusCode, 4);
 
-#ifdef DEBUG
   // Intentionally crash on debug builds when the debug 1102923 pref is set
   // We hope to get useful stack traces to solve bug 1102923 when running
   // test_spdy.js
   if (gHttpHandler->Debug1102923()) {
     MOZ_CRASH("Debug 1102923");
   }
-#endif
 
   LogIO(this, nullptr, "Generate Reset", packet, 16);
   FlushOutputQueue();
 }
 
 void
 SpdySession31::GenerateGoAway(uint32_t aStatusCode)
 {
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1929,17 +1929,17 @@ class Mochitest(MochitestUtilsMixin):
     finally:
       if options.vmwareRecording:
         self.stopVMwareRecording();
       self.stopServers()
 
     processLeakLog(self.leak_report_file, options)
 
     if self.nsprLogs:
-      with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip:
+      with zipfile.ZipFile("%s/nsprlog.zip" % self.browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip:
         for logfile in glob.glob("%s/nspr*.log*" % tempfile.gettempdir()):
           logzip.write(logfile)
           os.remove(logfile)
 
     self.log.info("runtests.py | Running tests: end.")
 
     if self.manifest is not None:
       self.cleanup(options)
--- a/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
@@ -1,23 +1,22 @@
 [mediasource-append-buffer.html]
   type: testharness
-  expected:
-    if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT
-    if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): TIMEOUT
+  disabled:
+    if (os == "win") and (version != "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1128332
+
   [Test MediaSource.removeSourceBuffer() call during a pending appendBuffer().]
     expected: FAIL
 
   [Test appendBuffer with partial init segments.]
     expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+      if (os == "win") and (version != "5.1.2600"): FAIL
+      if os == "mac": FAIL
 
   [Test appendBuffer with partial media segments.]
     expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+      if (os == "win") and (version != "5.1.2600"): FAIL
+      if os == "mac": FAIL
 
   [Test abort in the middle of an initialization segment.]
-    expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT
-      if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): TIMEOUT
+    disabled:
+      if (os == "win") and (version != "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1128332
 
--- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini
@@ -1,4 +1,11 @@
 [mediasource-config-change-mp4-a-bitrate.html]
   type: testharness
+  disabled:
+    if (os == "win") and (version != "5.1.2600"): occasional timeout
   [Tests mp4 audio-only bitrate changes.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1106776
+    disabled:
+      if (os == "win") and (version != "5.1.2600"): occasional timeout.
+    expected:
+      if os == "mac": PASS
+      FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-duration.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-duration.html.ini
@@ -1,27 +1,19 @@
 [mediasource-duration.html]
   type: testharness
-  expected:
-    if (os == "win") and (version != "5.1.2600"): OK
-    if os == "mac": OK
-    TIMEOUT
+  disabled:
+    if (os == "win") and (version != "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1128332
+
   [Test seek starts on duration truncation below currentTime]
-    expected:
-      if (os == "win") and (version != "5.1.2600"): FAIL
-      if os == "mac": FAIL
-      TIMEOUT
+    expected: FAIL
 
   [Test appendBuffer completes previous seek to truncated duration]
-    expected:
-      if (os == "win") and (version != "5.1.2600"): FAIL
-      if os == "mac": FAIL
-      TIMEOUT
+    expected: FAIL
 
   [Test endOfStream completes previous seek to truncated duration]
-    expected:
-      if (os == "win") and (version != "5.1.2600"): FAIL
-      if os == "mac": FAIL
-      TIMEOUT
+    expected: FAIL
 
   [Test setting same duration multiple times does not fire duplicate durationchange]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1116007
+    disabled:
+      if (os == "win") and (version != "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1128332
+    expected: FAIL
 
--- a/testing/web-platform/meta/media-source/mediasource-remove.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-remove.html.ini
@@ -15,11 +15,11 @@
   [Test remove with a NEGATIVE_INFINITY end.]
     expected: FAIL
 
   [Test remove with a NaN end.]
     expected: FAIL
 
   [Test remove with a start at the duration.]
     expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+      if (os == "win") and (version != "5.1.2600"): FAIL
+      if os == "mac": FAIL
 
--- a/testing/web-platform/meta/media-source/mediasource-removesourcebuffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-removesourcebuffer.html.ini
@@ -1,7 +1,7 @@
 [mediasource-removesourcebuffer.html]
   type: testharness
   [Test removesourcebuffer event on activeSourceBuffers.]
     expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+      if (os == "win") and (version != "5.1.2600"): FAIL
+      if os == "mac": FAIL
 
--- a/toolkit/devtools/server/tests/unit/test_breakpoint-actor-map.js
+++ b/toolkit/devtools/server/tests/unit/test_breakpoint-actor-map.js
@@ -116,35 +116,35 @@ function test_find_actors() {
   let bpStore = new BreakpointActorMap();
 
   for (let bp of bps) {
     bpStore.setActor(bp, bp);
   }
 
   // All breakpoints
 
-  let bpSet = Set(bps);
+  let bpSet = new Set(bps);
   for (let bp of bpStore.findActors()) {
     bpSet.delete(bp);
   }
   do_check_eq(bpSet.size, 0,
               "Should be able to iterate over all breakpoints");
 
   // Breakpoints by URL
 
-  bpSet = Set(bps.filter(bp => { return bp.sourceActor.actorID === "actor1" }));
+  bpSet = new Set(bps.filter(bp => { return bp.sourceActor.actorID === "actor1" }));
   for (let bp of bpStore.findActors({ sourceActor: { actorID: "actor1" } })) {
     bpSet.delete(bp);
   }
   do_check_eq(bpSet.size, 0,
               "Should be able to filter the iteration by url");
 
   // Breakpoints by URL and line
 
-  bpSet = Set(bps.filter(bp => { return bp.sourceActor.actorID === "actor1" && bp.line === 10; }));
+  bpSet = new Set(bps.filter(bp => { return bp.sourceActor.actorID === "actor1" && bp.line === 10; }));
   let first = true;
   for (let bp of bpStore.findActors({ sourceActor: { actorID: "actor1" }, line: 10 })) {
     if (first) {
       do_check_eq(bp.column, undefined,
                   "Should always get the whole line breakpoint first");
       first = false;
     } else {
       do_check_neq(bp.column, undefined,
--- a/toolkit/devtools/server/tests/unit/test_forwardingprefix.js
+++ b/toolkit/devtools/server/tests/unit/test_forwardingprefix.js
@@ -99,17 +99,17 @@ function tryActors(aReachables, aComplet
 
 /*
  * With no forwarding established, sending messages to root should work,
  * but sending messages to prefixed actor names, or anyone else, should get
  * an error.
  */
 function TestNoForwardingYet()
 {
-  tryActors(Set(['root']), run_next_test);
+  tryActors(new Set(['root']), run_next_test);
 }
 
 /*
  * Create a new pipe connection which forwards its reply packets to
  * gMainConnection's client, and to which gMainConnection forwards packets
  * directed to actors whose names begin with |aPrefix + '/'|, and.
  *
  * Return an object { conn, transport }, as for newConnection.
@@ -133,31 +133,31 @@ function createSubconnection1()
   gSubconnection1 = conn;
   transport.ready();
   gClient.expectReply('prefix1/root', (aReply) => run_next_test());
 }
 
 // Establish forwarding, but don't put any actors in that server.
 function TestForwardPrefix1OnlyRoot()
 {
-  tryActors(Set(['root', 'prefix1/root']), run_next_test);
+  tryActors(new Set(['root', 'prefix1/root']), run_next_test);
 }
 
 /* Create a third root actor, to which we can forward things. */
 function createSubconnection2()
 {
   let { conn, transport } = newSubconnection('prefix2');
   gSubconnection2 = conn;
   transport.ready();
   gClient.expectReply('prefix2/root', (aReply) => run_next_test());
 }
 
 function TestForwardPrefix12OnlyRoot()
 {
-  tryActors(Set(['root', 'prefix1/root', 'prefix2/root']), run_next_test);
+  tryActors(new Set(['root', 'prefix1/root', 'prefix2/root']), run_next_test);
 }
 
 // A dumb actor that implements 'echo'.
 //
 // It's okay that both subconnections' actors behave identically, because
 // the reply-sending code attaches the replying actor's name to the packet,
 // so simply matching the 'from' field in the reply ensures that we heard
 // from the right actor.
@@ -178,19 +178,19 @@ EchoActor.prototype.requestTypes = {
 };
 
 function TestForwardPrefix12WithActor1()
 {
   let actor = new EchoActor(gSubconnection1)
   actor.actorID = 'prefix1/actor';
   gSubconnection1.addActor(actor);
 
-  tryActors(Set(['root', 'prefix1/root', 'prefix1/actor', 'prefix2/root']), run_next_test);
+  tryActors(new Set(['root', 'prefix1/root', 'prefix1/actor', 'prefix2/root']), run_next_test);
 }
 
 function TestForwardPrefix12WithActor12()
 {
   let actor = new EchoActor(gSubconnection2)
   actor.actorID = 'prefix2/actor';
   gSubconnection2.addActor(actor);
 
-  tryActors(Set(['root', 'prefix1/root', 'prefix1/actor', 'prefix2/root', 'prefix2/actor']), run_next_test);
+  tryActors(new Set(['root', 'prefix1/root', 'prefix1/actor', 'prefix2/root', 'prefix2/actor']), run_next_test);
 }
--- a/toolkit/mozapps/installer/package-name.mk
+++ b/toolkit/mozapps/installer/package-name.mk
@@ -16,17 +16,17 @@ MOZ_PKG_VERSION = $(MOZ_APP_VERSION)
 endif
 
 ifndef MOZ_PKG_PLATFORM
 MOZ_PKG_PLATFORM := $(TARGET_OS)-$(TARGET_CPU)
 
 # TARGET_OS/TARGET_CPU may be unintuitive, so we hardcode some special formats
 ifeq ($(OS_ARCH),WINNT)
 ifeq ($(TARGET_CPU),x86_64)
-MOZ_PKG_PLATFORM := win64-$(TARGET_CPU)
+MOZ_PKG_PLATFORM := win64
 else
 MOZ_PKG_PLATFORM := win32
 endif
 endif
 ifeq ($(OS_ARCH),Darwin)
 ifdef UNIVERSAL_BINARY
 MOZ_PKG_PLATFORM := mac
 else
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -32,17 +32,23 @@
 #include "gfx2DGlue.h"
 #include "gfxPlatform.h"
 #include "VsyncSource.h"
 
 #if ANDROID_VERSION >= 17
 #include "libdisplay/FramebufferSurface.h"
 #include "gfxPrefs.h"
 #include "nsThreadUtils.h"
+#endif
 
+#if ANDROID_VERSION >= 21
+#ifndef HWC_BLIT
+#define HWC_BLIT 0xFF
+#endif
+#elif ANDROID_VERSION >= 17
 #ifndef HWC_BLIT
 #define HWC_BLIT (HWC_FRAMEBUFFER_TARGET + 1)
 #endif
 #endif
 
 #ifdef LOG_TAG
 #undef LOG_TAG
 #endif
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -9,16 +9,17 @@
 
 #include "nsTArrayForwardDeclare.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
+#include "mozilla/ReverseIterator.h"
 #include "mozilla/TypeTraits.h"
 
 #include <string.h>
 
 #include "nsCycleCollectionNoteChild.h"
 #include "nsAlgorithm.h"
 #include "nscore.h"
 #include "nsQuickSort.h"
@@ -763,16 +764,20 @@ public:
   typedef typename nsTArray_CopyChooser<E>::Type     copy_type;
   typedef nsTArray_base<Alloc, copy_type>            base_type;
   typedef typename base_type::size_type              size_type;
   typedef typename base_type::index_type             index_type;
   typedef E                                          elem_type;
   typedef nsTArray_Impl<E, Alloc>                    self_type;
   typedef nsTArrayElementTraits<E>                   elem_traits;
   typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type;
+  typedef elem_type*                                 iterator;
+  typedef const elem_type*                           const_iterator;
+  typedef mozilla::ReverseIterator<elem_type*>       reverse_iterator;
+  typedef mozilla::ReverseIterator<const elem_type*>