merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 09 Oct 2015 11:43:07 +0200
changeset 267027 d01dd42e654b8735d86f9e7c723cc869a3b56798
parent 267026 462074ffada4969135709b86de53210a277b11d3 (current diff)
parent 266963 4fed2969228466303141ed72f29cba5d123ea079 (diff)
child 267028 3540caeae07fcc81c94de4af7dae732847b7253d
child 267087 814b316fd833702970f0d73e3cda34032689d2c1
child 267100 9ac872ca63fafbf1f9513064a38b0f700b2f07f9
push id66353
push usercbook@mozilla.com
push dateFri, 09 Oct 2015 12:26:06 +0000
treeherdermozilla-inbound@3540caeae07f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
d01dd42e654b / 44.0a1 / 20151009030230 / files
nightly linux64
d01dd42e654b / 44.0a1 / 20151009030230 / files
nightly mac
d01dd42e654b / 44.0a1 / 20151009030230 / files
nightly win32
d01dd42e654b / 44.0a1 / 20151009030230 / files
nightly win64
d01dd42e654b / 44.0a1 / 20151009030230 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
mobile/android/base/GeckoAppShell.java
mobile/android/base/gfx/InputConnectionHandler.java
mobile/android/base/moz.build
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64/opt-static-analysis
@@ -0,0 +1,19 @@
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_PACKAGE_TESTS=0
+MOZ_AUTOMATION_L10N_CHECK=0
+
+. $topsrcdir/build/macosx/mozconfig.common
+
+ac_add_options --disable-debug
+ac_add_options --enable-optimize
+ac_add_options --enable-dmd
+
+# Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
+ac_add_options --enable-warnings-as-errors
+
+ac_add_options --enable-clang-plugin
+
+. "$topsrcdir/build/macosx/mozconfig.rust"
+. "$topsrcdir/build/mozconfig.common.override"
+. "$topsrcdir/build/mozconfig.cache"
+
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,12 +1,12 @@
 [
 {
 "clang_version": "r247539"
 }, 
 {
 "size": 93197192,
 "digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
 "algorithm": "sha512", 
-"filename": "clang.tar.bz2",
+"filename": "clang.tar.xz",
 "unpack": true,
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,17 +1,17 @@
 [
 {
 "clang_version": "r247539"
 },
 {
 "size": 97314461,
 "digest": "9a74670fa917f760a4767923485d5166bbd258a8023c8aeb899b8c4d22f2847be76508ac5f26d7d2193318a2bb368a71bc62888d1bfe9d81eb45329a60451aa4",
 "algorithm": "sha512",
-"filename": "clang.tar.xz",
+"filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -61,25 +61,47 @@ public class CodeGenerator {
                         "public mozilla::jni::NativeImpl<" + unqualifiedName + ", Impl>\n" +
                 "{\n");
     }
 
     private String getTraitsName(String uniqueName, boolean includeScope) {
         return (includeScope ? clsName + "::" : "") + uniqueName + "_t";
     }
 
+    /**
+     * Return the C++ type name for this class or any class within the chain
+     * of declaring classes, if the target class matches the given type.
+     *
+     * Return null if the given type does not match any class searched.
+     */
+    private String getMatchingClassType(final Class<?> type) {
+        Class<?> cls = this.cls;
+        String clsName = this.clsName;
+
+        while (cls != null) {
+            if (type == cls) {
+                return clsName;
+            }
+            cls = cls.getDeclaringClass();
+            clsName = clsName.substring(0, Math.max(0, clsName.lastIndexOf("::")));
+        }
+        return null;
+    }
+
     private String getNativeParameterType(Class<?> type, AnnotationInfo info) {
-        if (type == cls) {
+        final String clsName = getMatchingClassType(type);
+        if (clsName != null) {
             return Utils.getUnqualifiedName(clsName) + "::Param";
         }
         return Utils.getNativeParameterType(type, info);
     }
 
     private String getNativeReturnType(Class<?> type, AnnotationInfo info) {
-        if (type == cls) {
+        final String clsName = getMatchingClassType(type);
+        if (clsName != null) {
             return Utils.getUnqualifiedName(clsName) + "::LocalRef";
         }
         return Utils.getNativeReturnType(type, info);
     }
 
     private void generateMember(AnnotationInfo info, Member member,
                                 String uniqueName, Class<?> type, Class<?>[] argTypes) {
         final StringBuilder args = new StringBuilder();
@@ -365,16 +387,17 @@ public class CodeGenerator {
         }
 
         final boolean isStatic = Utils.isStatic(field);
         final boolean isFinal = Utils.isFinal(field);
 
         if (isStatic && isFinal && (type.isPrimitive() || type == String.class)) {
             Object val = null;
             try {
+                field.setAccessible(true);
                 val = field.get(null);
             } catch (final IllegalAccessException e) {
             }
 
             if (val != null && type.isPrimitive()) {
                 // For static final primitive fields, we can use a "static const" declaration.
                 header.append(
                     "public:\n" +
--- a/build/unix/build-clang/build-clang.py
+++ b/build/unix/build-clang/build-clang.py
@@ -59,17 +59,17 @@ def updated_env(env):
     yield
     os.environ.clear()
     os.environ.update(old_env)
 
 
 def build_tar_package(tar, name, base, directory):
     name = os.path.realpath(name)
     run_in(base, [tar,
-                  "-c -%s -f" % "J" if ".xz" in name else "j",
+                  "-c -%s -f" % ("J" if ".xz" in name else "j"),
                   name, directory])
 
 
 def copy_dir_contents(src, dest):
     for f in glob.glob("%s/*" % src):
         try:
             destname = "%s/%s" % (dest, os.path.basename(f))
             shutil.copytree(f, destname)
--- a/devtools/client/styleinspector/test/browser_ruleview_authored.js
+++ b/devtools/client/styleinspector/test/browser_ruleview_authored.js
@@ -50,26 +50,27 @@ function* basicTest() {
     let prop = rule.textProps[i];
     is(prop.name, expected[i].name, "test name for prop " + i);
     is(prop.overridden, expected[i].overridden,
        "test overridden for prop " + i);
   }
 }
 
 function* overrideTest() {
-  let gradientText = "(45deg, rgba(255,255,255,0.2) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.2) 75%, transparent 75%, transparent);";
+  let gradientText1 = "(orange, blue);";
+  let gradientText2 = "(pink, teal);";
 
   let view =
       yield createTestContent("#testid {" +
-                              "  background-image: -moz-linear-gradient" +
-                              gradientText +
-                              "  background-image: -webkit-linear-gradient" +
-                              gradientText +
                               "  background-image: linear-gradient" +
-                              gradientText +
+                              gradientText1 +
+                              "  background-image: -ms-linear-gradient" +
+                              gradientText2 +
+                              "  background-image: linear-gradient" +
+                              gradientText2 +
                               "} ");
 
   let elementStyle = view._elementStyle;
   let rule = elementStyle.rules[1];
 
   // Initially the last property should be active.
   for (let i = 0; i < 3; ++i) {
     let prop = rule.textProps[i];
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -4101,21 +4101,16 @@ this.DOMApplicationRegistry = {
         eventType: "downloaderror",
         manifestURL:  aNewApp.manifestURL
       });
     });
     AppDownloadManager.remove(aNewApp.manifestURL);
   },
 
   doUninstall: Task.async(function*(aData, aMm) {
-    // The yields here could get stuck forever, so we only hold
-    // a weak reference to the message manager while yielding, to avoid
-    // leaking the whole page associationed with the message manager.
-    aMm = Cu.getWeakReference(aMm);
-
     let response = "Webapps:Uninstall:Return:OK";
 
     try {
       aData.app = yield this._getAppWithManifest(aData.manifestURL);
 
       if (this.kAndroid == aData.app.kind) {
         debug("Uninstalling android app " + aData.app.origin);
         let [packageName, className] =
@@ -4135,19 +4130,17 @@ this.DOMApplicationRegistry = {
           yield this._promptForUninstall(aData);
         }
       }
     } catch (error) {
       aData.error = error;
       response = "Webapps:Uninstall:Return:KO";
     }
 
-    if ((aMm = aMm.get())) {
-      aMm.sendAsyncMessage(response, this.formatMessage(aData));
-    }
+    aMm.sendAsyncMessage(response, this.formatMessage(aData));
   }),
 
   uninstall: function(aManifestURL) {
     return this._getAppWithManifest(aManifestURL)
       .then(this._uninstallApp.bind(this));
   },
 
   _uninstallApp: Task.async(function*(aApp) {
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -109,19 +109,17 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_E
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(EventSource,
                                                DOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrc)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadGroup)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHttpChannel)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnicodeDecoder)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource,
                                                 DOMEventTargetHelper)
   tmp->Close();
@@ -485,58 +483,16 @@ EventSource::OnStopRequest(nsIRequest *a
   NS_ENSURE_STATE(event);
 
   rv = NS_DispatchToMainThread(event);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-/**
- * Simple helper class that just forwards the redirect callback back
- * to the EventSource.
- */
-class AsyncVerifyRedirectCallbackFwr final : public nsIAsyncVerifyRedirectCallback
-{
-public:
-  explicit AsyncVerifyRedirectCallbackFwr(EventSource* aEventsource)
-    : mEventSource(aEventsource)
-  {
-  }
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
-
-  // nsIAsyncVerifyRedirectCallback implementation
-  NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult) override
-  {
-    nsresult rv = mEventSource->OnRedirectVerifyCallback(aResult);
-    if (NS_FAILED(rv)) {
-      mEventSource->mErrorLoadOnRedirect = true;
-      mEventSource->DispatchFailConnection();
-    }
-
-    return NS_OK;
-  }
-
-private:
-  ~AsyncVerifyRedirectCallbackFwr() {}
-  nsRefPtr<EventSource> mEventSource;
-};
-
-NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr, mEventSource)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackFwr)
-
 //-----------------------------------------------------------------------------
 // EventSource::nsIChannelEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 EventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
                                     nsIChannel *aNewChannel,
                                     uint32_t    aFlags,
@@ -559,99 +515,48 @@ EventSource::AsyncOnChannelRedirect(nsIC
     (NS_SUCCEEDED(newURI->SchemeIs("https", &isValidScheme)) && isValidScheme);
 
   rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv) || !isValidScheme) {
      DispatchFailConnection();
      return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  // Prepare to receive callback
-  mRedirectFlags = aFlags;
-  mRedirectCallback = aCallback;
-  mNewRedirectChannel = aNewChannel;
-
-  if (mChannelEventSink) {
-    nsRefPtr<AsyncVerifyRedirectCallbackFwr> fwd =
-      new AsyncVerifyRedirectCallbackFwr(this);
-
-    rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
-                                                   aNewChannel,
-                                                   aFlags, fwd);
-    if (NS_FAILED(rv)) {
-      mRedirectCallback = nullptr;
-      mNewRedirectChannel = nullptr;
-      mErrorLoadOnRedirect = true;
-      DispatchFailConnection();
-    }
-    return rv;
-  }
-  OnRedirectVerifyCallback(NS_OK);
-  return NS_OK;
-}
-
-nsresult
-EventSource::OnRedirectVerifyCallback(nsresult aResult)
-{
-  MOZ_ASSERT(mRedirectCallback, "mRedirectCallback not set in callback");
-  MOZ_ASSERT(mNewRedirectChannel,
-             "mNewRedirectChannel not set in callback");
-
-  NS_ENSURE_SUCCESS(aResult, aResult);
-
   // update our channel
 
-  mHttpChannel = do_QueryInterface(mNewRedirectChannel);
+  mHttpChannel = do_QueryInterface(aNewChannel);
   NS_ENSURE_STATE(mHttpChannel);
 
-  nsresult rv = SetupHttpChannel();
+  rv = SetupHttpChannel();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if ((mRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
+  if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
     rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  mNewRedirectChannel = nullptr;
-
-  mRedirectCallback->OnRedirectVerifyCallback(aResult);
-  mRedirectCallback = nullptr;
+  aCallback->OnRedirectVerifyCallback(NS_OK);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // EventSource::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 EventSource::GetInterface(const nsIID & aIID,
                           void **aResult)
 {
-  // Make sure to return ourselves for the channel event sink interface,
-  // no matter what.  We can forward these to mNotificationCallbacks
-  // if it wants to get notifications for them.  But we
-  // need to see these notifications for proper functioning.
   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
-    mChannelEventSink = do_GetInterface(mNotificationCallbacks);
     *aResult = static_cast<nsIChannelEventSink*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
 
-  // Now give mNotificationCallbacks (if non-null) a chance to return the
-  // desired interface.
-  if (mNotificationCallbacks) {
-    nsresult rv = mNotificationCallbacks->GetInterface(aIID, aResult);
-    if (NS_SUCCEEDED(rv)) {
-      NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
-      return rv;
-    }
-  }
-
   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
       aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
     nsresult rv = CheckInnerWindowCorrectness();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
 
     nsCOMPtr<nsIPromptFactory> wwatch =
       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -802,22 +707,24 @@ EventSource::InitChannelAndRequestEventS
   NS_ENSURE_SUCCESS(rv, rv);
 
   mHttpChannel = do_QueryInterface(channel);
   NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE);
 
   rv = SetupHttpChannel();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
-  mHttpChannel->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
-  if (notificationCallbacks != this) {
-    mNotificationCallbacks = notificationCallbacks;
-    mHttpChannel->SetNotificationCallbacks(this);
+#ifdef DEBUG
+  {
+    nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
+    mHttpChannel->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
+    MOZ_ASSERT(!notificationCallbacks);
   }
+#endif
+  mHttpChannel->SetNotificationCallbacks(this);
 
   // Start reading from the channel
   rv = mHttpChannel->AsyncOpen2(this);
   if (NS_FAILED(rv)) {
     DispatchFailConnection();
     return rv;
   }
   mWaitingForOnStopRequest = true;
@@ -873,21 +780,17 @@ EventSource::ResetConnection()
   }
 
   if (mUnicodeDecoder) {
     mUnicodeDecoder->Reset();
   }
   mLastConvertionResult = NS_OK;
 
   mHttpChannel = nullptr;
-  mNotificationCallbacks = nullptr;
-  mChannelEventSink = nullptr;
   mStatus = PARSE_STATE_OFF;
-  mRedirectCallback = nullptr;
-  mNewRedirectChannel = nullptr;
 
   mReadyState = CONNECTING;
 
   return NS_OK;
 }
 
 void
 EventSource::ReestablishConnection()
--- a/dom/base/EventSource.h
+++ b/dom/base/EventSource.h
@@ -29,28 +29,25 @@
 class nsPIDOMWindow;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
-class AsyncVerifyRedirectCallbackFwr;
 struct EventSourceInit;
 
 class EventSource final : public DOMEventTargetHelper
                         , public nsIObserver
                         , public nsIStreamListener
                         , public nsIChannelEventSink
                         , public nsIInterfaceRequestor
                         , public nsSupportsWeakReference
 {
-friend class AsyncVerifyRedirectCallbackFwr;
-
 public:
   explicit EventSource(nsPIDOMWindow* aOwnerWindow);
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(
     EventSource, DOMEventTargetHelper)
 
   NS_DECL_NSIOBSERVER
   NS_DECL_NSISTREAMLISTENER
@@ -227,37 +224,26 @@ protected:
   // used while reading the input streams
   nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
   nsresult mLastConvertionResult;
   nsString mLastFieldName;
   nsString mLastFieldValue;
 
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
-  /**
-   * The notification callbacks the channel had initially.
-   * We want to forward things here as needed.
-   */
-  nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks;
-  nsCOMPtr<nsIChannelEventSink>   mChannelEventSink;
-
   nsCOMPtr<nsIHttpChannel> mHttpChannel;
 
   nsCOMPtr<nsITimer> mTimer;
 
   uint16_t mReadyState;
   nsString mOriginalURL;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsString mOrigin;
 
-  uint32_t mRedirectFlags;
-  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
-  nsCOMPtr<nsIChannel> mNewRedirectChannel;
-
   // Event Source owner information:
   // - the script file name
   // - source code line number and column number where the Event Source object
   //   was constructed.
   // - the ID of the inner window where the script lives. Note that this may not
   //   be the same as the Event Source owner window.
   // These attributes are used for error reporting.
   nsString mScriptFile;
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -269,19 +269,33 @@ static const DOMIfaceAndProtoJSClass Win
 // static
 JSObject*
 WindowNamedPropertiesHandler::Create(JSContext* aCx,
                                      JS::Handle<JSObject*> aProto)
 {
   // Note: since the scope polluter proxy lives on the window's prototype
   // chain, it needs a singleton type to avoid polluting type information
   // for properties on the window.
-  JS::Rooted<JSObject*> gsp(aCx);
   js::ProxyOptions options;
   options.setSingleton(true);
   options.setClass(&WindowNamedPropertiesClass.mBase);
-  return js::NewProxyObject(aCx, WindowNamedPropertiesHandler::getInstance(),
-                            JS::NullHandleValue, aProto,
-                            options);
+
+  JS::Rooted<JSObject*> gsp(aCx);
+  gsp = js::NewProxyObject(aCx, WindowNamedPropertiesHandler::getInstance(),
+                           JS::NullHandleValue, aProto,
+                           options);
+  if (!gsp) {
+    return nullptr;
+  }
+
+  bool succeeded;
+  if (!JS_SetImmutablePrototype(aCx, gsp, &succeeded)) {
+    return nullptr;
+  }
+  MOZ_ASSERT(succeeded,
+             "errors making the [[Prototype]] of the named properties object "
+             "immutable should have been JSAPI failures, not !succeeded");
+
+  return gsp;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -195,16 +195,59 @@ MessageEvent::InitMessageEvent(const nsA
   mOrigin = aOrigin;
   mLastEventId = aLastEventId;
   mWindowSource = aSource;
 
   return NS_OK;
 }
 
 void
+MessageEvent::InitMessageEvent(JSContext* aCx, const nsAString& aType,
+                               bool aCanBubble, bool aCancelable,
+                               JS::Handle<JS::Value> aData,
+                               const nsAString& aOrigin,
+                               const nsAString& aLastEventId,
+                               const Nullable<WindowProxyOrMessagePort>& aSource,
+                               const Nullable<Sequence<OwningNonNull<MessagePort>>>& aPorts,
+                               ErrorResult& aRv)
+{
+  aRv = Event::InitEvent(aType, aCanBubble, aCancelable);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  mData = aData;
+  mozilla::HoldJSObjects(this);
+  mOrigin = aOrigin;
+  mLastEventId = aLastEventId;
+
+  mWindowSource = nullptr;
+  mPortSource = nullptr;
+
+  if (!aSource.IsNull()) {
+    if (aSource.Value().IsWindowProxy()) {
+      mWindowSource = aSource.Value().GetAsWindowProxy();
+    } else {
+      mPortSource = &aSource.Value().GetAsMessagePort();
+    }
+  }
+
+  mPorts = nullptr;
+
+  if (!aPorts.IsNull()) {
+    nsTArray<nsRefPtr<MessagePort>> ports;
+    for (uint32_t i = 0, len = aPorts.Value().Length(); i < len; ++i) {
+      ports.AppendElement(aPorts.Value()[i]);
+    }
+
+    mPorts = new MessagePortList(static_cast<Event*>(this), ports);
+  }
+}
+
+void
 MessageEvent::SetPorts(MessagePortList* aPorts)
 {
   MOZ_ASSERT(!mPorts && aPorts);
   mPorts = aPorts;
 }
 
 void
 MessageEvent::SetSource(mozilla::dom::MessagePort* aPort)
@@ -222,13 +265,13 @@ MessageEvent::SetSource(mozilla::dom::wo
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 already_AddRefed<MessageEvent>
 NS_NewDOMMessageEvent(EventTarget* aOwner,
                       nsPresContext* aPresContext,
-                      WidgetEvent* aEvent) 
+                      WidgetEvent* aEvent)
 {
   nsRefPtr<MessageEvent> it = new MessageEvent(aOwner, aPresContext, aEvent);
   return it.forget();
 }
--- a/dom/events/MessageEvent.h
+++ b/dom/events/MessageEvent.h
@@ -3,27 +3,29 @@
 /* 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_MessageEvent_h_
 #define mozilla_dom_MessageEvent_h_
 
 #include "mozilla/dom/Event.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/MessagePortList.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMMessageEvent.h"
-#include "mozilla/dom/MessagePortList.h"
 
 namespace mozilla {
 namespace dom {
 
 struct MessageEventInit;
 class MessagePort;
 class MessagePortList;
 class OwningWindowProxyOrMessagePortOrClient;
+class WindowProxyOrMessagePort;
 
 namespace workers {
 
 class ServiceWorkerClient;
 
 } // namespace workers
 
 /**
@@ -80,16 +82,23 @@ public:
               ErrorResult& aRv);
 
   static already_AddRefed<MessageEvent>
   Constructor(EventTarget* aEventTarget,
               const nsAString& aType,
               const MessageEventInit& aEventInit,
               ErrorResult& aRv);
 
+  void InitMessageEvent(JSContext* aCx, const nsAString& aType, bool aCanBubble,
+                        bool aCancelable, JS::Handle<JS::Value> aData,
+                        const nsAString& aOrigin, const nsAString& aLastEventId,
+                        const Nullable<WindowProxyOrMessagePort>& aSource,
+                        const Nullable<Sequence<OwningNonNull<MessagePort>>>& aPorts,
+                        ErrorResult& aRv);
+
 protected:
   ~MessageEvent();
 
 private:
   JS::Heap<JS::Value> mData;
   nsString mOrigin;
   nsString mLastEventId;
   nsCOMPtr<nsIDOMWindow> mWindowSource;
--- a/dom/events/test/test_messageEvent.html
+++ b/dom/events/test/test_messageEvent.html
@@ -8,16 +8,33 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 848294</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <script type="application/javascript">
+  function testMessageEvent(e, test) {
+    ok(e, "MessageEvent created");
+    is(e.type, 'message', 'MessageEvent.type is right');
+
+    is(e.data, 'data' in test ? test.data : undefined, 'MessageEvent.data is ok');
+    is(e.origin, 'origin' in test ? test.origin : '', 'MessageEvent.origin is ok');
+    is(e.lastEventId, 'lastEventId' in test ? test.lastEventId : '', 'MessageEvent.lastEventId is ok');
+    is(e.source, 'source' in test ? test.source : null, 'MessageEvent.source is ok');
+
+    if (test.ports != undefined) {
+      is(e.ports.length, test.ports.length, 'MessageEvent.ports is ok');
+      is(e.ports, e.ports, 'MessageEvent.ports is ok');
+    } else {
+      ok(!('ports' in test) || test.ports == null, 'MessageEvent.ports is ok');
+    }
+  }
+
   function runTest() {
     var channel = new MessageChannel();
 
     var tests = [
       {},
       { data: 42 },
       { data: {} },
       { data: true, origin: 'wow' },
@@ -28,30 +45,26 @@ https://bugzilla.mozilla.org/show_bug.cg
       { data: window, source: channel.port1, ports: [ channel.port1, channel.port2 ] },
       { data: null, ports: null },
     ];
 
     while (tests.length) {
       var test = tests.shift();
 
       var e = new MessageEvent('message', test);
-      ok(e, "MessageEvent created");
-      is(e.type, 'message', 'MessageEvent.type is right');
+      testMessageEvent(e, test);
 
-      is(e.data, 'data' in test ? test.data : undefined, 'MessageEvent.data is ok');
-      is(e.origin, 'origin' in test ? test.origin : '', 'MessageEvent.origin is ok');
-      is(e.lastEventId, 'lastEventId' in test ? test.lastEventId : '', 'MessageEvent.lastEventId is ok');
-      is(e.source, 'source' in test ? test.source : null, 'MessageEvent.source is ok');
-
-      if (test.ports != undefined) {
-        is(e.ports.length, test.ports.length, 'MessageEvent.ports is ok');
-        is(e.ports, e.ports, 'MessageEvent.ports is ok');
-      } else {
-        ok(!('ports' in test) || test.ports == null, 'MessageEvent.ports is ok');
-      }
+      e = new MessageEvent('message');
+      e.initMessageEvent('message', true, true,
+                         'data' in test ? test.data : undefined,
+                         'origin' in test ? test.origin : '',
+                         'lastEventId' in test ? test.lastEventId : '',
+                         'source' in test ? test.source : null,
+                         'ports' in test ? test.ports : null);
+      testMessageEvent(e, test);
     }
 
     try {
       var e = new MessageEvent('foobar', { source: 42 });
       ok(false, "Source has to be a window or a port");
     } catch(e) {
       ok(true, "Source has to be a window or a port");
     }
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -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/. */
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/FetchDriver.h"
 
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIDocument.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIJARChannel.h"
 #include "nsIScriptSecurityManager.h"
@@ -37,17 +38,17 @@
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FetchDriver,
                   nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
-                  nsIAsyncVerifyRedirectCallback, nsIThreadRetargetableStreamListener)
+                  nsIThreadRetargetableStreamListener)
 
 FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                          nsILoadGroup* aLoadGroup)
   : mPrincipal(aPrincipal)
   , mLoadGroup(aLoadGroup)
   , mRequest(aRequest)
   , mFetchRecursionCount(0)
   , mCORSFlagEverSet(false)
@@ -454,17 +455,23 @@ FetchDriver::HttpFetch(bool aCORSFlag, b
   mLoadGroup = nullptr;
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     return rv;
   }
 
   // Insert ourselves into the notification callbacks chain so we can handle
   // cross-origin redirects.
-  chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
+#ifdef DEBUG
+  {
+    nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
+    chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
+    MOZ_ASSERT(!notificationCallbacks);
+  }
+#endif
   chan->SetNotificationCallbacks(this);
 
   // FIXME(nsm): Bug 1120715.
   // Step 3.4 "If request's cache mode is default and request's header list
   // contains a header named `If-Modified-Since`, `If-None-Match`,
   // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
   // to no-store."
 
@@ -910,18 +917,16 @@ FetchDriver::OnStopRequest(nsIRequest* a
 NS_IMETHODIMP
 FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
                                     nsIChannel* aNewChannel,
                                     uint32_t aFlags,
                                     nsIAsyncVerifyRedirectCallback *aCallback)
 {
   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
 
-  nsresult rv;
-
   // HTTP Fetch step 5, "redirect status", step 1
   if (NS_WARN_IF(mRequest->GetRedirectMode() == RequestRedirect::Error)) {
     aOldChannel->Cancel(NS_BINDING_FAILED);
     return NS_BINDING_FAILED;
   }
 
   // HTTP Fetch step 5, "redirect status", steps 2 through 6 are automatically
   // handled by necko before calling AsyncOnChannelRedirect() with the new
@@ -965,37 +970,95 @@ FetchDriver::AsyncOnChannelRedirect(nsIC
   // The following steps are from HTTP Fetch step 5, "redirect status", step 11
   // which requires the RequestRedirect to be "follow".
   MOZ_ASSERT(mRequest->GetRedirectMode() == RequestRedirect::Follow);
 
   // HTTP Fetch step 5, "redirect status", steps 11.1 and 11.2 block redirecting
   // to a URL with credentials in CORS mode.  This is implemented in
   // nsCORSListenerProxy.
 
-  mRedirectCallback = aCallback;
-  mOldRedirectChannel = aOldChannel;
-  mNewRedirectChannel = aNewChannel;
+  // On a successful redirect we perform the following substeps of HTTP Fetch,
+  // step 5, "redirect status", step 11.
 
-  nsCOMPtr<nsIChannelEventSink> outer =
-    do_GetInterface(mNotificationCallbacks);
-  if (outer) {
-    // The callee is supposed to call OnRedirectVerifyCallback() on success,
-    // and nobody has to call it on failure, so we can just return after this
-    // block.
-    rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this);
-    if (NS_FAILED(rv)) {
-      aOldChannel->Cancel(rv);
-      mRedirectCallback = nullptr;
-      mOldRedirectChannel = nullptr;
-      mNewRedirectChannel = nullptr;
-    }
+  // Step 11.5 "Append locationURL to request's url list." so that when we set the
+  // Response's URL from the Request's URL in Main Fetch, step 15, we get the
+  // final value. Note, we still use a single URL value instead of a list.
+  nsCOMPtr<nsIURI> newURI;
+  nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
+  if (NS_FAILED(rv)) {
+    aOldChannel->Cancel(rv);
     return rv;
   }
 
-  (void) OnRedirectVerifyCallback(NS_OK);
+  // We need to update our request's URL.
+  nsAutoCString newUrl;
+  newURI->GetSpec(newUrl);
+  mRequest->SetURL(newUrl);
+
+  // Implement Main Fetch step 8 again on redirect.
+  MainFetchOp nextOp = SetTaintingAndGetNextOp(mCORSFlagEverSet);
+
+  if (nextOp.mType == NETWORK_ERROR) {
+    // Cancel the channel if Main Fetch blocks the redirect from continuing.
+    aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+  // Otherwise, we rely on necko and the CORS proxy to do the right thing
+  // as the redirect is followed.  In general this means basic or http
+  // fetch.  If we've ever been CORS, we need to stay CORS.
+  MOZ_ASSERT(nextOp.mType == BASIC_FETCH || nextOp.mType == HTTP_FETCH);
+  MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mType == HTTP_FETCH);
+  MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mCORSFlag);
+
+  // Examine and possibly set the LOAD_ANONYMOUS flag on the channel.
+  nsLoadFlags flags;
+  rv = aNewChannel->GetLoadFlags(&flags);
+  if (NS_FAILED(rv)) {
+    aOldChannel->Cancel(rv);
+    return rv;
+  }
+
+  if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
+      mRequest->GetResponseTainting() == InternalRequest::RESPONSETAINT_OPAQUE) {
+    // In the case of a "no-cors" mode request with "same-origin" credentials,
+    // we have to set LOAD_ANONYMOUS manually here in order to avoid sending
+    // credentials on a cross-origin redirect.
+    flags |= nsIRequest::LOAD_ANONYMOUS;
+    rv = aNewChannel->SetLoadFlags(flags);
+    if (NS_FAILED(rv)) {
+      aOldChannel->Cancel(rv);
+      return rv;
+    }
+
+  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
+    // Make sure nothing in the redirect chain screws up our credentials
+    // settings.  LOAD_ANONYMOUS must be set if we RequestCredentials is "omit".
+    MOZ_ASSERT(flags & nsIRequest::LOAD_ANONYMOUS);
+
+  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
+             nextOp.mCORSFlag) {
+    // We also want to verify the LOAD_ANONYMOUS flag is set when we are in
+    // "same-origin" credentials mode and the CORS flag is set.  We can't
+    // unconditionally assert here, however, because the nsCORSListenerProxy
+    // will set the flag later in the redirect callback chain.  Instead,
+    // perform a weaker assertion here by checking if CORS flag was set
+    // before this redirect.  In that case LOAD_ANONYMOUS must still be set.
+    MOZ_ASSERT_IF(mCORSFlagEverSet, flags & nsIRequest::LOAD_ANONYMOUS);
+
+  } else {
+    // Otherwise, we should be sending credentials
+    MOZ_ASSERT(!(flags & nsIRequest::LOAD_ANONYMOUS));
+  }
+
+  // Track the CORSFlag through redirects.
+  mCORSFlagEverSet = mCORSFlagEverSet || nextOp.mCORSFlag;
+
+  aCallback->OnRedirectVerifyCallback(NS_OK);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FetchDriver::CheckListenerChain()
 {
   return NS_OK;
 }
@@ -1031,123 +1094,30 @@ NS_IMETHODIMP
 FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
 {
   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
     *aResult = static_cast<nsIChannelEventSink*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
 
-  nsresult rv;
-
-  if (mNotificationCallbacks) {
-    rv = mNotificationCallbacks->GetInterface(aIID, aResult);
-    if (NS_SUCCEEDED(rv)) {
-      NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
-      return rv;
-    }
-  }
-  else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
+  if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
     *aResult = static_cast<nsIStreamListener*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
-  else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
+  if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
     *aResult = static_cast<nsIRequestObserver*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
 
   return QueryInterface(aIID, aResult);
 }
 
-NS_IMETHODIMP
-FetchDriver::OnRedirectVerifyCallback(nsresult aResult)
-{
-  // On a successful redirect we perform the following substeps of HTTP Fetch,
-  // step 5, "redirect status", step 11.
-  if (NS_SUCCEEDED(aResult)) {
-    // Step 11.5 "Append locationURL to request's url list." so that when we set the
-    // Response's URL from the Request's URL in Main Fetch, step 15, we get the
-    // final value. Note, we still use a single URL value instead of a list.
-    nsCOMPtr<nsIURI> newURI;
-    nsresult rv = NS_GetFinalChannelURI(mNewRedirectChannel, getter_AddRefs(newURI));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aResult = rv;
-    } else {
-      // We need to update our request's URL.
-      nsAutoCString newUrl;
-      newURI->GetSpec(newUrl);
-      mRequest->SetURL(newUrl);
-    }
-  }
-
-  if (NS_FAILED(aResult)) {
-    mOldRedirectChannel->Cancel(aResult);
-  }
-
-  // Implement Main Fetch step 8 again on redirect.
-  MainFetchOp nextOp = SetTaintingAndGetNextOp(mCORSFlagEverSet);
-
-  if (nextOp.mType == NETWORK_ERROR) {
-    // Cancel the channel if Main Fetch blocks the redirect from continuing.
-    aResult = NS_ERROR_DOM_BAD_URI;
-    mOldRedirectChannel->Cancel(aResult);
-  } else {
-    // Otherwise, we rely on necko and the CORS proxy to do the right thing
-    // as the redirect is followed.  In general this means basic or http
-    // fetch.  If we've ever been CORS, we need to stay CORS.
-    MOZ_ASSERT(nextOp.mType == BASIC_FETCH || nextOp.mType == HTTP_FETCH);
-    MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mType == HTTP_FETCH);
-    MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mCORSFlag);
-
-    // Examine and possibly set the LOAD_ANONYMOUS flag on the channel.
-    nsLoadFlags flags;
-    aResult = mNewRedirectChannel->GetLoadFlags(&flags);
-    if (NS_SUCCEEDED(aResult)) {
-      if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
-          mRequest->GetResponseTainting() == InternalRequest::RESPONSETAINT_OPAQUE) {
-        // In the case of a "no-cors" mode request with "same-origin" credentials,
-        // we have to set LOAD_ANONYMOUS manually here in order to avoid sending
-        // credentials on a cross-origin redirect.
-        flags |= nsIRequest::LOAD_ANONYMOUS;
-        aResult = mNewRedirectChannel->SetLoadFlags(flags);
-
-      } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
-        // Make sure nothing in the redirect chain screws up our credentials
-        // settings.  LOAD_ANONYMOUS must be set if we RequestCredentials is "omit".
-        MOZ_ASSERT(flags & nsIRequest::LOAD_ANONYMOUS);
-
-      } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
-                 nextOp.mCORSFlag) {
-        // We also want to verify the LOAD_ANONYMOUS flag is set when we are in
-        // "same-origin" credentials mode and the CORS flag is set.  We can't
-        // unconditionally assert here, however, because the nsCORSListenerProxy
-        // will set the flag later in the redirect callback chain.  Instead,
-        // perform a weaker assertion here by checking if CORS flag was set
-        // before this redirect.  In that case LOAD_ANONYMOUS must still be set.
-        MOZ_ASSERT_IF(mCORSFlagEverSet, flags & nsIRequest::LOAD_ANONYMOUS);
-
-      } else {
-        // Otherwise, we should be sending credentials
-        MOZ_ASSERT(!(flags & nsIRequest::LOAD_ANONYMOUS));
-      }
-    }
-
-    // Track the CORSFlag through redirects.
-    mCORSFlagEverSet = mCORSFlagEverSet || nextOp.mCORSFlag;
-  }
-
-  mOldRedirectChannel = nullptr;
-  mNewRedirectChannel = nullptr;
-  mRedirectCallback->OnRedirectVerifyCallback(aResult);
-  mRedirectCallback = nullptr;
-  return NS_OK;
-}
-
 void
 FetchDriver::SetDocument(nsIDocument* aDocument)
 {
   // Cannot set document after Fetch() has been called.
   MOZ_ASSERT(mFetchRecursionCount == 0);
   mDocument = aDocument;
 }
 
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FetchDriver_h
 #define mozilla_dom_FetchDriver_h
 
 #include "nsAutoPtr.h"
-#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIStreamListener.h"
 #include "nsIThreadRetargetableStreamListener.h"
 #include "mozilla/nsRefPtr.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/net/ReferrerPolicy.h"
@@ -53,46 +52,40 @@ protected:
 
 private:
   bool mGotResponseAvailable;
 };
 
 class FetchDriver final : public nsIStreamListener,
                           public nsIChannelEventSink,
                           public nsIInterfaceRequestor,
-                          public nsIAsyncVerifyRedirectCallback,
                           public nsIThreadRetargetableStreamListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
-  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 
   explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                        nsILoadGroup* aLoadGroup);
   NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
 
   void
   SetDocument(nsIDocument* aDocument);
 
 private:
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsRefPtr<InternalRequest> mRequest;
   nsRefPtr<InternalResponse> mResponse;
   nsCOMPtr<nsIOutputStream> mPipeOutputStream;
   nsRefPtr<FetchDriverObserver> mObserver;
-  nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks;
-  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
-  nsCOMPtr<nsIChannel> mOldRedirectChannel;
-  nsCOMPtr<nsIChannel> mNewRedirectChannel;
   nsCOMPtr<nsIDocument> mDocument;
   uint32_t mFetchRecursionCount;
   bool mCORSFlagEverSet;
 
   DebugOnly<bool> mResponseAvailableCalled;
 
   FetchDriver() = delete;
   FetchDriver(const FetchDriver&) = delete;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1438,27 +1438,28 @@ TabParent::RecvPDocAccessibleConstructor
   }
 #endif
   return true;
 }
 
 a11y::DocAccessibleParent*
 TabParent::GetTopLevelDocAccessible() const
 {
+#ifdef ACCESSIBILITY
   // XXX Consider managing non top level PDocAccessibles with their parent
   // document accessible.
   const nsTArray<PDocAccessibleParent*>& docs = ManagedPDocAccessibleParent();
   size_t docCount = docs.Length();
   for (size_t i = 0; i < docCount; i++) {
     auto doc = static_cast<a11y::DocAccessibleParent*>(docs[i]);
     if (!doc->ParentDoc()) {
       return doc;
     }
   }
-
+#endif
   return nullptr;
 }
 
 PDocumentRendererParent*
 TabParent::AllocPDocumentRendererParent(const nsRect& documentRect,
                                         const gfx::Matrix& transform,
                                         const nsString& bgcolor,
                                         const uint32_t& renderFlags,
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -34,19 +34,19 @@ class nsIFrameLoader;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 class nsILoadContext;
 class nsIDocShell;
 
 namespace mozilla {
 
-  namespace a11y {
+namespace a11y {
 class DocAccessibleParent;
-  }
+}
 
 namespace jsipc {
 class CpowHolder;
 } // namespace jsipc
 
 namespace layers {
 class AsyncDragMetrics;
 struct FrameMetrics;
@@ -261,17 +261,17 @@ public:
     virtual bool
     RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                   PDocAccessibleParent* aParentDoc,
                                   const uint64_t& aParentID) override;
 
     /**
      * Return the top level doc accessible parent for this tab.
      */
-     a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
+    a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
 
     void LoadURL(nsIURI* aURI);
     // XXX/cjones: it's not clear what we gain by hiding these
     // message-sending functions under a layer of indirection and
     // eating the return values
     void Show(const ScreenIntSize& size, bool aParentIsActive);
     void UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size);
     void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -844,16 +844,20 @@ MediaDecoderStateMachine::OnVideoDecoded
   aVideoSample->AdjustForStartTime(StartTime());
   mDecodedVideoEndTime = video ? video->GetEndTime() : mDecodedVideoEndTime;
 
   SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
              (video ? video->mTime : -1),
              (video ? video->GetEndTime() : -1),
              (video ? video->mDiscontinuity : 0));
 
+  // Check frame validity here for every decoded frame in order to have a
+  // better chance to make the decision of turning off HW acceleration.
+  CheckFrameValidity(aVideoSample->As<VideoData>());
+
   switch (mState) {
     case DECODER_STATE_BUFFERING: {
       // If we're buffering, this may be the sample we need to stop buffering.
       // Save it and schedule the state machine.
       Push(video, MediaData::VIDEO_DATA);
       ScheduleStateMachine();
       return;
     }
@@ -2443,26 +2447,21 @@ MediaDecoderStateMachine::Reset()
 
   mPlaybackOffset = 0;
 
   nsCOMPtr<nsIRunnable> resetTask =
     NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
   DecodeTaskQueue()->Dispatch(resetTask.forget());
 }
 
-bool MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData)
+void
+MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData)
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  // If we've sent this frame before then only return the valid state,
-  // don't update the statistics.
-  if (aData->mSentToCompositor) {
-    return !aData->mImage || aData->mImage->IsValid();
-  }
-
   // Update corrupt-frames statistics
   if (aData->mImage && !aData->mImage->IsValid()) {
     FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
     frameStats.NotifyCorruptFrame();
     // If more than 10% of the last 30 frames have been corrupted, then try disabling
     // hardware acceleration. We use 10 as the corrupt value because RollingMean<>
     // only supports integer types.
     mCorruptFrames.insert(10);
@@ -2470,20 +2469,18 @@ bool MediaDecoderStateMachine::CheckFram
         frameStats.GetPresentedFrames() > 60 &&
         mCorruptFrames.mean() >= 2 /* 20% */) {
         nsCOMPtr<nsIRunnable> task =
           NS_NewRunnableMethod(mReader, &MediaDecoderReader::DisableHardwareAcceleration);
         DecodeTaskQueue()->Dispatch(task.forget());
         mCorruptFrames.clear();
       gfxCriticalNote << "Too many dropped/corrupted frames, disabling DXVA";
     }
-    return false;
   } else {
     mCorruptFrames.insert(0);
-    return true;
   }
 }
 
 void MediaDecoderStateMachine::RenderVideoFrames(int32_t aMaxFrames,
                                                  int64_t aClockTime,
                                                  const TimeStamp& aClockTimeStamp)
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -2495,17 +2492,17 @@ void MediaDecoderStateMachine::RenderVid
     return;
   }
 
   nsAutoTArray<ImageContainer::NonOwningImage,16> images;
   TimeStamp lastFrameTime;
   for (uint32_t i = 0; i < frames.Length(); ++i) {
     VideoData* frame = frames[i]->As<VideoData>();
 
-    bool valid = CheckFrameValidity(frame);
+    bool valid = !frame->mImage || frame->mImage->IsValid();
     frame->mSentToCompositor = true;
 
     if (!valid) {
       continue;
     }
 
     int64_t frameTime = frame->mTime;
     if (frameTime < 0) {
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -458,18 +458,19 @@ protected:
   void SetStartTime(int64_t aStartTimeUsecs);
 
   // Update only the state machine's current playback position (and duration,
   // if unknown).  Does not update the playback position on the decoder or
   // media element -- use UpdatePlaybackPosition for that.  Called on the state
   // machine thread, caller must hold the decoder lock.
   void UpdatePlaybackPositionInternal(int64_t aTime);
 
-  // Decode monitor must be held.
-  bool CheckFrameValidity(VideoData* aData);
+  // Decode monitor must be held. To determine if MDSM needs to turn off HW
+  // acceleration.
+  void CheckFrameValidity(VideoData* aData);
 
   // Sets VideoQueue images into the VideoFrameContainer. Called on the shared
   // state machine thread. Decode monitor must be held. The first aMaxFrames
   // (at most) are set.
   // aClockTime and aClockTimeStamp are used as the baseline for deriving
   // timestamps for the frames; when omitted, aMaxFrames must be 1 and
   // a null timestamp is passed to the VideoFrameContainer.
   // If the VideoQueue is empty, this does nothing.
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = e10s || buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined)
+skip-if = buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined)
 support-files =
   mediasource.js
   seek.webm seek.webm^headers^
   seek_lowres.webm seek_lowres.webm^headers^
   bipbop/bipbop2s.mp4 bipbop/bipbop2s.mp4^headers^
   bipbop/bipbopinit.mp4 bipbop/bipbop_audioinit.mp4 bipbop/bipbop_videoinit.mp4
   bipbop/bipbop1.m4s bipbop/bipbop_audio1.m4s bipbop/bipbop_video1.m4s
   bipbop/bipbop2.m4s bipbop/bipbop_audio2.m4s bipbop/bipbop_video2.m4s
--- a/dom/messagechannel/MessagePortList.h
+++ b/dom/messagechannel/MessagePortList.h
@@ -61,17 +61,17 @@ public:
   {
     aFound = aIndex < mPorts.Length();
     if (!aFound) {
       return nullptr;
     }
     return mPorts[aIndex];
   }
 
-public:
+private:
   nsCOMPtr<nsISupports> mOwner;
   nsTArray<nsRefPtr<MessagePort>> mPorts;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MessagePortList_h
--- a/dom/webidl/MessageEvent.webidl
+++ b/dom/webidl/MessageEvent.webidl
@@ -39,16 +39,22 @@ interface MessageEvent : Event {
   readonly attribute (WindowProxy or MessagePort or Client)? source;
 
   /**
    * Initializes this event with the given data, in a manner analogous to
    * the similarly-named method on the nsIDOMEvent interface, also setting the
    * data, origin, source, and lastEventId attributes of this appropriately.
    */
   readonly attribute MessagePortList? ports;
+
+  [Throws]
+  void initMessageEvent(DOMString type, boolean bubbles, boolean cancelable,
+                        any data, DOMString origin, DOMString lastEventId,
+                        (WindowProxy or MessagePort)? source,
+                        sequence<MessagePort>? ports);
 };
 
 dictionary MessageEventInit : EventInit {
   any data;
   DOMString origin;
   DOMString lastEventId;
   (Window or MessagePort)? source = null;
   sequence<MessagePort>? ports;
--- a/editor/libeditor/nsHTMLEditRules.cpp
+++ b/editor/libeditor/nsHTMLEditRules.cpp
@@ -6388,35 +6388,35 @@ nsHTMLEditRules::IsInListItem(nsINode* a
 // ReturnInHeader: do the right thing for returns pressed in headers
 //
 nsresult
 nsHTMLEditRules::ReturnInHeader(Selection* aSelection,
                                 nsIDOMNode *aHeader,
                                 nsIDOMNode *aNode,
                                 int32_t aOffset)
 {
-  NS_ENSURE_TRUE(aSelection && aHeader && aNode, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<Element> header = do_QueryInterface(aHeader);
+  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(aSelection && header && node, NS_ERROR_NULL_POINTER);
 
   // remeber where the header is
   int32_t offset;
   nsCOMPtr<nsIDOMNode> headerParent = nsEditor::GetNodeLocation(aHeader, &offset);
 
   // get ws code to adjust any ws
-  nsCOMPtr<nsINode> selNode(do_QueryInterface(aNode));
   NS_ENSURE_STATE(mHTMLEditor);
   nsresult res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor,
-                                                           address_of(selNode),
+                                                           address_of(node),
                                                            &aOffset);
   NS_ENSURE_SUCCESS(res, res);
 
   // split the header
-  int32_t newOffset;
+  NS_ENSURE_STATE(node->IsContent());
   NS_ENSURE_STATE(mHTMLEditor);
-  res = mHTMLEditor->SplitNodeDeep(aHeader, GetAsDOMNode(selNode), aOffset, &newOffset);
-  NS_ENSURE_SUCCESS(res, res);
+  mHTMLEditor->SplitNodeDeep(*header, *node->AsContent(), aOffset);
 
   // if the leftand heading is empty, put a mozbr in it
   nsCOMPtr<nsIDOMNode> prevItem;
   NS_ENSURE_STATE(mHTMLEditor);
   mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
   if (prevItem && nsHTMLEditUtils::IsHeader(prevItem))
   {
     bool bIsEmptyNode;
@@ -6593,52 +6593,55 @@ nsHTMLEditRules::ReturnInParagraph(Selec
 //
 nsresult
 nsHTMLEditRules::SplitParagraph(nsIDOMNode *aPara,
                                 nsIDOMNode *aBRNode,
                                 Selection* aSelection,
                                 nsCOMPtr<nsIDOMNode> *aSelNode,
                                 int32_t *aOffset)
 {
-  NS_ENSURE_TRUE(aPara && aBRNode && aSelNode && *aSelNode && aOffset && aSelection, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<Element> para = do_QueryInterface(aPara);
+  NS_ENSURE_TRUE(para && aBRNode && aSelNode && *aSelNode && aOffset &&
+                 aSelection, NS_ERROR_NULL_POINTER);
   nsresult res = NS_OK;
 
   // split para
-  int32_t newOffset;
   // get ws code to adjust any ws
-  nsCOMPtr<nsIDOMNode> leftPara, rightPara;
+  nsCOMPtr<nsIContent> leftPara, rightPara;
   NS_ENSURE_STATE(mHTMLEditor);
   nsCOMPtr<nsINode> selNode(do_QueryInterface(*aSelNode));
   res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), aOffset);
   *aSelNode = GetAsDOMNode(selNode);
   NS_ENSURE_SUCCESS(res, res);
   // split the paragraph
   NS_ENSURE_STATE(mHTMLEditor);
-  res = mHTMLEditor->SplitNodeDeep(aPara, *aSelNode, *aOffset, &newOffset, false,
-                                   address_of(leftPara), address_of(rightPara));
-  NS_ENSURE_SUCCESS(res, res);
+  NS_ENSURE_STATE(selNode->IsContent());
+  mHTMLEditor->SplitNodeDeep(*para, *selNode->AsContent(), *aOffset,
+                             nsHTMLEditor::EmptyContainers::yes,
+                             getter_AddRefs(leftPara),
+                             getter_AddRefs(rightPara));
   // get rid of the break, if it is visible (otherwise it may be needed to prevent an empty p)
   NS_ENSURE_STATE(mHTMLEditor);
   if (mHTMLEditor->IsVisBreak(aBRNode))
   {
     NS_ENSURE_STATE(mHTMLEditor);
     res = mHTMLEditor->DeleteNode(aBRNode);
     NS_ENSURE_SUCCESS(res, res);
   }
 
   // remove ID attribute on the paragraph we just created
   nsCOMPtr<nsIDOMElement> rightElt = do_QueryInterface(rightPara);
   NS_ENSURE_STATE(mHTMLEditor);
   res = mHTMLEditor->RemoveAttribute(rightElt, NS_LITERAL_STRING("id"));
   NS_ENSURE_SUCCESS(res, res);
 
   // check both halves of para to see if we need mozBR
-  res = InsertMozBRIfNeeded(leftPara);
+  res = InsertMozBRIfNeeded(GetAsDOMNode(leftPara));
   NS_ENSURE_SUCCESS(res, res);
-  res = InsertMozBRIfNeeded(rightPara);
+  res = InsertMozBRIfNeeded(GetAsDOMNode(rightPara));
   NS_ENSURE_SUCCESS(res, res);
 
   // selection to beginning of right hand para;
   // look inside any containers that are up front.
   nsCOMPtr<nsINode> rightParaNode = do_QueryInterface(rightPara);
   NS_ENSURE_STATE(mHTMLEditor && rightParaNode);
   nsCOMPtr<nsIDOMNode> child =
     GetAsDOMNode(mHTMLEditor->GetLeftmostChild(rightParaNode, true));
@@ -6746,20 +6749,19 @@ nsHTMLEditRules::ReturnInListItem(Select
 
   // else we want a new list item at the same list level.
   // get ws code to adjust any ws
   nsCOMPtr<nsINode> selNode(do_QueryInterface(aNode));
   NS_ENSURE_STATE(mHTMLEditor);
   res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
   NS_ENSURE_SUCCESS(res, res);
   // now split list item
-  int32_t newOffset;
   NS_ENSURE_STATE(mHTMLEditor);
-  res = mHTMLEditor->SplitNodeDeep(aListItem, GetAsDOMNode(selNode), aOffset, &newOffset, false);
-  NS_ENSURE_SUCCESS(res, res);
+  NS_ENSURE_STATE(selNode->IsContent());
+  mHTMLEditor->SplitNodeDeep(*listItem, *selNode->AsContent(), aOffset);
   // hack: until I can change the damaged doc range code back to being
   // extra inclusive, I have to manually detect certain list items that
   // may be left empty.
   nsCOMPtr<nsIDOMNode> prevItem;
   NS_ENSURE_STATE(mHTMLEditor);
   mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
 
   if (prevItem && nsHTMLEditUtils::IsListItem(prevItem))
@@ -7151,24 +7153,25 @@ nsHTMLEditRules::SplitAsNeeded(nsIAtom& 
     }
 
     splitNode = parent;
   }
   if (!tagParent) {
     // Could not find a place to build tag!
     return NS_ERROR_FAILURE;
   }
-  if (splitNode) {
+  if (splitNode && splitNode->IsContent() && inOutParent->IsContent()) {
     // We found a place for block, but above inOutParent. We need to split.
     NS_ENSURE_STATE(mHTMLEditor);
-    nsresult res = mHTMLEditor->SplitNodeDeep(splitNode->AsDOMNode(),
-                                              inOutParent->AsDOMNode(),
-                                              inOutOffset, &inOutOffset);
-    NS_ENSURE_SUCCESS(res, res);
+    int32_t offset = mHTMLEditor->SplitNodeDeep(*splitNode->AsContent(),
+                                                *inOutParent->AsContent(),
+                                                inOutOffset);
+    NS_ENSURE_STATE(offset != -1);
     inOutParent = tagParent;
+    inOutOffset = offset;
   }
   return NS_OK;
 }
 
 /**
  * JoinNodesSmart: Join two nodes, doing whatever makes sense for their
  * children (which often means joining them, too).  aNodeLeft & aNodeRight must
  * be same type of node.
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -1578,53 +1578,53 @@ nsHTMLEditor::InsertNodeAtPoint(nsIDOMNo
 {
   nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(ioParent, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(*ioParent, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(ioOffset, NS_ERROR_NULL_POINTER);
 
   nsresult res = NS_OK;
-  nsCOMPtr<nsINode> parent = do_QueryInterface(*ioParent);
+  nsCOMPtr<nsIContent> parent = do_QueryInterface(*ioParent);
   NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
-  nsCOMPtr<nsINode> topChild = parent;
-  int32_t offsetOfInsert = *ioOffset;
+  nsCOMPtr<nsIContent> topChild = parent;
+  nsCOMPtr<nsIContent> origParent = parent;
 
   // Search up the parent chain to find a suitable container
   while (!CanContain(*parent, *node)) {
     // If the current parent is a root (body or table element)
     // then go no further - we can't insert
     if (parent->IsHTMLElement(nsGkAtoms::body) ||
         nsHTMLEditUtils::IsTableElement(parent)) {
       return NS_ERROR_FAILURE;
     }
     // Get the next parent
     NS_ENSURE_TRUE(parent->GetParentNode(), NS_ERROR_FAILURE);
     if (!IsEditable(parent->GetParentNode())) {
       // There's no suitable place to put the node in this editing host.  Maybe
       // someone is trying to put block content in a span.  So just put it
       // where we were originally asked.
-      parent = topChild = do_QueryInterface(*ioParent);
-      NS_ENSURE_STATE(parent);
+      parent = topChild = origParent;
       break;
     }
     topChild = parent;
-    parent = parent->GetParentNode();
+    parent = parent->GetParent();
   }
   if (parent != topChild)
   {
     // we need to split some levels above the original selection parent
-    res = SplitNodeDeep(GetAsDOMNode(topChild), *ioParent, *ioOffset,
-                        &offsetOfInsert, aNoEmptyNodes);
-    NS_ENSURE_SUCCESS(res, res);
+    int32_t offset = SplitNodeDeep(*topChild, *origParent, *ioOffset,
+                                   aNoEmptyNodes ? EmptyContainers::no
+                                                 : EmptyContainers::yes);
+    NS_ENSURE_STATE(offset != -1);
     *ioParent = GetAsDOMNode(parent);
-    *ioOffset = offsetOfInsert;
+    *ioOffset = offset;
   }
   // Now we can insert the new node
-  res = InsertNode(*node, *parent, offsetOfInsert);
+  res = InsertNode(*node, *parent, *ioOffset);
   return res;
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::SelectElement(nsIDOMElement* aElement)
 {
   nsresult res = NS_ERROR_NULL_POINTER;
 
@@ -1922,40 +1922,41 @@ nsHTMLEditor::MakeOrChangeList(const nsA
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   if (!handled)
   {
     // Find out if the selection is collapsed:
     bool isCollapsed = selection->Collapsed();
 
-    nsCOMPtr<nsINode> node;
-    int32_t offset;
-    res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
-    if (!node) res = NS_ERROR_FAILURE;
-    NS_ENSURE_SUCCESS(res, res);
+    NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
+                   selection->GetRangeAt(0)->GetStartParent() &&
+                   selection->GetRangeAt(0)->GetStartParent()->IsContent(),
+                   NS_ERROR_FAILURE);
+    OwningNonNull<nsIContent> node =
+      *selection->GetRangeAt(0)->GetStartParent()->AsContent();
+    int32_t offset = selection->GetRangeAt(0)->StartOffset();
 
     if (isCollapsed)
     {
       // have to find a place to put the list
-      nsCOMPtr<nsINode> parent = node;
-      nsCOMPtr<nsINode> topChild = node;
+      nsCOMPtr<nsIContent> parent = node;
+      nsCOMPtr<nsIContent> topChild = node;
 
       nsCOMPtr<nsIAtom> listAtom = do_GetAtom(aListType);
       while (!CanContainTag(*parent, *listAtom)) {
         topChild = parent;
-        parent = parent->GetParentNode();
+        parent = parent->GetParent();
       }
 
       if (parent != node)
       {
         // we need to split up to the child of parent
-        res = SplitNodeDeep(GetAsDOMNode(topChild), GetAsDOMNode(node), offset,
-                            &offset);
-        NS_ENSURE_SUCCESS(res, res);
+        offset = SplitNodeDeep(*topChild, *node, offset);
+        NS_ENSURE_STATE(offset != -1);
       }
 
       // make a list
       nsCOMPtr<Element> newList = CreateNode(listAtom, parent, offset);
       NS_ENSURE_STATE(newList);
       // make a list item
       nsCOMPtr<Element> newItem = CreateNode(nsGkAtoms::li, newList, 0);
       NS_ENSURE_STATE(newItem);
@@ -2053,41 +2054,42 @@ nsHTMLEditor::InsertBasicBlock(const nsA
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   if (!handled)
   {
     // Find out if the selection is collapsed:
     bool isCollapsed = selection->Collapsed();
 
-    nsCOMPtr<nsINode> node;
-    int32_t offset;
-    res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
-    if (!node) res = NS_ERROR_FAILURE;
-    NS_ENSURE_SUCCESS(res, res);
+    NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
+                   selection->GetRangeAt(0)->GetStartParent() &&
+                   selection->GetRangeAt(0)->GetStartParent()->IsContent(),
+                   NS_ERROR_FAILURE);
+    OwningNonNull<nsIContent> node =
+      *selection->GetRangeAt(0)->GetStartParent()->AsContent();
+    int32_t offset = selection->GetRangeAt(0)->StartOffset();
 
     if (isCollapsed)
     {
       // have to find a place to put the block
-      nsCOMPtr<nsINode> parent = node;
-      nsCOMPtr<nsINode> topChild = node;
+      nsCOMPtr<nsIContent> parent = node;
+      nsCOMPtr<nsIContent> topChild = node;
 
       nsCOMPtr<nsIAtom> blockAtom = do_GetAtom(aBlockType);
       while (!CanContainTag(*parent, *blockAtom)) {
-        NS_ENSURE_TRUE(parent->GetParentNode(), NS_ERROR_FAILURE);
+        NS_ENSURE_TRUE(parent->GetParent(), NS_ERROR_FAILURE);
         topChild = parent;
-        parent = parent->GetParentNode();
+        parent = parent->GetParent();
       }
 
       if (parent != node)
       {
         // we need to split up to the child of parent
-        res = SplitNodeDeep(GetAsDOMNode(topChild), GetAsDOMNode(node), offset,
-                            &offset);
-        NS_ENSURE_SUCCESS(res, res);
+        offset = SplitNodeDeep(*topChild, *node, offset);
+        NS_ENSURE_STATE(offset != -1);
       }
 
       // make a block
       nsCOMPtr<Element> newBlock = CreateNode(blockAtom, parent, offset);
       NS_ENSURE_STATE(newBlock);
 
       // reposition selection to inside the block
       res = selection->Collapse(newBlock,0);
@@ -2123,57 +2125,58 @@ nsHTMLEditor::Indent(const nsAString& aI
 
   nsTextRulesInfo ruleInfo(opID);
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   if (!handled)
   {
     // Do default - insert a blockquote node if selection collapsed
-    nsCOMPtr<nsINode> node;
-    int32_t offset;
     bool isCollapsed = selection->Collapsed();
 
-    res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
-    if (!node) res = NS_ERROR_FAILURE;
-    NS_ENSURE_SUCCESS(res, res);
+    NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
+                   selection->GetRangeAt(0)->GetStartParent() &&
+                   selection->GetRangeAt(0)->GetStartParent()->IsContent(),
+                   NS_ERROR_FAILURE);
+    OwningNonNull<nsIContent> node =
+      *selection->GetRangeAt(0)->GetStartParent()->AsContent();
+    int32_t offset = selection->GetRangeAt(0)->StartOffset();
 
     if (aIndent.EqualsLiteral("indent"))
     {
       if (isCollapsed)
       {
         // have to find a place to put the blockquote
-        nsCOMPtr<nsINode> parent = node;
-        nsCOMPtr<nsINode> topChild = node;
+        nsCOMPtr<nsIContent> parent = node;
+        nsCOMPtr<nsIContent> topChild = node;
         while (!CanContainTag(*parent, *nsGkAtoms::blockquote)) {
-          NS_ENSURE_TRUE(parent->GetParentNode(), NS_ERROR_FAILURE);
+          NS_ENSURE_TRUE(parent->GetParent(), NS_ERROR_FAILURE);
           topChild = parent;
-          parent = parent->GetParentNode();
+          parent = parent->GetParent();
         }
 
         if (parent != node)
         {
           // we need to split up to the child of parent
-          res = SplitNodeDeep(GetAsDOMNode(topChild), GetAsDOMNode(node),
-                              offset, &offset);
-          NS_ENSURE_SUCCESS(res, res);
+          offset = SplitNodeDeep(*topChild, *node, offset);
+          NS_ENSURE_STATE(offset != -1);
         }
 
         // make a blockquote
         nsCOMPtr<Element> newBQ = CreateNode(nsGkAtoms::blockquote, parent, offset);
         NS_ENSURE_STATE(newBQ);
         // put a space in it so layout will draw the list item
         res = selection->Collapse(newBQ,0);
         NS_ENSURE_SUCCESS(res, res);
         res = InsertText(NS_LITERAL_STRING(" "));
         NS_ENSURE_SUCCESS(res, res);
         // reposition selection to before the space character
-        res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
-        NS_ENSURE_SUCCESS(res, res);
-        res = selection->Collapse(node,0);
+        NS_ENSURE_STATE(selection->GetRangeAt(0));
+        res = selection->Collapse(selection->GetRangeAt(0)->GetStartParent(),
+                                  0);
         NS_ENSURE_SUCCESS(res, res);
       }
     }
   }
   res = mRules->DidDoAction(selection, &ruleInfo, res);
   return res;
 }
 
--- a/editor/libeditor/nsHTMLEditorStyle.cpp
+++ b/editor/libeditor/nsHTMLEditorStyle.cpp
@@ -587,22 +587,32 @@ nsresult nsHTMLEditor::SplitStyleAbovePo
         (aProperty && node->IsHTMLElement(aProperty)) ||
         // node is href - test if really <a href=...
         (aProperty == nsGkAtoms::href && nsHTMLEditUtils::IsLink(node)) ||
         // or node is any prop, and we asked to split them all
         (!aProperty && NodeIsProperty(GetAsDOMNode(node))) ||
         // or the style is specified in the style attribute
         isSet) {
       // found a style node we need to split
-      nsresult rv = SplitNodeDeep(GetAsDOMNode(node), *aNode, *aOffset,
-                                  &offset, false, outLeftNode, outRightNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsCOMPtr<nsIContent> outLeftContent, outRightContent;
+      nsCOMPtr<nsIContent> nodeParam = do_QueryInterface(*aNode);
+      NS_ENSURE_STATE(nodeParam || !*aNode);
+      offset = SplitNodeDeep(*node, *nodeParam, *aOffset, EmptyContainers::yes,
+                             getter_AddRefs(outLeftContent),
+                             getter_AddRefs(outRightContent));
+      NS_ENSURE_TRUE(offset != -1, NS_ERROR_FAILURE);
       // reset startNode/startOffset
       *aNode = GetAsDOMNode(node->GetParent());
       *aOffset = offset;
+      if (outLeftNode) {
+        *outLeftNode = GetAsDOMNode(outLeftContent);
+      }
+      if (outRightNode) {
+        *outRightNode = GetAsDOMNode(outRightContent);
+      }
     }
     node = node->GetParent();
   }
   return NS_OK;
 }
 
 nsresult
 nsHTMLEditor::ClearStyle(nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset,
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -832,30 +832,28 @@ DrawTargetSkia::Init(const IntSize &aSiz
 {
   SkAlphaType alphaType = (aFormat == SurfaceFormat::B8G8R8X8) ?
     kOpaque_SkAlphaType : kPremul_SkAlphaType;
 
   SkImageInfo skiInfo = SkImageInfo::Make(
         aSize.width, aSize.height,
         GfxFormatToSkiaColorType(aFormat),
         alphaType);
+  // we need to have surfaces that have a stride aligned to 4 for interop with cairo
+  int stride = (BytesPerPixel(aFormat)*aSize.width + (4-1)) & -4;
 
-  SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(skiInfo));
-  if (!device) {
-      return false;
-  }
-
-  SkBitmap bitmap = device->accessBitmap(true);
+  SkBitmap bitmap;
+  bitmap.setInfo(skiInfo, stride);
   if (!bitmap.allocPixels()) {
     return false;
   }
 
   bitmap.eraseARGB(0, 0, 0, 0);
 
-  mCanvas.adopt(new SkCanvas(device.get()));
+  mCanvas.adopt(new SkCanvas(bitmap));
   mSize = aSize;
 
   mFormat = aFormat;
   return true;
 }
 
 #ifdef USE_SKIA_GPU
 bool
--- a/gfx/2d/SourceSurfaceD2DTarget.cpp
+++ b/gfx/2d/SourceSurfaceD2DTarget.cpp
@@ -59,16 +59,21 @@ SourceSurfaceD2DTarget::GetDataSurface()
   D3D10_TEXTURE2D_DESC desc;
   mTexture->GetDesc(&desc);
 
   desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
   desc.Usage = D3D10_USAGE_STAGING;
   desc.BindFlags = 0;
   desc.MiscFlags = 0;
 
+  if (!Factory::GetDirect3D10Device()) {
+    gfxCriticalError() << "Invalid D3D10 device in D2D target surface";
+    return nullptr;
+  }
+
   HRESULT hr = Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, byRef(dataSurf->mTexture));
 
   if (FAILED(hr)) {
     gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hexa(hr);
     return nullptr;
   }
   Factory::GetDirect3D10Device()->CopyResource(dataSurf->mTexture, mTexture);
 
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1347,16 +1347,17 @@ GLContext::InitWithPrefix(const char *pr
                 ClearSymbols(gpuShader4Symbols);
             }
         }
 
         if (IsSupported(GLFeature::map_buffer_range)) {
             SymLoadStruct mapBufferRangeSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fMapBufferRange, { "MapBufferRange", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fFlushMappedBufferRange, { "FlushMappedBufferRange", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fUnmapBuffer, { "UnmapBuffer", nullptr } },
                 END_SYMBOLS
             };
 
             if (!LoadSymbols(mapBufferRangeSymbols, trygl, prefix)) {
                 NS_ERROR("GL supports map_buffer_range without supplying its functions.");
 
                 MarkUnsupported(GLFeature::map_buffer_range);
                 ClearSymbols(mapBufferRangeSymbols);
--- a/gfx/skia/skia/src/core/SkDraw.cpp
+++ b/gfx/skia/skia/src/core/SkDraw.cpp
@@ -729,16 +729,26 @@ void SkDraw::drawPoints(SkCanvas::PointM
                     path.rewind();
                 }
                 break;
             }
         }
     }
 }
 
+static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
+    SkASSERT(matrix.rectStaysRect());
+    SkASSERT(SkPaint::kFill_Style != paint.getStyle());
+
+    SkVector size;
+    SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
+    matrix.mapVectors(&size, &pt, 1);
+    return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
+}
+
 static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
                            SkPoint* strokeSize) {
     if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
         paint.getStrokeMiter() < SK_ScalarSqrt2) {
         return false;
     }
 
     SkASSERT(matrix.rectStaysRect());
@@ -807,22 +817,32 @@ void SkDraw::drawRect(const SkRect& rect
     const SkMatrix& matrix = *fMatrix;
     SkRect          devRect;
 
     // transform rect into devRect
     matrix.mapPoints(rect_points(devRect), rect_points(rect), 2);
     devRect.sort();
 
     // look for the quick exit, before we build a blitter
-    SkIRect ir;
-    devRect.roundOut(&ir);
+    SkRect bbox = devRect;
     if (paint.getStyle() != SkPaint::kFill_Style) {
         // extra space for hairlines
-        ir.inset(-1, -1);
+        if (paint.getStrokeWidth() == 0) {
+            bbox.outset(1, 1);
+        } else {
+            // For kStroke_RectType, strokeSize is already computed.
+            const SkPoint& ssize = (kStroke_RectType == rtype)
+                ? strokeSize
+                : compute_stroke_size(paint, *fMatrix);
+            bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
+        }
     }
+
+    SkIRect ir;
+    bbox.roundOut(&ir);
     if (fRC->quickReject(ir)) {
         return;
     }
 
     SkDeviceLooper looper(*fBitmap, *fRC, ir, paint.isAntiAlias());
     while (looper.next()) {
         SkRect localDevRect;
         looper.mapRect(&localDevRect, devRect);
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2152,40 +2152,50 @@ gfxPlatform::UseProgressivePaint()
 }
 
 /*static*/ bool
 gfxPlatform::PerfWarnings()
 {
   return gfxPrefs::PerfWarnings();
 }
 
-void
-gfxPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aBackends)
+static inline bool
+AllowOpenGL(bool* aWhitelisted)
 {
-  // Being whitelisted is not enough to accelerate, but not being whitelisted is
-  // enough not to:
-  bool whitelisted = false;
-
   nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
   if (gfxInfo) {
     // bug 655578: on X11 at least, we must always call GetData (even if we don't need that information)
     // as that's what causes GfxInfo initialization which kills the zombie 'glxtest' process.
     // initially we relied on the fact that GetFeatureStatus calls GetData for us, but bug 681026 showed
     // that assumption to be unsafe.
     gfxInfo->GetData();
 
     int32_t status;
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status))) {
       if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
-        aBackends.AppendElement(LayersBackend::LAYERS_OPENGL);
-        whitelisted = true;
+        *aWhitelisted = true;
+        return true;
       }
     }
   }
 
+  return gfxPrefs::LayersAccelerationForceEnabled();
+}
+
+void
+gfxPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aBackends)
+{
+  // Being whitelisted is not enough to accelerate, but not being whitelisted is
+  // enough not to:
+  bool whitelisted = false;
+
+  if (AllowOpenGL(&whitelisted)) {
+    aBackends.AppendElement(LayersBackend::LAYERS_OPENGL);
+  }
+
   if (!whitelisted) {
     static int tell_me_once = 0;
     if (!tell_me_once) {
       NS_WARNING("OpenGL-accelerated layers are not supported on this system");
       tell_me_once = 1;
     }
 #ifdef MOZ_WIDGET_ANDROID
     NS_RUNTIMEABORT("OpenGL-accelerated layers are a hard requirement on this platform. "
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1962,16 +1962,20 @@ gfxWindowsPlatform::CheckD3D11Support(bo
     *aCanUseHardware = !GetParentDevicePrefs().useD3D11WARP();
     return FeatureStatus::Available;
   }
 
   if (gfxPrefs::LayersD3D11ForceWARP()) {
     *aCanUseHardware = false;
     return FeatureStatus::Available;
   }
+  if (gfxPrefs::LayersAccelerationForceEnabled()) {
+    *aCanUseHardware = true;
+    return FeatureStatus::Available;
+  }
 
   if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
     int32_t status;
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
       if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
         if (CanUseWARP()) {
           *aCanUseHardware = false;
           return FeatureStatus::Available;
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -1787,16 +1787,17 @@ AsmJSModule::setProfilingEnabled(bool en
         uint8_t* caller = callerRetAddr - 4;
         Instruction* callerInsn = reinterpret_cast<Instruction*>(caller);
         BOffImm calleeOffset;
         callerInsn->as<InstBLImm>()->extractImm(&calleeOffset);
         void* callee = calleeOffset.getDest(callerInsn);
 #elif defined(JS_CODEGEN_ARM64)
         MOZ_CRASH();
         void* callee = nullptr;
+        (void)callerRetAddr;
 #elif defined(JS_CODEGEN_MIPS32)
         Instruction* instr = (Instruction*)(callerRetAddr - 4 * sizeof(uint32_t));
         void* callee = (void*)Assembler::ExtractLuiOriValue(instr, instr->next());
 #elif defined(JS_CODEGEN_NONE)
         MOZ_CRASH();
         void* callee = nullptr;
 #else
 # error "Missing architecture"
@@ -1812,16 +1813,17 @@ AsmJSModule::setProfilingEnabled(bool en
         MOZ_ASSERT_IF(!profilingEnabled_, callee == entry);
         uint8_t* newCallee = enabled ? profilingEntry : entry;
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         X86Encoding::SetRel32(callerRetAddr, newCallee);
 #elif defined(JS_CODEGEN_ARM)
         new (caller) InstBLImm(BOffImm(newCallee - caller), Assembler::Always);
 #elif defined(JS_CODEGEN_ARM64)
+        (void)newCallee;
         MOZ_CRASH();
 #elif defined(JS_CODEGEN_MIPS32)
         Assembler::WriteLuiOriInstructions(instr, instr->next(),
                                            ScratchRegister, (uint32_t)newCallee);
         instr[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
 #elif defined(JS_CODEGEN_NONE)
         MOZ_CRASH();
 #else
@@ -1877,16 +1879,18 @@ AsmJSModule::setProfilingEnabled(bool en
         if (enabled) {
             MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
             new (jump) InstBImm(BOffImm(profilingEpilogue - jump), Assembler::Always);
         } else {
             MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstBImm>());
             new (jump) InstNOP();
         }
 #elif defined(JS_CODEGEN_ARM64)
+        (void)jump;
+        (void)profilingEpilogue;
         MOZ_CRASH();
 #elif defined(JS_CODEGEN_MIPS32)
         Instruction* instr = (Instruction*)jump;
         if (enabled) {
             Assembler::WriteLuiOriInstructions(instr, instr->next(),
                                                ScratchRegister, (uint32_t)profilingEpilogue);
             instr[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr);
         } else {
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3106,23 +3106,30 @@ ASTSerializer::expression(ParseNode* pn,
 #if JS_HAS_GENERATOR_EXPRS
       case PNK_GENEXP:
         return generatorExpression(pn->generatorExpr(), dst);
 #endif
 
       case PNK_NEW:
       case PNK_TAGGED_TEMPLATE:
       case PNK_CALL:
+      case PNK_SUPERCALL:
       {
         ParseNode* next = pn->pn_head;
         MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
 
         RootedValue callee(cx);
-        if (!expression(next, &callee))
-            return false;
+        if (pn->isKind(PNK_SUPERCALL)) {
+            MOZ_ASSERT(next->isKind(PNK_POSHOLDER));
+            if (!builder.super(&next->pn_pos, &callee))
+                return false;
+        } else {
+            if (!expression(next, &callee))
+                return false;
+        }
 
         NodeVector args(cx);
         if (!args.reserve(pn->pn_count - 1))
             return false;
 
         for (next = next->pn_next; next; next = next->pn_next) {
             MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
 
@@ -3130,16 +3137,17 @@ ASTSerializer::expression(ParseNode* pn,
             if (!expression(next, &arg))
                 return false;
             args.infallibleAppend(arg);
         }
 
         if (pn->getKind() == PNK_TAGGED_TEMPLATE)
             return builder.taggedTemplate(callee, args, &pn->pn_pos, dst);
 
+        // SUPERCALL is Call(super, args)
         return pn->isKind(PNK_NEW)
                ? builder.newExpression(callee, args, &pn->pn_pos, dst)
 
             : builder.callExpression(callee, args, &pn->pn_pos, dst);
       }
 
       case PNK_DOT:
       {
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -863,17 +863,17 @@ SetSavedStacksRNGState(JSContext* cx, un
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1))
         return false;
 
     int32_t seed;
     if (!ToInt32(cx, args[0], &seed))
         return false;
 
-    cx->compartment()->savedStacks().setRNGState((seed ^ RNG_MULTIPLIER) & RNG_MASK);
+    cx->compartment()->savedStacks().setRNGState(seed, seed * 33);
     return true;
 }
 
 static bool
 GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setNumber(cx->compartment()->savedStacks().count());
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1929,25 +1929,30 @@ BytecodeEmitter::checkSideEffects(ParseN
       // Trivial cases with no side effects.
       case PNK_NOP:
       case PNK_STRING:
       case PNK_TEMPLATE_STRING:
       case PNK_REGEXP:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
-      case PNK_THIS:
       case PNK_ELISION:
       case PNK_GENERATOR:
       case PNK_NUMBER:
       case PNK_OBJECT_PROPERTY_NAME:
         MOZ_ASSERT(pn->isArity(PN_NULLARY));
         *answer = false;
         return true;
 
+      // |this| can throw in derived class constructors.
+      case PNK_THIS:
+        MOZ_ASSERT(pn->isArity(PN_NULLARY));
+        *answer = sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor();
+        return true;
+
       // Trivial binary nodes with more token pos holders.
       case PNK_NEWTARGET:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         MOZ_ASSERT(pn->pn_left->isKind(PNK_POSHOLDER));
         MOZ_ASSERT(pn->pn_right->isKind(PNK_POSHOLDER));
         *answer = false;
         return true;
 
@@ -2180,16 +2185,17 @@ BytecodeEmitter::checkSideEffects(ParseN
         if ((pn = pn->pn_kid3))
             goto restart;
         return true;
 
       // Function calls can invoke non-local code.
       case PNK_NEW:
       case PNK_CALL:
       case PNK_TAGGED_TEMPLATE:
+      case PNK_SUPERCALL:
         MOZ_ASSERT(pn->isArity(PN_LIST));
         *answer = true;
         return true;
 
       // Classes typically introduce names.  Even if no name is introduced,
       // the heritage and/or class body (through computed property names)
       // usually have effects.
       case PNK_CLASS:
@@ -6722,55 +6728,72 @@ BytecodeEmitter::emitCallOrNew(ParseNode
                 return false;
             emittingRunOnceLambda = false;
         } else {
             if (!emitTree(pn2))
                 return false;
         }
         callop = false;
         break;
+      case PNK_POSHOLDER:
+        MOZ_ASSERT(pn->isKind(PNK_SUPERCALL));
+        MOZ_ASSERT(parser->handler.isSuperBase(pn2, cx));
+        if (!emit1(JSOP_SUPERFUN))
+            return false;
+        break;
       default:
         if (!emitTree(pn2))
             return false;
         callop = false;             /* trigger JSOP_UNDEFINED after */
         break;
     }
     if (!callop) {
         JSOp thisop = pn->isKind(PNK_GENEXP) ? JSOP_THIS : JSOP_UNDEFINED;
         if (!emit1(thisop))
             return false;
     }
 
-    bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW;
+    bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
+                   pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;;
 
     /*
      * Emit code for each argument in order, then emit the JSOP_*CALL or
      * JSOP_NEW bytecode with a two-byte immediate telling how many args
      * were pushed on the operand stack.
      */
     bool oldEmittingForInit = emittingForInit;
     emittingForInit = false;
     if (!spread) {
         for (ParseNode* pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
             if (!emitTree(pn3))
                 return false;
         }
 
         if (isNewOp) {
-            // Repush the callee as new.target
-            if (!emitDupAt(argc + 1))
-                return false;
+            if (pn->isKind(PNK_SUPERCALL)) {
+                if (!emit1(JSOP_NEWTARGET))
+                    return false;
+            } else {
+                // Repush the callee as new.target
+                if (!emitDupAt(argc + 1))
+                    return false;
+            }
         }
     } else {
         if (!emitArray(pn2->pn_next, argc, JSOP_SPREADCALLARRAY))
             return false;
 
         if (isNewOp) {
-            if (!emitDupAt(2))
-                return false;
+            if (pn->isKind(PNK_SUPERCALL)) {
+                if (!emit1(JSOP_NEWTARGET))
+                    return false;
+            } else {
+                if (!emitDupAt(2))
+                    return false;
+            }
         }
     }
     emittingForInit = oldEmittingForInit;
 
     if (!spread) {
         if (!emitCall(pn->getOp(), argc, pn))
             return false;
     } else {
@@ -6786,16 +6809,19 @@ BytecodeEmitter::emitCallOrNew(ParseNode
         uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
         if (!emitUint32Operand(JSOP_LINENO, lineNum))
             return false;
     }
     if (pn->pn_xflags & PNX_SETCALL) {
         if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return false;
     }
+
+    if (pn->isKind(PNK_SUPERCALL) && !emit1(JSOP_SETTHIS))
+        return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitLogical(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
 
@@ -7842,16 +7868,17 @@ BytecodeEmitter::emitTree(ParseNode* pn)
                 return false;
         }
         break;
 
       case PNK_NEW:
       case PNK_TAGGED_TEMPLATE:
       case PNK_CALL:
       case PNK_GENEXP:
+      case PNK_SUPERCALL:
         ok = emitCallOrNew(pn);
         break;
 
       case PNK_LEXICALSCOPE:
         ok = emitLexicalScope(pn);
         break;
 
       case PNK_LETBLOCK:
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -409,16 +409,17 @@ ContainsHoistedDeclaration(ExclusiveCont
       case PNK_FOROF:
       case PNK_FORHEAD:
       case PNK_FRESHENBLOCK:
       case PNK_CLASSMETHOD:
       case PNK_CLASSMETHODLIST:
       case PNK_CLASSNAMES:
       case PNK_NEWTARGET:
       case PNK_POSHOLDER:
+      case PNK_SUPERCALL:
         MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
                   "some parent node without recurring to test this node");
 
       case PNK_LIMIT: // invalid sentinel value
         MOZ_CRASH("unexpected PNK_LIMIT in node");
     }
 
     MOZ_CRASH("invalid node kind");
@@ -1574,17 +1575,18 @@ FoldAdd(ExclusiveContext* cx, ParseNode*
 
     return true;
 }
 
 static bool
 FoldCall(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
          bool inGenexpLambda)
 {
-    MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_TAGGED_TEMPLATE));
+    MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
+               node->isKind(PNK_TAGGED_TEMPLATE));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     // Don't fold a parenthesized callable component in an invocation, as this
     // might cause a different |this| value to be used, changing semantics:
     //
     //   var prop = "global";
     //   var obj = { prop: "obj", f: function() { return this.prop; } };
     //   assertEq((true ? obj.f : null)(), "global");
@@ -1871,16 +1873,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
       case PNK_ELEM:
         return FoldElement(cx, pnp, parser, inGenexpLambda);
 
       case PNK_ADD:
         return FoldAdd(cx, pnp, parser, inGenexpLambda);
 
       case PNK_CALL:
+      case PNK_SUPERCALL:
       case PNK_TAGGED_TEMPLATE:
         return FoldCall(cx, pn, parser, inGenexpLambda);
 
       case PNK_SWITCH:
       case PNK_CASE:
       case PNK_COLON:
       case PNK_ASSIGN:
       case PNK_ADDASSIGN:
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -371,16 +371,17 @@ class NameResolver
           case PNK_GENERATOR:
           case PNK_NUMBER:
           case PNK_BREAK:
           case PNK_CONTINUE:
           case PNK_DEBUGGER:
           case PNK_EXPORT_BATCH_SPEC:
           case PNK_FRESHENBLOCK:
           case PNK_OBJECT_PROPERTY_NAME:
+          case PNK_POSHOLDER:
             MOZ_ASSERT(cur->isArity(PN_NULLARY));
             break;
 
           case PNK_TYPEOFNAME:
             MOZ_ASSERT(cur->isArity(PN_UNARY));
             MOZ_ASSERT(cur->pn_kid->isKind(PNK_NAME));
             MOZ_ASSERT(!cur->pn_kid->maybeExpr());
             break;
@@ -668,16 +669,17 @@ class NameResolver
           case PNK_SUB:
           case PNK_STAR:
           case PNK_DIV:
           case PNK_MOD:
           case PNK_POW:
           case PNK_COMMA:
           case PNK_NEW:
           case PNK_CALL:
+          case PNK_SUPERCALL:
           case PNK_GENEXP:
           case PNK_ARRAY:
           case PNK_STATEMENTLIST:
           case PNK_ARGSBODY:
           // Initializers for individual variables, and computed property names
           // within destructuring patterns, may contain unnamed functions.
           case PNK_VAR:
           case PNK_CONST:
@@ -791,17 +793,16 @@ class NameResolver
             break;
 
           // Kinds that should be handled by parent node resolution.
 
           case PNK_IMPORT_SPEC: // by PNK_IMPORT_SPEC_LIST
           case PNK_EXPORT_SPEC: // by PNK_EXPORT_SPEC_LIST
           case PNK_CALLSITEOBJ: // by PNK_TAGGED_TEMPLATE
           case PNK_CLASSNAMES:  // by PNK_CLASS
-          case PNK_POSHOLDER:   // by PNK_NEWTARGET, PNK_DOT
             MOZ_CRASH("should have been handled by a parent node");
 
           case PNK_LIMIT: // invalid sentinel value
             MOZ_CRASH("invalid node kind");
         }
 
         nparents--;
         return true;
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -484,16 +484,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
       case PNK_POW:
       case PNK_COMMA:
       case PNK_NEW:
       case PNK_CALL:
+      case PNK_SUPERCALL:
       case PNK_GENEXP:
       case PNK_ARRAY:
       case PNK_OBJECT:
       case PNK_TEMPLATE_STRING_LIST:
       case PNK_TAGGED_TEMPLATE:
       case PNK_CALLSITEOBJ:
       case PNK_VAR:
       case PNK_CONST:
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -170,16 +170,17 @@ class PackedScopeCoordinate
     F(SPREAD) \
     F(MUTATEPROTO) \
     F(CLASS) \
     F(CLASSMETHOD) \
     F(CLASSMETHODLIST) \
     F(CLASSNAMES) \
     F(NEWTARGET) \
     F(POSHOLDER) \
+    F(SUPERCALL) \
     \
     /* Unary operators. */ \
     F(TYPEOFNAME) \
     F(TYPEOFEXPR) \
     F(VOID) \
     F(NOT) \
     F(BITNOT) \
     \
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -8643,19 +8643,41 @@ Parser<ParseHandler>::memberExpr(YieldHa
             nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
             if (!nextMember)
                 return null();
         } else if ((allowCallSyntax && tt == TOK_LP) ||
                    tt == TOK_TEMPLATE_HEAD ||
                    tt == TOK_NO_SUBS_TEMPLATE)
         {
             if (handler.isSuperBase(lhs, context)) {
-                // For now...
-                report(ParseError, false, null(), JSMSG_BAD_SUPER);
-                return null();
+                if (!pc->sc->isFunctionBox() || !pc->sc->asFunctionBox()->isDerivedClassConstructor()) {
+                    report(ParseError, false, null(), JSMSG_BAD_SUPERCALL);
+                    return null();
+                }
+
+                if (tt != TOK_LP) {
+                    report(ParseError, false, null(), JSMSG_BAD_SUPER);
+                    return null();
+                }
+
+                nextMember = handler.newList(PNK_SUPERCALL, lhs, JSOP_SUPERCALL);
+                if (!nextMember)
+                    return null();
+
+                // Despite the fact that it's impossible to have |super()| is a
+                // generator, we still inherity the yieldHandling of the
+                // memberExpression, per spec. Curious.
+                bool isSpread = false;
+                if (!argumentList(yieldHandling, nextMember, &isSpread))
+                    return null();
+
+                if (isSpread)
+                    handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
+
+                return nextMember;
             }
 
             nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate();
             if (!nextMember)
                 return null();
 
             JSOp op = JSOP_CALL;
             if (PropertyName* name = handler.maybeNameAnyParentheses(lhs)) {
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -369,16 +369,17 @@ class FunctionBox : public ObjectBox, pu
 
     bool needsCallObject()
     {
         // Note: this should be kept in sync with JSFunction::needsCallObject().
         return bindings.hasAnyAliasedBindings() ||
                hasExtensibleScope() ||
                needsDeclEnvObject() ||
                needsHomeObject()    ||
+               isDerivedClassConstructor() ||
                isGenerator();
     }
 };
 
 class ModuleBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings bindings;
--- a/js/src/jit-test/tests/auto-regress/bug759306.js
+++ b/js/src/jit-test/tests/auto-regress/bug759306.js
@@ -1,9 +1,9 @@
-// |jit-test| error:InternalError
+// |jit-test| error:TypeError
 
 // Binary: cache/js-dbg-32-4ce3983a43f4-linux
 // Flags:
 //
 
 function assertEq(setter) {
         if (setter > 10)
             return {assertEq: 3.3};
--- a/js/src/jit-test/tests/basic/testProxyConstructors.js
+++ b/js/src/jit-test/tests/basic/testProxyConstructors.js
@@ -1,17 +1,19 @@
 // |jit-test| error: ExitCleanly
 
-assertEq((new (Proxy.createFunction({},
+var handler = { getPropertyDescriptor() { return undefined; } }
+
+assertEq((new (Proxy.createFunction(handler,
                                     function(){ this.x = 1 },
                                     function(){ this.x = 2 }))).x, 2);
 // proxies can return the callee
-var x = Proxy.createFunction({}, function (q) { return q; });
+var x = Proxy.createFunction(handler, function (q) { return q; });
 assertEq(new x(x), x);
 try {
-    var x = (Proxy.createFunction({}, "".indexOf));
+    var x = (Proxy.createFunction(handler, "".indexOf));
     new x;
     throw "Should not be reached"
 }
 catch (e) {
     assertEq(String(e.message).indexOf('is not a constructor') === -1, false);
 }
 throw "ExitCleanly"
--- a/js/src/jit-test/tests/debug/Memory-allocationSamplingProbability-02.js
+++ b/js/src/jit-test/tests/debug/Memory-allocationSamplingProbability-02.js
@@ -27,10 +27,10 @@ function measure(P, expected) {
 dbg.memory.trackingAllocationSites = true;
 
 // These are the sample counts that were correct when this test was last
 // updated; changes to SpiderMonkey may occasionally cause changes
 // here. Anything that is within a plausible range for the given sampling
 // probability is fine.
 measure(0.0, 0);
 measure(1.0, 100);
-measure(0.1, 9);
-measure(0.5, 51);
+measure(0.1, 7);
+measure(0.5, 44);
--- a/js/src/jit-test/tests/ion/bug825705.js
+++ b/js/src/jit-test/tests/ion/bug825705.js
@@ -1,23 +1,8 @@
 // Test 1: When constructing x, we shouldn't take the prototype for this.
 // it will crash if that happens
 evalcx("\
     var x = newGlobal().Object;\
     function f() { return new x; }\
     f();\
     f();\
 ", newGlobal());
-
-// Test 2: Don't take the prototype of proxy's to create |this|,
-// as this will throw... Not expected behaviour.
-var O = new Proxy(function() {}, {
-    get: function() {
-	    throw "get trap";
-    }
-});
-
-function f() {
-  new O();
-}
-
-f();
-f();
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -975,17 +975,17 @@ InitFromBailout(JSContext* cx, HandleScr
     JitSpew(JitSpew_BaselineBailouts, "      Resuming %s pc offset %d (op %s) (line %d) of %s:%" PRIuSIZE,
                 resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op],
                 PCToLineNumber(script, pc), script->filename(), script->lineno());
     JitSpew(JitSpew_BaselineBailouts, "      Bailout kind: %s",
             BailoutKindString(bailoutKind));
 #endif
 
     bool pushedNewTarget = op == JSOP_NEW;
-    
+
     // If this was the last inline frame, or we are bailing out to a catch or
     // finally block in this frame, then unpacking is almost done.
     if (!iter.moreFrames() || catchingException) {
         // Last frame, so PC for call to next frame is set to nullptr.
         *callPC = nullptr;
 
         // If the bailout was a resumeAfter, and the opcode is monitored,
         // then the bailed out state should be in a position to enter
@@ -1872,16 +1872,18 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_NonBooleanInput:
       case Bailout_NonObjectInput:
       case Bailout_NonStringInput:
       case Bailout_NonSymbolInput:
       case Bailout_NonSimdInt32x4Input:
       case Bailout_NonSimdFloat32x4Input:
       case Bailout_InitialState:
       case Bailout_Debugger:
+      case Bailout_UninitializedThis:
+      case Bailout_BadDerivedConstructorReturn:
         // Do nothing.
         break;
 
       // Invalid assumption based on baseline code.
       case Bailout_OverflowInvalidate:
       case Bailout_NonStringInputInvalidate:
       case Bailout_DoubleOutput:
       case Bailout_ObjectIdentityOrTypeGuard:
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1280,30 +1280,60 @@ BaselineCompiler::emit_JSOP_HOLE()
 
 bool
 BaselineCompiler::emit_JSOP_NULL()
 {
     frame.push(NullValue());
     return true;
 }
 
+typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
+static const VMFunction ThrowUninitializedThisInfo =
+    FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis);
+
+bool
+BaselineCompiler::emitCheckThis()
+{
+    frame.assertSyncedStack();
+
+    Label thisOK;
+    masm.branchTestMagic(Assembler::NotEqual, frame.addressOfThis(), &thisOK);
+
+    prepareVMCall();
+
+    masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
+    pushArg(R0.scratchReg());
+
+    if (!callVM(ThrowUninitializedThisInfo))
+        return false;
+
+    masm.bind(&thisOK);
+    return true;
+}
+
 bool
 BaselineCompiler::emit_JSOP_THIS()
 {
     if (function() && function()->isArrow()) {
         // Arrow functions store their (lexical) |this| value in an
         // extended slot.
         frame.syncStack(0);
         Register scratch = R0.scratchReg();
         masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), scratch);
         masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowThisSlot()), R0);
         frame.push(R0);
         return true;
     }
 
+    if (script->isDerivedClassConstructor()) {
+        frame.syncStack(0);
+        if (!emitCheckThis())
+            return false;
+    }
+
     // Keep this value in R0
     frame.pushThis();
 
     // In strict mode code or self-hosted functions, |this| is left alone.
     if (script->strict() || (function() && function()->isSelfHostedBuiltin()))
         return true;
 
     Label skipIC;
@@ -2868,17 +2898,17 @@ BaselineCompiler::emit_JSOP_UNINITIALIZE
     return true;
 }
 
 bool
 BaselineCompiler::emitCall()
 {
     MOZ_ASSERT(IsCallPC(pc));
 
-    bool construct = JSOp(*pc) == JSOP_NEW;
+    bool construct = JSOp(*pc) == JSOP_NEW || JSOp(*pc) == JSOP_SUPERCALL;
     uint32_t argc = GET_ARGC(pc);
 
     frame.syncStack(0);
     masm.move32(Imm32(argc), R0.scratchReg());
 
     // Call IC
     ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ construct,
                                            /* isSpread = */ false);
@@ -2895,23 +2925,23 @@ bool
 BaselineCompiler::emitSpreadCall()
 {
     MOZ_ASSERT(IsCallPC(pc));
 
     frame.syncStack(0);
     masm.move32(Imm32(1), R0.scratchReg());
 
     // Call IC
-    ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ JSOp(*pc) == JSOP_SPREADNEW,
+    bool construct = JSOp(*pc) == JSOP_SPREADNEW || JSOp(*pc) == JSOP_SPREADSUPERCALL;
+    ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ construct,
                                            /* isSpread = */ true);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     // Update FrameInfo.
-    bool construct = JSOp(*pc) == JSOP_SPREADNEW;
     frame.popn(3 + construct);
     frame.push(R0);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_CALL()
 {
@@ -2920,16 +2950,22 @@ BaselineCompiler::emit_JSOP_CALL()
 
 bool
 BaselineCompiler::emit_JSOP_NEW()
 {
     return emitCall();
 }
 
 bool
+BaselineCompiler::emit_JSOP_SUPERCALL()
+{
+    return emitCall();
+}
+
+bool
 BaselineCompiler::emit_JSOP_FUNCALL()
 {
     return emitCall();
 }
 
 bool
 BaselineCompiler::emit_JSOP_FUNAPPLY()
 {
@@ -2956,16 +2992,22 @@ BaselineCompiler::emit_JSOP_SPREADCALL()
 
 bool
 BaselineCompiler::emit_JSOP_SPREADNEW()
 {
     return emitSpreadCall();
 }
 
 bool
+BaselineCompiler::emit_JSOP_SPREADSUPERCALL()
+{
+    return emitSpreadCall();
+}
+
+bool
 BaselineCompiler::emit_JSOP_SPREADEVAL()
 {
     return emitSpreadCall();
 }
 
 bool
 BaselineCompiler::emit_JSOP_STRICTSPREADEVAL()
 {
@@ -3285,23 +3327,50 @@ BaselineCompiler::emit_JSOP_DEBUGGER()
     {
         masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
         masm.jump(&return_);
     }
     masm.bind(&done);
     return true;
 }
 
+typedef bool (*ThrowBadDerivedReturnFn)(JSContext*, HandleValue);
+static const VMFunction ThrowBadDerivedReturnInfo =
+    FunctionInfo<ThrowBadDerivedReturnFn>(jit::ThrowBadDerivedReturn);
+
 typedef bool (*DebugEpilogueFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction DebugEpilogueInfo =
     FunctionInfo<DebugEpilogueFn>(jit::DebugEpilogueOnBaselineReturn);
 
 bool
 BaselineCompiler::emitReturn()
 {
+    if (script->isDerivedClassConstructor()) {
+        frame.syncStack(0);
+
+        Label derivedDone, returnOK;
+        masm.branchTestObject(Assembler::Equal, JSReturnOperand, &derivedDone);
+        masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, &returnOK);
+
+        // This is going to smash JSReturnOperand, but we don't care, because it's
+        // also going to throw unconditionally.
+        prepareVMCall();
+        pushArg(JSReturnOperand);
+        if (!callVM(ThrowBadDerivedReturnInfo))
+            return false;
+        masm.assumeUnreachable("Should throw on bad derived constructor return");
+
+        masm.bind(&returnOK);
+
+        if (!emitCheckThis())
+            return false;
+
+        masm.bind(&derivedDone);
+    }
+
     if (compileDebugInstrumentation_) {
         // Move return value into the frame's rval slot.
         masm.storeValue(JSReturnOperand, frame.addressOfReturnValue());
         masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
 
         // Load BaselineFrame pointer in R0.
         frame.syncStack(0);
         masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -196,17 +196,19 @@ namespace jit {
     _(JSOP_YIELD)              \
     _(JSOP_DEBUGAFTERYIELD)    \
     _(JSOP_FINALYIELDRVAL)     \
     _(JSOP_RESUME)             \
     _(JSOP_CALLEE)             \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETRVAL)            \
     _(JSOP_RETURN)             \
-    _(JSOP_NEWTARGET)
+    _(JSOP_NEWTARGET)          \
+    _(JSOP_SUPERCALL)          \
+    _(JSOP_SPREADSUPERCALL)
 
 class BaselineCompiler : public BaselineCompilerSpecific
 {
     FixedList<Label>            labels_;
     NonAssertingLabel           return_;
     NonAssertingLabel           postBarrierSlot_;
 
     // Native code offset right before the scope chain is initialized.
@@ -300,16 +302,17 @@ class BaselineCompiler : public Baseline
     bool emitSpreadCall();
 
     bool emitInitPropGetterSetter();
     bool emitInitElemGetterSetter();
 
     bool emitFormalArgAccess(uint32_t arg, bool get);
 
     bool emitUninitializedLexicalCheck(const ValueOperand& val);
+    bool emitCheckThis();
 
     bool addPCMappingEntry(bool addIndexEntry);
 
     bool addYieldOffset();
 
     void getScopeCoordinateObject(Register reg);
     Address getScopeCoordinateAddressFromObject(Register objReg, Register reg);
     Address getScopeCoordinateAddress(Register reg);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -390,16 +390,18 @@ ICTypeMonitor_Fallback::addMonitorStubFo
 
     if (numOptimizedMonitorStubs_ >= MAX_OPTIMIZED_STUBS) {
         // TODO: if the TypeSet becomes unknown or has the AnyObject type,
         // replace stubs with a single stub to handle these.
         return true;
     }
 
     if (val.isPrimitive()) {
+        if (val.isMagic(JS_UNINITIALIZED_LEXICAL))
+            return true;
         MOZ_ASSERT(!val.isMagic());
         JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType();
 
         // Check for existing TypeMonitor stub.
         ICTypeMonitor_PrimitiveSet* existingStub = nullptr;
         for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) {
             if (iter->isTypeMonitor_PrimitiveSet()) {
                 existingStub = iter->toTypeMonitor_PrimitiveSet();
@@ -498,30 +500,38 @@ ICTypeMonitor_Fallback::addMonitorStubFo
 }
 
 static bool
 DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame, ICTypeMonitor_Fallback* stub,
                       HandleValue value, MutableHandleValue res)
 {
     // It's possible that we arrived here from bailing out of Ion, and that
     // Ion proved that the value is dead and optimized out. In such cases, do
-    // nothing.
-    if (value.isMagic(JS_OPTIMIZED_OUT)) {
-        res.set(value);
-        return true;
+    // nothing. However, it's also possible that we have an uninitialized this,
+    // in which case we should not look for other magic values.
+    if (stub->monitorsThis()) {
+        MOZ_ASSERT_IF(value.isMagic(), value.isMagic(JS_UNINITIALIZED_LEXICAL));
+    } else {
+        if (value.isMagic(JS_OPTIMIZED_OUT)) {
+            res.set(value);
+            return true;
+        }
     }
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     TypeFallbackICSpew(cx, stub, "TypeMonitor");
 
     uint32_t argument;
     if (stub->monitorsThis()) {
         MOZ_ASSERT(pc == script->code());
-        TypeScript::SetThis(cx, script, value);
+        if (value.isMagic(JS_UNINITIALIZED_LEXICAL))
+            TypeScript::SetThis(cx, script, TypeSet::UnknownType());
+        else
+            TypeScript::SetThis(cx, script, value);
     } else if (stub->monitorsArgument(&argument)) {
         MOZ_ASSERT(pc == script->code());
         TypeScript::SetArgument(cx, script, argument, value);
     } else {
         TypeScript::Monitor(cx, script, pc, value);
     }
 
     if (!stub->addMonitorStubForValue(cx, script, value))
@@ -5010,16 +5020,28 @@ TryAttachGlobalNameAccessorStub(JSContex
     // requires a Baseline stub) handles non-outerized this objects correctly.
     bool isScripted;
     if (IsCacheableGetPropCall(cx, global, current, shape, &isScripted, isTemporarilyUnoptimizable) &&
         !isScripted)
     {
         ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
         RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
 
+        // The CallNativeGlobal stub needs to generate 3 shape checks:
+        //
+        // 1. The global lexical scope shape check.
+        // 2. The global object shape check.
+        // 3. The holder shape check.
+        //
+        // 1 is done as the receiver check, as for GETNAME the global lexical scope is in the
+        // receiver position. 2 is done as a manual check that other GetProp stubs don't do. 3 is
+        // done as the holder check per normal.
+        //
+        // In the case the holder is the global object, check 2 is redundant but is not yet
+        // optimized away.
         JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName/NativeGetter) stub");
         if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativeGlobal, current,
                                            globalLexical, getter))
         {
             *attached = true;
             return true;
         }
         ICGetPropCallNativeCompiler compiler(cx, ICStub::GetProp_CallNativeGlobal,
@@ -8617,16 +8639,18 @@ IsOptimizableCallStringSplit(Value calle
     return true;
 }
 
 static bool
 TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
                   JSOp op, uint32_t argc, Value* vp, bool constructing, bool isSpread,
                   bool createSingleton, bool* handled)
 {
+    bool isSuper = op == JSOP_SUPERCALL || op == JSOP_SPREADSUPERCALL;
+
     if (createSingleton || op == JSOP_EVAL || op == JSOP_STRICTEVAL)
         return true;
 
     if (stub->numOptimizedStubs() >= ICCall_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
@@ -8724,50 +8748,53 @@ TryAttachCallStub(JSContext* cx, ICCall_
         }
 
         // Keep track of the function's |prototype| property in type
         // information, for use during Ion compilation.
         if (IsIonEnabled(cx))
             EnsureTrackPropertyTypes(cx, fun, NameToId(cx->names().prototype));
 
         // Remember the template object associated with any script being called
-        // as a constructor, for later use during Ion compilation.
+        // as a constructor, for later use during Ion compilation. This is unsound
+        // for super(), as a single callsite can have multiple possible prototype object
+        // created (via different newTargets)
         RootedObject templateObject(cx);
-        if (constructing) {
+        if (constructing && !isSuper) {
             // If we are calling a constructor for which the new script
             // properties analysis has not been performed yet, don't attach a
             // stub. After the analysis is performed, CreateThisForFunction may
             // start returning objects with a different type, and the Ion
             // compiler will get confused.
 
             // Only attach a stub if the function already has a prototype and
             // we can look it up without causing side effects.
+            RootedObject newTarget(cx, &vp[2 + argc].toObject());
             RootedValue protov(cx);
-            if (!GetPropertyPure(cx, fun, NameToId(cx->names().prototype), protov.address())) {
+            if (!GetPropertyPure(cx, newTarget, NameToId(cx->names().prototype), protov.address())) {
                 JitSpew(JitSpew_BaselineIC, "  Can't purely lookup function prototype");
                 return true;
             }
 
             if (protov.isObject()) {
                 TaggedProto proto(&protov.toObject());
-                ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, nullptr, proto, fun);
+                ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, nullptr, proto, newTarget);
                 if (!group)
                     return false;
 
                 if (group->newScript() && !group->newScript()->analyzed()) {
                     JitSpew(JitSpew_BaselineIC, "  Function newScript has not been analyzed");
 
                     // This is temporary until the analysis is perfomed, so
                     // don't treat this as unoptimizable.
                     *handled = true;
                     return true;
                 }
             }
 
-            JSObject* thisObject = CreateThisForFunction(cx, fun, TenuredObject);
+            JSObject* thisObject = CreateThisForFunction(cx, fun, newTarget, TenuredObject);
             if (!thisObject)
                 return false;
 
             if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
                 templateObject = thisObject;
         }
 
         JitSpew(JitSpew_BaselineIC,
@@ -8825,17 +8852,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
                 return false;
 
             stub->addNewStub(newStub);
             *handled = true;
             return true;
         }
 
         RootedObject templateObject(cx);
-        if (MOZ_LIKELY(!isSpread)) {
+        if (MOZ_LIKELY(!isSpread && !isSuper)) {
             bool skipAttach = false;
             CallArgs args = CallArgsFromVp(argc, vp);
             if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject, &skipAttach))
                 return false;
             if (skipAttach) {
                 *handled = true;
                 return true;
             }
@@ -9511,17 +9538,18 @@ ICCall_Fallback::Compiler::postGenerateS
         return;
 
     CodeOffsetLabel offset(returnOffset_);
     offset.fixup(&masm);
     cx->compartment()->jitCompartment()->initBaselineCallReturnAddr(code->raw() + offset.offset(),
                                                                     isConstructing_);
 }
 
-typedef bool (*CreateThisFn)(JSContext* cx, HandleObject callee, MutableHandleValue rval);
+typedef bool (*CreateThisFn)(JSContext* cx, HandleObject callee, HandleObject newTarget,
+                             MutableHandleValue rval);
 static const VMFunction CreateThisInfoBaseline = FunctionInfo<CreateThisFn>(CreateThis);
 
 bool
 ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
 {
     MOZ_ASSERT(engine_ == Engine::Baseline);
 
     Label failure;
@@ -9605,34 +9633,41 @@ ICCallScriptedCompiler::generateStubCode
     Label failureLeaveStubFrame;
 
     if (isConstructing_) {
         // Save argc before call.
         masm.push(argcReg);
 
         // Stack now looks like:
         //      [..., Callee, ThisV, Arg0V, ..., ArgNV, NewTarget, StubFrameHeader, ArgC ]
+        masm.loadValue(Address(masm.getStackPointer(), STUB_FRAME_SIZE + sizeof(size_t)), R1);
+        masm.push(masm.extractObject(R1, ExtractTemp0));
+
         if (isSpread_) {
             masm.loadValue(Address(masm.getStackPointer(),
-                                   3 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t)), R1);
+                                   3 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t) +
+                                   sizeof(JSObject*)),
+                                   R1);
         } else {
             BaseValueIndex calleeSlot2(masm.getStackPointer(), argcReg,
-                                       2 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t));
+                                       2 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t) +
+                                       sizeof(JSObject*));
             masm.loadValue(calleeSlot2, R1);
         }
         masm.push(masm.extractObject(R1, ExtractTemp0));
         if (!callVM(CreateThisInfoBaseline, masm))
             return false;
 
-        // Return of CreateThis must be an object.
+        // Return of CreateThis must be an object or uninitialized.
 #ifdef DEBUG
-        Label createdThisIsObject;
-        masm.branchTestObject(Assembler::Equal, JSReturnOperand, &createdThisIsObject);
-        masm.assumeUnreachable("The return of CreateThis must be an object.");
-        masm.bind(&createdThisIsObject);
+        Label createdThisOK;
+        masm.branchTestObject(Assembler::Equal, JSReturnOperand, &createdThisOK);
+        masm.branchTestMagic(Assembler::Equal, JSReturnOperand, &createdThisOK);
+        masm.assumeUnreachable("The return of CreateThis must be an object or uninitialized.");
+        masm.bind(&createdThisOK);
 #endif
 
         // Reset the register set from here on in.
         MOZ_ASSERT(JSReturnOperand == R0);
         regs = availableGeneralRegs(0);
         regs.take(R0);
         regs.take(ArgumentsRectifierReg);
         argcReg = regs.takeAny();
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -106,17 +106,18 @@ EnterBaseline(JSContext* cx, EnterJitDat
     }
 
     MOZ_ASSERT(jit::IsBaselineEnabled(cx));
     MOZ_ASSERT_IF(data.osrFrame, CheckFrame(data.osrFrame));
 
     EnterJitCode enter = cx->runtime()->jitRuntime()->enterBaseline();
 
     // Caller must construct |this| before invoking the Ion function.
-    MOZ_ASSERT_IF(data.constructing, data.maxArgv[0].isObject());
+    MOZ_ASSERT_IF(data.constructing, data.maxArgv[0].isObject() ||
+                                     data.maxArgv[0].isMagic(JS_UNINITIALIZED_LEXICAL));
 
     data.result.setInt32(data.numActualArgs);
     {
         AssertCompartmentUnchanged pcc(cx);
         JitActivation activation(cx, data.calleeToken);
 
         if (data.osrFrame)
             data.osrFrame->setRunningInJit();
@@ -126,19 +127,22 @@ EnterBaseline(JSContext* cx, EnterJitDat
                             data.scopeChain.get(), data.osrNumStackValues, data.result.address());
 
         if (data.osrFrame)
             data.osrFrame->clearRunningInJit();
     }
 
     MOZ_ASSERT(!cx->runtime()->jitRuntime()->hasIonReturnOverride());
 
-    // Jit callers wrap primitive constructor return.
-    if (!data.result.isMagic() && data.constructing && data.result.isPrimitive())
+    // Jit callers wrap primitive constructor return, except for derived
+    // class constructors, which are forced to do it themselves.
+    if (!data.result.isMagic() && data.constructing && data.result.isPrimitive()) {
+        MOZ_ASSERT(data.maxArgv[0].isObject());
         data.result = data.maxArgv[0];
+    }
 
     // Release temporary buffer used for OSR into Ion.
     cx->runtime()->getJitRuntime(cx)->freeOsrTempData();
 
     MOZ_ASSERT_IF(data.result.isMagic(), data.result.isMagic(JS_ION_ERROR));
     return data.result.isMagic() ? JitExec_Error : JitExec_Ok;
 }
 
@@ -206,17 +210,17 @@ jit::EnterBaselineAtBranch(JSContext* cx
         else
             data.calleeToken = CalleeToToken(fp->script());
 
         if (fp->isEvalFrame()) {
             if (!vals.reserve(2))
                 return JitExec_Aborted;
 
             vals.infallibleAppend(thisv);
-            
+
             if (fp->isFunctionFrame())
                 vals.infallibleAppend(fp->newTarget());
             else
                 vals.infallibleAppend(NullValue());
 
             data.maxArgc = 2;
             data.maxArgv = vals.begin();
         }
@@ -301,25 +305,16 @@ CanEnterBaselineJIT(JSContext* cx, Handl
     // script being a debuggee script, e.g., when performing
     // Debugger.Frame.prototype.eval.
     return BaselineCompile(cx, script, osrFrame && osrFrame->isDebuggee());
 }
 
 MethodStatus
 jit::CanEnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp, bool newType)
 {
-   // If constructing, allocate a new |this| object.
-   if (fp->isConstructing() && fp->functionThis().isPrimitive()) {
-       RootedObject callee(cx, &fp->callee());
-       RootedObject obj(cx, CreateThisForFunction(cx, callee, newType ? SingletonObject : GenericObject));
-       if (!obj)
-           return Method_Skipped;
-       fp->functionThis().setObject(*obj);
-   }
-
    if (!CheckFrame(fp))
        return Method_CantCompile;
 
    // This check is needed in the following corner case. Consider a function h,
    //
    //   function h(x) {
    //      h(false);
    //      if (!x)
@@ -351,18 +346,23 @@ jit::CanEnterBaselineMethod(JSContext* c
     if (state.isInvoke()) {
         InvokeState& invoke = *state.asInvoke();
 
         if (invoke.args().length() > BASELINE_MAX_ARGS_LENGTH) {
             JitSpew(JitSpew_BaselineAbort, "Too many arguments (%u)", invoke.args().length());
             return Method_CantCompile;
         }
 
-        if (!state.maybeCreateThisForConstructor(cx))
-            return Method_Skipped;
+        if (!state.maybeCreateThisForConstructor(cx)) {
+            if (cx->isThrowingOutOfMemory()) {
+                cx->recoverFromOutOfMemory();
+                return Method_Skipped;
+            }
+            return Method_Error;
+        }
     } else {
         MOZ_ASSERT(state.isExecute());
         ExecuteType type = state.asExecute()->type();
         if (type == EXECUTE_DEBUG) {
             JitSpew(JitSpew_BaselineAbort, "debugger frame");
             return Method_CantCompile;
         }
     }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4923,53 +4923,67 @@ CodeGenerator::visitInitPropGetterSetter
     pushArg(value);
     pushArg(ImmGCPtr(lir->mir()->name()));
     pushArg(obj);
     pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
 
     callVM(InitPropGetterSetterInfo, lir);
 }
 
-typedef bool (*CreateThisFn)(JSContext* cx, HandleObject callee, MutableHandleValue rval);
+typedef bool (*CreateThisFn)(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval);
 static const VMFunction CreateThisInfoCodeGen = FunctionInfo<CreateThisFn>(CreateThis);
 
 void
 CodeGenerator::visitCreateThis(LCreateThis* lir)
 {
     const LAllocation* callee = lir->getCallee();
+    const LAllocation* newTarget = lir->getNewTarget();
+
+    if (newTarget->isConstant())
+        pushArg(ImmGCPtr(&newTarget->toConstant()->toObject()));
+    else
+        pushArg(ToRegister(newTarget));
 
     if (callee->isConstant())
         pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
     else
         pushArg(ToRegister(callee));
 
     callVM(CreateThisInfoCodeGen, lir);
 }
 
 static JSObject*
-CreateThisForFunctionWithProtoWrapper(JSContext* cx, js::HandleObject callee, HandleObject proto)
-{
-    return CreateThisForFunctionWithProto(cx, callee, proto);
-}
-
-typedef JSObject* (*CreateThisWithProtoFn)(JSContext* cx, HandleObject callee, HandleObject proto);
+CreateThisForFunctionWithProtoWrapper(JSContext* cx, HandleObject callee, HandleObject newTarget,
+                                      HandleObject proto)
+{
+    return CreateThisForFunctionWithProto(cx, callee, newTarget, proto);
+}
+
+typedef JSObject* (*CreateThisWithProtoFn)(JSContext* cx, HandleObject callee,
+                                           HandleObject newTarget, HandleObject proto);
 static const VMFunction CreateThisWithProtoInfo =
 FunctionInfo<CreateThisWithProtoFn>(CreateThisForFunctionWithProtoWrapper);
 
 void
 CodeGenerator::visitCreateThisWithProto(LCreateThisWithProto* lir)
 {
     const LAllocation* callee = lir->getCallee();
+    const LAllocation* newTarget = lir->getNewTarget();
     const LAllocation* proto = lir->getPrototype();
 
     if (proto->isConstant())
         pushArg(ImmGCPtr(&proto->toConstant()->toObject()));
     else
         pushArg(ToRegister(proto));
 
+    if (newTarget->isConstant())
+        pushArg(ImmGCPtr(&newTarget->toConstant()->toObject()));
+    else
+        pushArg(ToRegister(newTarget));
+
     if (callee->isConstant())
         pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
     else
         pushArg(ToRegister(callee));
 
     callVM(CreateThisWithProtoInfo, lir);
 }
 
@@ -10320,16 +10334,29 @@ CodeGenerator::visitNewTarget(LNewTarget
     masm.bind(&actualArgsSufficient);
 
     BaseValueIndex newTarget(masm.getStackPointer(), argvLen, frameSize() + JitFrameLayout::offsetOfActualArgs());
     masm.loadValue(newTarget, output);
 
     masm.bind(&done);
 }
 
+void
+CodeGenerator::visitCheckReturn(LCheckReturn* ins)
+{
+    ValueOperand returnValue = ToValue(ins, LCheckReturn::ReturnValue);
+    ValueOperand thisValue = ToValue(ins, LCheckReturn::ThisValue);
+    Label bail, noChecks;
+    masm.branchTestObject(Assembler::Equal, returnValue, &noChecks);
+    masm.branchTestUndefined(Assembler::NotEqual, returnValue, &bail);
+    masm.branchTestMagicValue(Assembler::Equal, thisValue, JS_UNINITIALIZED_LEXICAL, &bail);
+    bailoutFrom(&bail, ins->snapshot());
+    masm.bind(&noChecks);
+}
+
 // Out-of-line math_random_no_outparam call for LRandom.
 class OutOfLineRandom : public OutOfLineCodeBase<CodeGenerator>
 {
     LRandom* lir_;
 
   public:
     explicit OutOfLineRandom(LRandom* lir)
       : lir_(lir)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -330,16 +330,17 @@ class CodeGenerator : public CodeGenerat
     void visitAsmJSReturn(LAsmJSReturn* ret);
     void visitAsmJSVoidReturn(LAsmJSVoidReturn* ret);
     void visitLexicalCheck(LLexicalCheck* ins);
     void visitThrowUninitializedLexical(LThrowUninitializedLexical* ins);
     void visitGlobalNameConflictsCheck(LGlobalNameConflictsCheck* ins);
     void visitDebugger(LDebugger* ins);
     void visitNewTarget(LNewTarget* ins);
     void visitArrowNewTarget(LArrowNewTarget* ins);
+    void visitCheckReturn(LCheckReturn* ins);
 
     void visitCheckOverRecursed(LCheckOverRecursed* lir);
     void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
 
     void visitInterruptCheckImplicit(LInterruptCheckImplicit* ins);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
 
     void visitUnboxFloatingPoint(LUnboxFloatingPoint* lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2542,18 +2542,21 @@ jit::CanEnter(JSContext* cx, RunState& s
 
         if (TooManyFormalArguments(invoke.args().callee().as<JSFunction>().nargs())) {
             TrackAndSpewIonAbort(cx, script, "too many args");
             ForbidCompilation(cx, script);
             return Method_CantCompile;
         }
 
         if (!state.maybeCreateThisForConstructor(cx)) {
-            cx->recoverFromOutOfMemory();
-            return Method_Skipped;
+            if (cx->isThrowingOutOfMemory()) {
+                cx->recoverFromOutOfMemory();
+                return Method_Skipped;
+            }
+            return Method_Error;
         }
     }
 
     // If --ion-eager is used, compile with Baseline first, so that we
     // can directly enter IonMonkey.
     if (js_JitOptions.eagerCompilation && !rscript->hasBaselineScript()) {
         MethodStatus status = CanEnterBaselineMethod(cx, state);
         if (status != Method_Compiled)
@@ -2654,32 +2657,37 @@ EnterIon(JSContext* cx, EnterJitData& da
 {
     JS_CHECK_RECURSION(cx, return JitExec_Aborted);
     MOZ_ASSERT(jit::IsIonEnabled(cx));
     MOZ_ASSERT(!data.osrFrame);
 
     EnterJitCode enter = cx->runtime()->jitRuntime()->enterIon();
 
     // Caller must construct |this| before invoking the Ion function.
-    MOZ_ASSERT_IF(data.constructing, data.maxArgv[0].isObject());
+    MOZ_ASSERT_IF(data.constructing,
+                  data.maxArgv[0].isObject() || data.maxArgv[0].isMagic(JS_UNINITIALIZED_LEXICAL));
 
     data.result.setInt32(data.numActualArgs);
     {
         AssertCompartmentUnchanged pcc(cx);
         JitActivation activation(cx, data.calleeToken);
 
         CALL_GENERATED_CODE(enter, data.jitcode, data.maxArgc, data.maxArgv, /* osrFrame = */nullptr, data.calleeToken,
                             /* scopeChain = */ nullptr, 0, data.result.address());
     }
 
     MOZ_ASSERT(!cx->runtime()->jitRuntime()->hasIonReturnOverride());
 
-    // Jit callers wrap primitive constructor return.
-    if (!data.result.isMagic() && data.constructing && data.result.isPrimitive())
+    // Jit callers wrap primitive constructor return, except for derived class constructors.
+    if (!data.result.isMagic() && data.constructing &&
+        data.result.isPrimitive())
+    {
+        MOZ_ASSERT(data.maxArgv[0].isObject());
         data.result = data.maxArgv[0];
+    }
 
     // Release temporary buffer used for OSR into Ion.
     cx->runtime()->getJitRuntime(cx)->freeOsrTempData();
 
     MOZ_ASSERT_IF(data.result.isMagic(), data.result.isMagic(JS_ION_ERROR));
     return data.result.isMagic() ? JitExec_Error : JitExec_Ok;
 }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1862,17 +1862,18 @@ IonBuilder::inspectOpcode(JSOp op)
       case JSOP_FUNCALL:
         return jsop_funcall(GET_ARGC(pc));
 
       case JSOP_FUNAPPLY:
         return jsop_funapply(GET_ARGC(pc));
 
       case JSOP_CALL:
       case JSOP_NEW:
-        return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW);
+      case JSOP_SUPERCALL:
+        return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL);
 
       case JSOP_EVAL:
       case JSOP_STRICTEVAL:
         return jsop_eval(GET_ARGC(pc));
 
       case JSOP_INT8:
         return pushConstant(Int32Value(GET_INT8(pc)));
 
@@ -4464,16 +4465,25 @@ IonBuilder::processReturn(JSOp op)
         def = current->getSlot(info().returnValueSlot());
         break;
 
       default:
         def = nullptr;
         MOZ_CRASH("unknown return op");
     }
 
+    if (script()->isDerivedClassConstructor() &&
+        def->type() != MIRType_Object)
+    {
+        MOZ_ASSERT(info().funMaybeLazy() && info().funMaybeLazy()->isClassConstructor());
+        MCheckReturn* checkRet = MCheckReturn::New(alloc(), def, current->getSlot(info().thisSlot()));
+        current->add(checkRet);
+        def = checkRet;
+    }
+
     MReturn* ret = MReturn::New(alloc(), def);
     current->end(ret);
 
     if (!graph().addReturn(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
     setCurrent(nullptr);
@@ -4939,17 +4949,17 @@ IonBuilder::inlineScriptedCall(CallInfo&
     uint32_t depth = current->stackDepth() + callInfo.numFormals();
     if (depth > current->nslots()) {
         if (!current->increaseSlots(depth - current->nslots()))
             return false;
     }
 
     // Create new |this| on the caller-side for inlined constructors.
     if (callInfo.constructing()) {
-        MDefinition* thisDefn = createThis(target, callInfo.fun());
+        MDefinition* thisDefn = createThis(target, callInfo.fun(), callInfo.getNewTarget());
         if (!thisDefn)
             return false;
         callInfo.setThis(thisDefn);
     }
 
     // Capture formals in the outer resume point.
     callInfo.pushFormals(current);
 
@@ -6033,45 +6043,45 @@ IonBuilder::createCallObject(MDefinition
             current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param));
         }
     }
 
     return callObj;
 }
 
 MDefinition*
-IonBuilder::createThisScripted(MDefinition* callee)
+IonBuilder::createThisScripted(MDefinition* callee, MDefinition* newTarget)
 {
     // Get callee.prototype.
     //
     // This instruction MUST be idempotent: since it does not correspond to an
     // explicit operation in the bytecode, we cannot use resumeAfter().
     // Getters may not override |prototype| fetching, so this operation is indeed idempotent.
     // - First try an idempotent property cache.
     // - Upon failing idempotent property cache, we can't use a non-idempotent cache,
     //   therefore we fallback to CallGetProperty
     //
     // Note: both CallGetProperty and GetPropertyCache can trigger a GC,
     //       and thus invalidation.
     MInstruction* getProto;
     if (!invalidatedIdempotentCache()) {
-        MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), callee, names().prototype,
+        MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), newTarget, names().prototype,
                                                                  /* monitored = */ false);
         getPropCache->setIdempotent();
         getProto = getPropCache;
     } else {
-        MCallGetProperty* callGetProp = MCallGetProperty::New(alloc(), callee, names().prototype,
+        MCallGetProperty* callGetProp = MCallGetProperty::New(alloc(), newTarget, names().prototype,
                                                               /* callprop = */ false);
         callGetProp->setIdempotent();
         getProto = callGetProp;
     }
     current->add(getProto);
 
     // Create this from prototype
-    MCreateThisWithProto* createThis = MCreateThisWithProto::New(alloc(), callee, getProto);
+    MCreateThisWithProto* createThis = MCreateThisWithProto::New(alloc(), callee, newTarget, getProto);
     current->add(createThis);
 
     return createThis;
 }
 
 JSObject*
 IonBuilder::getSingletonPrototype(JSFunction* target)
 {
@@ -6126,16 +6136,18 @@ IonBuilder::createThisScriptedBaseline(M
 {
     // Try to inline |this| creation based on Baseline feedback.
 
     JSFunction* target = inspector->getSingleCallee(pc);
     if (!target || !target->hasScript())
         return nullptr;
 
     JSObject* templateObject = inspector->getTemplateObject(pc);
+    if (!templateObject)
+        return nullptr;
     if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
         return nullptr;
 
     Shape* shape = target->lookupPure(compartment->runtime()->names().prototype);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return nullptr;
 
     Value protov = target->getSlot(shape->slot());
@@ -6176,46 +6188,51 @@ IonBuilder::createThisScriptedBaseline(M
                                      templateObject->group()->initialHeap(constraints()));
     current->add(templateConst);
     current->add(createThis);
 
     return createThis;
 }
 
 MDefinition*
-IonBuilder::createThis(JSFunction* target, MDefinition* callee)
+IonBuilder::createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget)
 {
     // Create |this| for unknown target.
     if (!target) {
         if (MDefinition* createThis = createThisScriptedBaseline(callee))
             return createThis;
 
-        MCreateThis* createThis = MCreateThis::New(alloc(), callee);
+        MCreateThis* createThis = MCreateThis::New(alloc(), callee, newTarget);
         current->add(createThis);
         return createThis;
     }
 
     // Native constructors build the new Object themselves.
     if (target->isNative()) {
         if (!target->isConstructor())
             return nullptr;
 
         MConstant* magic = MConstant::New(alloc(), MagicValue(JS_IS_CONSTRUCTING));
         current->add(magic);
         return magic;
     }
 
+    if (target->isDerivedClassConstructor()) {
+        MOZ_ASSERT(target->isClassConstructor());
+        return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
+    }
+
     // Try baking in the prototype.
     if (MDefinition* createThis = createThisScriptedSingleton(target, callee))
         return createThis;
 
     if (MDefinition* createThis = createThisScriptedBaseline(callee))
         return createThis;
 
-    return createThisScripted(callee);
+    return createThisScripted(callee, newTarget);
 }
 
 bool
 IonBuilder::jsop_funcall(uint32_t argc)
 {
     // Stack for JSOP_FUNCALL:
     // 1:      arg0
     // ...
@@ -6595,17 +6612,17 @@ IonBuilder::makeCallHelper(JSFunction* t
     for (int32_t i = callInfo.argc() - 1; i >= 0; i--)
         call->addArg(i + 1, callInfo.getArg(i));
 
     // Now that we've told it about all the args, compute whether it's movable
     call->computeMovable();
 
     // Inline the constructor on the caller-side.
     if (callInfo.constructing()) {
-        MDefinition* create = createThis(target, callInfo.fun());
+        MDefinition* create = createThis(target, callInfo.fun(), callInfo.getNewTarget());
         if (!create) {
             abort("Failure inlining constructor for call.");
             return nullptr;
         }
 
         callInfo.thisArg()->setImplicitlyUsedUnchecked();
         callInfo.setThis(create);
     }
@@ -12744,19 +12761,31 @@ IonBuilder::jsop_this()
         MLoadArrowThis* thisObj = MLoadArrowThis::New(alloc(), getCallee());
         current->add(thisObj);
         current->push(thisObj);
         return true;
     }
 
     if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
         // No need to wrap primitive |this| in strict mode or self-hosted code.
-        current->pushSlot(info().thisSlot());
-        return true;
-    }
+        MDefinition* thisVal = current->getSlot(info().thisSlot());
+        if (script()->isDerivedClassConstructor()) {
+            MOZ_ASSERT(info().funMaybeLazy()->isClassConstructor());
+            MOZ_ASSERT(script()->strict());
+
+            MLexicalCheck* checkThis = MLexicalCheck::New(alloc(), thisVal, Bailout_UninitializedThis);
+            current->add(checkThis);
+            thisVal = checkThis;
+        }
+
+        current->push(thisVal);
+        return true;
+    }
+
+    MOZ_ASSERT(!info().funMaybeLazy()->isClassConstructor());
 
     if (thisTypes && (thisTypes->getKnownMIRType() == MIRType_Object ||
         (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject())))
     {
         // This is safe, because if the entry type of |this| is an object, it
         // will necessarily be an object throughout the entire function. OSR
         // can introduce a phi, but this phi will be specialized.
         current->pushSlot(info().thisSlot());
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -375,20 +375,20 @@ class IonBuilder
 
     // Creates a MDefinition based on the given def improved with type as TypeSet.
     MDefinition* ensureDefiniteTypeSet(MDefinition* def, TemporaryTypeSet* types);
 
     void maybeMarkEmpty(MDefinition* ins);
 
     JSObject* getSingletonPrototype(JSFunction* target);
 
-    MDefinition* createThisScripted(MDefinition* callee);
+    MDefinition* createThisScripted(MDefinition* callee, MDefinition* newTarget);
     MDefinition* createThisScriptedSingleton(JSFunction* target, MDefinition* callee);
     MDefinition* createThisScriptedBaseline(MDefinition* callee);
-    MDefinition* createThis(JSFunction* target, MDefinition* callee);
+    MDefinition* createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget);
     MInstruction* createDeclEnvObject(MDefinition* callee, MDefinition* scopeObj);
     MInstruction* createCallObject(MDefinition* callee, MDefinition* scopeObj);
 
     MDefinition* walkScopeChain(unsigned hops);
 
     MInstruction* addConvertElementsToDoubles(MDefinition* elements);
     MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
     MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -106,19 +106,24 @@ enum BailoutKind
     Bailout_NonSimdFloat32x4Input,
 
     // For the initial snapshot when entering a function.
     Bailout_InitialState,
 
     // We hit a |debugger;| statement.
     Bailout_Debugger,
 
+    // |this| used uninitialized in a derived constructor
+    Bailout_UninitializedThis,
+
+    // Derived constructors must return object or undefined
+    Bailout_BadDerivedConstructorReturn,
+
     // END Normal bailouts
 
-
     // Bailouts caused by invalid assumptions based on Baseline code.
     // Causes immediate invalidation.
 
     // Like Bailout_Overflow, but causes immediate invalidation.
     Bailout_OverflowInvalidate,
 
     // Like NonStringInput, but should cause immediate invalidation.
     // Used for jsop_iternext.
@@ -146,17 +151,17 @@ enum BailoutKind
     // (We saw an object whose shape does not match that / any of those observed
     // by the baseline IC.)
     Bailout_ShapeGuard,
 
     // When we're trying to use an uninitialized lexical.
     Bailout_UninitializedLexical,
 
     // A bailout to baseline from Ion on exception to handle Debugger hooks.
-    Bailout_IonExceptionDebugMode,
+    Bailout_IonExceptionDebugMode
 };
 
 inline const char*
 BailoutKindString(BailoutKind kind)
 {
     switch (kind) {
       // Normal bailouts.
       case Bailout_Inevitable:
@@ -204,16 +209,20 @@ BailoutKindString(BailoutKind kind)
       case Bailout_NonSimdInt32x4Input:
         return "Bailout_NonSimdInt32x4Input";
       case Bailout_NonSimdFloat32x4Input:
         return "Bailout_NonSimdFloat32x4Input";
       case Bailout_InitialState:
         return "Bailout_InitialState";
       case Bailout_Debugger:
         return "Bailout_Debugger";
+      case Bailout_UninitializedThis:
+        return "Bailout_UninitializedThis";
+      case Bailout_BadDerivedConstructorReturn:
+        return "Bailout_BadDerivedConstructorReturn";
 
       // Bailouts caused by invalid assumptions.
       case Bailout_OverflowInvalidate:
         return "Bailout_OverflowInvalidate";
       case Bailout_NonStringInputInvalidate:
         return "Bailout_NonStringInputInvalidate";
       case Bailout_DoubleOutput:
         return "Bailout_DoubleOutput";
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -323,25 +323,27 @@ LIRGenerator::visitCreateThisWithTemplat
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitCreateThisWithProto(MCreateThisWithProto* ins)
 {
     LCreateThisWithProto* lir =
         new(alloc()) LCreateThisWithProto(useRegisterOrConstantAtStart(ins->getCallee()),
+                                          useRegisterOrConstantAtStart(ins->getNewTarget()),
                                           useRegisterOrConstantAtStart(ins->getPrototype()));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitCreateThis(MCreateThis* ins)
 {
-    LCreateThis* lir = new(alloc()) LCreateThis(useRegisterOrConstantAtStart(ins->getCallee()));
+    LCreateThis* lir = new(alloc()) LCreateThis(useRegisterOrConstantAtStart(ins->getCallee()),
+                                                useRegisterOrConstantAtStart(ins->getNewTarget()));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitCreateArgumentsObject(MCreateArgumentsObject* ins)
 {
     // LAllocation callObj = useRegisterAtStart(ins->getCallObject());
@@ -4253,17 +4255,17 @@ LIRGenerator::visitSimdShift(MSimdShift*
 
 void
 LIRGenerator::visitLexicalCheck(MLexicalCheck* ins)
 {
     MDefinition* input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Value);
     LLexicalCheck* lir = new(alloc()) LLexicalCheck();
     useBox(lir, LLexicalCheck::Input, input);
-    assignSnapshot(lir, Bailout_UninitializedLexical);
+    assignSnapshot(lir, ins->bailoutKind());
     add(lir, ins);
     redefine(ins, input);
 }
 
 void
 LIRGenerator::visitThrowUninitializedLexical(MThrowUninitializedLexical* ins)
 {
     LThrowUninitializedLexical* lir = new(alloc()) LThrowUninitializedLexical();
@@ -4288,16 +4290,32 @@ LIRGenerator::visitDebugger(MDebugger* i
 }
 
 void
 LIRGenerator::visitAtomicIsLockFree(MAtomicIsLockFree* ins)
 {
     define(new(alloc()) LAtomicIsLockFree(useRegister(ins->input())), ins);
 }
 
+void
+LIRGenerator::visitCheckReturn(MCheckReturn* ins)
+{
+    MDefinition* retVal = ins->returnValue();
+    MDefinition* thisVal = ins->thisValue();
+    MOZ_ASSERT(retVal->type() == MIRType_Value);
+    MOZ_ASSERT(thisVal->type() == MIRType_Value);
+
+    LCheckReturn* lir = new(alloc()) LCheckReturn();
+    useBoxAtStart(lir, LCheckReturn::ReturnValue, retVal);
+    useBoxAtStart(lir, LCheckReturn::ThisValue, thisVal);
+    assignSnapshot(lir, Bailout_BadDerivedConstructorReturn);
+    add(lir, ins);
+    redefine(ins, retVal);
+}
+
 static void
 SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint)
 {
     Fprinter& out = JitSpewPrinter();
     out.printf("Current resume point %p details:\n", (void*)resumePoint);
     out.printf("    frame count: %u\n", resumePoint->frameCount());
 
     if (ins) {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -301,14 +301,15 @@ class LIRGenerator : public LIRGenerator
     void visitUnknownValue(MUnknownValue* ins);
     void visitLexicalCheck(MLexicalCheck* ins);
     void visitThrowUninitializedLexical(MThrowUninitializedLexical* ins);
     void visitGlobalNameConflictsCheck(MGlobalNameConflictsCheck* ins);
     void visitDebugger(MDebugger* ins);
     void visitNewTarget(MNewTarget* ins);
     void visitArrowNewTarget(MArrowNewTarget* ins);
     void visitAtomicIsLockFree(MAtomicIsLockFree* ins);
+    void visitCheckReturn(MCheckReturn* ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Lowering_h */
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4608,71 +4608,77 @@ class MCreateThisWithTemplate
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override;
 };
 
 // Caller-side allocation of |this| for |new|:
 // Given a prototype operand, construct |this| for JSOP_NEW.
 class MCreateThisWithProto
-  : public MBinaryInstruction,
-    public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
-{
-    MCreateThisWithProto(MDefinition* callee, MDefinition* prototype)
-      : MBinaryInstruction(callee, prototype)
+  : public MTernaryInstruction,
+    public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >::Data
+{
+    MCreateThisWithProto(MDefinition* callee, MDefinition* newTarget, MDefinition* prototype)
+      : MTernaryInstruction(callee, newTarget, prototype)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(CreateThisWithProto)
     static MCreateThisWithProto* New(TempAllocator& alloc, MDefinition* callee,
-                                     MDefinition* prototype)
-    {
-        return new(alloc) MCreateThisWithProto(callee, prototype);
+                                     MDefinition* newTarget, MDefinition* prototype)
+    {
+        return new(alloc) MCreateThisWithProto(callee, newTarget, prototype);
     }
 
     MDefinition* getCallee() const {
         return getOperand(0);
     }
+    MDefinition* getNewTarget() const {
+        return getOperand(1);
+    }
     MDefinition* getPrototype() const {
-        return getOperand(1);
+        return getOperand(2);
     }
 
     // Although creation of |this| modifies global state, it is safely repeatable.
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 // Caller-side allocation of |this| for |new|:
 // Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING).
 class MCreateThis
-  : public MUnaryInstruction,
-    public ObjectPolicy<0>::Data
-{
-    explicit MCreateThis(MDefinition* callee)
-      : MUnaryInstruction(callee)
+  : public MBinaryInstruction,
+    public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
+{
+    explicit MCreateThis(MDefinition* callee, MDefinition* newTarget)
+      : MBinaryInstruction(callee, newTarget)
     {
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(CreateThis)
-    static MCreateThis* New(TempAllocator& alloc, MDefinition* callee)
-    {
-        return new(alloc) MCreateThis(callee);
+    static MCreateThis* New(TempAllocator& alloc, MDefinition* callee, MDefinition* newTarget)
+    {
+        return new(alloc) MCreateThis(callee, newTarget);
     }
 
     MDefinition* getCallee() const {
         return getOperand(0);
     }
+    MDefinition* getNewTarget() const {
+        return getOperand(0);
+    }
 
     // Although creation of |this| modifies global state, it is safely repeatable.
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
@@ -7225,40 +7231,47 @@ class MAsmJSInterruptCheck
 };
 
 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
 // it to baseline to throw at the correct pc.
 class MLexicalCheck
   : public MUnaryInstruction,
     public BoxPolicy<0>::Data
 {
-    explicit MLexicalCheck(MDefinition* input)
-      : MUnaryInstruction(input)
+    BailoutKind kind_;
+    explicit MLexicalCheck(MDefinition* input, BailoutKind kind)
+      : MUnaryInstruction(input),
+        kind_(kind)
     {
         setResultType(MIRType_Value);
         setResultTypeSet(input->resultTypeSet());
         setMovable();
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(LexicalCheck)
 
-    static MLexicalCheck* New(TempAllocator& alloc, MDefinition* input) {
-        return new(alloc) MLexicalCheck(input);
+    static MLexicalCheck* New(TempAllocator& alloc, MDefinition* input,
+                              BailoutKind kind = Bailout_UninitializedLexical) {
+        return new(alloc) MLexicalCheck(input, kind);
     }
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     MDefinition* input() const {
         return getOperand(0);
     }
 
+    BailoutKind bailoutKind() const {
+        return kind_;
+    }
+
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
     }
 };
 
 // Unconditionally throw an uninitialized let error.
 class MThrowUninitializedLexical : public MNullaryInstruction
 {
@@ -12931,16 +12944,43 @@ class MHasClass
         if (!ins->isHasClass())
             return false;
         if (getClass() != ins->toHasClass()->getClass())
             return false;
         return congruentIfOperandsEqual(ins);
     }
 };
 
+class MCheckReturn
+  : public MBinaryInstruction,
+    public BoxInputsPolicy::Data
+{
+    explicit MCheckReturn(MDefinition* retVal, MDefinition* thisVal)
+      : MBinaryInstruction(retVal, thisVal)
+    {
+        setGuard();
+        setResultType(MIRType_Value);
+        setResultTypeSet(retVal->resultTypeSet());
+    }
+
+  public:
+    INSTRUCTION_HEADER(CheckReturn)
+
+    static MCheckReturn* New(TempAllocator& alloc, MDefinition* retVal, MDefinition* thisVal) {
+        return new (alloc) MCheckReturn(retVal, thisVal);
+    }
+
+    MDefinition* returnValue() const {
+        return getOperand(0);
+    }
+    MDefinition* thisValue() const {
+        return getOperand(1);
+    }
+};
+
 // Increase the warm-up counter of the provided script upon execution and test if
 // the warm-up counter surpasses the threshold. Upon hit it will recompile the
 // outermost script (i.e. not the inlined script).
 class MRecompileCheck : public MNullaryInstruction
 {
   public:
     enum RecompileCheckType {
         RecompileCheck_OptimizationLevel,
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -272,17 +272,18 @@ namespace jit {
     _(AsmJSAtomicExchangeHeap)                                              \
     _(AsmJSAtomicBinopHeap)                                                 \
     _(UnknownValue)                                                         \
     _(LexicalCheck)                                                         \
     _(ThrowUninitializedLexical)                                            \
     _(GlobalNameConflictsCheck)                                             \
     _(Debugger)                                                             \
     _(NewTarget)                                                            \
-    _(ArrowNewTarget)
+    _(ArrowNewTarget)                                                       \
+    _(CheckReturn)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
 class MDefinitionVisitor // interface i.e. pure abstract class
 {
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -1191,16 +1191,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(IntPolicy<1>)                                                     \
     _(Mix3Policy<ObjectPolicy<0>, StringPolicy<1>, BoxPolicy<2> >) \
     _(Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >)         \
     _(Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >)      \
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >)         \
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >)         \
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2> >) \
     _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2> >)      \
+    _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >)   \
     _(Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>)          \
     _(Mix3Policy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2> >)   \
     _(Mix3Policy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >)   \
     _(Mix4Policy<ObjectPolicy<0>, StringPolicy<1>, BoxPolicy<2>, BoxPolicy<3>>) \
     _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>) \
     _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3> >) \
     _(Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \
     _(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >)                        \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -560,30 +560,34 @@ GetIntrinsicValue(JSContext* cx, HandleP
     // guaranteed to bail out after this function, but because of its AliasSet,
     // type info will not be reflowed. Manually monitor here.
     TypeScript::Monitor(cx, rval);
 
     return true;
 }
 
 bool
-CreateThis(JSContext* cx, HandleObject callee, MutableHandleValue rval)
+CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval)
 {
     rval.set(MagicValue(JS_IS_CONSTRUCTING));
 
     if (callee->is<JSFunction>()) {
         JSFunction* fun = &callee->as<JSFunction>();
         if (fun->isInterpreted() && fun->isConstructor()) {
             JSScript* script = fun->getOrCreateScript(cx);
             if (!script || !script->ensureHasTypes(cx))
                 return false;
-            JSObject* thisObj = CreateThisForFunction(cx, callee, GenericObject);
-            if (!thisObj)
-                return false;
-            rval.set(ObjectValue(*thisObj));
+            if (script->isDerivedClassConstructor()) {
+                rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL));
+            } else {
+                JSObject* thisObj = CreateThisForFunction(cx, callee, newTarget, GenericObject);
+                if (!thisObj)
+                    return false;
+                rval.set(ObjectValue(*thisObj));
+            }
         }
     }
 
     return true;
 }
 
 void
 GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp)
@@ -1286,10 +1290,23 @@ bool
 ThrowUninitializedLexical(JSContext* cx)
 {
     ScriptFrameIter iter(cx);
     RootedScript script(cx, iter.script());
     ReportUninitializedLexical(cx, script, iter.pc());
     return false;
 }
 
+bool
+ThrowBadDerivedReturn(JSContext* cx, HandleValue v)
+{
+    ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, v, nullptr);
+    return false;
+}
+
+bool
+BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame)
+{
+    return ThrowUninitializedThis(cx, frame);
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -629,17 +629,17 @@ JSObject* NewCallObject(JSContext* cx, H
 JSObject* NewSingletonCallObject(JSContext* cx, HandleShape shape, uint32_t lexicalBegin);
 JSObject* NewStringObject(JSContext* cx, HandleString str);
 
 bool OperatorIn(JSContext* cx, HandleValue key, HandleObject obj, bool* out);
 bool OperatorInI(JSContext* cx, uint32_t index, HandleObject obj, bool* out);
 
 bool GetIntrinsicValue(JSContext* cx, HandlePropertyName name, MutableHandleValue rval);
 
-bool CreateThis(JSContext* cx, HandleObject callee, MutableHandleValue rval);
+bool CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval);
 
 void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp);
 
 void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
 void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
 
 uint32_t GetIndexFromString(JSString* str);
 
@@ -729,13 +729,15 @@ IonMarkFunction(MIRType type)
         return JS_FUNC_TO_DATA_PTR(void*, MarkObjectGroupFromIon);
       default: MOZ_CRASH();
     }
 }
 
 bool ObjectIsCallable(JSObject* obj);
 
 bool ThrowUninitializedLexical(JSContext* cx);
+bool BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame);
+bool ThrowBadDerivedReturn(JSContext* cx, HandleValue v);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/jit/arm64/BaselineIC-arm64.cpp
+++ b/js/src/jit/arm64/BaselineIC-arm64.cpp
@@ -8,16 +8,17 @@
 #include "jit/SharedICHelpers.h"
 
 #ifdef JS_SIMULATOR_ARM64
 #include "jit/arm64/Assembler-arm64.h"
 #include "jit/arm64/BaselineCompiler-arm64.h"
 #include "jit/arm64/vixl/Debugger-vixl.h"
 #endif
 
+#include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 namespace js {
 namespace jit {
 
 // ICCompare_Int32
--- a/js/src/jit/arm64/MoveEmitter-arm64.cpp
+++ b/js/src/jit/arm64/MoveEmitter-arm64.cpp
@@ -1,19 +1,30 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/arm64/MoveEmitter-arm64.h"
+#include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
+MemOperand
+MoveEmitterARM64::toMemOperand(const MoveOperand& operand) const
+{
+    MOZ_ASSERT(operand.isMemory());
+    ARMRegister base(operand.base(), 64);
+    if (operand.base() == masm.getStackPointer())
+        return MemOperand(base, operand.disp() + (masm.framePushed() - pushedAtStart_));
+    return MemOperand(base, operand.disp());
+}
+
 void
 MoveEmitterARM64::emit(const MoveResolver& moves)
 {
     if (moves.numCycles()) {
         masm.reserveStack(sizeof(void*));
         pushedAtCycle_ = masm.framePushed();
     }
 
--- a/js/src/jit/arm64/MoveEmitter-arm64.h
+++ b/js/src/jit/arm64/MoveEmitter-arm64.h
@@ -30,23 +30,17 @@ class MoveEmitterARM64
     int32_t pushedAtCycle_;
     int32_t pushedAtSpill_;
 
     void assertDone() {
         MOZ_ASSERT(!inCycle_);
     }
 
     MemOperand cycleSlot();
-    MemOperand toMemOperand(const MoveOperand& operand) const {
-        MOZ_ASSERT(operand.isMemory());
-        ARMRegister base(operand.base(), 64);
-        if (operand.base() == masm.getStackPointer())
-            return MemOperand(base, operand.disp() + (masm.framePushed() -  pushedAtStart_));
-        return MemOperand(base, operand.disp());
-    }
+    MemOperand toMemOperand(const MoveOperand& operand) const;
     ARMRegister toARMReg32(const MoveOperand& operand) const {
         MOZ_ASSERT(operand.isGeneralReg());
         return ARMRegister(operand.reg(), 32);
     }
     ARMRegister toARMReg64(const MoveOperand& operand) const {
         if (operand.isGeneralReg())
             return ARMRegister(operand.reg(), 64);
         else
--- a/js/src/jit/arm64/vixl/Instructions-vixl.cpp
+++ b/js/src/jit/arm64/vixl/Instructions-vixl.cpp
@@ -251,17 +251,17 @@ const Instruction* Instruction::ImmPCOff
     VIXL_ASSERT(BranchType() != UnknownBranchType);
     // Relative branch offsets are instruction-size-aligned.
     offset = ImmBranch() << kInstructionSizeLog2;
   }
   return base + offset;
 }
 
 
-inline int Instruction::ImmBranch() const {
+int Instruction::ImmBranch() const {
   switch (BranchType()) {
     case CondBranchType: return ImmCondBranch();
     case UncondBranchType: return ImmUncondBranch();
     case CompareBranchType: return ImmCmpBranch();
     case TestBranchType: return ImmTestBranch();
     default: VIXL_UNREACHABLE();
   }
   return 0;
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1295,53 +1295,62 @@ class LToIdV : public LInstructionHelper
 
     const LDefinition* tempFloat() {
         return getTemp(0);
     }
 };
 
 // Allocate an object for |new| on the caller-side,
 // when there is no templateObject or prototype known
-class LCreateThis : public LCallInstructionHelper<BOX_PIECES, 1, 0>
+class LCreateThis : public LCallInstructionHelper<BOX_PIECES, 2, 0>
 {
   public:
     LIR_HEADER(CreateThis)
 
-    explicit LCreateThis(const LAllocation& callee)
+    LCreateThis(const LAllocation& callee, const LAllocation& newTarget)
     {
         setOperand(0, callee);
+        setOperand(1, newTarget);
     }
 
     const LAllocation* getCallee() {
         return getOperand(0);
     }
+    const LAllocation* getNewTarget() {
+        return getOperand(1);
+    }
 
     MCreateThis* mir() const {
         return mir_->toCreateThis();
     }
 };
 
 // Allocate an object for |new| on the caller-side,
 // when the prototype is known.
-class LCreateThisWithProto : public LCallInstructionHelper<1, 2, 0>
+class LCreateThisWithProto : public LCallInstructionHelper<1, 3, 0>
 {
   public:
     LIR_HEADER(CreateThisWithProto)
 
-    LCreateThisWithProto(const LAllocation& callee, const LAllocation& prototype)
+    LCreateThisWithProto(const LAllocation& callee, const LAllocation& newTarget,
+                         const LAllocation& prototype)
     {
         setOperand(0, callee);
-        setOperand(1, prototype);
+        setOperand(1, newTarget);
+        setOperand(2, prototype);
     }
 
     const LAllocation* getCallee() {
         return getOperand(0);
     }
+    const LAllocation* getNewTarget() {
+        return getOperand(1);
+    }
     const LAllocation* getPrototype() {
-        return getOperand(1);
+        return getOperand(2);
     }
 
     MCreateThis* mir() const {
         return mir_->toCreateThis();
     }
 };
 
 // Allocate an object for |new| on the caller-side.
@@ -7242,12 +7251,21 @@ class LRandom : public LInstructionHelpe
     }
 #endif
 
     MRandom* mir() const {
         return mir_->toRandom();
     }
 };
 
+class LCheckReturn : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0>
+{
+  public:
+    static const size_t ReturnValue = 0;
+    static const size_t ThisValue = BOX_PIECES;
+
+    LIR_HEADER(CheckReturn)
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_LIR_shared_h */
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -363,11 +363,12 @@
     _(AssertRangeV)                 \
     _(AssertResultV)                \
     _(AssertResultT)                \
     _(LexicalCheck)                 \
     _(ThrowUninitializedLexical)    \
     _(GlobalNameConflictsCheck)     \
     _(Debugger)                     \
     _(NewTarget)                    \
-    _(ArrowNewTarget)
+    _(ArrowNewTarget)               \
+    _(CheckReturn)
 
 #endif /* jit_shared_LOpcodes_shared_h */
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -101,16 +101,18 @@ MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE, 0
 MSG_DEF(JSMSG_CANT_SET_PROTO,          0, JSEXN_TYPEERR, "can't set prototype of this object")
 MSG_DEF(JSMSG_CANT_SET_PROTO_OF,       1, JSEXN_TYPEERR, "can't set prototype of {0}")
 MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE,    0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
 MSG_DEF(JSMSG_INVALID_ARG_TYPE,        3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
 MSG_DEF(JSMSG_TERMINATED,              1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
 MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL,     1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
 MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
 MSG_DEF(JSMSG_DISABLED_DERIVED_CLASS,  1, JSEXN_INTERNALERR, "{0} temporarily disallowed in derived class constructors")
+MSG_DEF(JSMSG_UNINITIALIZED_THIS,      1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
+MSG_DEF(JSMSG_BAD_DERIVED_RETURN,      1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
 
 // JSON
 MSG_DEF(JSMSG_JSON_BAD_PARSE,          3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
 MSG_DEF(JSMSG_JSON_CYCLIC_VALUE,       1, JSEXN_TYPEERR, "cyclic {0} value")
 
 // Runtime errors
 MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS,      1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")
 MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS,     0, JSEXN_REFERENCEERR, "invalid assignment left-hand side")
@@ -207,16 +209,17 @@ MSG_DEF(JSMSG_BAD_METHOD_DEF,          0
 MSG_DEF(JSMSG_BAD_OCTAL,               1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
 MSG_DEF(JSMSG_BAD_OPERAND,             1, JSEXN_SYNTAXERR, "invalid {0} operand")
 MSG_DEF(JSMSG_BAD_PROP_ID,             0, JSEXN_SYNTAXERR, "invalid property id")
 MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD,     1, JSEXN_SYNTAXERR, "{0} not in function")
 MSG_DEF(JSMSG_BAD_STRICT_ASSIGN,       1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode")
 MSG_DEF(JSMSG_BAD_SWITCH,              0, JSEXN_SYNTAXERR, "invalid switch statement")
 MSG_DEF(JSMSG_BAD_SUPER,               0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
 MSG_DEF(JSMSG_BAD_SUPERPROP,           1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
+MSG_DEF(JSMSG_BAD_SUPERCALL,           0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
 MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
 MSG_DEF(JSMSG_BRACKET_AFTER_LIST,      0, JSEXN_SYNTAXERR, "missing ] after element list")
 MSG_DEF(JSMSG_BRACKET_IN_INDEX,        0, JSEXN_SYNTAXERR, "missing ] in index expression")
 MSG_DEF(JSMSG_CATCH_AFTER_GENERAL,     0, JSEXN_SYNTAXERR, "catch after unconditional catch")
 MSG_DEF(JSMSG_CATCH_IDENTIFIER,        0, JSEXN_SYNTAXERR, "missing identifier in catch")
 MSG_DEF(JSMSG_CATCH_OR_FINALLY,        0, JSEXN_SYNTAXERR, "missing catch or finally after try")
 MSG_DEF(JSMSG_CATCH_WITHOUT_TRY,       0, JSEXN_SYNTAXERR, "catch without try")
 MSG_DEF(JSMSG_COLON_AFTER_CASE,        0, JSEXN_SYNTAXERR, "missing : after case label")
@@ -502,15 +505,16 @@ MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
 MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS,   0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
 MSG_DEF(JSMSG_NO_NAMED_SETTER,           2, JSEXN_TYPEERR, "{0} doesn't have a named property setter for '{1}'")
 MSG_DEF(JSMSG_NO_INDEXED_SETTER,         2, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter for '{1}'")
 
 // Super
 MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
+MSG_DEF(JSMSG_REINIT_THIS,       0, JSEXN_REFERENCEERR, "super() called twice in derived class constructor")
 
 // Modules
 MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT,        0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
 MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT,   0, JSEXN_SYNTAXERR, "indirect export not found")
 MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "ambiguous indirect export")
 MSG_DEF(JSMSG_MISSING_IMPORT,            0, JSEXN_SYNTAXERR, "import not found")
 MSG_DEF(JSMSG_AMBIGUOUS_IMPORT,          0, JSEXN_SYNTAXERR, "ambiguous import")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6385,16 +6385,22 @@ JS_DecodeInterpretedFunction(JSContext* 
 {
     XDRDecoder decoder(cx, data, length);
     RootedFunction funobj(cx);
     if (!decoder.codeFunction(&funobj))
         return nullptr;
     return funobj;
 }
 
+JS_PUBLIC_API(bool)
+JS_SetImmutablePrototype(JSContext *cx, JS::HandleObject obj, bool *succeeded)
+{
+    return SetImmutablePrototype(cx, obj, succeeded);
+}
+
 JS_PUBLIC_API(void)
 JS::SetAsmJSCacheOps(JSRuntime* rt, const JS::AsmJSCacheOps* ops)
 {
     rt->asmJSCacheOps = *ops;
 }
 
 char*
 JSAutoByteString::encodeLatin1(ExclusiveContext* cx, JSString* str)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2457,16 +2457,26 @@ JS_FreezeObject(JSContext* cx, JS::Handl
  * attempt, return false (with a pending exception set, depending upon the
  * nature of the error).  If no error occurs, return true with |result| set
  * to indicate whether the attempt successfully set the [[Extensible]] property
  * to false.
  */
 extern JS_PUBLIC_API(bool)
 JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result);
 
+/*
+ * Attempt to make the [[Prototype]] of |obj| immutable, such that any attempt
+ * to modify it will fail.  If an error occurs during the attempt, return false
+ * (with a pending exception set, depending upon the nature of the error).  If
+ * no error occurs, return true with |*succeeded| set to indicate whether the
+ * attempt successfully made the [[Prototype]] immutable.
+ */
+extern JS_PUBLIC_API(bool)
+JS_SetImmutablePrototype(JSContext* cx, JS::HandleObject obj, bool* succeeded);
+
 extern JS_PUBLIC_API(JSObject*)
 JS_New(JSContext* cx, JS::HandleObject ctor, const JS::HandleValueArray& args);
 
 
 /*** Property descriptors ************************************************************************/
 
 struct JSPropertyDescriptor : public JS::Traceable {
     JSObject* obj;
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -369,16 +369,22 @@ struct JSCompartment
     // Non-zero if any typed objects in this compartment might be neutered.
     int32_t                      neuteredTypedObjects;
 
   private:
     friend class js::AutoSetNewObjectMetadata;
     js::NewObjectMetadataState objectMetadataState;
 
   public:
+    // Recompute the probability with which this compartment should record
+    // profiling data (stack traces, allocations log, etc.) about each
+    // allocation. We consult the probabilities requested by the Debugger
+    // instances observing us, if any.
+    void chooseAllocationSamplingProbability() { savedStacks_.chooseSamplingProbability(this); }
+
     bool hasObjectPendingMetadata() const { return objectMetadataState.is<js::PendingMetadata>(); }
 
     void setObjectPendingMetadata(JSContext* cx, JSObject* obj) {
         MOZ_ASSERT(objectMetadataState.is<js::DelayMetadata>());
         objectMetadataState = js::NewObjectMetadataState(js::PendingMetadata(obj));
     }
 
     void setObjectPendingMetadata(js::ExclusiveContext* ecx, JSObject* obj) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -151,16 +151,17 @@ class JSFunction : public js::NativeObje
         if (isNative())
             return false;
 
         // Note: this should be kept in sync with FunctionBox::needsCallObject().
         return nonLazyScript()->hasAnyAliasedBindings() ||
                nonLazyScript()->funHasExtensibleScope() ||
                nonLazyScript()->funNeedsDeclEnvObject() ||
                nonLazyScript()->needsHomeObject()       ||
+               nonLazyScript()->isDerivedClassConstructor() ||
                isGenerator();
     }
 
     size_t nargs() const {
         return nargs_;
     }
 
     uint16_t flags() const {
@@ -511,16 +512,26 @@ class JSFunction : public js::NativeObje
         return u.n.jitinfo;
     }
 
     void setJitInfo(const JSJitInfo* data) {
         MOZ_ASSERT(isNative());
         u.n.jitinfo = data;
     }
 
+    bool isDerivedClassConstructor() {
+        bool derived;
+        if (isInterpretedLazy())
+            derived = lazyScript()->isDerivedClassConstructor();
+        else
+            derived = nonLazyScript()->isDerivedClassConstructor();
+        MOZ_ASSERT_IF(derived, isClassConstructor());
+        return derived;
+    }
+
     static unsigned offsetOfNativeOrScript() {
         static_assert(offsetof(U, n.native) == offsetof(U, i.s.script_),
                       "native and script pointers must be in the same spot "
                       "for offsetOfNativeOrScript() have any sense");
         static_assert(offsetof(U, n.native) == offsetof(U, nativeOrScript),
                       "U::nativeOrScript must be at the same offset as "
                       "native");
 
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -729,25 +729,21 @@ js::math_pow_handle(JSContext* cx, Handl
 bool
 js::math_pow(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     return math_pow_handle(cx, args.get(0), args.get(1), args.rval());
 }
 
-static uint64_t
-random_generateSeed()
+void
+js::random_generateSeed(uint64_t* seedBuffer, size_t length)
 {
-    union {
-        uint8_t     u8[8];
-        uint32_t    u32[2];
-        uint64_t    u64;
-    } seed;
-    seed.u64 = 0;
+    if (length == 0)
+        return;
 
 #if defined(XP_WIN)
     /*
      * Temporary diagnostic for bug 1167248: Test whether the injected hooks
      * react differently to LoadLibraryW / LoadLibraryExW.
      */
     HMODULE oldWay = LoadLibraryW(L"ADVAPI32.DLL");
     HMODULE newWay = LoadLibraryExW(L"ADVAPI32.DLL",
@@ -755,55 +751,82 @@ random_generateSeed()
                                     LOAD_LIBRARY_SEARCH_SYSTEM32);
     /* Fallback for older versions of Windows */
     if (!newWay && GetLastError() == ERROR_INVALID_PARAMETER)
         newWay = LoadLibraryExW(L"ADVAPI32.DLL", nullptr, 0);
 
     if (oldWay && !newWay)
         MOZ_CRASH();
 
+    union {
+        uint32_t    u32[2];
+        uint64_t    u64;
+    } seed;
+    seed.u64 = 0;
+
     errno_t error = rand_s(&seed.u32[0]);
 
     if (oldWay)
         FreeLibrary(oldWay);
     if (newWay)
         FreeLibrary(newWay);
 
     MOZ_ASSERT(error == 0, "rand_s() error?!");
 
     error = rand_s(&seed.u32[1]);
     MOZ_ASSERT(error == 0, "rand_s() error?!");
+
+    seedBuffer[0] = seed.u64 ^= PRMJ_Now();
+    for (size_t i = 1; i < length; i++) {
+        error = rand_s(&seed.u32[0]);
+        MOZ_ASSERT(error == 0, "rand_s() error?!");
+
+        error = rand_s(&seed.u32[1]);
+        MOZ_ASSERT(error == 0, "rand_s() error?!");
+
+        seedBuffer[i] = seed.u64 ^ PRMJ_Now();
+    }
+
 #elif defined(HAVE_ARC4RANDOM)
-    seed.u32[0] = arc4random();
-    seed.u32[1] = arc4random();
+    union {
+        uint32_t    u32[2];
+        uint64_t    u64;
+    } seed;
+    seed.u64 = 0;
+
+    for (size_t i = 0; i < length; i++) {
+        seed.u32[0] = arc4random();
+        seed.u32[1] = arc4random();
+        seedBuffer[i] = seed.u64 ^ PRMJ_Now();
+    }
+
 #elif defined(XP_UNIX)
     int fd = open("/dev/urandom", O_RDONLY);
     MOZ_ASSERT(fd >= 0, "Can't open /dev/urandom?!");
     if (fd >= 0) {
-        ssize_t nread = read(fd, seed.u8, mozilla::ArrayLength(seed.u8));
-        MOZ_ASSERT(nread == 8, "Can't read /dev/urandom?!");
+        ssize_t size = length * sizeof(seedBuffer[0]);
+        ssize_t nread = read(fd, (char *) seedBuffer, size);
+        MOZ_ASSERT(nread == size, "Can't read /dev/urandom?!");
         mozilla::unused << nread;
         close(fd);
     }
 #else
 # error "Platform needs to implement random_generateSeed()"
 #endif
-
-    seed.u64 ^= PRMJ_Now();
-    return seed.u64;
 }
 
 /*
  * Math.random() support, lifted from java.util.Random.java.
  */
 void
 js::random_initState(uint64_t* rngState)
 {
     /* Our PRNG only uses 48 bits, so squeeze our entropy into those bits. */
-    uint64_t seed = random_generateSeed();
+    uint64_t seed;
+    random_generateSeed(&seed, 1);
     seed ^= (seed >> 16);
     *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
 }
 
 uint64_t
 js::random_next(uint64_t* rngState, int bits)
 {
     MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState");
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -104,16 +104,23 @@ class MathCache
 
 /*
  * JS math functions.
  */
 
 extern JSObject*
 InitMathClass(JSContext* cx, HandleObject obj);
 
+/*
+ * Fill |seed[0]| through |seed[length-1]| with random bits, suitable for
+ * seeding a random number generator.
+ */
+extern void
+random_generateSeed(uint64_t* seed, size_t length);
+
 extern void
 random_initState(uint64_t* rngState);
 
 extern uint64_t
 random_next(uint64_t* rngState, int bits);
 
 static const double RNG_DSCALE = double(1LL << 53);
 static const int RNG_STATE_WIDTH = 48;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -962,36 +962,36 @@ CreateThisForFunctionWithGroup(JSContext
     if (newKind == SingletonObject) {
         Rooted<TaggedProto> protoRoot(cx, group->proto());
         return NewObjectWithGivenTaggedProto(cx, &PlainObject::class_, protoRoot, allocKind, newKind);
     }
     return NewObjectWithGroup<PlainObject>(cx, group, allocKind, newKind);
 }
 
 JSObject*
-js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObject proto,
-                                   NewObjectKind newKind /* = GenericObject */)
+js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObject newTarget,
+                                   HandleObject proto, NewObjectKind newKind /* = GenericObject */)
 {
     RootedObject res(cx);
 
     if (proto) {
         RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
-                                                                 &callee->as<JSFunction>()));
+                                                                 newTarget));
         if (!group)
             return nullptr;
 
         if (group->newScript() && !group->newScript()->analyzed()) {
             bool regenerate;
             if (!group->newScript()->maybeAnalyze(cx, group, &regenerate))
                 return nullptr;
             if (regenerate) {
                 // The script was analyzed successfully and may have changed
                 // the new type table, so refetch the group.
                 group = ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
-                                                     &callee->as<JSFunction>());
+                                                     newTarget);
                 MOZ_ASSERT(group && group->newScript());
             }
         }
 
         res = CreateThisForFunctionWithGroup(cx, group, newKind);
     } else {
         res = NewBuiltinClassInstance<PlainObject>(cx, newKind);
     }
@@ -1002,25 +1002,26 @@ js::CreateThisForFunctionWithProto(JSCon
             return nullptr;
         TypeScript::SetThis(cx, script, TypeSet::ObjectType(res));
     }
 
     return res;
 }
 
 JSObject*
-js::CreateThisForFunction(JSContext* cx, HandleObject callee, NewObjectKind newKind)
+js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget,
+                          NewObjectKind newKind)
 {
     RootedValue protov(cx);
-    if (!GetProperty(cx, callee, callee, cx->names().prototype, &protov))
+    if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov))
         return nullptr;
     RootedObject proto(cx);
     if (protov.isObject())
         proto = &protov.toObject();
-    JSObject* obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
+    JSObject* obj = CreateThisForFunctionWithProto(cx, callee, newTarget, proto, newKind);
 
     if (obj && newKind == SingletonObject) {
         RootedPlainObject nobj(cx, &obj->as<PlainObject>());
 
         /* Reshape the singleton before passing it as the 'this' value. */
         NativeObject::clear(cx, nobj);
 
         JSScript* calleeScript = callee->as<JSFunction>().nonLazyScript();
@@ -2389,17 +2390,17 @@ JSObject::reportNotExtensible(JSContext*
 }
 
 // Our immutable-prototype behavior is non-standard, and it's unclear whether
 // it's shippable.  (Or at least it's unclear whether it's shippable with any
 // provided-by-default uses exposed to script.)  If this bool is true,
 // immutable-prototype behavior is enforced; if it's false, behavior is not
 // enforced, and immutable-prototype bits stored on objects are completely
 // ignored.
-static const bool ImmutablePrototypesEnabled = true;
+static const bool ImmutablePrototypesEnabled = false;
 
 JS_FRIEND_API(bool)
 JS_ImmutablePrototypesEnabled()
 {
     return ImmutablePrototypesEnabled;
 }
 
 /*** ES6 standard internal methods ***************************************************************/
@@ -3335,16 +3336,17 @@ JSObject::dump()
     if (obj->isQualifiedVarObj()) fprintf(stderr, " varobj");
     if (obj->isUnqualifiedVarObj()) fprintf(stderr, " unqualified_varobj");
     if (obj->watched()) fprintf(stderr, " watched");
     if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton");
     if (obj->isNewGroupUnknown()) fprintf(stderr, " new_type_unknown");
     if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
     if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
     if (obj->wasNewScriptCleared()) fprintf(stderr, " new_script_cleared");
+    if (!obj->hasLazyPrototype() && obj->nonLazyPrototypeIsImmutable()) fprintf(stderr, " immutable_prototype");
 
     if (obj->isNative()) {
         NativeObject* nobj = &obj->as<NativeObject>();
         if (nobj->inDictionaryMode())
             fprintf(stderr, " inDictionaryMode");
         if (nobj->hasShapeTable())
             fprintf(stderr, " hasShapeTable");
     }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1157,22 +1157,23 @@ GetInitialHeap(NewObjectKind newKind, co
     if (clasp->finalize && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
         return gc::TenuredHeap;
     return gc::DefaultHeap;
 }
 
 // Specialized call for constructing |this| with a known function callee,
 // and a known prototype.
 extern JSObject*
-CreateThisForFunctionWithProto(JSContext* cx, js::HandleObject callee, HandleObject proto,
-                               NewObjectKind newKind = GenericObject);
+CreateThisForFunctionWithProto(JSContext* cx, js::HandleObject callee, HandleObject newTarget,
+                               HandleObject proto, NewObjectKind newKind = GenericObject);
 
 // Specialized call for constructing |this| with a known function callee.
 extern JSObject*
-CreateThisForFunction(JSContext* cx, js::HandleObject callee, NewObjectKind newKind);
+CreateThisForFunction(JSContext* cx, js::HandleObject callee, js::HandleObject newTarget,
+                      NewObjectKind newKind);
 
 // Generic call for constructing |this|.
 extern JSObject*
 CreateThis(JSContext* cx, const js::Class* clasp, js::HandleObject callee);
 
 extern JSObject*
 CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto);
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -120,16 +120,17 @@ js::StackUses(JSScript* script, jsbyteco
     if (cs.nuses >= 0)
         return cs.nuses;
 
     MOZ_ASSERT(js_CodeSpec[op].nuses == -1);
     switch (op) {
       case JSOP_POPN:
         return GET_UINT16(pc);
       case JSOP_NEW:
+      case JSOP_SUPERCALL:
         return 2 + GET_ARGC(pc) + 1;
       default:
         /* stack: fun, this, [argc arguments] */
         MOZ_ASSERT(op == JSOP_CALL || op == JSOP_EVAL ||
                    op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
         return 2 + GET_ARGC(pc);
     }
 }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -104,100 +104,107 @@ using mozilla::UniquePtr;
 
 enum JSShellExitCode {
     EXITCODE_RUNTIME_ERROR      = 3,
     EXITCODE_FILE_NOT_FOUND     = 4,
     EXITCODE_OUT_OF_MEMORY      = 5,
     EXITCODE_TIMEOUT            = 6
 };
 
-static size_t gStackChunkSize = 8192;
+static const size_t gStackChunkSize = 8192;
 
 /*
  * Note: This limit should match the stack limit set by the browser in
  *       js/xpconnect/src/XPCJSRuntime.cpp
  */
 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
-static size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
+static const size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
 #else
-static size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
+static const size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
 #endif
 
 /*
  * Limit the timeout to 30 minutes to prevent an overflow on platfoms
  * that represent the time internally in microseconds using 32-bit int.
  */
-static double MAX_TIMEOUT_INTERVAL = 1800.0;
-static double gTimeoutInterval = -1.0;
-static Atomic<bool> gServiceInterrupt;
-static JS::PersistentRootedValue gInterruptFunc;
-
-static bool gLastWarningEnabled = false;
-static JS::PersistentRootedValue gLastWarning;
-
+static const double MAX_TIMEOUT_INTERVAL = 1800.0;
+
+// Per-runtime shell state.
+struct ShellRuntime
+{
+    ShellRuntime();
+
+    bool isWorker;
+    double timeoutInterval;
+    Atomic<bool> serviceInterrupt;
+    JS::PersistentRootedValue interruptFunc;
+    bool lastWarningEnabled;
+    JS::PersistentRootedValue lastWarning;
+
+    /*
+     * Watchdog thread state.
+     */
+    PRLock* watchdogLock;
+    PRCondVar* watchdogWakeup;
+    PRThread* watchdogThread;
+    bool watchdogHasTimeout;
+    int64_t watchdogTimeout;
+
+    PRCondVar* sleepWakeup;
+
+    int exitCode;
+    bool quitting;
+    bool gotError;
+};
+
+// Shell state set once at startup.
 static bool enableCodeCoverage = false;
 static bool enableDisassemblyDumps = false;
 static bool offthreadCompilation = false;
 static bool enableBaseline = false;
 static bool enableIon = false;
 static bool enableAsmJS = false;
 static bool enableNativeRegExp = false;
 static bool enableUnboxedArrays = false;
 #ifdef JS_GC_ZEAL
 static char gZealStr[128];
 #endif
-
 static bool printTiming = false;
 static const char* jsCacheDir = nullptr;
 static const char* jsCacheAsmJSPath = nullptr;
-static bool jsCachingEnabled = false;
+static FILE* gErrFile = nullptr;
+static FILE* gOutFile = nullptr;
+static bool reportWarnings = true;
+static bool compileOnly = false;
+static bool fuzzingSafe = false;
+static bool disableOOMFunctions = false;
+#ifdef DEBUG
+static bool dumpEntrainedVariables = false;
+static bool OOM_printAllocationCount = false;
+#endif
+
+// Shell state this is only accessed on the main thread.
+bool jsCachingEnabled = false;
 mozilla::Atomic<bool> jsCacheOpened(false);
 
 static bool
 SetTimeoutValue(JSContext* cx, double t);
 
 static bool
 InitWatchdog(JSRuntime* rt);
 
 static void
-KillWatchdog();
+KillWatchdog(JSRuntime *rt);
 
 static bool
 ScheduleWatchdog(JSRuntime* rt, double t);
 
 static void
 CancelExecution(JSRuntime* rt);
 
-/*
- * Watchdog thread state.
- */
-static PRLock* gWatchdogLock = nullptr;
-static PRCondVar* gWatchdogWakeup = nullptr;
-static PRThread* gWatchdogThread = nullptr;
-static bool gWatchdogHasTimeout = false;
-static int64_t gWatchdogTimeout = 0;
-
-static PRCondVar* gSleepWakeup = nullptr;
-
-static int gExitCode = 0;
-static bool gQuitting = false;
-static bool gGotError = false;
-static FILE* gErrFile = nullptr;
-static FILE* gOutFile = nullptr;
-
-static bool reportWarnings = true;
-static bool compileOnly = false;
-static bool fuzzingSafe = false;
-static bool disableOOMFunctions = false;
-
-#ifdef DEBUG
-static bool dumpEntrainedVariables = false;
-static bool OOM_printAllocationCount = false;
-#endif
-
 static JSContext*
 NewContext(JSRuntime* rt);
 
 static void
 DestroyContext(JSContext* cx, bool withGC);
 
 static JSObject*
 NewGlobalObject(JSContext* cx, JS::CompartmentOptions& options,
@@ -262,16 +269,46 @@ ShellPrincipals ShellPrincipals::fullyTr
 
 #ifdef EDITLINE
 extern "C" {
 extern JS_EXPORT_API(char*) readline(const char* prompt);
 extern JS_EXPORT_API(void)   add_history(char* line);
 } // extern "C"
 #endif
 
+ShellRuntime::ShellRuntime()
+  : isWorker(false),
+    timeoutInterval(-1.0),
+    serviceInterrupt(false),
+    lastWarningEnabled(false),
+    watchdogLock(nullptr),
+    watchdogWakeup(nullptr),
+    watchdogThread(nullptr),
+    watchdogHasTimeout(false),
+    watchdogTimeout(0),
+    sleepWakeup(nullptr),
+    exitCode(0),
+    quitting(false),
+    gotError(false)
+{}
+
+static ShellRuntime*
+GetShellRuntime(JSRuntime *rt)
+{
+    ShellRuntime* sr = static_cast<ShellRuntime*>(JS_GetRuntimePrivate(rt));
+    MOZ_ASSERT(sr);
+    return sr;
+}
+
+static ShellRuntime*
+GetShellRuntime(JSContext* cx)
+{
+    return GetShellRuntime(cx->runtime());
+}
+
 static char*
 GetLine(FILE* file, const char * prompt)
 {
 #ifdef EDITLINE
     /*
      * Use readline only if file is stdin, because there's no way to specify
      * another handle.  Are other filehandles interactive?
      */
@@ -365,27 +402,28 @@ GetContextData(JSContext* cx)
 
     MOZ_ASSERT(data);
     return data;
 }
 
 static bool
 ShellInterruptCallback(JSContext* cx)
 {
-    if (!gServiceInterrupt)
+    ShellRuntime* sr = GetShellRuntime(cx);
+    if (!sr->serviceInterrupt)
         return true;
 
-    // Reset gServiceInterrupt. CancelExecution or InterruptIf will set it to
+    // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
     // true to distinguish watchdog or user triggered interrupts.
     // Do this first to prevent other interrupts that may occur while the
     // user-supplied callback is executing from re-entering the handler.
-    gServiceInterrupt = false;
+    sr->serviceInterrupt = false;
 
     bool result;
-    RootedValue interruptFunc(cx, gInterruptFunc);
+    RootedValue interruptFunc(cx, sr->interruptFunc);
     if (!interruptFunc.isNull()) {
         JS::AutoSaveExceptionState savedExc(cx);
         JSAutoCompartment ac(cx, &interruptFunc.toObject());
         RootedValue rval(cx);
         if (!JS_CallFunctionValue(cx, nullptr, interruptFunc,
                                   JS::HandleValueArray::empty(), &rval))
         {
             return false;
@@ -393,18 +431,18 @@ ShellInterruptCallback(JSContext* cx)
         if (rval.isBoolean())
             result = rval.toBoolean();
         else
             result = false;
     } else {
         result = false;
     }
 
-    if (!result && gExitCode == 0)
-        gExitCode = EXITCODE_TIMEOUT;
+    if (!result && sr->exitCode == 0)
+        sr->exitCode = EXITCODE_TIMEOUT;
 
     return result;
 }
 
 /*
  * Some UTF-8 files, notably those written using Notepad, have a Unicode
  * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
  * is meaningless for UTF-8) but causes a syntax error unless we skip it.
@@ -427,16 +465,18 @@ SkipUTF8BOM(FILE* file)
         ungetc(ch2, file);
     if (ch1 != EOF)
         ungetc(ch1, file);
 }
 
 static void
 RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
+
     SkipUTF8BOM(file);
 
     // To support the UNIX #! shell hack, gobble the first line if it starts
     // with '#'.
     int ch = fgetc(file);
     if (ch == '#') {
         while ((ch = fgetc(file)) != EOF) {
             if (ch == '\n' || ch == '\r')
@@ -451,29 +491,29 @@ RunFile(JSContext* cx, const char* filen
     {
         CompileOptions options(cx);
         options.setIntroductionType("js shell file")
                .setUTF8(true)
                .setFileAndLine(filename, 1)
                .setIsRunOnce(true)
                .setNoScriptRval(true);
 
-        gGotError = false;
+        sr->gotError = false;
         (void) JS::Compile(cx, options, file, &script);
-        MOZ_ASSERT_IF(!script, gGotError);
+        MOZ_ASSERT_IF(!script, sr->gotError);
     }
 
     #ifdef DEBUG
         if (dumpEntrainedVariables)
             AnalyzeEntrainedVariables(cx, script);
     #endif
     if (script && !compileOnly) {
         if (!JS_ExecuteScript(cx, script)) {
-            if (!gQuitting && gExitCode != EXITCODE_TIMEOUT)
-                gExitCode = EXITCODE_RUNTIME_ERROR;
+            if (!sr->quitting && sr->exitCode != EXITCODE_TIMEOUT)
+                sr->exitCode = EXITCODE_RUNTIME_ERROR;
         }
         int64_t t2 = PRMJ_Now() - t1;
         if (printTiming)
             printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
     }
 }
 
 static bool
@@ -509,64 +549,65 @@ EvalAndPrint(JSContext* cx, const char* 
         JS_free(cx, utf8chars);
     }
     return true;
 }
 
 static void
 ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
     int lineno = 1;
     bool hitEOF = false;
 
     do {
         /*
          * Accumulate lines until we get a 'compilable unit' - one that either
          * generates an error (before running out of source) or that compiles
          * cleanly.  This should be whenever we get a complete statement that
          * coincides with the end of a line.
          */
         int startline = lineno;
         typedef Vector<char, 32> CharBuffer;
         CharBuffer buffer(cx);
         do {
             ScheduleWatchdog(cx->runtime(), -1);
-            gServiceInterrupt = false;
+            sr->serviceInterrupt = false;
             errno = 0;
 
             char* line = GetLine(in, startline == lineno ? "js> " : "");
             if (!line) {
                 if (errno) {
                     JS_ReportError(cx, strerror(errno));
                     return;
                 }
                 hitEOF = true;
                 break;
             }
 
             if (!buffer.append(line, strlen(line)) || !buffer.append('\n'))
                 return;
 
             lineno++;
-            if (!ScheduleWatchdog(cx->runtime(), gTimeoutInterval)) {
+            if (!ScheduleWatchdog(cx->runtime(), sr->timeoutInterval)) {
                 hitEOF = true;
                 break;
             }
         } while (!JS_BufferIsCompilableUnit(cx, cx->global(), buffer.begin(), buffer.length()));
 
         if (hitEOF && buffer.empty())
             break;
 
         if (!EvalAndPrint(cx, buffer.begin(), buffer.length(), startline, compileOnly,
                           out))
         {
             // Catch the error, report it, and keep going.
             JS_ReportPendingException(cx);
         }
-    } while (!hitEOF && !gQuitting);
+    } while (!hitEOF && !sr->quitting);
 
     fprintf(out, "\n");
 }
 
 static void
 Process(JSContext* cx, const char* filename, bool forceTTY)
 {
     FILE* file;
@@ -1538,16 +1579,18 @@ PrintErr(JSContext* cx, unsigned argc, V
 }
 
 static bool
 Help(JSContext* cx, unsigned argc, Value* vp);
 
 static bool
 Quit(JSContext* cx, unsigned argc, Value* vp)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
+
 #ifdef JS_MORE_DETERMINISTIC
     // Print a message to stderr in more-deterministic builds to help jsfunfuzz
     // find uncatchable-exception bugs.
     fprintf(stderr, "quit called\n");
 #endif
 
     CallArgs args = CallArgsFromVp(argc, vp);
     int32_t code;
@@ -1559,18 +1602,18 @@ Quit(JSContext* cx, unsigned argc, Value
     // POSIX platforms, the exit code is 8-bit and negative values can also
     // result in an exit code >= 128. We restrict the value to range [0, 127] to
     // avoid false positives.
     if (code < 0 || code >= 128) {
         JS_ReportError(cx, "quit exit code should be in range 0-127");
         return false;
     }
 
-    gExitCode = code;
-    gQuitting = true;
+    sr->exitCode = code;
+    sr->quitting = true;
     return false;
 }
 
 static bool
 StartTimingMutator(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() > 0) {
@@ -2540,19 +2583,35 @@ WorkerMain(void* arg)
 {
     WorkerInput* input = (WorkerInput*) arg;
 
     JSRuntime* rt = JS_NewRuntime(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->runtime);
     if (!rt) {
         js_delete(input);
         return;
     }
+
+    mozilla::UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>();
+    if (!sr) {
+        JS_DestroyRuntime(rt);
+        js_delete(input);
+        return;
+    }
+
+    sr->isWorker = true;
+    JS_SetRuntimePrivate(rt, sr.get());
     JS_SetErrorReporter(rt, my_ErrorReporter);
     SetWorkerRuntimeOptions(rt);
 
+    if (!InitWatchdog(rt)) {
+        JS_DestroyRuntime(rt);
+        js_delete(input);
+        return;
+    }
+
     JSContext* cx = NewContext(rt);
     if (!cx) {
         JS_DestroyRuntime(rt);
         js_delete(input);
         return;
     }
 
     JS::SetLargeAllocationFailureCallback(rt, my_LargeAllocFailCallback, (void*)cx);
@@ -2648,16 +2707,17 @@ static bool
 IsBefore(int64_t t1, int64_t t2)
 {
     return int32_t(t1 - t2) < 0;
 }
 
 static bool
 Sleep_fn(JSContext* cx, unsigned argc, Value* vp)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
     CallArgs args = CallArgsFromVp(argc, vp);
     int64_t t_ticks;
 
     if (args.length() == 0) {
         t_ticks = 0;
     } else {
         double t_secs;
 
@@ -2668,185 +2728,192 @@ Sleep_fn(JSContext* cx, unsigned argc, V
         if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
             JS_ReportError(cx, "Excessive sleep interval");
             return false;
         }
         t_ticks = (t_secs <= 0.0)
                   ? 0
                   : int64_t(PRMJ_USEC_PER_SEC * t_secs);
     }
-    PR_Lock(gWatchdogLock);
+    PR_Lock(sr->watchdogLock);
     int64_t to_wakeup = PRMJ_Now() + t_ticks;
     for (;;) {
-        PR_WaitCondVar(gSleepWakeup, PR_MillisecondsToInterval(t_ticks / 1000));
-        if (gServiceInterrupt)
+        PR_WaitCondVar(sr->sleepWakeup, PR_MillisecondsToInterval(t_ticks / 1000));
+        if (sr->serviceInterrupt)
             break;
         int64_t now = PRMJ_Now();
         if (!IsBefore(now, to_wakeup))
             break;
         t_ticks = to_wakeup - now;
     }
-    PR_Unlock(gWatchdogLock);
+    PR_Unlock(sr->watchdogLock);
     args.rval().setUndefined();
-    return !gServiceInterrupt;
+    return !sr->serviceInterrupt;
 }
 
 static bool
 InitWatchdog(JSRuntime* rt)
 {
-    MOZ_ASSERT(!gWatchdogThread);
-    gWatchdogLock = PR_NewLock();
-    if (gWatchdogLock) {
-        gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
-        if (gWatchdogWakeup) {
-            gSleepWakeup = PR_NewCondVar(gWatchdogLock);
-            if (gSleepWakeup)
+    ShellRuntime* sr = GetShellRuntime(rt);
+    MOZ_ASSERT(!sr->watchdogThread);
+    sr->watchdogLock = PR_NewLock();
+    if (sr->watchdogLock) {
+        sr->watchdogWakeup = PR_NewCondVar(sr->watchdogLock);
+        if (sr->watchdogWakeup) {
+            sr->sleepWakeup = PR_NewCondVar(sr->watchdogLock);
+            if (sr->sleepWakeup)
                 return true;
-            PR_DestroyCondVar(gWatchdogWakeup);
+            PR_DestroyCondVar(sr->watchdogWakeup);
         }
-        PR_DestroyLock(gWatchdogLock);
+        PR_DestroyLock(sr->watchdogLock);
     }
     return false;
 }
 
 static void
-KillWatchdog()
-{
+KillWatchdog(JSRuntime* rt)
+{
+    ShellRuntime* sr = GetShellRuntime(rt);
     PRThread* thread;
 
-    PR_Lock(gWatchdogLock);
-    thread = gWatchdogThread;
+    PR_Lock(sr->watchdogLock);
+    thread = sr->watchdogThread;
     if (thread) {
         /*
          * The watchdog thread is running, tell it to terminate waking it up
          * if necessary.
          */
-        gWatchdogThread = nullptr;
-        PR_NotifyCondVar(gWatchdogWakeup);
-    }
-    PR_Unlock(gWatchdogLock);
+        sr->watchdogThread = nullptr;
+        PR_NotifyCondVar(sr->watchdogWakeup);
+    }
+    PR_Unlock(sr->watchdogLock);
     if (thread)
         PR_JoinThread(thread);
-    PR_DestroyCondVar(gSleepWakeup);
-    PR_DestroyCondVar(gWatchdogWakeup);
-    PR_DestroyLock(gWatchdogLock);
+    PR_DestroyCondVar(sr->sleepWakeup);
+    PR_DestroyCondVar(sr->watchdogWakeup);
+    PR_DestroyLock(sr->watchdogLock);
 }
 
 static void
 WatchdogMain(void* arg)
 {
     PR_SetCurrentThreadName("JS Watchdog");
 
     JSRuntime* rt = (JSRuntime*) arg;
-
-    PR_Lock(gWatchdogLock);
-    while (gWatchdogThread) {
+    ShellRuntime* sr = GetShellRuntime(rt);
+
+    PR_Lock(sr->watchdogLock);
+    while (sr->watchdogThread) {
         int64_t now = PRMJ_Now();
-        if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
+        if (sr->watchdogHasTimeout && !IsBefore(now, sr->watchdogTimeout)) {
             /*
              * The timeout has just expired. Request an interrupt callback
              * outside the lock.
              */
-            gWatchdogHasTimeout = false;
-            PR_Unlock(gWatchdogLock);
+            sr->watchdogHasTimeout = false;
+            PR_Unlock(sr->watchdogLock);
             CancelExecution(rt);
-            PR_Lock(gWatchdogLock);
+            PR_Lock(sr->watchdogLock);
 
             /* Wake up any threads doing sleep. */
-            PR_NotifyAllCondVar(gSleepWakeup);
+            PR_NotifyAllCondVar(sr->sleepWakeup);
         } else {
-            if (gWatchdogHasTimeout) {
+            if (sr->watchdogHasTimeout) {
                 /*
                  * Time hasn't expired yet. Simulate an interrupt callback
                  * which doesn't abort execution.
                  */
                 JS_RequestInterruptCallback(rt);
             }
 
             uint64_t sleepDuration = PR_INTERVAL_NO_TIMEOUT;
-            if (gWatchdogHasTimeout)
+            if (sr->watchdogHasTimeout)
                 sleepDuration = PR_TicksPerSecond() / 10;
             mozilla::DebugOnly<PRStatus> status =
-              PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
+              PR_WaitCondVar(sr->watchdogWakeup, sleepDuration);
             MOZ_ASSERT(status == PR_SUCCESS);
         }
     }
-    PR_Unlock(gWatchdogLock);
+    PR_Unlock(sr->watchdogLock);
 }
 
 static bool
 ScheduleWatchdog(JSRuntime* rt, double t)
 {
+    ShellRuntime* sr = GetShellRuntime(rt);
+
     if (t <= 0) {
-        PR_Lock(gWatchdogLock);
-        gWatchdogHasTimeout = false;
-        PR_Unlock(gWatchdogLock);
+        PR_Lock(sr->watchdogLock);
+        sr->watchdogHasTimeout = false;
+        PR_Unlock(sr->watchdogLock);
         return true;
     }
 
     int64_t interval = int64_t(ceil(t * PRMJ_USEC_PER_SEC));
     int64_t timeout = PRMJ_Now() + interval;
-    PR_Lock(gWatchdogLock);
-    if (!gWatchdogThread) {
-        MOZ_ASSERT(!gWatchdogHasTimeout);
-        gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
+    PR_Lock(sr->watchdogLock);
+    if (!sr->watchdogThread) {
+        MOZ_ASSERT(!sr->watchdogHasTimeout);
+        sr->watchdogThread = PR_CreateThread(PR_USER_THREAD,
                                           WatchdogMain,
                                           rt,
                                           PR_PRIORITY_NORMAL,
                                           PR_GLOBAL_THREAD,
                                           PR_JOINABLE_THREAD,
                                           0);
-        if (!gWatchdogThread) {
-            PR_Unlock(gWatchdogLock);
+        if (!sr->watchdogThread) {
+            PR_Unlock(sr->watchdogLock);
             return false;
         }
-    } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
-         PR_NotifyCondVar(gWatchdogWakeup);
-    }
-    gWatchdogHasTimeout = true;
-    gWatchdogTimeout = timeout;
-    PR_Unlock(gWatchdogLock);
+    } else if (!sr->watchdogHasTimeout || IsBefore(timeout, sr->watchdogTimeout)) {
+         PR_NotifyCondVar(sr->watchdogWakeup);
+    }
+    sr->watchdogHasTimeout = true;
+    sr->watchdogTimeout = timeout;
+    PR_Unlock(sr->watchdogLock);
     return true;
 }
 
 static void
 CancelExecution(JSRuntime* rt)
 {
-    gServiceInterrupt = true;
+    ShellRuntime* sr = GetShellRuntime(rt);
+    sr->serviceInterrupt = true;
     JS_RequestInterruptCallback(rt);
 
-    if (!gInterruptFunc.isNull()) {
+    if (!sr->interruptFunc.isNull()) {
         static const char msg[] = "Script runs for too long, terminating.\n";
         fputs(msg, stderr);
     }
 }
 
 static bool
 SetTimeoutValue(JSContext* cx, double t)
 {
     /* NB: The next condition also filter out NaNs. */
     if (!(t <= MAX_TIMEOUT_INTERVAL)) {
         JS_ReportError(cx, "Excessive timeout value");
         return false;
     }
-    gTimeoutInterval = t;
+    GetShellRuntime(cx)->timeoutInterval = t;
     if (!ScheduleWatchdog(cx->runtime(), t)) {
         JS_ReportError(cx, "Failed to create the watchdog");
         return false;
     }
     return true;
 }
 
 static bool
 Timeout(JSContext* cx, unsigned argc, Value* vp)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
-        args.rval().setNumber(gTimeoutInterval);
+        args.rval().setNumber(sr->timeoutInterval);
         return true;
     }
 
     if (args.length() > 2) {
         JS_ReportError(cx, "Wrong number of arguments");
         return false;
     }
 
@@ -2855,17 +2922,17 @@ Timeout(JSContext* cx, unsigned argc, Va
         return false;
 
     if (args.length() > 1) {
         RootedValue value(cx, args[1]);
         if (!value.isObject() || !value.toObject().is<JSFunction>()) {
             JS_ReportError(cx, "Second argument must be a timeout function");
             return false;
         }
-        gInterruptFunc = value;
+        sr->interruptFunc = value;
     }
 
     args.rval().setUndefined();
     return SetTimeoutValue(cx, t);
 }
 
 static bool
 InterruptIf(JSContext* cx, unsigned argc, Value* vp)
@@ -2873,34 +2940,34 @@ InterruptIf(JSContext* cx, unsigned argc
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         JS_ReportError(cx, "Wrong number of arguments");
         return false;
     }
 
     if (ToBoolean(args[0])) {
-        gServiceInterrupt = true;
+        GetShellRuntime(cx)->serviceInterrupt = true;
         JS_RequestInterruptCallback(cx->runtime());
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1) {
         JS_ReportError(cx, "Wrong number of arguments");
         return false;
     }
 
-    gServiceInterrupt = true;
+    GetShellRuntime(cx)->serviceInterrupt = true;
     JS_RequestInterruptCallback(cx->runtime());
     bool interruptRv = CheckForInterrupt(cx);
 
     // The interrupt handler could have set a pending exception. Since we call
     // back into JS, don't have it see the pending exception. If we have an
     // uncatchable exception that's not propagating a debug mode forced
     // return, return.
     if (!interruptRv && !cx->isExceptionPending() && !cx->isPropagatingForcedReturn())
@@ -2926,74 +2993,78 @@ SetInterruptCallback(JSContext* cx, unsi
         return false;
     }
 
     RootedValue value(cx, args[0]);
     if (!value.isObject() || !value.toObject().is<JSFunction>()) {
         JS_ReportError(cx, "Argument must be a function");
         return false;
     }
-    gInterruptFunc = value;
+    GetShellRuntime(cx)->interruptFunc = value;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 EnableLastWarning(JSContext* cx, unsigned argc, Value* vp)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    gLastWarningEnabled = true;
-    gLastWarning.setNull();
+    sr->lastWarningEnabled = true;
+    sr->lastWarning.setNull();
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 DisableLastWarning(JSContext* cx, unsigned argc, Value* vp)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    gLastWarningEnabled = false;
-    gLastWarning.setNull();
+    sr->lastWarningEnabled = false;
+    sr->lastWarning.setNull();
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 GetLastWarning(JSContext* cx, unsigned argc, Value* vp)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (!gLastWarningEnabled) {
+    if (!sr->lastWarningEnabled) {
         JS_ReportError(cx, "Call enableLastWarning first.");
         return false;
     }
 
-    if (!JS_WrapValue(cx, &gLastWarning))
-        return false;
-
-    args.rval().set(gLastWarning);
+    if (!JS_WrapValue(cx, &sr->lastWarning))
+        return false;
+
+    args.rval().set(sr->lastWarning);
     return true;
 }
 
 static bool
 ClearLastWarning(JSContext* cx, unsigned argc, Value* vp)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (!gLastWarningEnabled) {
+    if (!sr->lastWarningEnabled) {
         JS_ReportError(cx, "Call enableLastWarning first.");
         return false;
     }
 
-    gLastWarning.setNull();
+    sr->lastWarning.setNull();
 
     args.rval().setUndefined();
     return true;
 }
 
 #ifdef DEBUG
 static bool
 StackDump(JSContext* cx, unsigned argc, Value* vp)
@@ -3888,16 +3959,21 @@ IsCachingEnabled(JSContext* cx, unsigned
     args.rval().setBoolean(jsCachingEnabled && jsCacheAsmJSPath != nullptr);
     return true;
 }
 
 static bool
 SetCachingEnabled(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
+    if (GetShellRuntime(cx)->isWorker) {
+        JS_ReportError(cx, "Caching is not supported in workers");
+        return false;
+    }
+
     jsCachingEnabled = ToBoolean(args.get(0));
     args.rval().setUndefined();
     return true;
 }
 
 static void
 PrintProfilerEvents_Callback(const char* msg)
 {
@@ -5137,17 +5213,17 @@ CreateLastWarningObject(JSContext* cx, J
     RootedValue linenoVal(cx, Int32Value(report->lineno));
     if (!DefineProperty(cx, warningObj, cx->names().lineNumber, linenoVal))
         return false;
 
     RootedValue columnVal(cx, Int32Value(report->column));
     if (!DefineProperty(cx, warningObj, cx->names().columnNumber, columnVal))
         return false;
 
-    gLastWarning.setObject(*warningObj);
+    GetShellRuntime(cx)->lastWarning.setObject(*warningObj);
     return true;
 }
 
 static bool
 PrintStackTrace(JSContext* cx, HandleValue exn)
 {
     if (!exn.isObject())
         return false;
@@ -5179,55 +5255,56 @@ PrintStackTrace(JSContext* cx, HandleVal
     fputs(stack.get(), gErrFile);
 
     return true;
 }
 
 void
 js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
 {
-    if (report && JSREPORT_IS_WARNING(report->flags) && gLastWarningEnabled) {
+    ShellRuntime* sr = GetShellRuntime(cx);
+
+    if (report && JSREPORT_IS_WARNING(report->flags) && sr->lastWarningEnabled) {
         JS::AutoSaveExceptionState savedExc(cx);
         if (!CreateLastWarningObject(cx, report)) {
             fputs("Unhandled error happened while creating last warning object.\n", gOutFile);
             fflush(gOutFile);
         }
         savedExc.restore();
     }
 
     // Get exception object before printing and clearing exception.
     RootedValue exn(cx);
     if (JS_IsExceptionPending(cx))
         (void) JS_GetPendingException(cx, &exn);
 
-    gGotError = PrintError(cx, gErrFile, message, report, reportWarnings);
+    sr->gotError = PrintError(cx, gErrFile, message, report, reportWarnings);
     if (!exn.isUndefined()) {
         JS::AutoSaveExceptionState savedExc(cx);
         if (!PrintStackTrace(cx, exn))
             fputs("(Unable to print stack trace)\n", gOutFile);
         savedExc.restore();
     }
 
     if (report->exnType != JSEXN_NONE && !JSREPORT_IS_WARNING(report->flags)) {
-        if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
-            gExitCode = EXITCODE_OUT_OF_MEMORY;
-        } else {
-            gExitCode = EXITCODE_RUNTIME_ERROR;
-        }
+        if (report->errorNumber == JSMSG_OUT_OF_MEMORY)
+            sr->exitCode = EXITCODE_OUT_OF_MEMORY;
+        else
+            sr->exitCode = EXITCODE_RUNTIME_ERROR;
     }
 }
 
 static void
 my_OOMCallback(JSContext* cx, void* data)
 {
     // If a script is running, the engine is about to throw the string "out of
     // memory", which may or may not be caught. Otherwise the engine will just
     // unwind and return null/false, with no exception set.
     if (!JS_IsRunning(cx))
-        gGotError = true;
+        GetShellRuntime(cx)->gotError = true;
 }
 
 static bool
 global_enumerate(JSContext* cx, HandleObject obj)
 {
 #ifdef LAZY_STANDARD_CLASSES
     return JS_EnumerateStandardClasses(cx, obj);
 #else
@@ -5762,16 +5839,23 @@ NewGlobalObject(JSContext* cx, JS::Compa
     {
         JSAutoCompartment ac(cx, glob);
 
 #ifndef LAZY_STANDARD_CLASSES
         if (!JS_InitStandardClasses(cx, glob))
             return nullptr;
 #endif
 
+        bool succeeded;
+        if (!JS_SetImmutablePrototype(cx, glob, &succeeded))
+            return nullptr;
+        MOZ_ASSERT(succeeded,
+                   "a fresh, unexposed global object is always capable of "
+                   "having its [[Prototype]] be immutable");
+
 #ifdef JS_HAS_CTYPES
         if (!JS_InitCTypesClass(cx, glob))
             return nullptr;
 #endif
         if (!JS_InitReflectParse(cx, glob))
             return nullptr;
         if (!JS_DefineDebuggerObject(cx, glob))
             return nullptr;
@@ -5875,67 +5959,69 @@ OptionFailure(const char* option, const 
 {
     fprintf(stderr, "Unrecognized option for %s: %s\n", option, str);
     return false;
 }
 
 static int
 ProcessArgs(JSContext* cx, OptionParser* op)
 {
+    ShellRuntime* sr = GetShellRuntime(cx);
+
     if (op->getBoolOption('s'))
         JS::RuntimeOptionsRef(cx).toggleExtraWarnings();
 
     /* |scriptArgs| gets bound on the global before any code is run. */
     if (!BindScriptArgs(cx, op))
         return EXIT_FAILURE;
 
     MultiStringRange filePaths = op->getMultiStringOption('f');
     MultiStringRange codeChunks = op->getMultiStringOption('e');
 
     if (filePaths.empty() && codeChunks.empty() && !op->getStringArg("script")) {
         Process(cx, nullptr, true); /* Interactive. */
-        return gExitCode;
+        return sr->exitCode;
     }
 
     while (!filePaths.empty() || !codeChunks.empty()) {
         size_t fpArgno = filePaths.empty() ? -1 : filePaths.argno();
         size_t ccArgno = codeChunks.empty() ? -1 : codeChunks.argno();
         if (fpArgno < ccArgno) {
             char* path = filePaths.front();
             Process(cx, path, false);
-            if (gExitCode)
-                return gExitCode;
+            if (sr->exitCode)
+                return sr->exitCode;
             filePaths.popFront();
         } else {
             const char* code = codeChunks.front();
             RootedValue rval(cx);
             JS::CompileOptions opts(cx);
             opts.setFileAndLine("-e", 1);
             if (!JS::Evaluate(cx, opts, code, strlen(code), &rval))
-                return gExitCode ? gExitCode : EXITCODE_RUNTIME_ERROR;
+                return sr->exitCode ? sr->exitCode : EXITCODE_RUNTIME_ERROR;
             codeChunks.popFront();
-            if (gQuitting)
+            if (sr->quitting)
                 break;
         }
     }
 
-    if (gQuitting)
-        return gExitCode ? gExitCode : EXIT_SUCCESS;
+    if (sr->quitting)
+        return sr->exitCode ? sr->exitCode : EXIT_SUCCESS;
 
     /* The |script| argument is processed after all options. */
     if (const char* path = op->getStringArg("script")) {
         Process(cx, path, false);
-        if (gExitCode)
-            return gExitCode;
+        if (sr->exitCode)
+            return sr->exitCode;
     }
 
     if (op->getBoolOption('i'))
         Process(cx, nullptr, true);
 
-    return gExitCode ? gExitCode : EXIT_SUCCESS;
+    return sr->exitCode ? sr->exitCode : EXIT_SUCCESS;
 }
 
 static bool
 SetRuntimeOptions(JSRuntime* rt, const OptionParser& op)
 {
     enableBaseline = !op.getBoolOption("no-baseline");
     enableIon = !op.getBoolOption("no-ion");
     enableAsmJS = !op.getBoolOption("no-asmjs");
@@ -6525,23 +6611,28 @@ main(int argc, char** argv, char** envp)
     size_t nurseryBytes = JS::DefaultNurseryBytes;
     nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
 
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
     rt = JS_NewRuntime(JS::DefaultHeapMaxBytes, nurseryBytes);
     if (!rt)
         return 1;
 
+    mozilla::UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>();
+    if (!sr)
+        return 1;
+
+    JS_SetRuntimePrivate(rt, sr.get());
     JS_SetErrorReporter(rt, my_ErrorReporter);
     JS::SetOutOfMemoryCallback(rt, my_OOMCallback, nullptr);
     if (!SetRuntimeOptions(rt, op))
         return 1;
 
-    gInterruptFunc.init(rt, NullValue());
-    gLastWarning.init(rt, NullValue());
+    sr->interruptFunc.init(rt, NullValue());
+    sr->lastWarning.init(rt, NullValue());
 
     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
 
     size_t availMem = op.getIntOption("available-memory");
     if (availMem > 0)
         JS_SetGCParametersBasedOnAvailableMemory(rt, availMem);
 
     JS_SetTrustedPrincipals(rt, &ShellPrincipals::fullyTrusted);
@@ -6589,17 +6680,17 @@ main(int argc, char** argv, char** envp)
     if (OOM_printAllocationCount)
         printf("OOM max count: %u\n", OOM_counter);
 #endif
 
     JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr);
 
     DestroyContext(cx, true);
 
-    KillWatchdog();
+    KillWatchdog(rt);
 
     MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
     for (size_t i = 0; i < workerThreads.length(); i++)
         PR_JoinThread(workerThreads[i]);
 
     DestructSharedArrayBufferMailbox();
 
     JS_DestroyRuntime(rt);
--- a/js/src/tests/ecma_6/Class/classHeritage.js
+++ b/js/src/tests/ecma_6/Class/classHeritage.js
@@ -31,21 +31,21 @@ var staticMethodCalled;
 var overrideCalled;
 class base {
     constructor() { };
     method() { baseMethodCalled = true; }
     static staticMethod() { staticMethodCalled = true; }
     override() { overrideCalled = "base" }
 }
 class derived extends base {
-    constructor() { };
+    constructor() { super(); };
     override() { overrideCalled = "derived"; }
 }
 var derivedExpr = class extends base {
-    constructor() { };
+    constructor() { super(); };
     override() { overrideCalled = "derived"; }
 };
 
 // Make sure we get the right object layouts.
 for (let extension of [derived, derivedExpr]) {
     baseMethodCalled = false;
     staticMethodCalled = false;
     overrideCalled = "";
--- a/js/src/tests/ecma_6/Class/derivedConstructorDisabled.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorDisabled.js
@@ -13,25 +13,26 @@ class derived extends base {
     constructor() {
         eval('');
     }
 }
 
 // Make sure eval and arrows are still valid in non-derived constructors.
 new base();
 
+
 // Eval throws in derived class constructors, in both class expressions and
 // statements.
 assertThrowsInstanceOf((() => new derived()), InternalError);
 assertThrowsInstanceOf((() => new class extends base { constructor() { eval('') } }()), InternalError);
 
 var g = newGlobal();
 var dbg = Debugger(g);
 dbg.onDebuggerStatement = function(frame) { assertThrowsInstanceOf(()=>frame.eval(''), InternalError); };
-g.eval("new class foo extends null { constructor() { debugger; } }()");
 
+g.eval("new class foo extends null { constructor() { debugger; return {}; } }()");
 `;
 
 if (classesEnabled())
     eval(test);
 
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/derivedConstructorInlining.js
@@ -0,0 +1,19 @@
+// Since we (for now!) can't emit jitcode for derived class statements. Make
+// sure we can corectly invoke derived class constructors.
+
+class foo extends null {
+    constructor() {
+        // Anything that tests |this| should throw, so just let it run off the
+        // end.
+    }
+}
+
+function intermediate() {
+    new foo();
+}
+
+for (let i = 0; i < 1100; i++)
+    assertThrownErrorContains(intermediate, "|this|");
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/derivedConstructorReturnPrimitive.js
@@ -0,0 +1,15 @@
+class foo extends null {
+    constructor() {
+        // Returning a primitive is a TypeError in derived constructors. This
+        // ensures that super() can take the return value directly, without
+        // checking it. Use |null| here, as a tricky check to make sure we
+        // didn't lump it in with the object check, somehow.
+        return null;
+    }
+}
+
+for (let i = 0; i < 1100; i++)
+    assertThrownErrorContains(() => new foo(), "return");
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZExplicitThis.js
@@ -0,0 +1,18 @@
+function pleaseRunMyCode() { }
+
+class foo extends null {
+    constructor() {
+        // Just bareword |this| is DCEd by the BytecodeEmitter. Your guess as
+        // to why we think this is a good idea is as good as mine. In order to
+        // combat this inanity, make it a function arg, so we have to compute
+        // it.
+        pleaseRunMyCode(this);
+        assertEq(false, true);
+    }
+}
+
+for (let i = 0; i < 1100; i++)
+    assertThrownErrorContains(() => new foo(), "|this|");
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZOffEdge.js
@@ -0,0 +1,11 @@
+class foo extends null {
+    constructor() {
+        // Let it fall off the edge and throw.
+    }
+}
+
+for (let i = 0; i < 1100; i++)
+    assertThrownErrorContains(() => new foo(), "|this|");
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnObject.js
@@ -0,0 +1,13 @@
+class foo extends null {
+    constructor() {
+        // If you return an object, we don't care that |this| went
+        // uninitialized
+        return {};
+    }
+}
+
+for (let i = 0; i < 1100; i++)
+    new foo();
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnUndefined.js
@@ -0,0 +1,13 @@
+class foo extends null {
+    constructor() {
+        // Explicit returns of undefined should act the same as falling off the
+        // end of the function. That is to say, they should throw.
+        return undefined;
+    }
+}
+
+for (let i = 0; i < 1100; i++)
+    assertThrownErrorContains(() => new foo(), "|this|");
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallBadDynamicSuperClass.js
@@ -0,0 +1,17 @@
+var test = `
+
+class base { constructor() { } }
+
+class inst extends base { constructor() { super(); } }
+
+Object.setPrototypeOf(inst, Math.sin);
+
+assertThrowsInstanceOf(() => new inst(), TypeError);
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallBadNewTargetPrototype.js
@@ -0,0 +1,30 @@
+var test = `
+
+class base { constructor() { } }
+
+// lies and the lying liars who tell them
+function lies() { }
+lies.prototype = 4;
+
+assertThrowsInstanceOf(()=>Reflect.consruct(base, [], lies), TypeError);
+
+// lie a slightly different way
+function get(target, property, receiver) {
+    if (property === "prototype")
+        return 42;
+    return Reflect.get(target, property, receiver);
+}
+
+class inst extends base {
+    constructor() { super(); }
+}
+
+assertThrowsInstanceOf(()=>new new Proxy(inst, {get})(), TypeError);
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallBaseInvoked.js
@@ -0,0 +1,53 @@
+var test = `
+
+function testBase(base) {
+    class instance extends base {
+        constructor(inst, one) {
+            super(inst, one);
+        }
+    }
+
+    let inst = new instance(instance, 1);
+    assertEq(Object.getPrototypeOf(inst), instance.prototype);
+    assertEq(inst.calledBase, true);
+}
+
+class base {
+    // Base class must be [[Construct]]ed, as you cannot [[Call]] a class
+    // constructor
+    constructor(nt, one) {
+        assertEq(new.target, nt);
+
+        // Check argument ordering
+        assertEq(one, 1);
+        this.calledBase = true;
+    }
+}
+
+testBase(base);
+testBase(class extends base {
+             constructor(nt, one) {
+                 // Every step of the way, new.target and args should be right
+                 assertEq(new.target, nt);
+                 assertEq(one, 1);
+                 super(nt, one);
+             }
+         });
+function baseFunc(nt, one) {
+    assertEq(new.target, nt);
+    assertEq(one, 1);
+    this.calledBase = true;
+}
+
+testBase(baseFunc);
+testBase(new Proxy(baseFunc, {}));
+
+// Object will have to wait for fixed builtins.
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallIllegal.js
@@ -0,0 +1,7 @@
+// super() invalid outside derived class constructors, including in dynamic
+// functions and eval
+assertThrowsInstanceOf(() => new Function("super();"), SyntaxError);
+assertThrowsInstanceOf(() => eval("super()"), SyntaxError);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallInvalidBase.js
@@ -0,0 +1,15 @@
+var test = `
+
+class instance extends null {
+    constructor() { super(); }
+}
+
+assertThrowsInstanceOf(() => new instance(), TypeError);
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallOrder.js
@@ -0,0 +1,33 @@
+var test = `
+
+function base() { }
+
+class beforeSwizzle extends base {
+    constructor() {
+        super(Object.setPrototypeOf(beforeSwizzle, null));
+    }
+}
+
+new beforeSwizzle();
+
+// Again, testing both dynamic prototype dispatch, and that we get the function
+// before evaluating args
+class beforeThrow extends base {
+    constructor() {
+        function thrower() { throw new Error(); }
+        super(thrower());
+    }
+}
+
+Object.setPrototypeOf(beforeThrow, Math.sin);
+
+// Will throw that Math.sin is not a constructor before evaluating the args
+assertThrowsInstanceOf(() => new beforeThrow(), TypeError);
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallProperBase.js
@@ -0,0 +1,33 @@
+var test = `
+
+class base1 {
+    constructor() {
+        this.base = 1;
+    }
+}
+
+class base2 {
+    constructor() {
+        this.base = 2;
+    }
+}
+
+class inst extends base1 {
+    constructor() {
+        super();
+    }
+}
+
+assertEq(new inst().base, 1);
+
+Object.setPrototypeOf(inst, base2);
+
+assertEq(new inst().base, 2);
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallSpreadCall.js
@@ -0,0 +1,34 @@
+var test = `
+
+class base {
+    constructor(a, b, c) {
+        assertEq(a, 1);
+        assertEq(b, 2);
+        assertEq(c, 3);
+        this.calledBase = true;
+    }
+}
+
+class test extends base {
+    constructor(arr) {
+        super(...arr);
+    }
+}
+
+assertEq(new test([1,2,3]).calledBase, true);
+
+class testRest extends base {
+   constructor(...args) {
+       super(...args);
+   }
+}
+
+assertEq(new testRest(1,2,3).calledBase, true);
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/superCallThisInit.js
@@ -0,0 +1,52 @@
+var test = `
+
+function base() { this.prop = 42; }
+
+class testInitialize extends base {
+    constructor() {
+        // A poor man's assertThrowsInstanceOf, as arrow functions are currently
+        // disabled in this context
+        try {
+            this;
+            throw new Error();
+        } catch (e if e instanceof ReferenceError) { }
+        super();
+        assertEq(this.prop, 42);
+    }
+}
+assertEq(new testInitialize().prop, 42);
+
+// super() twice is a no-go.
+class willThrow extends base {
+    constructor() {
+        super();
+        super();
+    }
+}
+assertThrowsInstanceOf(()=>new willThrow(), ReferenceError);
+
+// This is determined at runtime, not the syntax level.
+class willStillThrow extends base {
+    constructor() {
+        for (let i = 0; i < 3; i++) {
+            super();
+        }
+    }
+}
+assertThrowsInstanceOf(()=>new willStillThrow(), ReferenceError);
+
+class canCatchThrow extends base {
+    constructor() {
+        super();
+        try { super(); } catch(e) { }
+    }
+}
+assertEq(new canCatchThrow().prop, 42);
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropBasicGetter.js
+++ b/js/src/tests/ecma_6/Class/superPropBasicGetter.js
@@ -8,17 +8,17 @@ class base {
     }
 
     setValue(v) {
         this._prop = v;
     }
 }
 
 class derived extends base {
-    constructor() {}
+    constructor() { super(); }
 
     get a() { return super.getValue(); }
     set a(v) { super.setValue(v); }
 
     get b() { return eval('super.getValue()'); }
     set b(v) { eval('super.setValue(v);'); }
 
     test() {
--- a/js/src/tests/ecma_6/Class/superPropBasicNew.js
+++ b/js/src/tests/ecma_6/Class/superPropBasicNew.js
@@ -1,20 +1,21 @@
 var test = `
 
 class Base {
     constructor() {}
 }
 class Mid extends Base {
-    constructor() {}
+    constructor() { super(); }
     f() { return new super.constructor(); }
 }
 class Derived extends Mid {
-    constructor() {}
+    constructor() { super(); }
 }
+
 let d = new Derived();
 var df = d.f();
 assertEq(df.constructor, Base);
 
 `;
 
 if (classesEnabled())
     eval(test);
--- a/js/src/tests/ecma_6/Class/superPropChains.js
+++ b/js/src/tests/ecma_6/Class/superPropChains.js
@@ -4,55 +4,55 @@ var test = `
 class base {
     constructor() { }
     testChain() {
         this.baseCalled = true;
     }
 }
 
 class middle extends base {
-    constructor() { }
+    constructor() { super(); }
     testChain() {
         this.middleCalled = true;
         super.testChain();
     }
 }
 
 class derived extends middle {
-    constructor() { }
+    constructor() { super(); }
     testChain() {
         super.testChain();
         assertEq(this.middleCalled, true);
         assertEq(this.baseCalled, true);
     }
 }
 
 new derived().testChain();
 
 // Super even chains in a wellbehaved fashion with normal functions.
 function bootlegMiddle() { }
 bootlegMiddle.prototype = middle.prototype;
 
 new class extends bootlegMiddle {
-        constructor() { }
+        constructor() { super(); }
         testChain() {
             super.testChain();
             assertEq(this.middleCalled, true);
             assertEq(this.baseCalled, true);
         }
     }().testChain();
 
 // Now let's try out some "long" chains
 base.prototype.x = "yeehaw";
 
-let chain = class extends base { constructor() { } }
+let chain = class extends base { constructor() { super(); } }
 
 const CHAIN_LENGTH = 100;
 for (let i = 0; i < CHAIN_LENGTH; i++)
-    chain = class extends chain { constructor() { } }
+    chain = class extends chain { constructor() { super(); } }
 
 // Now we poke the chain
 let inst = new chain();
 inst.testChain();
 assertEq(inst.baseCalled, true);
 
 assertEq(inst.x, "yeehaw");
 
--- a/js/src/tests/ecma_6/Class/superPropDelete.js
+++ b/js/src/tests/ecma_6/Class/superPropDelete.js
@@ -3,17 +3,17 @@ var test = `
 // |delete super.prop| and |delete super[expr]| throw universally.
 // Even so, we should make sure we get proper side effects
 
 class base {
     constructor() { }
 }
 
 class derived extends base {
-    constructor() { }
+    constructor() { super(); }
     testDeleteProp() { delete super.prop; }
     testDeleteElem() {
         let sideEffect = 0;
         assertThrowsInstanceOf(() => delete super[sideEffect = 1], ReferenceError);
         assertEq(sideEffect, 1);
     }
     testDeleteElemPropValFirst() {
         // The deletion error is a reference error, but by munging the prototype
--- a/js/src/tests/ecma_6/Class/superPropDerivedCalls.js
+++ b/js/src/tests/ecma_6/Class/superPropDerivedCalls.js
@@ -21,17 +21,17 @@ class base {
     set prop(val) {
         assertEq(this, derivedInstance);
         this.setterCalled = true;
         this._prop = val;
     }
 }
 
 class derived extends base {
-    constructor() { }
+    constructor() { super(); }
 
     // |super| actually checks the chain, not |this|
     method() { throw "FAIL"; }
     get prop() { throw "FAIL"; }
     set prop(v) { throw "FAIL"; }
 
     test() {
         this.reset();
--- a/js/src/tests/ecma_6/Class/superPropDestructuring.js
+++ b/js/src/tests/ecma_6/Class/superPropDestructuring.js
@@ -17,17 +17,17 @@ Object.defineProperty(base.prototype, "i
                         set(x) {
                             assertEq(x, "Fred");
                             seenValues.push(x)
                         }
                       });
 
 const testArr = [525600, "Fred"];
 class derived extends base {
-    constructor() { }
+    constructor() { super(); }
     prepForTest() { seenValues = []; }
     testAsserts() { assertDeepEq(seenValues, testArr); }
     testProps() {
         this.prepForTest();
         [super.minutes, super.intendent] = testArr;
         this.testAsserts();
     }
     testElems() {
--- a/js/src/tests/ecma_6/Class/superPropHomeObject.js
+++ b/js/src/tests/ecma_6/Class/superPropHomeObject.js
@@ -9,17 +9,17 @@ var test = `
 // means it's gonna vary wildly as stuff gets moved around.
 
 class base {
     constructor() { }
     test(expectedThis) { assertEq(this, expectedThis); }
 }
 
 class derived extends base {
-    constructor() { }
+    constructor() { super(); }
     test(expected) { super.test(expected); }
     testArrow() { return (() => super.test(this)); }
     ["testCPN"](expected) { super.test(expected); }
 }
 
 let derivedInstance = new derived();
 derivedInstance.test(derivedInstance);
 derivedInstance.testCPN(derivedInstance);
@@ -50,17 +50,17 @@ class base1 {
 class base2 {
     constructor() { }
     test() { return "alpaca"; }
 }
 
 let animals = [];
 for (let exprBase of [base1, base2])
     new class extends exprBase {
-        constructor() { }
+        constructor() { super(); }
         test() { animals.push(super["test"]()); }
     }().test();
 assertDeepEq(animals, ["llama", "alpaca"]);
 
 `;
 
 if (classesEnabled())
     eval(test);
--- a/js/src/tests/ecma_6/Class/superPropOrdering.js
+++ b/js/src/tests/ecma_6/Class/superPropOrdering.js
@@ -1,26 +1,26 @@
 var test = `
 
 class base {
     constructor() { }
     method() { this.methodCalled++; }
 }
 
 class derived extends base {
-    constructor() { this.methodCalled = 0; }
+    constructor() { super(); this.methodCalled = 0; }
 
     // Test orderings of various evaluations relative to the superbase
-    
+
     // Unlike in regular element evaluation, the propVal is evaluated before
     // checking the starting object ([[HomeObject]].[[Prototype]])
     testElem() { super[ruin()]; }
-   
+
     // The starting object for looking up super.method is determined before
-    // ruin() is called. 
+    // ruin() is called.
     testProp() { super.method(ruin()); }
 
     // The entire super.method property lookup has concluded before the args
     // are evaluated
     testPropCallDeleted() { super.method(()=>delete base.prototype.method); }
 
     // The starting object for looking up super["prop"] is determined before
     // ruin() is called.
@@ -66,17 +66,16 @@ function ruin() {
     return 5;
 }
 
 function reset() {
     Object.setPrototypeOf(derived.prototype, base.prototype);
 }
 
 let instance = new derived();
-
 assertThrowsInstanceOf(() => instance.testElem(), TypeError);
 reset();
 
 instance.testProp();
 assertEq(instance.methodCalled, 1);
 reset();
 
 instance.testPropCallDeleted();
@@ -86,15 +85,16 @@ instance.testElemAssign();
 assertEq(instance.prop, 5);
 reset();
 
 instance.testAssignElemPropValChange();
 
 instance.testAssignProp();
 
 instance.testCompoundAssignProp();
+
 `;
 
 if (classesEnabled())
     eval(test);
 
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropProtoChanges.js
+++ b/js/src/tests/ecma_6/Class/superPropProtoChanges.js
@@ -5,17 +5,17 @@ class base {
     test() {
         return false;
     }
 }
 
 let standin = { test() { return true; } };
 
 class derived extends base {
-    constructor() { }
+    constructor() { super(); }
     test() {
         assertEq(super.test(), false);
         Object.setPrototypeOf(derived.prototype, standin);
         assertEq(super["test"](), true);
     }
 }
 
 new derived().test();
--- a/js/src/tests/ecma_6/Class/superPropProxies.js
+++ b/js/src/tests/ecma_6/Class/superPropProxies.js
@@ -6,36 +6,35 @@ class base {
 
 let midStaticHandler = { };
 
 // We shouldn't use the |this.called| strategy here, since we have proxies
 // snooping property accesses.
 let getterCalled, setterCalled;
 
 class mid extends new Proxy(base, midStaticHandler) {
-    constructor() { }
+    constructor() { super(); }
     testSuperInProxy() {
         super.prop = "looking";
         assertEq(setterCalled, true);
         assertEq(super.prop, "found");
         assertEq(getterCalled, true);
     }
 }
 
 class child extends mid {
-    constructor() { }
+    constructor() { super(); }
     static testStaticLookups() {
         // This funtion is called more than once.
         this.called = false;
         super.prop;
         assertEq(this.called, true);
     }
 }
 
-
 let midInstance = new mid();
 
 // Make sure proxies are searched properly on the prototype chain
 let baseHandler = {
     get(target, p, receiver) {
         assertEq(receiver, midInstance);
         getterCalled = true;
         return "found";
--- a/js/src/tests/ecma_6/Class/superPropSkips.js
+++ b/js/src/tests/ecma_6/Class/superPropSkips.js
@@ -4,17 +4,17 @@ var test = `
 // That is, super lookups start with the superclass, not the current class.
 
 // The whole point: an empty superclass
 class base {
     constructor() { }
 }
 
 class derived extends base {
-    constructor() { this.prop = "flamingo"; }
+    constructor() { super(); this.prop = "flamingo"; }
 
     toString() { throw "No!"; }
 
     testSkipGet() {
         assertEq(super.prop, undefined);
     }
 
     testSkipDerivedOverrides() {
@@ -32,16 +32,17 @@ class derived extends base {
         // skipped in the super lookup.
         assertEq(this.nonWritableProp, "pony");
         super.nonWritableProp = "bear";
         assertEq(this.nonWritableProp, "bear");
     }
 }
 
 Object.defineProperty(derived.prototype, "nonWritableProp", { writable: false, value: "pony" });
+
 let instance = new derived();
 instance.testSkipGet();
 instance.testSkipDerivedOverrides();
 instance.testSkipSet();
 
 `;
 
 if (classesEnabled())
--- a/js/src/tests/ecma_6/Proxy/revoke-as-side-effect.js
+++ b/js/src/tests/ecma_6/Proxy/revoke-as-side-effect.js
@@ -66,13 +66,18 @@ for (var k in createProxy({a: 5})) {
 // [[OwnPropertyKeys]]
 assertEq(Object.getOwnPropertyNames(createProxy({})).length, 0);
 assertEq(Object.getOwnPropertyNames(createProxy({a: 5})).length, 1);
 
 // [[Call]]
 assertEq(createProxy(function() { return "ok" })(), "ok");
 
 // [[Construct]]
-// This should throw per bug 1141865.
-assertEq(new (createProxy(function(){ return obj; })), obj);
+// This throws because after the "construct" trap on the proxy is consulted,
+// OrdinaryCreateFromConstructor (called because the |q| function's
+// [[ConstructorKind]] is "base" per FunctionAllocate) accesses
+// |new.target.prototype| to create the |this| for the construct operation, that
+// would be returned if |return obj;| didn't override it.
+assertThrowsInstanceOf(() => new (createProxy(function q(){ return obj; })),
+                       TypeError);
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
--- a/js/src/tests/ecma_6/Reflect/construct.js
+++ b/js/src/tests/ecma_6/Reflect/construct.js
@@ -33,30 +33,25 @@ assertDeepEq(Reflect.construct(bound, []
 if (classesEnabled()) {
     eval(`{
         class Base {
             constructor(...args) {
                 this.args = args;
                 this.newTarget = new.target;
             }
         }
-        //class Derived extends Base {
-        //    constructor(...args) { super(...args); }
-        //}
+        class Derived extends Base {
+            constructor(...args) { super(...args); }
+        }
 
         assertDeepEq(Reflect.construct(Base, []), new Base);
-        //assertDeepEq(Reflect.construct(Derived, [7]), new Derived(7));
-        //g = Derived.bind(null, "q");
-        //assertDeepEq(Reflect.construct(g, [8, 9]), new g(8, 9));
+        assertDeepEq(Reflect.construct(Derived, [7]), new Derived(7));
+        g = Derived.bind(null, "q");
+        assertDeepEq(Reflect.construct(g, [8, 9]), new g(8, 9));
     }`);
-
-    if (classesEnabled("class X extends Y { constructor() { super(); } }")) {
-        throw new Error("Congratulations on implementing super()! " +
-                        "Please uncomment the Derived tests in this file!");
-    }
 }
 
 // Cross-compartment wrappers:
 var g = newGlobal();
 var local = {here: this};
 g.eval("function F(arg) { this.arg = arg }");
 assertDeepEq(Reflect.construct(g.F, [local]), new g.F(local));
 
--- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
+++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
@@ -202,16 +202,19 @@ function seqExpr(exprs) {
     return Pattern({ type: "SequenceExpression", expressions: exprs });
 }
 function newExpr(callee, args) {
     return Pattern({ type: "NewExpression", callee: callee, arguments: args });
 }
 function callExpr(callee, args) {
     return Pattern({ type: "CallExpression", callee: callee, arguments: args });
 }
+function superCallExpr(args) {
+    return callExpr({ type: "Super" }, args);
+}
 function arrExpr(elts) {
     return Pattern({ type: "ArrayExpression", elements: elts });
 }
 function objExpr(elts) {
     return Pattern({ type: "ObjectExpression", properties: elts });
 }
 function computedName(elts) {
     return Pattern({ type: "ComputedName", name: elts });
--- a/js/src/tests/js1_8_5/reflect-parse/classes.js
+++ b/js/src/tests/js1_8_5/reflect-parse/classes.js
@@ -20,39 +20,40 @@ function testClasses() {
         return methodMaker(methodName, args.map(ident), blockStmt(body));
     }
 
     function simpleMethod(id, kind, generator, args=[], isStatic=false) {
         return classMethod(ident(id),
                            methodFun(id, kind, generator, args),
                            kind, isStatic);
     }
-    function ctorWithName(id) {
+    function ctorWithName(id, body = []) {
         return classMethod(ident("constructor"),
-                           methodFun(id, "method", false, []),
+                           methodFun(id, "method", false, [], body),
                            "method", false);
     }
     function emptyCPNMethod(id, isStatic) {
         return classMethod(computedName(lit(id)),
                            funExpr(null, [], blockStmt([])),
                            "method", isStatic);
     }
 
     function assertClassExpr(str, methods, heritage=null, name=null) {
         let template = classExpr(name, heritage, methods);
         assertExpr("(" + str + ")", template);
     }
+
     // FunctionExpression of constructor has class name as its id.
     // FIXME: Implement ES6 function "name" property semantics (bug 883377).
     let ctorPlaceholder = {};
-    function assertClass(str, methods, heritage=null) {
+    function assertClass(str, methods, heritage=null, constructorBody=[]) {
         let namelessStr = str.replace("NAME", "");
         let namedStr = str.replace("NAME", "Foo");
-        let namedCtor = ctorWithName("Foo");
-        let namelessCtor = ctorWithName(null);
+        let namedCtor = ctorWithName("Foo", constructorBody);
+        let namelessCtor = ctorWithName(null, constructorBody);
         let namelessMethods = methods.map(x => x == ctorPlaceholder ? namelessCtor : x);
         let namedMethods = methods.map(x => x == ctorPlaceholder ? namedCtor : x);
         assertClassExpr(namelessStr, namelessMethods, heritage);
         assertClassExpr(namedStr, namedMethods, heritage, ident("Foo"));
 
         let template = classStmt(ident("Foo"), heritage, namedMethods);
         assertStmt(namedStr, template);
     }
@@ -432,16 +433,52 @@ function testClasses() {
 
     // Bare super is forbidden
     assertError("super", SyntaxError);
 
     // Even where super is otherwise allowed
     assertError("{ foo() { super } }", SyntaxError);
     assertClassError("class NAME { constructor() { super; } }", SyntaxError);
 
+    /* SuperCall */
+
+    // SuperCall is invalid outside derived class constructors.
+    assertError("super()", SyntaxError);
+    assertError("(function() { super(); })", SyntaxError);
+
+    // SuperCall is invalid in generator comprehensions, even inside derived
+    // class constructors
+    assertError("(super() for (x in y))", SyntaxError);
+    assertClassError("class NAME { constructor() { (super() for (x in y))", SyntaxError);
+
+
+    // Even in class constructors
+    assertClassError("class NAME { constructor() { super(); } }", SyntaxError);
+
+    function superConstructor(args) {
+        return classMethod(ident("constructor"),
+                           methodFun("NAME", "method", false,
+                                     [], [exprStmt(superCallExpr(args))]),
+                           "method", false);
+    }
+
+    function superCallBody(args) {
+        return [exprStmt(superCallExpr(args))];
+    }
+
+    // SuperCall works with various argument configurations.
+    assertClass("class NAME extends null { constructor() { super() } }",
+                [ctorPlaceholder], lit(null), superCallBody([]));
+    assertClass("class NAME extends null { constructor() { super(1) } }",
+                [ctorPlaceholder], lit(null), superCallBody([lit(1)]));
+    assertClass("class NAME extends null { constructor() { super(1, a) } }",
+                [ctorPlaceholder], lit(null), superCallBody([lit(1), ident("a")]));
+    assertClass("class NAME extends null { constructor() { super(...[]) } }",
+                [ctorPlaceholder], lit(null), superCallBody([spread(arrExpr([]))]));
+
     /* EOF */
     // Clipped classes should throw a syntax error
     assertClassError("class NAME {", SyntaxError);
     assertClassError("class NAME {;", SyntaxError);
     assertClassError("class NAME { constructor", SyntaxError);
     assertClassError("class NAME { constructor(", SyntaxError);
     assertClassError("class NAME { constructor()", SyntaxError);
     assertClassError("class NAME { constructor()", SyntaxError);
--- a/js/src/tests/jstests.py
+++ b/js/src/tests/jstests.py
@@ -345,24 +345,25 @@ def main():
 
     if test_count == 0:
         print('no tests selected')
         return 1
 
     test_dir = dirname(abspath(__file__))
 
     if options.debug:
-        if len(list(test_gen)) > 1:
+        tests = list(test_gen)
+        if len(tests) > 1:
             print('Multiple tests match command line arguments,'
                   ' debugger can only run one')
-            for tc in test_gen:
+            for tc in tests:
                 print('    {}'.format(tc.path))
             return 2
 
-        cmd = test_gen[0].get_command(RefTestCase.js_cmd_prefix)
+        cmd = tests[0].get_command(prefix)
         if options.show_cmd:
             print(list2cmdline(cmd))
         with changedir(test_dir):
             call(cmd)
         return 0
 
     with changedir(test_dir):
         # Force Pacific time zone to avoid failures in Date tests.
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1730,17 +1730,17 @@ Debugger::logTenurePromotion(JSRuntime* 
         tenurePromotionsLogOverflowed = true;
     }
 }
 
 bool
 Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
                                double when)
 {
-    MOZ_ASSERT(trackingAllocationSites);
+    MOZ_ASSERT(trackingAllocationSites && enabled);
 
     AutoCompartment ac(cx, object);
     RootedObject wrappedFrame(cx, frame);
     if (!cx->compartment()->wrap(cx, &wrappedFrame))
         return false;
 
     RootedAtom ctorName(cx);
     {
@@ -2314,39 +2314,43 @@ Debugger::isObservedByDebuggerTrackingAl
             }
         }
     }
 
     return false;
 }
 
 /* static */ bool
-Debugger::addAllocationsTracking(JSContext* cx, GlobalObject& debuggee)
+Debugger::addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee)
 {
     // Precondition: the given global object is being observed by at least one
     // Debugger that is tracking allocations.
-    MOZ_ASSERT(isObservedByDebuggerTrackingAllocations(debuggee));
-
-    if (Debugger::cannotTrackAllocations(debuggee)) {
+    MOZ_ASSERT(isObservedByDebuggerTrackingAllocations(*debuggee));
+
+    if (Debugger::cannotTrackAllocations(*debuggee)) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
         return false;
     }
 
-    debuggee.compartment()->setObjectMetadataCallback(SavedStacksMetadataCallback);
+    debuggee->compartment()->setObjectMetadataCallback(SavedStacksMetadataCallback);
+    debuggee->compartment()->chooseAllocationSamplingProbability();
     return true;
 }
 
 /* static */ void
 Debugger::removeAllocationsTracking(GlobalObject& global)
 {
     // If there are still Debuggers that are observing allocations, we cannot
-    // remove the metadata callback yet.
-    if (isObservedByDebuggerTrackingAllocations(global))
+    // remove the metadata callback yet. Recompute the sampling probability
+    // based on the remaining debuggers' needs.
+    if (isObservedByDebuggerTrackingAllocations(global)) {
+        global.compartment()->chooseAllocationSamplingProbability();
         return;
+    }
 
     global.compartment()->forgetObjectMetadataCallback();
 }
 
 bool
 Debugger::addAllocationsTrackingForAllDebuggees(JSContext* cx)
 {
     MOZ_ASSERT(trackingAllocationSites);
@@ -2359,31 +2363,33 @@ Debugger::addAllocationsTrackingForAllDe
     for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         if (Debugger::cannotTrackAllocations(*r.front().get())) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                                  JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
             return false;
         }
     }
 
+    Rooted<GlobalObject*> g(cx);
     for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         // This should always succeed, since we already checked for the
         // error case above.
-        MOZ_ALWAYS_TRUE(Debugger::addAllocationsTracking(cx, *r.front().get()));
+        g = r.front().get();
+        MOZ_ALWAYS_TRUE(Debugger::addAllocationsTracking(cx, g));
     }
 
     return true;
 }
 
 void
 Debugger::removeAllocationsTrackingForAllDebuggees()
 {
-    for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
+    for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront())
         Debugger::removeAllocationsTracking(*r.front().get());
-    }
+
     allocationsLog.clear();
 }
 
 
 
 /*** Debugger JSObjects **************************************************************************/
 
 void
@@ -3417,18 +3423,19 @@ Debugger::addDebuggeeGlobal(JSContext* c
         return false;
     }
     auto debuggeeZonesGuard = MakeScopeExit([&] {
         if (addingZoneRelation)
             debuggeeZones.remove(zone);
     });
 
     // (5)
-    if (trackingAllocationSites && enabled && !Debugger::addAllocationsTracking(cx, *global))
-            return false;
+    if (trackingAllocationSites && enabled && !Debugger::addAllocationsTracking(cx, global))
+        return false;
+
     auto allocationsTrackingGuard = MakeScopeExit([&] {
         if (trackingAllocationSites && enabled)
             Debugger::removeAllocationsTracking(*global);
     });
 
     // (6)
     debuggeeCompartment->setIsDebuggee();
     debuggeeCompartment->updateDebuggerObservesAsmJS();
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -391,17 +391,17 @@ class Debugger : private mozilla::Linked
      */
     static bool isObservedByDebuggerTrackingAllocations(const GlobalObject& global);
 
     /*
      * Add allocations tracking for objects allocated within the given
      * debuggee's compartment. The given debuggee global must be observed by at
      * least one Debugger that is enabled and tracking allocations.
      */
-    static bool addAllocationsTracking(JSContext* cx, GlobalObject& debuggee);
+    static bool addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee);
 
     /*
      * Remove allocations tracking for objects allocated within the given
      * global's compartment. This is a no-op if there are still Debuggers
      * observing this global and who are tracking allocations.
      */
     static void removeAllocationsTracking(GlobalObject& global);
 
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -309,17 +309,28 @@ DebuggerMemory::setAllocationSamplingPro
 
     if (probability < 0.0 || probability > 1.0) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
                              "(set allocationSamplingProbability)'s parameter",
                              "not a number between 0 and 1");
         return false;
     }
 
-    memory->getDebugger()->allocationSamplingProbability = probability;
+    Debugger* dbg = memory->getDebugger();
+    if (dbg->allocationSamplingProbability != probability) {
+        dbg->allocationSamplingProbability = probability;
+
+        // If this is a change any debuggees would observe, have all debuggee
+        // compartments recompute their sampling probabilities.
+        if (dbg->enabled && dbg->trackingAllocationSites) {
+            for (auto r = dbg->debuggees.all(); !r.empty(); r.popFront())
+                r.front()->compartment()->chooseAllocationSamplingProbability();
+        }
+    }
+
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 DebuggerMemory::getAllocationsLogOverflowed(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get allocationsLogOverflowed)", args, memory);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -85,19 +85,16 @@ js::ProtoKeyToClass(JSProtoKey key)
 TypedObjectModuleObject&
 js::GlobalObject::getTypedObjectModule() const {
     Value v = getConstructor(JSProto_TypedObject);
     // only gets called from contexts where TypedObject must be initialized
     MOZ_ASSERT(v.isObject());
     return v.toObject().as<TypedObjectModuleObject>();
 }
 
-
-
-
 /* static */ bool
 GlobalObject::ensureConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
 {
     if (global->isStandardClassResolved(key))
         return true;
     return resolveConstructor(cx, global, key);
 }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -342,21 +342,27 @@ js::ValueToCallable(JSContext* cx, Handl
 
 bool
 RunState::maybeCreateThisForConstructor(JSContext* cx)
 {
     if (isInvoke()) {
         InvokeState& invoke = *asInvoke();
         if (invoke.constructing() && invoke.args().thisv().isPrimitive()) {
             RootedObject callee(cx, &invoke.args().callee());
-            NewObjectKind newKind = invoke.createSingleton() ? SingletonObject : GenericObject;
-            JSObject* obj = CreateThisForFunction(cx, callee, newKind);
-            if (!obj)
-                return false;
-            invoke.args().setThis(ObjectValue(*obj));
+            if (script()->isDerivedClassConstructor()) {
+                MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
+                invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL));
+            } else {
+                RootedObject newTarget(cx, &invoke.args().newTarget().toObject());
+                NewObjectKind newKind = invoke.createSingleton() ? SingletonObject : GenericObject;
+                JSObject* obj = CreateThisForFunction(cx, callee, newTarget, newKind);
+                if (!obj)
+                    return false;
+                invoke.args().setThis(ObjectValue(*obj));
+            }
         }
     }
     return true;
 }
 
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state);
 
@@ -864,19 +870,19 @@ static bool
 StackCheckIsConstructorCalleeNewTarget(JSContext* cx, HandleValue callee, HandleValue newTarget)
 {
     // Calls from the stack could have any old non-constructor callee.
     if (!IsConstructor(callee)) {
         ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_SEARCH_STACK, callee, nullptr);
         return false;
     }
 
-    // The new.target for a stack construction attempt is just the callee: no
-    // need to check that it's a constructor.
-    MOZ_ASSERT(&callee.toObject() == &newTarget.toObject());
+    // The new.target has already been vetted by previous calls, or is the callee.
+    // We can just assert that it's a constructor.
+    MOZ_ASSERT(IsConstructor(newTarget));
 
     return true;
 }
 
 static bool
 ConstructFromStack(JSContext* cx, const CallArgs& args)
 {
     if (!StackCheckIsConstructorCalleeNewTarget(cx, args.calleev(), args.newTarget()))
@@ -1740,16 +1746,44 @@ SetObjectElementOperation(JSContext* cx,
 
     RootedValue tmp(cx, value);
     ObjectOpResult result;
     return SetProperty(cx, obj, id, tmp, receiver, result) &&
            result.checkStrictErrorOrWarning(cx, obj, id, strict);
 }
 
 /*
+ * Get the innermost enclosing function that has a 'this' binding.
+ *
+ * Implements ES6 12.3.5.2 GetSuperConstructor() steps 1-3, including
+ * the loop in ES6 8.3.2 GetThisEnvironment(). Our implementation of
+ * ES6 12.3.5.3 MakeSuperPropertyReference() also uses this code.
+ */
+static JSFunction&
+GetSuperEnvFunction(JSContext *cx, InterpreterRegs& regs)
+{
+    ScopeIter si(cx, regs.fp()->scopeChain(), regs.fp()->script()->innermostStaticScope(regs.pc));
+    for (; !si.done(); ++si) {
+        if (si.hasSyntacticScopeObject() && si.type() == ScopeIter::Call) {
+            JSFunction& callee = si.scope().as<CallObject>().callee();
+
+            // Arrow functions don't have the information we're looking for,
+            // their enclosing scopes do. Nevertheless, they might have call
+            // objects. Skip them to find what we came for.
+            if (callee.isArrow())
+                continue;
+
+            return callee;
+        }
+    }
+    MOZ_CRASH("unexpected scope chain for GetSuperEnvFunction");
+}
+
+
+/*
  * As an optimization, the interpreter creates a handful of reserved Rooted<T>
  * variables at the beginning, thus inserting them into the Rooted list once
  * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it
  * within a local scope, resetting the value to nullptr (or the appropriate
  * equivalent for T) at scope end. This avoids inserting/removing the Rooted
  * from the rooter list, while preventing stale values from being kept alive
  * unnecessarily.
  */
@@ -2048,20 +2082,16 @@ CASE(EnableInterruptsPseudoOpcode)
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_UNUSED2)
 CASE(JSOP_UNUSED14)
 CASE(JSOP_BACKPATCH)
-CASE(JSOP_UNUSED163)
-CASE(JSOP_UNUSED164)
-CASE(JSOP_UNUSED165)
-CASE(JSOP_UNUSED166)
 CASE(JSOP_UNUSED167)
 CASE(JSOP_UNUSED168)
 CASE(JSOP_UNUSED169)
 CASE(JSOP_UNUSED170)
 CASE(JSOP_UNUSED171)
 CASE(JSOP_UNUSED172)
 CASE(JSOP_UNUSED173)
 CASE(JSOP_UNUSED174)
@@ -2192,18 +2222,22 @@ CASE(JSOP_RETURN)
 CASE(JSOP_RETRVAL)
 {
     /*
      * When the inlined frame exits with an exception or an error, ok will be
      * false after the inline_return label.
      */
     CHECK_BRANCH();
 
+    if (!REGS.fp()->checkReturn(cx))
+        goto error;
+
   successful_return_continuation:
     interpReturnOK = true;
+
   return_continuation:
     if (activation.entryFrame() != REGS.fp()) {
         // Stop the engine. (No details about which engine exactly, could be
         // interpreter, Baseline or IonMonkey.)
         TraceLogStopEvent(logger, TraceLogger_Engine);
         TraceLogStopEvent(logger, TraceLogger_Scripts);
 
         interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
@@ -2770,16 +2804,18 @@ END_CASE(JSOP_TYPEOF)
 
 CASE(JSOP_VOID)
     REGS.sp[-1].setUndefined();
 END_CASE(JSOP_VOID)
 
 CASE(JSOP_THIS)
     if (!ComputeThis(cx, REGS.fp()))
         goto error;
+    if (!REGS.fp()->checkThis(cx))
+        goto error;
     PUSH_COPY(REGS.fp()->thisValue());
 END_CASE(JSOP_THIS)
 
 CASE(JSOP_GETPROP)
 CASE(JSOP_LENGTH)
 CASE(JSOP_CALLPROP)
 {
     MutableHandleValue lval = REGS.stackHandleAt(-1);
@@ -2989,26 +3025,27 @@ CASE(JSOP_STRICTEVAL)
     }
     REGS.sp = args.spAfterCall();
     TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
 }
 END_CASE(JSOP_EVAL)
 
 CASE(JSOP_SPREADNEW)
 CASE(JSOP_SPREADCALL)
+CASE(JSOP_SPREADSUPERCALL)
     if (REGS.fp()->hasPushedSPSFrame())
         cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
     /* FALL THROUGH */
 
 CASE(JSOP_SPREADEVAL)
 CASE(JSOP_STRICTSPREADEVAL)
 {
     static_assert(JSOP_SPREADEVAL_LENGTH == JSOP_STRICTSPREADEVAL_LENGTH,
                   "spreadeval and strictspreadeval must be the same size");
-    bool construct = JSOp(*REGS.pc) == JSOP_SPREADNEW;
+    bool construct = JSOp(*REGS.pc) == JSOP_SPREADNEW || JSOp(*REGS.pc) == JSOP_SPREADSUPERCALL;;
 
     MOZ_ASSERT(REGS.stackDepth() >= 3u + construct);
 
     HandleValue callee = REGS.stackHandleAt(-3 - construct);
     HandleValue thisv = REGS.stackHandleAt(-2 - construct);
     HandleValue arr = REGS.stackHandleAt(-1 - construct);
     MutableHandleValue ret = REGS.stackHandleAt(-3 - construct);
 
@@ -3030,22 +3067,23 @@ CASE(JSOP_FUNAPPLY)
     CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
     if (!GuardFunApplyArgumentsOptimization(cx, REGS.fp(), args))
         goto error;
     /* FALL THROUGH */
 }
 
 CASE(JSOP_NEW)
 CASE(JSOP_CALL)
+CASE(JSOP_SUPERCALL)
 CASE(JSOP_FUNCALL)
 {
     if (REGS.fp()->hasPushedSPSFrame())
         cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
 
-    bool construct = (*REGS.pc == JSOP_NEW);
+    bool construct = (*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL);
     unsigned argStackSlots = GET_ARGC(REGS.pc) + construct;
 
     MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
     CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct);
 
     JSFunction* maybeFun;
     bool isFunction = IsFunctionObject(args.calleev(), &maybeFun);
 
@@ -4058,55 +4096,82 @@ CASE(JSOP_INITHOMEOBJECT)
     MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || obj->is<JSFunction>());
 
     func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj));
 }
 END_CASE(JSOP_INITHOMEOBJECT)
 
 CASE(JSOP_SUPERBASE)
 {
-    ScopeIter si(cx, REGS.fp()->scopeChain(), REGS.fp()->script()->innermostStaticScope(REGS.pc));
-    for (; !si.done(); ++si) {
-        if (si.hasSyntacticScopeObject() && si.type() == ScopeIter::Call) {
-            JSFunction& callee = si.scope().as<CallObject>().callee();
-
-            // Arrow functions don't have the information we're looking for,
-            // their enclosing scopes do. Nevertheless, they might have call
-            // objects. Skip them to find what we came for.
-            if (callee.isArrow())
-                continue;
-
-            MOZ_ASSERT(callee.allowSuperProperty());
-            MOZ_ASSERT(callee.nonLazyScript()->needsHomeObject());
-            const Value& homeObjVal = callee.getExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT);
-
-            ReservedRooted<JSObject*> homeObj(&rootObject0, &homeObjVal.toObject());
-            ReservedRooted<JSObject*> superBase(&rootObject1);
-            if (!GetPrototype(cx, homeObj, &superBase))
-                goto error;
-
-            if (!superBase) {
-                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
-                                     "null", "object");
-                goto error;
-            }
-            PUSH_OBJECT(*superBase);
-            break;
-        }
+    JSFunction& superEnvFunc = GetSuperEnvFunction(cx, REGS);
+    MOZ_ASSERT(superEnvFunc.allowSuperProperty());
+    MOZ_ASSERT(superEnvFunc.nonLazyScript()->needsHomeObject());
+    const Value& homeObjVal = superEnvFunc.getExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT);
+
+    ReservedRooted<JSObject*> homeObj(&rootObject0, &homeObjVal.toObject());
+    ReservedRooted<JSObject*> superBase(&rootObject1);
+    if (!GetPrototype(cx, homeObj, &superBase))
+        goto error;
+
+    if (!superBase) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
+                                "null", "object");
+        goto error;
     }
-    if (si.done())
-        MOZ_CRASH("Unexpected scope chain in superbase");
+    PUSH_OBJECT(*superBase);
 }
 END_CASE(JSOP_SUPERBASE)
 
 CASE(JSOP_NEWTARGET)
     PUSH_COPY(REGS.fp()->newTarget());
     MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
 END_CASE(JSOP_NEWTARGET)
 
+CASE(JSOP_SUPERFUN)
+{
+    ReservedRooted<JSObject*> superEnvFunc(&rootObject0, &GetSuperEnvFunction(cx, REGS));
+    MOZ_ASSERT(superEnvFunc->as<JSFunction>().isClassConstructor());
+    MOZ_ASSERT(superEnvFunc->as<JSFunction>().nonLazyScript()->isDerivedClassConstructor());
+
+    ReservedRooted<JSObject*> superFun(&rootObject1);
+
+    if (!GetPrototype(cx, superEnvFunc, &superFun))
+        goto error;
+
+    ReservedRooted<Value> superFunVal(&rootValue0, UndefinedValue());
+    if (!superFun)
+        superFunVal = NullValue();
+    else if (!superFun->isConstructor())
+        superFunVal = ObjectValue(*superFun);
+
+    if (superFunVal.isObjectOrNull()) {
+        ReportIsNotFunction(cx, superFunVal, JSDVG_IGNORE_STACK, CONSTRUCT);
+        goto error;
+    }
+
+    PUSH_OBJECT(*superFun);
+}
+END_CASE(JSOP_SUPERFUN)
+
+CASE(JSOP_SETTHIS)
+{
+    MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
+    MOZ_ASSERT(REGS.fp()->script()->isDerivedClassConstructor());
+    MOZ_ASSERT(REGS.fp()->callee().isClassConstructor());
+
+    if (!REGS.fp()->thisValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_REINIT_THIS);
+        goto error;
+    }
+
+    ReservedRooted<JSObject*> thisv(&rootObject0, &REGS.sp[-1].toObject());
+    REGS.fp()->setDerivedConstructorThis(thisv);
+}
+END_CASE(JSOP_SETTHIS)
+
 DEFAULT()
 {
     char numBuf[12];
     JS_snprintf(numBuf, sizeof numBuf, "%d", *REGS.pc);
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                          JSMSG_BAD_BYTECODE, numBuf);
     goto error;
 }
@@ -4697,17 +4762,17 @@ js::InitGetterSetterOperation(JSContext*
 
 bool
 js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv,
                         HandleValue callee, HandleValue arr, HandleValue newTarget, MutableHandleValue res)
 {
     RootedArrayObject aobj(cx, &arr.toObject().as<ArrayObject>());
     uint32_t length = aobj->length();
     JSOp op = JSOp(*pc);
-    bool constructing = op == JSOP_SPREADNEW;
+    bool constructing = op == JSOP_SPREADNEW || op == JSOP_SPREADSUPERCALL;
 
     if (length > ARGS_LENGTH_MAX) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              constructing ? JSMSG_TOO_MANY_CON_SPREADARGS
                                           : JSMSG_TOO_MANY_FUN_SPREADARGS);
         return false;
     }
 
@@ -4715,17 +4780,17 @@ js::SpreadCallOperation(JSContext* cx, H
     // The object must be an array with dense elements and no holes. Baseline's
     // optimized spread call stubs rely on this.
     MOZ_ASSERT(aobj->getDenseInitializedLength() == length);
     MOZ_ASSERT(!aobj->isIndexed());
     for (uint32_t i = 0; i < length; i++)
         MOZ_ASSERT(!aobj->getDenseElement(i).isMagic());
 #endif
 
-    if (op == JSOP_SPREADNEW) {
+    if (constructing) {
         if (!StackCheckIsConstructorCalleeNewTarget(cx, callee, newTarget))
             return false;
 
         ConstructArgs cargs(cx);
         if (!cargs.init(length))
             return false;
 
         if (!GetElements(cx, aobj, length, cargs.array()))
@@ -4975,8 +5040,28 @@ js::ReportRuntimeRedeclaration(JSContext
         if (declKind == frontend::Definition::VAR)
             kindStr = "non-configurable global property";
         else
             kindStr = frontend::Definition::kindString(declKind);
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_REDECLARED_VAR,
                              kindStr, printable.ptr());
     }
 }
+
+bool
+js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame)
+{
+    RootedFunction fun(cx, frame.callee());
+
+    MOZ_ASSERT(fun->isClassConstructor());
+    MOZ_ASSERT(fun->nonLazyScript()->isDerivedClassConstructor());
+
+    const char* name = "anonymous";
+    JSAutoByteString str;
+    if (fun->atom()) {
+        if (!AtomToPrintableString(cx, fun->atom(), &str))
+            return false;
+        name = str.ptr();
+    }
+
+    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS, name);
+    return false;
+}
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -480,12 +480,14 @@ void
 ReportUninitializedLexical(JSContext* cx, HandleScript script, jsbytecode* pc);
 
 // The parser only reports redeclarations that occurs within a single
 // script. Due to the extensibility of the global lexical scope, we also check
 // for redeclarations during runtime in JSOP_DEF{VAR,LET,CONST}.
 void
 ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name,
                            frontend::Definition::Kind declKind);
+bool
+ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);
 
 }  /* namespace js */
 
 #endif /* vm_Interpreter_h */
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -460,54 +460,58 @@ ObjectGroupCompartment::newTablePostBarr
     }
 }
 
 /* static */ ObjectGroup*
 ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
                              TaggedProto proto, JSObject* associated)
 {
     MOZ_ASSERT_IF(associated, proto.isObject());
-    MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>());
     MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
 
     // A null lookup clasp is used for 'new' groups with an associated
     // function. The group starts out as a plain object but might mutate into an
     // unboxed plain object.
-    MOZ_ASSERT(!clasp == (associated && associated->is<JSFunction>()));
+    MOZ_ASSERT_IF(!clasp, !!associated);
 
     AutoEnterAnalysis enter(cx);
 
     ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.defaultNewTable;
 
     if (!table) {
         table = cx->new_<ObjectGroupCompartment::NewTable>();
         if (!table || !table->init()) {
             js_delete(table);
             table = nullptr;
             ReportOutOfMemory(cx);
             return nullptr;
         }
     }
 
-    if (associated && associated->is<JSFunction>()) {
+    if (associated && !associated->is<TypeDescr>()) {
         MOZ_ASSERT(!clasp);
+        if (associated->is<JSFunction>()) {
 
-        // Canonicalize new functions to use the original one associated with its script.
-        JSFunction* fun = &associated->as<JSFunction>();
-        if (fun->hasScript())
-            associated = fun->nonLazyScript()->functionNonDelazifying();
-        else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
-            associated = fun->lazyScript()->functionNonDelazifying();
-        else
+            // Canonicalize new functions to use the original one associated with its script.
+            JSFunction* fun = &associated->as<JSFunction>();
+            if (fun->hasScript())
+                associated = fun->nonLazyScript()->functionNonDelazifying();
+            else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
+                associated = fun->lazyScript()->functionNonDelazifying();
+            else
+                associated = nullptr;
+
+            // If we have previously cleared the 'new' script information for this
+            // function, don't try to construct another one.
+            if (associated && associated->wasNewScriptCleared())
+                associated = nullptr;
+
+        } else {
             associated = nullptr;
-
-        // If we have previously cleared the 'new' script information for this
-        // function, don't try to construct another one.
-        if (associated && associated->wasNewScriptCleared())
-            associated = nullptr;
+        }
 
         if (!associated)
             clasp = &PlainObject::class_;
     }
 
     if (proto.isObject() && !proto.toObject()->isDelegate()) {
         RootedObject protoObj(cx, proto.toObject());
         if (!protoObj->setDelegate(cx))
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1664,20 +1664,55 @@ 1234567890123456789012345678901234567890
      * if a var binding with the same name exists on the global.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: =>
      */ \
     macro(JSOP_DEFLET,        162,"deflet",     NULL,     5,  0,  0,  JOF_ATOM) \
     \
-    macro(JSOP_UNUSED163,     163,"unused163",  NULL,     1,  0,  0,  JOF_BYTE) \
-    macro(JSOP_UNUSED164,     164,"unused164",  NULL,     1,  0,  0,  JOF_BYTE) \
-    macro(JSOP_UNUSED165,     165,"unused165",  NULL,     1,  0,  0,  JOF_BYTE) \
-    macro(JSOP_UNUSED166,     166,"unused166",  NULL,     1,  0,  0,  JOF_BYTE) \
+    /*
+     * Bind the |this| value of a function to the supplied value.
+     *
+     *  Category: Variables and Scopes
+     *  Type: This
+     *  Operands:
+     *  Stack: this => this
+     */ \
+    macro(JSOP_SETTHIS  ,     163,"setthis",    NULL,     1,  1,  1,  JOF_BYTE) \
+    /*
+     * Find the function to invoke with |super()| on the scope chain.
+     *
+     *  Category: Variables and Scopes
+     *  Type: Super
+     *  Operands:
+     *  Stack: => superFun
+     */ \
+    macro(JSOP_SUPERFUN,      164,"superfun",   NULL,     1,  0,  1,  JOF_BYTE) \
+    /*
+     * Behaves exactly like JSOP_NEW, but allows JITs to distinguish the two cases.
+     *
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint16_t argc
+     *   Stack: callee, this, args[0], ..., args[argc-1], newTarget => rval
+     *   nuses: (argc+3)
+     */ \
+    macro(JSOP_SUPERCALL,     165,"supercall",  NULL,     3, -1,  1,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
+    /*
+     * spreadcall variant of JSOP_SUPERCALL.
+     *
+     * Behaves exactly like JSOP_SPREADNEW.
+     *
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: callee, this, args, newTarget => rval
+     */ \
+    macro(JSOP_SPREADSUPERCALL, 166, "spreadsupercall", NULL, 1,  4,  1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \
     macro(JSOP_UNUSED167,     167,"unused167",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED168,     168,"unused168",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED169,     169,"unused169",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED170,     170,"unused170",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED171,     171,"unused171",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED172,     172,"unused172",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED173,     173,"unused173",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED174,     174,"unused174",  NULL,     1,  0,  0,  JOF_BYTE) \
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/SavedStacks.h"
 
+#include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 
 #include <algorithm>
 #include <math.h>
 
 #include "jsapi.h"
@@ -899,16 +900,20 @@ SavedFrame::toStringMethod(JSContext* cx
         return false;
     args.rval().setString(string);
     return true;
 }
 
 bool
 SavedStacks::init()
 {
+    uint64_t seed[2];
+    random_generateSeed(seed, mozilla::ArrayLength(seed));
+    bernoulli.setRandomState(seed[0], seed[1]);
+
     if (!pcLocationMap.init())
         return false;
 
     return frames.init();
 }
 
 bool
 SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsigned maxFrameCount)
@@ -1321,80 +1326,55 @@ SavedStacks::getLocation(JSContext* cx, 
         }
     }
 
     locationp.set(p->value());
     return true;
 }
 
 void
-SavedStacks::chooseSamplingProbability(JSContext* cx)
+SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
 {
-    GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
+    GlobalObject* global = compartment->maybeGlobal();
+    if (!global)
+        return;
+
+    GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
     if (!dbgs || dbgs->empty())
         return;
 
     mozilla::DebugOnly<Debugger**> begin = dbgs->begin();
     mozilla::DebugOnly<bool> foundAnyDebuggers = false;
 
-    allocationSamplingProbability = 0;
+    double probability = 0;
     for (Debugger** dbgp = dbgs->begin(); dbgp < dbgs->end(); dbgp++) {
         // The set of debuggers had better not change while we're iterating,
         // such that the vector gets reallocated.
         MOZ_ASSERT(dbgs->begin() == begin);
 
         if ((*dbgp)->trackingAllocationSites && (*dbgp)->enabled) {
             foundAnyDebuggers = true;
-            allocationSamplingProbability = std::max((*dbgp)->allocationSamplingProbability,
-                                                     allocationSamplingProbability);
+            probability = std::max((*dbgp)->allocationSamplingProbability,
+                                   probability);
         }
     }
     MOZ_ASSERT(foundAnyDebuggers);
+
+    bernoulli.setProbability(probability);
 }
 
 JSObject*
 SavedStacksMetadataCallback(JSContext* cx, JSObject* target)
 {
     RootedObject obj(cx, target);
 
     SavedStacks& stacks = cx->compartment()->savedStacks();
-    if (stacks.allocationSkipCount > 0) {
-        stacks.allocationSkipCount--;
-        return nullptr;
-    }
-
-    stacks.chooseSamplingProbability(cx);
-    if (stacks.allocationSamplingProbability == 0.0)
+    if (!stacks.bernoulli.trial())
         return nullptr;
 
-    // If the sampling probability is set to 1.0, we are always taking a sample
-    // and can therefore leave allocationSkipCount at 0.
-    if (stacks.allocationSamplingProbability != 1.0) {
-        // Rather than generating a random number on every allocation to decide
-        // if we want to sample that particular allocation (which would be
-        // expensive), we calculate the number of allocations to skip before
-        // taking the next sample.
-        //
-        // P = the probability we sample any given event.
-        //
-        // ~P = 1-P, the probability we don't sample a given event.
-        //
-        // (~P)^n = the probability that we skip at least the next n events.
-        //
-        // let X = random between 0 and 1.
-        //
-        // floor(log base ~P of X) = n, aka the number of events we should skip
-        // until we take the next sample. Any value for X less than (~P)^n
-        // yields a skip count greater than n, so the likelihood of a skip count
-        // greater than n is (~P)^n, as required.
-        double notSamplingProb = 1.0 - stacks.allocationSamplingProbability;
-        stacks.allocationSkipCount = std::floor(std::log(random_nextDouble(&stacks.rngState)) /
-                                                std::log(notSamplingProb));
-    }
-
     AutoEnterOOMUnsafeRegion oomUnsafe;
     RootedSavedFrame frame(cx);
     if (!stacks.saveCurrentStack(cx, &frame))
         oomUnsafe.crash("SavedStacksMetadataCallback");
 
     if (!Debugger::onLogAllocationSite(cx, obj, frame, JS_GetCurrentEmbedderTime()))
         oomUnsafe.crash("SavedStacksMetadataCallback");
 
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 vm_SavedStacks_h
 #define vm_SavedStacks_h
 
+#include "mozilla/FastBernoulliTrial.h"
+
 #include "jscntxt.h"
 #include "jsmath.h"
 #include "jswrapper.h"
 #include "js/HashTable.h"
 #include "vm/SavedFrame.h"
 #include "vm/Stack.h"
 
 namespace js {
@@ -149,39 +151,36 @@ class SavedStacks {
     friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
     friend bool JS::ubi::ConstructSavedFrameStackSlow(JSContext* cx,
                                                       JS::ubi::StackFrame& ubiFrame,
                                                       MutableHandleObject outSavedFrameStack);
 
   public:
     SavedStacks()
       : frames(),
-        allocationSamplingProbability(1.0),
-        allocationSkipCount(0),
-        rngState(0),
+        bernoulli(1.0, 0x59fdad7f6b4cc573, 0x91adf38db96a9354),
         creatingSavedFrame(false)
     { }
 
     bool     init();
     bool     initialized() const { return frames.initialized(); }
     bool     saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsigned maxFrameCount = 0);
     void     sweep(JSRuntime* rt);
     void     trace(JSTracer* trc);
     uint32_t count();
     void     clear();
-    void     setRNGState(uint64_t state) { rngState = state; }
+    void     setRNGState(uint64_t state0, uint64_t state1) { bernoulli.setRandomState(state0, state1); }
+    void     chooseSamplingProbability(JSCompartment*);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
   private:
     SavedFrame::Set frames;
-    double          allocationSamplingProbability;
-    uint32_t        allocationSkipCount;
-    uint64_t        rngState;
-    bool            creatingSavedFrame;
+    mozilla::FastBernoulliTrial bernoulli;
+    bool creatingSavedFrame;
 
     // Similar to mozilla::ReentrancyGuard, but instead of asserting against
     // reentrancy, just change the behavior of SavedStacks::saveCurrentStack to
     // return a nullptr SavedFrame.
     struct MOZ_RAII AutoReentrancyGuard {
         MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
         SavedStacks& stacks;
 
@@ -201,17 +200,16 @@ class SavedStacks {
     bool        insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFrame frame,
                              unsigned maxFrameCount = 0);
     bool        adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
                                 HandleString asyncCause,
                                 MutableHandleSavedFrame adoptedStack,
                                 unsigned maxFrameCount);
     SavedFrame* getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup lookup);
     SavedFrame* createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup);
-    void        chooseSamplingProbability(JSContext* cx);
 
     // Cache for memoizing PCToLineNumber lookups.
 
     struct PCKey {
         PCKey(JSScript* script, jsbytecode* pc) : script(script), pc(pc) { }
 
         PreBarrieredScript script;
         jsbytecode*        pc;
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -253,23 +253,29 @@ InterpreterFrame::prologue(JSContext* cx
         pushOnScopeChain(*scope);
         return probes::EnterScript(cx, script, nullptr, this);
     }
 
     MOZ_ASSERT(isNonEvalFunctionFrame());
     if (fun()->needsCallObject() && !initFunctionScopeObjects(cx))
         return false;
 
-    if (isConstructing() && functionThis().isPrimitive()) {
-        RootedObject callee(cx, &this->callee());
-        JSObject* obj = CreateThisForFunction(cx, callee,
-                                              createSingleton() ? SingletonObject : GenericObject);
-        if (!obj)
-            return false;
-        functionThis() = ObjectValue(*obj);
+    if (isConstructing()) {
+        if (script->isDerivedClassConstructor()) {
+            MOZ_ASSERT(callee().isClassConstructor());
+            functionThis() = MagicValue(JS_UNINITIALIZED_LEXICAL);
+        } else if (functionThis().isPrimitive()) {
+            RootedObject callee(cx, &this->callee());
+            RootedObject newTarget(cx, &this->newTarget().toObject());
+            JSObject* obj = CreateThisForFunction(cx, callee, newTarget,
+                                                  createSingleton() ? SingletonObject : GenericObject);
+            if (!obj)
+                return false;
+            functionThis() = ObjectValue(*obj);
+        }
     }
 
     return probes::EnterScript(cx, script, script->functionNonDelazifying(), this);
 }
 
 void
 InterpreterFrame::epilogue(JSContext* cx)
 {
@@ -312,16 +318,53 @@ InterpreterFrame::epilogue(JSContext* cx
     if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
         DebugScopes::onPopCall(this, cx);
 
     if (!fun()->isGenerator() && isConstructing() && thisValue().isObject() && returnValue().isPrimitive())
         setReturnValue(ObjectValue(constructorThis()));
 }
 
 bool
+InterpreterFrame::checkThis(JSContext* cx)
+{
+    if (script()->isDerivedClassConstructor()) {
+        MOZ_ASSERT(isNonEvalFunctionFrame());
+        MOZ_ASSERT(fun()->isClassConstructor());
+
+        if (thisValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
+            RootedFunction func(cx, fun());
+            return ThrowUninitializedThis(cx, this);
+        }
+    }
+    return true;
+}
+
+bool
+InterpreterFrame::checkReturn(JSContext* cx)
+{
+    if (script()->isDerivedClassConstructor()) {
+        MOZ_ASSERT(isNonEvalFunctionFrame());
+        MOZ_ASSERT(callee().isClassConstructor());
+
+        HandleValue retVal = returnValue();
+        if (retVal.isObject())
+            return true;
+
+        if (!retVal.isUndefined()) {
+            ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal, nullptr);
+            return false;
+        }
+
+        if (!checkThis(cx))
+            return false;
+    }
+    return true;
+}
+
+bool
 InterpreterFrame::pushBlock(JSContext* cx, StaticBlockObject& block)
 {
     MOZ_ASSERT(block.needsClone());
 
     Rooted<StaticBlockObject*> blockHandle(cx, &block);
     ClonedBlockObject* clone = ClonedBlockObject::create(cx, blockHandle, this);
     if (!clone)
         return false;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -447,16 +447,19 @@ class InterpreterFrame
      * over-recursed) after pushing the stack frame but before 'prologue' is
      * called or completes fully. To simplify usage, 'epilogue' does not assume
      * 'prologue' has completed and handles all the intermediate state details.
      */
 
     bool prologue(JSContext* cx);
     void epilogue(JSContext* cx);
 
+    bool checkReturn(JSContext* cx);
+    bool checkThis(JSContext* cx);
+
     bool initFunctionScopeObjects(JSContext* cx);
 
     /*
      * Initialize local variables of newly-pushed frame. 'var' bindings are
      * initialized to undefined and lexical bindings are initialized to
      * JS_UNINITIALIZED_LEXICAL.
      */
     void initLocals();
@@ -733,16 +736,24 @@ class InterpreterFrame
     }
 
     Value& thisValue() const {
         if (flags_ & (EVAL | GLOBAL))
             return ((Value*)this)[-1];
         return argv()[-1];
     }
 
+    void setDerivedConstructorThis(HandleObject thisv) {
+        MOZ_ASSERT(isNonEvalFunctionFrame());
+        MOZ_ASSERT(script()->isDerivedClassConstructor());
+        MOZ_ASSERT(callee().isClassConstructor());
+        MOZ_ASSERT(thisValue().isMagic(JS_UNINITIALIZED_LEXICAL));
+        argv()[-1] = ObjectValue(*thisv);
+    }
+
     /*
      * Callee
      *
      * Only function frames have a callee. An eval frame in a function has the
      * same callee as its containing function frame. maybeCalleev can be used
      * to return a value that is either the callee object (for function frames) or
      * null (for global frames).
      */
--- 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 = 310;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 312;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 415,
+static_assert(JSErr_Limit == 419,
               "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/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -118,17 +118,17 @@ interface nsIXPCComponents_utils_Sandbox
 interface ScheduledGCCallback : nsISupports
 {
     void callback();
 };
 
 /**
 * interface of Components.utils
 */
-[scriptable, uuid(e04a4a58-2b5e-4a74-941a-0d344b057c5a)]
+[scriptable, uuid(eaeab2c0-ada3-4c22-b36d-70320dd923ba)]
 interface nsIXPCComponents_Utils : nsISupports
 {
 
     /* reportError is designed to be called from JavaScript only.
      *
      * It will report a JS Error object to the JS console, and return. It
      * is meant for use in exception handler blocks which want to "eat"
      * an exception, but still want to report it to the console.
@@ -484,26 +484,16 @@ interface nsIXPCComponents_Utils : nsISu
      * For gecko internal automation use only. Calling this in production code
      * would result in security vulnerabilities, so it will crash if used outside
      * of automation.
      */
     [implicit_jscontext]
     void forcePermissiveCOWs();
 
     /*
-     * Disables the XPConnect security checks that deny access to callables and
-     * accessor descriptors on COWs. Do not use this unless you are bholley.
-     *
-     * For the benefit of his magesty TCPSocket while he takes his sweet time
-     * converting to WebIDL. See bug 885982.
-     */
-    [implicit_jscontext]
-    void skipCOWCallableChecks();
-
-    /*
      * Forces the usage of a privileged |Components| object for a potentially-
      * unprivileged scope. This will crash if used outside of automation.
      */
     [implicit_jscontext]
     void forcePrivilegedComponentsForScope(in jsval vscope);
 
     /*
      * This seemingly-paradoxical API allows privileged code to explicitly give
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2945,23 +2945,16 @@ NS_IMETHODIMP
 nsXPCComponents_Utils::ForcePermissiveCOWs(JSContext* cx)
 {
     CrashIfNotInAutomation();
     CompartmentPrivate::Get(CurrentGlobalOrNull(cx))->forcePermissiveCOWs = true;
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsXPCComponents_Utils::SkipCOWCallableChecks(JSContext* cx)
-{
-    CompartmentPrivate::Get(CurrentGlobalOrNull(cx))->skipCOWCallableChecks = true;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
 nsXPCComponents_Utils::ForcePrivilegedComponentsForScope(HandleValue vscope,
                                                          JSContext* cx)
 {
     if (!vscope.isObject())
         return NS_ERROR_INVALID_ARG;
     CrashIfNotInAutomation();
     JSObject* scopeObj = js::UncheckedUnwrap(&vscope.toObject());
     XPCWrappedNativeScope* scope = ObjectScope(scopeObj);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3677,17 +3677,16 @@ public:
 
     explicit CompartmentPrivate(JSCompartment* c)
         : wantXrays(false)
         , allowWaivers(true)
         , writeToGlobalPrototype(false)
         , skipWriteToGlobalPrototype(false)
         , universalXPConnectEnabled(false)
         , forcePermissiveCOWs(false)
-        , skipCOWCallableChecks(false)
         , scriptability(c)
         , scope(nullptr)
     {
         MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
         mozilla::PodArrayZero(wrapperDenialWarnings);
     }
 
     ~CompartmentPrivate();
@@ -3738,20 +3737,16 @@ public:
     // This is only ever set during mochitest runs when enablePrivilege is called.
     // It allows the SpecialPowers scope to waive the normal chrome security
     // wrappers and expose properties directly to content. This lets us avoid a
     // bunch of overhead and complexity in our SpecialPowers automation glue.
     //
     // Using it in production is inherently unsafe.
     bool forcePermissiveCOWs;
 
-    // Disables the XPConnect security checks that deny access to callables and
-    // accessor descriptors on COWs. Do not use this unless you are bholley.
-    bool skipCOWCallableChecks;
-
     // Whether we've emitted a warning about a property that was filtered out
     // by a security wrapper. See XrayWrapper.cpp.
     bool wrapperDenialWarnings[WrapperDenialTypeCount];
 
     // The scriptability of this compartment.
     Scriptability scriptability;
 
     // Our XPCWrappedNativeScope. This is non-null if and only if this is an
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -407,28 +407,27 @@ ExposedPropertiesOnly::check(JSContext* 
     }
 
     if ((act == Wrapper::SET && !(access & WRITE)) ||
         (act != Wrapper::SET && !(access & READ))) {
         return false;
     }
 
     // Inspect the property on the underlying object to check for red flags.
-    bool skipCallableChecks = CompartmentPrivate::Get(wrappedObject)->skipCOWCallableChecks;
     if (!JS_GetPropertyDescriptorById(cx, wrappedObject, id, &desc))
         return false;
 
     // Reject accessor properties.
-    if (!skipCallableChecks && desc.hasGetterOrSetter()) {
+    if (desc.hasGetterOrSetter()) {
         EnterAndThrow(cx, wrapper, "Exposing privileged accessor properties is prohibited");
         return false;
     }
 
     // Reject privileged or cross-origin callables.
-    if (!skipCallableChecks && desc.value().isObject()) {
+    if (desc.value().isObject()) {
         RootedObject maybeCallable(cx, js::UncheckedUnwrap(&desc.value().toObject()));
         if (JS::IsCallable(maybeCallable) && !AccessCheck::subsumes(wrapper, maybeCallable)) {
             EnterAndThrow(cx, wrapper, "Exposing privileged or cross-origin callable is prohibited");
             return false;
         }
     }
 
     return true;
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -448,33 +448,41 @@ AccessibleCaretManager::SelectWordOrShor
   return rv;
 }
 
 void
 AccessibleCaretManager::OnScrollStart()
 {
   AC_LOG("%s", __FUNCTION__);
 
+  if (GetCaretMode() == CaretMode::Cursor) {
+    mFirstCaretAppearanceOnScrollStart = mFirstCaret->GetAppearance();
+  }
+
   HideCarets();
 }
 
 void
 AccessibleCaretManager::OnScrollEnd()
 {
   if (mLastUpdateCaretMode != GetCaretMode()) {
     return;
   }
 
   if (GetCaretMode() == CaretMode::Cursor) {
-    AC_LOG("%s: HideCarets()", __FUNCTION__);
-    HideCarets();
-  } else {
-    AC_LOG("%s: UpdateCarets()", __FUNCTION__);
-    UpdateCarets();
+    mFirstCaret->SetAppearance(mFirstCaretAppearanceOnScrollStart);
+    if (!mFirstCaret->IsLogicallyVisible()) {
+      // If the caret is hide (Appearance::None) due to timeout or blur, no need
+      // to update it.
+      return;
+    }
   }
+
+  AC_LOG("%s: UpdateCarets()", __FUNCTION__);
+  UpdateCarets();
 }
 
 void
 AccessibleCaretManager::OnScrollPositionChanged()
 {
   if (mLastUpdateCaretMode != GetCaretMode()) {
     return;
   }
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.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 AccessibleCaretManager_h
 #define AccessibleCaretManager_h
 
+#include "AccessibleCaret.h"
 #include "nsCOMPtr.h"
 #include "nsCoord.h"
 #include "nsIFrame.h"
 #include "nsISelectionListener.h"
 #include "mozilla/nsRefPtr.h"
 #include "nsWeakReference.h"
 #include "mozilla/dom/CaretStateChangedEvent.h"
 #include "mozilla/EventForwards.h"
@@ -25,18 +26,16 @@ struct nsPoint;
 
 namespace mozilla {
 
 namespace dom {
 class Element;
 class Selection;
 } // namespace dom
 
-class AccessibleCaret;
-
 // -----------------------------------------------------------------------------
 // AccessibleCaretManager does not deal with events or callbacks directly. It
 // relies on AccessibleCaretEventHub to call its public methods to do the work.
 // All codes needed to interact with PresShell, Selection, and AccessibleCaret
 // should be written in AccessibleCaretManager.
 //
 // None the public methods in AccessibleCaretManager will flush layout or style
 // prior to performing its task. The caller must ensure the layout is up to
@@ -220,16 +219,21 @@ protected:
 
   // The timer for hiding the caret in cursor mode after timeout behind the
   // preference "layout.accessiblecaret.timeout_ms".
   nsCOMPtr<nsITimer> mCaretTimeoutTimer;
 
   // The caret mode since last update carets.
   CaretMode mLastUpdateCaretMode = CaretMode::None;
 
+  // Store the appearance of the first caret when calling OnScrollStart so that
+  // it can be restored in OnScrollEnd.
+  AccessibleCaret::Appearance mFirstCaretAppearanceOnScrollStart =
+                                 AccessibleCaret::Appearance::None;
+
   static const int32_t kAutoScrollTimerDelay = 30;
 
   // Clicking on the boundary of input or textarea will move the caret to the
   // front or end of the content. To avoid this, we need to deflate the content
   // boundary by 61 app units, which is 1 pixel + 1 app unit as defined in
   // AppUnit.h.
   static const int32_t kBoundaryAppUnits = 61;
 };
--- a/layout/base/gtest/TestAccessibleCaretManager.cpp
+++ b/layout/base/gtest/TestAccessibleCaretManager.cpp
@@ -52,16 +52,17 @@ public:
 
   }; // class MockAccessibleCaret
 
   class MockAccessibleCaretManager : public AccessibleCaretManager
   {
   public:
     using CaretMode = AccessibleCaretManager::CaretMode;
     using AccessibleCaretManager::UpdateCarets;
+    using AccessibleCaretManager::HideCarets;
 
     MockAccessibleCaretManager()
       : AccessibleCaretManager(nullptr)
     {
       mFirstCaret = MakeUnique<MockAccessibleCaret>();
       mSecondCaret = MakeUnique<MockAccessibleCaret>();
     }
 
@@ -374,9 +375,123 @@ TEST_F(AccessibleCaretManagerTester, Tes
   EXPECT_EQ(SecondCaretAppearance(), Appearance::None);
   check.Call("scrollstart");
 
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
 }
 
+TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible)
+{
+  EXPECT_CALL(mManager, GetCaretMode())
+    .WillRepeatedly(Return(CaretMode::Cursor));
+
+  EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
+    .WillRepeatedly(Return(true));
+
+  MockFunction<void(std::string aCheckPointName)> check;
+  {
+    InSequence dummy;
+
+    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                  CaretChangedReason::Updateposition)).Times(1);
+    EXPECT_CALL(check, Call("updatecarets"));
+
+    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                  CaretChangedReason::Visibilitychange)).Times(1);
+    EXPECT_CALL(check, Call("scrollstart1"));
+
+    // After scroll ended, the caret is out of scroll port.
+    EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+      .WillRepeatedly(Return(PositionChangedResult::Invisible));
+    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                  CaretChangedReason::Updateposition)).Times(1);
+    EXPECT_CALL(check, Call("scrollend1"));
+
+    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                  CaretChangedReason::Visibilitychange)).Times(1);
+    EXPECT_CALL(check, Call("scrollstart2"));
+
+    // After scroll ended, the caret is visible again.
+    EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+      .WillRepeatedly(Return(PositionChangedResult::Changed));
+    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                  CaretChangedReason::Updateposition)).Times(1);
+    EXPECT_CALL(check, Call("scrollend2"));
+  }
+
+  mManager.UpdateCarets();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+  check.Call("updatecarets");
+
+  mManager.OnScrollStart();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+  check.Call("scrollstart1");
+
+  mManager.OnScrollEnd();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+  check.Call("scrollend1");
+
+  mManager.OnScrollStart();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+  check.Call("scrollstart2");
+
+  mManager.OnScrollEnd();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+  check.Call("scrollend2");
+}
+
+TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden)
+{
+  EXPECT_CALL(mManager, GetCaretMode())
+    .WillRepeatedly(Return(CaretMode::Cursor));
+
+  EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
+    .WillRepeatedly(Return(true));
+
+  MockFunction<void(std::string aCheckPointName)> check;
+  {
+    InSequence dummy;
+
+    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                  CaretChangedReason::Updateposition)).Times(1);
+    EXPECT_CALL(check, Call("updatecarets"));
+
+    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                  CaretChangedReason::Visibilitychange)).Times(1);
+    EXPECT_CALL(check, Call("hidecarets"));
+
+    // After scroll ended, the caret is out of scroll port.
+    EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+      .WillRepeatedly(Return(PositionChangedResult::Invisible));
+    EXPECT_CALL(check, Call("scrollend1"));
+
+    // After scroll ended, the caret is visible again.
+    EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+      .WillRepeatedly(Return(PositionChangedResult::Changed));
+    EXPECT_CALL(check, Call("scrollend2"));
+  }
+
+  mManager.UpdateCarets();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+  check.Call("updatecarets");
+
+  mManager.HideCarets();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+  check.Call("hidecarets");
+
+  mManager.OnScrollStart();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+
+  mManager.OnScrollEnd();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+  check.Call("scrollend1");
+
+  mManager.OnScrollStart();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+
+  mManager.OnScrollEnd();
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+  check.Call("scrollend2");
+}
+
 } // namespace mozilla
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -1786,21 +1786,27 @@ CSSStyleSheet::SubjectSubsumesInnerPrinc
     return NS_OK;
   }
 
   // Allow access only if CORS mode is not NONE
   if (GetCORSMode() == CORS_NONE) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  // Now make sure we set the principal of our inner to the
-  // subjectPrincipal.  That means we need a unique inner, of
-  // course.  But we don't want to do that if we're not complete
-  // yet.  Luckily, all the callers of this method throw anyway if
-  // not complete, so we can just do that here too.
+  // Now make sure we set the principal of our inner to the subjectPrincipal.
+  // We do this because we're in a situation where the caller would not normally
+  // be able to access the sheet, but the sheet has opted in to being read.
+  // Unfortunately, that means it's also opted in to being _edited_, and if the
+  // caller now makes edits to the sheet we want the resulting resource loads,
+  // if any, to look as if they are coming from the caller's principal, not the
+  // original sheet principal.
+  //
+  // That means we need a unique inner, of course.  But we don't want to do that
+  // if we're not complete yet.  Luckily, all the callers of this method throw
+  // anyway if not complete, so we can just do that here too.
   if (!mInner->mComplete) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
   WillDirty();
 
   mInner->mPrincipal = subjectPrincipal;
 
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -3613,29 +3613,27 @@ CSS_PROP_POSITION(
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_UNITLESS_LENGTH_QUIRK |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHKLP | VARIANT_CALC,
     kWidthKTable,
     offsetof(nsStylePosition, mWidth),
     eStyleAnimType_Coord)
-#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_USERINTERFACE(
     -moz-window-dragging,
     _moz_window_dragging,
     CSS_PROP_DOMPROP_PREFIXED(WindowDragging),
-    CSS_PROPERTY_INTERNAL |
-        CSS_PROPERTY_PARSE_VALUE |
-        CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME,
+    CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     kWindowDraggingKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_UIRESET(
     -moz-window-shadow,
     _moz_window_shadow,
     CSS_PROP_DOMPROP_PREFIXED(WindowShadow),
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME,
     "",
--- a/layout/style/test/ListCSSProperties.cpp
+++ b/layout/style/test/ListCSSProperties.cpp
@@ -109,17 +109,16 @@ const char *gInaccessibleProperties[] = 
     "-moz-control-character-visibility",
     "-moz-script-level", // parsed by UA sheets only
     "-moz-script-size-multiplier",
     "-moz-script-min-size",
     "-moz-math-variant",
     "-moz-math-display", // parsed by UA sheets only
     "-moz-top-layer", // parsed by UA sheets only
     "-moz-min-font-size-ratio", // parsed by UA sheets only
-    "-moz-window-dragging", // chrome-only internal properties
     "-moz-window-shadow" // chrome-only internal properties
 };
 
 inline int
 is_inaccessible(const char* aPropName)
 {
     for (unsigned j = 0; j < MOZ_ARRAY_LENGTH(gInaccessibleProperties); ++j) {
         if (strcmp(aPropName, gInaccessibleProperties[j]) == 0)
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -3964,16 +3964,24 @@ var gCSSProperties = {
   "vector-effect": {
     domProp: "vectorEffect",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [ "non-scaling-stroke" ],
     invalid_values: []
   },
+  "-moz-window-dragging": {
+    domProp: "MozWindowDragging",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "no-drag" ],
+    other_values: [ "drag" ],
+    invalid_values: [ "none" ]
+  },
   "align-content": {
     domProp: "alignContent",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "stretch" ],
     other_values: [
       "flex-start",
       "flex-end",
@@ -6618,24 +6626,16 @@ for (var prop in gCSSProperties) {
       });
     }
   }
 }
 
 if (false) {
   // TODO These properties are chrome-only, and are not exposed via CSSOM.
   // We may still want to find a way to test them. See bug 1206999.
-  gCSSProperties["-moz-window-dragging"] = {
-    //domProp: "MozWindowDragging",
-    inherited: true,
-    type: CSS_TYPE_LONGHAND,
-    initial_values: [ "no-drag" ],
-    other_values: [ "drag" ],
-    invalid_values: [ "none" ]
-  };
   gCSSProperties["-moz-window-shadow"] = {
     //domProp: "MozWindowShadow",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "default" ],
     other_values: [ "none", "menu", "tooltip", "sheet" ],
     invalid_values: []
   };
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1840,18 +1840,17 @@ nsMenuPopupFrame::ChangeMenuItem(nsMenuF
     if (aFromKey && IsOpen()) {
       nsIFrame* parentMenu = GetParent();
       if (parentMenu) {
         nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(parentMenu->GetContent());
         if (menulist) {
           // Fire a command event as the new item, but we don't want to close
           // the menu, blink it, or update any other state of the menuitem. The
           // command event will cause the item to be selected.
-          nsContentUtils::DispatchXULCommand(aMenuItem->GetContent(),
-                                             nsContentUtils::IsCallerChrome(),
+          nsContentUtils::DispatchXULCommand(aMenuItem->GetContent(), /* aTrusted = */ true,
                                              nullptr, PresContext()->PresShell(),
                                              false, false, false, false);
         }
       }
     }
 #endif
   }
 
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -18,26 +18,29 @@
 #include "mozilla/Likely.h"
 #include "mozilla/MacroArgs.h"
 #ifdef MOZ_DUMP_ASSERTION_STACK
 #include "nsTraceRefcnt.h"
 #endif
 
 #if defined(MOZ_CRASHREPORTER) && defined(MOZILLA_INTERNAL_API) && \
     !defined(MOZILLA_EXTERNAL_LINKAGE) && defined(__cplusplus)
-#  define MOZ_CRASH_CRASHREPORT
 namespace CrashReporter {
 // This declaration is present here as well as in nsExceptionHandler.h
 // nsExceptionHandler.h is not directly included in this file as it includes
 // windows.h, which can cause problems when it is imported into some files due
 // to the number of macros defined.
 // XXX If you change this definition - also change the definition in
 // nsExceptionHandler.h
 void AnnotateMozCrashReason(const char* aReason);
 } // namespace CrashReporter
+
+#  define MOZ_CRASH_ANNOTATE(...) CrashReporter::AnnotateMozCrashReason("" __VA_ARGS__)
+#else
+#  define MOZ_CRASH_ANNOTATE(...) do { /* nothing */ } while (0)
 #endif
 
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #ifdef WIN32
    /*
     * TerminateProcess and GetCurrentProcess are defined in <winbase.h>, which
@@ -258,29 +261,26 @@ MOZ_ReportCrash(const char* aStr, const 
  *
  * If we're a DEBUG build and we crash at a MOZ_CRASH which provides an
  * explanation-string, we print the string to stderr.  Otherwise, we don't
  * print anything; this is because we want MOZ_CRASH to be 100% safe in release
  * builds, and it's hard to print to stderr safely when memory might have been
  * corrupted.
  */
 #ifndef DEBUG
-#  ifdef MOZ_CRASH_CRASHREPORT
-#    define MOZ_CRASH(...) \
-       do { \
-         CrashReporter::AnnotateMozCrashReason("MOZ_CRASH(" __VA_ARGS__ ")"); \
-         MOZ_REALLY_CRASH(); \
-       } while (0)
-#  else
-#    define MOZ_CRASH(...) MOZ_REALLY_CRASH()
-#  endif
+#  define MOZ_CRASH(...) \
+     do { \
+       MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
+       MOZ_REALLY_CRASH(); \
+     } while (0)
 #else
 #  define MOZ_CRASH(...) \
      do { \
        MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
+       MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
        MOZ_REALLY_CRASH(); \
      } while (0)
 #endif
 
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 
@@ -377,25 +377,27 @@ struct AssertionConditionType
 #endif
 
 /* First the single-argument form. */
 #define MOZ_ASSERT_HELPER1(expr) \
   do { \
     MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
     if (MOZ_UNLIKELY(!(expr))) { \
       MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \
+      MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ")"); \
       MOZ_REALLY_CRASH(); \
     } \
   } while (0)
 /* Now the two-argument form. */
 #define MOZ_ASSERT_HELPER2(expr, explain) \
   do { \
     MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
     if (MOZ_UNLIKELY(!(expr))) { \
       MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
+      MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ") (" explain ")"); \
       MOZ_REALLY_CRASH(); \
     } \
   } while (0)
 
 #define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
 #define MOZ_RELEASE_ASSERT(...) \
   MOZ_RELEASE_ASSERT_GLUE( \
     MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
new file mode 100644
--- /dev/null
+++ b/mfbt/FastBernoulliTrial.h
@@ -0,0 +1,376 @@
+/* -*- 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/. */
+
+#ifndef mozilla_FastBernoulliTrial_h
+#define mozilla_FastBernoulliTrial_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/XorShift128PlusRNG.h"
+
+#include <cmath>
+#include <stdint.h>
+
+namespace mozilla {
+
+/**
+ * class FastBernoulliTrial: Efficient sampling with uniform probability
+ *
+ * When gathering statistics about a program's behavior, we may be observing
+ * events that occur very frequently (e.g., function calls or memory
+ * allocations) and we may be gathering information that is somewhat expensive
+ * to produce (e.g., call stacks). Sampling all the events could have a
+ * significant impact on the program's performance.
+ *
+ * Why not just sample every N'th event? This technique is called "systematic
+ * sampling"; it's simple and efficient, and it's fine if we imagine a
+ * patternless stream of events. But what if we're sampling allocations, and the
+ * program happens to have a loop where each iteration does exactly N
+ * allocations? You would end up sampling the same allocation every time through
+ * the loop; the entire rest of the loop becomes invisible to your measurements!
+ * More generally, if each iteration does M allocations, and M and N have any
+ * common divisor at all, most allocation sites will never be sampled. If
+ * they're both even, say, the odd-numbered allocations disappear from your
+ * results.
+ *
+ * Ideally, we'd like each event to have some probability P of being sampled,
+ * independent of its neighbors and of its position in the sequence. This is
+ * called "Bernoulli sampling", and it doesn't suffer from any of the problems
+ * mentioned above.
+ *
+ * One disadvantage of Bernoulli sampling is that you can't be sure exactly how
+ * many samples you'll get: technically, it's possible that you might sample
+ * none of them, or all of them. But if the number of events N is large, these
+ * aren't likely outcomes; you can generally expect somewhere around P * N
+ * events to be sampled.
+ *
+ * The other disadvantage of Bernoulli sampling is that you have to generate a
+ * random number for every event, which can be slow.
+ *
+ * [significant pause]
+ *
+ * BUT NOT WITH THIS CLASS! FastBernoulliTrial lets you do true Bernoulli
+ * sampling, while generating a fresh random number only when we do decide to
+ * sample an event, not on every trial. When it decides not to sample, a call to
+ * |FastBernoulliTrial::trial| is nothing but decrementing a counter and
+ * comparing it to zero. So the lower your sampling probability is, the less
+ * overhead FastBernoulliTrial imposes.
+ *
+ * Probabilities of 0 and 1 are handled efficiently. (In neither case need we
+ * ever generate a random number at all.)
+ *
+ * The essential API:
+ *
+ * - FastBernoulliTrial(double P)
+ *   Construct an instance that selects events with probability P.
+ *
+ * - FastBernoulliTrial::trial()
+ *   Return true with probability P. Call this each time an event occurs, to
+ *   decide whether to sample it or not.
+ *
+ * - FastBernoulliTrial::trial(size_t n)
+ *   Equivalent to calling trial() |n| times, and returning true if any of those
+ *   calls do. However, like trial, this runs in fast constant time.
+ *
+ *   What is this good for? In some applications, some events are "bigger" than
+ *   others. For example, large allocations are more significant than small
+ *   allocations. Perhaps we'd like to imagine that we're drawing allocations
+ *   from a stream of bytes, and performing a separate Bernoulli trial on every
+ *   byte from the stream. We can accomplish this by calling |t.trial(S)| for
+ *   the number of bytes S, and sampling the event if that returns true.
+ *
+ *   Of course, this style of sampling needs to be paired with analysis and
+ *   presentation that makes the size of the event apparent, lest trials with
+ *   large values for |n| appear to be indistinguishable from those with small
+ *   values for |n|.
+ */
+class FastBernoulliTrial {
+  /*
+   * This comment should just read, "Generate skip counts with a geometric
+   * distribution", and leave everyone to go look that up and see why it's the
+   * right thing to do, if they don't know already.
+   *
+   * BUT IF YOU'RE CURIOUS, COMMENTS ARE FREE...
+   *
+   * Instead of generating a fresh random number for every trial, we can
+   * randomly generate a count of how many times we should return false before
+   * the next time we return true. We call this a "skip count". Once we've
+   * returned true, we generate a fresh skip count, and begin counting down
+   * again.
+   *
+   * Here's an awesome fact: by exercising a little care in the way we generate
+   * skip counts, we can produce results indistinguishable from those we would
+   * get "rolling the dice" afresh for every trial.
+   *
+   * In short, skip counts in Bernoulli trials of probability P obey a geometric
+   * distribution. If a random variable X is uniformly distributed from [0..1),
+   * then std::floor(std::log(X) / std::log(1-P)) has the appropriate geometric
+   * distribution for the skip counts.
+   *
+   * Why that formula?
+   *
+   * Suppose we're to return |true| with some probability P, say, 0.3. Spread
+   * all possible futures along a line segment of length 1. In portion P of
+   * those cases, we'll return true on the next call to |trial|; the skip count
+   * is 0. For the remaining portion 1-P of cases, the skip count is 1 or more.
+   *
+   * skip:                0                         1 or more
+   *             |------------------^-----------------------------------------|
+   * portion:            0.3                            0.7
+   *                      P                             1-P
+   *
+   * But the "1 or more" section of the line is subdivided the same way: *within
+   * that section*, in portion P the second call to |trial()| returns true, and in
+   * portion 1-P it returns false a second time; the skip count is two or more.
+   * So we return true on the second call in proportion 0.7 * 0.3, and skip at
+   * least the first two in proportion 0.7 * 0.7.
+   *
+   * skip:                0                1              2 or more
+   *             |------------------^------------^----------------------------|
+   * portion:            0.3           0.7 * 0.3          0.7 * 0.7
+   *                      P             (1-P)*P            (1-P)^2
+   *
+   * We can continue to subdivide:
+   *
+   * skip >= 0:  |------------------------------------------------- (1-P)^0 --|
+   * skip >= 1:  |                  ------------------------------- (1-P)^1 --|
+   * skip >= 2:  |                               ------------------ (1-P)^2 --|
+   * skip >= 3:  |                                 ^     ---------- (1-P)^3 --|
+   * skip >= 4:  |                                 .            --- (1-P)^4 --|
+   *                                               .
+   *                                               ^X, see below
+   *
+   * In other words, the likelihood of the next n calls to |trial| returning
+   * false is (1-P)^n. The longer a run we require, the more the likelihood
+   * drops. Further calls may return false too, but this is the probability
+   * we'll skip at least n.
+   *
+   * This is interesting, because we can pick a point along this line segment
+   * and see which skip count's range it falls within; the point X above, for
+   * example, is within the ">= 2" range, but not within the ">= 3" range, so it
+   * designates a skip count of 2. So if we pick points on the line at random
+   * and use the skip counts they fall under, that will be indistinguishable
+   * from generating a fresh random number between 0 and 1 for each trial and
+   * comparing it to P.
+   *
+   * So to find the skip count for a point X, we must ask: To what whole power
+   * must we raise 1-P such that we include X, but the next power would exclude
+   * it? This is exactly std::floor(std::log(X) / std::log(1-P)).
+   *
+   * Our algorithm is then, simply: When constructed, compute an initial skip
+   * count. Return false from |trial| that many times, and then compute a new skip
+   * count.
+   *
+   * For a call to |trial(n)|, if the skip count is greater than n, return false
+   * and subtract n from the skip count. If the skip count is less than n,
+   * return true and compute a new skip count. Since each trial is independent,
+   * it doesn't matter by how much n overshoots the skip count; we can actually
+   * compute a new skip count at *any* time without affecting the distribution.
+   * This is really beautiful.
+   */
+ public:
+  /**
+   * Construct a fast Bernoulli trial generator. Calls to |trial()| return true
+   * with probability |aProbability|. Use |aState0| and |aState1| to seed the
+   * random number generator; both may not be zero.
+   */
+  FastBernoulliTrial(double aProbability, uint64_t aState0, uint64_t aState1)
+   : mGenerator(aState0, aState1)
+  {
+    setProbability(aProbability);
+  }
+
+  /**
+   * Return true with probability |mProbability|. Call this each time an event
+   * occurs, to decide whether to sample it or not. The lower |mProbability| is,
+   * the faster this function runs.
+   */
+  bool trial() {
+    if (mSkipCount) {
+      mSkipCount--;
+      return false;
+    }
+
+    return chooseSkipCount();
+  }
+
+  /**
+   * Equivalent to calling trial() |n| times, and returning true if any of those
+   * calls do. However, like trial, this runs in fast constant time.
+   *
+   * What is this good for? In some applications, some events are "bigger" than
+   * others. For example, large allocations are more significant than small
+   * allocations. Perhaps we'd like to imagine that we're drawing allocations
+   * from a stream of bytes, and performing a separate Bernoulli trial on every
+   * byte from the stream. We can accomplish this by calling |t.trial(S)| for
+   * the number of bytes S, and sampling the event if that returns true.
+   *
+   * Of course, this style of sampling needs to be paired with analysis and
+   * presentation that makes the "size" of the event apparent, lest trials with
+   * large values for |n| appear to be indistinguishable from those with small
+   * values for |n|, despite being potentially much more likely to be sampled.
+   */
+  bool trial(size_t aCount) {
+    if (mSkipCount > aCount) {
+      mSkipCount -= aCount;
+      return false;
+    }
+
+    return chooseSkipCount();
+  }
+
+  void setRandomState(uint64_t aState0, uint64_t aState1) {
+    mGenerator.setState(aState0, aState1);
+  }
+
+  void setProbability(double aProbability) {
+    MOZ_ASSERT(0 <= aProbability && aProbability <= 1);
+    mProbability = aProbability;
+    if (0 < mProbability && mProbability < 1) {
+      /*
+       * Let's look carefully at how this calculation plays out in floating-
+       * point arithmetic. We'll assume IEEE, but the final C++ code we arrive
+       * at would still be fine if our numbers were mathematically perfect. So,
+       * while we've considered IEEE's edge cases, we haven't done anything that
+       * should be actively bad when using other representations.
+       *
+       * (In the below, read comparisons as exact mathematical comparisons: when
+       * we say something "equals 1", that means it's exactly equal to 1. We
+       * treat approximation using intervals with open boundaries: saying a
+       * value is in (0,1) doesn't specify how close to 0 or 1 the value gets.
+       * When we use closed boundaries like [1, 2**-53], we're careful to ensure
+       * the boundary values are actually representable.)
+       *
+       * - After the comparison above, we know mProbability is in (0,1).
+       *
+       * - The gaps below 1 are 2**-53, so that interval is (0, 1-2**-53].
+       *
+       * - Because the floating-point gaps near 1 are wider than those near
+       *   zero, there are many small positive doubles ε such that 1-ε rounds to
+       *   exactly 1. However, 2**-53 can be represented exactly. So
+       *   1-mProbability is in [2**-53, 1].
+       *
+       * - log(1 - mProbability) is thus in (-37, 0].
+       *
+       *   That range includes zero, but when we use mInvLogNotProbability, it
+       *   would be helpful if we could trust that it's negative. So when log(1
+       *   - mProbability) is 0, we'll just set mProbability to 0, so that
+       *   mInvLogNotProbability is not used in chooseSkipCount.
+       *
+       * - How much of the range of mProbability does this cause us to ignore?
+       *   The only value for which log returns 0 is exactly 1; the slope of log
+       *   at 1 is 1, so for small ε such that 1 - ε != 1, log(1 - ε) is -ε,
+       *   never 0. The gaps near one are larger than the gaps near zero, so if
+       *   1 - ε wasn't 1, then -ε is representable. So if log(1 - mProbability)
+       *   isn't 0, then 1 - mProbability isn't 1, which means that mProbability
+       *   is at least 2**-53, as discussed earlier. This is a sampling
+       *   likelihood of roughly one in ten trillion, which is unlikely to be
+       *   distinguishable from zero in practice.
+       *
+       *   So by forbidding zero, we've tightened our range to (-37, -2**-53].
+       *
+       * - Finally, 1 / log(1 - mProbability) is in [-2**53, -1/37). This all
+       *   falls readily within the range of an IEEE double.
+       *
+       * ALL THAT HAVING BEEN SAID: here are the five lines of actual code:
+       */
+      double logNotProbability = std::log(1 - mProbability);
+      if (logNotProbability == 0.0)
+        mProbability = 0.0;
+      else
+        mInvLogNotProbability = 1 / logNotProbability;
+    }
+
+    chooseSkipCount();
+  }
+
+ private:
+  /* The likelihood that any given call to |trial| should return true. */
+  double mProbability;
+
+  /*
+   * The value of 1/std::log(1 - mProbability), cached for repeated use.
+   *
+   * If mProbability is exactly 0 or exactly 1, we don't use this value.
+   * Otherwise, we guarantee this value is in the range [-2**53, -1/37), i.e.
+   * definitely negative, as required by chooseSkipCount. See setProbability for
+   * the details.
+   */
+  double mInvLogNotProbability;
+
+  /* Our random number generator. */
+  non_crypto::XorShift128PlusRNG mGenerator;
+
+  /* The number of times |trial| should return false before next returning true. */
+  size_t mSkipCount;
+
+  /*
+   * Choose the next skip count. This also returns the value that |trial| should
+   * return, since we have to check for the extreme values for mProbability
+   * anyway, and |trial| should never return true at all when mProbability is 0.
+   */
+  bool chooseSkipCount() {
+    /*
+     * If the probability is 1.0, every call to |trial| returns true. Make sure
+     * mSkipCount is 0.
+     */
+    if (mProbability == 1.0) {
+      mSkipCount = 0;
+      return true;
+    }
+
+    /*
+     * If the probabilility is zero, |trial| never returns true. Don't bother us
+     * for a while.
+     */
+    if (mProbability == 0.0) {
+      mSkipCount = SIZE_MAX;
+      return false;
+    }
+
+    /*
+     * What sorts of values can this call to std::floor produce?
+     *
+     * Since mGenerator.nextDouble returns a value in [0, 1-2**-53], std::log
+     * returns a value in the range [-infinity, -2**-53], all negative. Since
+     * mInvLogNotProbability is negative (see its comments), the product is
+     * positive and possibly infinite. std::floor returns +infinity unchanged.
+     * So the result will always be positive.
+     *
+     * Converting a double to an integer that is out of range for that integer
+     * is undefined behavior, so we must clamp our result to SIZE_MAX, to ensure
+     * we get an acceptable value for mSkipCount.
+     *
+     * The clamp is written carefully. Note that if we had said:
+     *
+     *    if (skipCount > SIZE_MAX)
+     *       skipCount = SIZE_MAX;
+     *
+     * that leads to undefined behavior 64-bit machines: SIZE_MAX coerced to
+     * double is 2^64, not 2^64-1, so this doesn't actually set skipCount to a
+     * value that can be safely assigned to mSkipCount.
+     *
+     * Jakub Oleson cleverly suggested flipping the sense of the comparison: if
+     * we require that skipCount < SIZE_MAX, then because of the gaps (2048)
+     * between doubles at that magnitude, the highest double less than 2^64 is
+     * 2^64 - 2048, which is fine to store in a size_t.
+     *
+     * (On 32-bit machines, all size_t values can be represented exactly in
+     * double, so all is well.)
+     */
+    double skipCount = std::floor(std::log(mGenerator.nextDouble())
+                                  * mInvLogNotProbability);
+    if (skipCount < SIZE_MAX)
+      mSkipCount = skipCount;
+    else
+      mSkipCount = SIZE_MAX;
+
+    return true;
+  }
+};
+
+}  /* namespace mozilla */
+
+#endif /* mozilla_FastBernoulliTrial_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -34,16 +34,17 @@ EXPORTS.mozilla = [
     'DebugOnly.h',
     'decimal/Decimal.h',
     'double-conversion/double-conversion.h',
     'double-conversion/utils.h',
     'Endian.h',
     'EnumeratedArray.h',
     'EnumeratedRange.h',
     'EnumSet.h',
+    'FastBernoulliTrial.h',
     'FloatingPoint.h',
     'Function.h',
     'GuardObjects.h',
     'HashFunctions.h',
     'IndexSequence.h',
     'IntegerPrintfMacros.h',
     'IntegerRange.h',
     'IntegerTypeTraits.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestFastBernoulliTrial.cpp
@@ -0,0 +1,209 @@
+/* -*- 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/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/FastBernoulliTrial.h"
+
+#include <math.h>
+
+// Note that because we always provide FastBernoulliTrial with a fixed
+// pseudorandom seed in these tests, the results here are completely
+// deterministic.
+//
+// A non-optimized version of this test runs in .009s on my laptop. Using larger
+// sample sizes lets us meet tighter bounds on the counts.
+
+static void
+TestProportions()
+{
+  mozilla::FastBernoulliTrial bernoulli(1.0,
+                                        698079309544035222ULL,
+                                        6012389156611637584ULL);
+
+  for (size_t i = 0; i < 100; i++)
+    MOZ_RELEASE_ASSERT(bernoulli.trial());
+
+  {
+    bernoulli.setProbability(0.5);
+    size_t count = 0;
+    for (size_t i = 0; i < 1000; i++)
+      count += bernoulli.trial();
+    MOZ_RELEASE_ASSERT(count == 496);
+  }
+
+  {
+    bernoulli.setProbability(0.001);
+    size_t count = 0;
+    for (size_t i = 0; i < 1000; i++)
+      count += bernoulli.trial();
+    MOZ_RELEASE_ASSERT(count == 2);
+  }
+
+  {
+    bernoulli.setProbability(0.85);
+    size_t count = 0;
+    for (size_t i = 0; i < 1000; i++)
+      count += bernoulli.trial();
+    MOZ_RELEASE_ASSERT(count == 852);
+  }
+
+  bernoulli.setProbability(0.0);
+  for (size_t i = 0; i < 100; i++)
+    MOZ_RELEASE_ASSERT(!bernoulli.trial());
+}
+
+static void
+TestHarmonics()
+{
+  mozilla::FastBernoulliTrial bernoulli(0.1,
+                                        698079309544035222ULL,
+                                        6012389156611637584ULL);
+
+  const size_t n = 100000;
+  bool trials[n];
+  for (size_t i = 0; i < n; i++)
+    trials[i] = bernoulli.trial();
+
+  // For each harmonic and phase, check that the proportion sampled is
+  // within acceptable bounds.
+  for (size_t harmonic = 1; harmonic < 20; harmonic++) {
+    size_t expected = n / harmonic / 10;
+    size_t low_expected = expected * 85 / 100;
+    size_t high_expected = expected * 115 / 100;
+
+    for (size_t phase = 0; phase < harmonic; phase++) {
+      size_t count = 0;
+      for (size_t i = phase; i < n; i += harmonic)
+        count += trials[i];
+
+      MOZ_RELEASE_ASSERT(low_expected <= count && count <= high_expected);
+    }
+  }
+}
+
+static void
+TestTrialN()
+{
+  mozilla::FastBernoulliTrial bernoulli(0.01,
+                                        0x67ff17e25d855942ULL,
+                                        0x74f298193fe1c5b1ULL);
+
+  {
+    size_t count = 0;
+    for (size_t i = 0; i < 10000; i++)
+      count += bernoulli.trial(1);
+
+    // Expected value: 0.01 * 10000 == 100
+    MOZ_RELEASE_ASSERT(count == 97);
+  }
+
+  {
+    size_t count = 0;
+    for (size_t i = 0; i < 10000; i++)
+      count += bernoulli.trial(3);
+
+    // Expected value: (1 - (1 - 0.01) ** 3) == 0.0297,
+    // 0.0297 * 10000 == 297
+    MOZ_RELEASE_ASSERT(count == 304);
+  }
+
+  {
+    size_t count = 0;
+    for (size_t i = 0; i < 10000; i++)
+      count += bernoulli.trial(10);
+
+    // Expected value: (1 - (1 - 0.01) ** 10) == 0.0956,
+    // 0.0956 * 10000 == 956
+    MOZ_RELEASE_ASSERT(count == 936);
+  }
+
+  {
+    size_t count = 0;
+    for (size_t i = 0; i < 10000; i++)
+      count += bernoulli.trial(100);
+
+    // Expected value: (1 - (1 - 0.01) ** 100) == 0.6339
+    // 0.6339 * 10000 == 6339
+    MOZ_RELEASE_ASSERT(count == 6372);
+  }
+
+  {
+    size_t count = 0;
+    for (size_t i = 0; i < 10000; i++)
+      count += bernoulli.trial(1000);
+
+    // Expected value: (1 - (1 - 0.01) ** 1000) == 0.9999
+    // 0.9999 * 10000 == 9999
+    MOZ_RELEASE_ASSERT(count == 9998);
+  }
+}
+
+static void
+TestChangeProbability()
+{
+  mozilla::FastBernoulliTrial bernoulli(1.0,
+                                        0x67ff17e25d855942ULL,
+                                        0x74f298193fe1c5b1ULL);
+
+  // Establish a very high skip count.
+  bernoulli.setProbability(0.0);
+
+  // This should re-establish a zero skip count.
+  bernoulli.setProbability(1.0);
+
+  // So this should return true.
+  MOZ_RELEASE_ASSERT(bernoulli.trial());
+}
+
+static void
+TestCuspProbabilities()
+{
+  /*
+   * FastBernoulliTrial takes care to avoid screwing up on edge cases. The
+   * checks here all look pretty dumb, but they exercise paths in the code that
+   * could exhibit undefined behavior if coded naïvely.
+   */
+
+  /*
+   * This should not be perceptibly different from 1; for 64-bit doubles, this
+   * is a one in ten trillion chance of the trial not succeeding. Overflows
+   * converting doubles to size_t skip counts may change this, though.
+   */
+  mozilla::FastBernoulliTrial bernoulli(nextafter(1, 0),
+                                        0x67ff17e25d855942ULL,
+                                        0x74f298193fe1c5b1ULL);
+
+  for (size_t i = 0; i < 1000; i++)
+    MOZ_RELEASE_ASSERT(bernoulli.trial());
+
+  /*
+   * This should not be perceptibly different from 0; for 64-bit doubles,
+   * the FastBernoulliTrial will actually treat this as exactly zero.
+   */
+  bernoulli.setProbability(nextafter(0, 1));
+  for (size_t i = 0; i < 1000; i++)
+    MOZ_RELEASE_ASSERT(!bernoulli.trial());
+
+  /*
+   * This should be a vanishingly low probability which FastBernoulliTrial does
+   * *not* treat as exactly zero.
+   */
+  bernoulli.setProbability(1 - nextafter(1, 0));
+  for (size_t i = 0; i < 1000; i++)
+    MOZ_RELEASE_ASSERT(!bernoulli.trial());
+}
+
+int
+main()
+{
+  TestProportions();
+  TestHarmonics();
+  TestTrialN();
+  TestChangeProbability();
+  TestCuspProbabilities();
+
+  return 0;
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -11,16 +11,17 @@ CppUnitTests([
     'TestBloomFilter',
     'TestCasting',
     'TestCeilingFloor',
     'TestCheckedInt',
     'TestCountPopulation',
     'TestCountZeroes',
     'TestEndian',
     'TestEnumSet',
+    'TestFastBernoulliTrial',
     'TestFloatingPoint',
     'TestFunction',
     'TestIntegerPrintfMacros',
     'TestIntegerRange',
     'TestJSONWriter',
     'TestMacroArgs',
     'TestMacroForEach',
     'TestMathAlgorithms',
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -128,18 +128,16 @@ import android.widget.Toast;
 public class GeckoAppShell
 {
     private static final String LOGTAG = "GeckoAppShell";
     private static final boolean LOGGING = false;
 
     // We have static members only.
     private GeckoAppShell() { }
 
-    private static GeckoEditableListener editableListener;
-
     private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
         @Override
         protected String getAppPackageName() {
             return AppConstants.ANDROID_PACKAGE_NAME;
         }
 
         @Override
         protected Context getAppContext() {
@@ -320,27 +318,16 @@ public class GeckoAppShell
 
     private static LayerView sLayerView;
 
     public static void setLayerView(LayerView lv) {
         if (sLayerView == lv) {
             return;
         }
         sLayerView = lv;
-
-        // We should have a unique GeckoEditable instance per nsWindow instance,
-        // so even though we have a new view here, the underlying nsWindow is the same,
-        // and we don't create a new GeckoEditable.
-        if (editableListener == null) {
-            // Starting up; istall new Gecko-to-Java editable listener.
-            editableListener = new GeckoEditable();
-        } else {
-            // Bind the existing GeckoEditable instance to the new LayerView
-            GeckoAppShell.notifyIMEContext(GeckoEditableListener.IME_STATE_DISABLED, "", "", "");
-        }
     }
 
     @RobocopTarget
     public static LayerView getLayerView() {
         return sLayerView;
     }
 
     /**
@@ -415,41 +402,16 @@ public class GeckoAppShell
      *  The Gecko-side API: API methods that Gecko calls
      */
 
     @WrapForJNI(allowMultithread = true, noThrow = true)
     public static void handleUncaughtException(Thread thread, Throwable e) {
         CRASH_HANDLER.uncaughtException(thread, e);
     }
 
-    @WrapForJNI
-    public static void notifyIME(int type) {
-        if (editableListener != null) {
-            editableListener.notifyIME(type);
-        }
-    }
-
-    @WrapForJNI
-    public static void notifyIMEContext(int state, String typeHint,
-                                        String modeHint, String actionHint) {
-        if (editableListener != null) {
-            editableListener.notifyIMEContext(state, typeHint,
-                                               modeHint, actionHint);
-        }
-    }
-
-    @WrapForJNI
-    public static void notifyIMEChange(String text, int start, int end, int newEnd) {
-        if (newEnd < 0) { // Selection change
-            editableListener.onSelectionChange(start, end);
-        } else { // Text change
-            editableListener.onTextChange(text, start, end, newEnd);
-        }
-    }
-
     private static final Object sEventAckLock = new Object();
     private static boolean sWaitingForEventAck;
 
     // Block the current thread until the Gecko event loop is caught up
     public static void sendEventToGeckoSync(GeckoEvent e) {
         e.setAckNeeded(true);
 
         long time = SystemClock.uptimeMillis();
@@ -2441,17 +2403,17 @@ public class GeckoAppShell
     @WrapForJNI
     @RobocopTarget
     public static boolean isTablet() {
         return HardwareUtils.isTablet();
     }
 
     private static boolean sImeWasEnabledOnLastResize = false;
     public static void viewSizeChanged() {
-        LayerView v = getLayerView();
+        GeckoView v = (GeckoView) getLayerView();
         if (v == null) {
             return;
         }
         boolean imeIsEnabled = v.isIMEEnabled();
         if (imeIsEnabled && !sImeWasEnabledOnLastResize) {
             // The IME just came up after not being up, so let's scroll
             // to the focused input.
             sendEventToGecko(GeckoEvent.createBroadcastEvent(
--- a/mobile/android/base/GeckoEditable.java
+++ b/mobile/android/base/GeckoEditable.java
@@ -11,17 +11,17 @@ import java.lang.reflect.InvocationHandl
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Semaphore;
 
 import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.gfx.InputConnectionHandler;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
 
 import android.os.Handler;
 import android.os.Looper;
 import android.text.Editable;
@@ -343,33 +343,67 @@ final class GeckoEditable
             }
         }
 
         boolean isEmpty() {
             return mActions.isEmpty();
         }
     }
 
+    @WrapForJNI
     GeckoEditable() {
+        if (DEBUG) {
+            // Called by nsWindow.
+            ThreadUtils.assertOnGeckoThread();
+        }
         mActionQueue = new ActionQueue();
         mSavedSelectionStart = -1;
         mUpdateGecko = true;
 
         mText = new SpannableStringBuilder();
         mChangedText = new SpannableStringBuilder();
 
         final Class<?>[] PROXY_INTERFACES = { Editable.class };
         mProxy = (Editable)Proxy.newProxyInstance(
                 Editable.class.getClassLoader(),
                 PROXY_INTERFACES, this);
 
-        LayerView v = GeckoAppShell.getLayerView();
-        mListener = GeckoInputConnection.create(v, this);
+        mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler();
+    }
+
+    @WrapForJNI
+    /* package */ void onViewChange(final GeckoView v) {
+        if (DEBUG) {
+            // Called by nsWindow.
+            ThreadUtils.assertOnGeckoThread();
+            Log.d(LOGTAG, "onViewChange(" + v + ")");
+        }
 
-        mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler();
+        final GeckoEditableListener newListener = GeckoInputConnection.create(v, this);
+        geckoPostToIc(new Runnable() {
+            @Override
+            public void run() {
+                if (DEBUG) {
+                    Log.d(LOGTAG, "onViewChange (set listener)");
+                }
+                // Make sure there are no other things going on
+                mActionQueue.syncWithGecko();
+                mListener = newListener;
+            }
+        });
+
+        ThreadUtils.postToUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (DEBUG) {
+                    Log.d(LOGTAG, "onViewChange (set IC)");
+                }
+                v.setInputConnectionListener((InputConnectionListener) newListener);
+            }
+        });
     }
 
     private boolean onIcThread() {
         return mIcRunHandler.getLooper() == Looper.myLooper();
     }
 
     private void assertOnIcThread() {
         ThreadUtils.assertOnThread(mIcRunHandler.getLooper().getThread(), AssertBehavior.THROW);
@@ -764,17 +798,17 @@ final class GeckoEditable
             for (Object span : spans) {
                 if ((mText.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
                     throw new IllegalStateException("composition not cancelled");
                 }
             }
         }
     }
 
-    @Override
+    @WrapForJNI @Override
     public void notifyIME(final int type) {
         if (DEBUG) {
             // GeckoEditableListener methods should all be called from the Gecko thread
             ThreadUtils.assertOnGeckoThread();
             // NOTIFY_IME_REPLY_EVENT is logged separately, inside geckoActionReply()
             if (type != NOTIFY_IME_REPLY_EVENT) {
                 Log.d(LOGTAG, "notifyIME(" +
                               getConstantName(GeckoEditableListener.class, "NOTIFY_IME_", type) +
@@ -840,45 +874,35 @@ final class GeckoEditable
         } else if (type == NOTIFY_IME_OF_FOCUS) {
             mGeckoFocused = true;
             mSuppressCompositions = false;
             EventDispatcher.getInstance().
                 registerGeckoThreadListener(this, "TextSelection:DraggingHandle");
         }
     }
 
-    @Override
+    @WrapForJNI @Override
     public void notifyIMEContext(final int state, final String typeHint,
-                          final String modeHint, final String actionHint) {
-        // Because we want to be able to bind GeckoEditable to the newest LayerView instance,
-        // this can be called from the Java IC thread in addition to the Gecko thread.
+                                 final String modeHint, final String actionHint) {
         if (DEBUG) {
+            // GeckoEditableListener methods should all be called from the Gecko thread
+            ThreadUtils.assertOnGeckoThread();
             Log.d(LOGTAG, "notifyIMEContext(" +
                           getConstantName(GeckoEditableListener.class, "IME_STATE_", state) +
                           ", \"" + typeHint + "\", \"" + modeHint + "\", \"" + actionHint + "\")");
         }
         geckoPostToIc(new Runnable() {
             @Override
             public void run() {
-                // Make sure there are no other things going on
-                mActionQueue.syncWithGecko();
-                // Set InputConnectionHandler in notifyIMEContext because
-                // GeckoInputConnection.notifyIMEContext calls restartInput() which will invoke
-                // InputConnectionHandler.onCreateInputConnection
-                LayerView v = GeckoAppShell.getLayerView();
-                if (v != null) {
-                    mListener = GeckoInputConnection.create(v, GeckoEditable.this);
-                    v.setInputConnectionHandler((InputConnectionHandler)mListener);
-                    mListener.notifyIMEContext(state, typeHint, modeHint, actionHint);
-                }
+                mListener.notifyIMEContext(state, typeHint, modeHint, actionHint);
             }
         });
     }
 
-    @Override
+    @WrapForJNI @Override
     public void onSelectionChange(final int start, final int end) {
         if (DEBUG) {
             // GeckoEditableListener methods should all be called from the Gecko thread
             ThreadUtils.assertOnGeckoThread();
             Log.d(LOGTAG, "onSelectionChange(" + start + ", " + end + ")");
         }
         if (start < 0 || start > mText.length() || end < 0 || end > mText.length()) {
             Log.e(LOGTAG, "invalid selection notification range: " +
@@ -924,19 +948,19 @@ final class GeckoEditable
         mText.insert(start, newText);
     }
 
     private boolean isSameText(int start, int oldEnd, CharSequence newText) {
         return oldEnd - start == newText.length() &&
                TextUtils.regionMatches(mText, start, newText, 0, oldEnd - start);
     }
 
-    @Override
+    @WrapForJNI @Override
     public void onTextChange(final CharSequence text, final int start,
-                      final int unboundedOldEnd, final int unboundedNewEnd) {
+                             final int unboundedOldEnd, final int unboundedNewEnd) {
         if (DEBUG) {
             // GeckoEditableListener methods should all be called from the Gecko thread
             ThreadUtils.assertOnGeckoThread();
             StringBuilder sb = new StringBuilder("onTextChange(");
             debugAppend(sb, text);
             sb.append(", ").append(start).append(", ")
                 .append(unboundedOldEnd).append(", ")
                 .append(unboundedNewEnd).append(")");
--- a/mobile/android/base/GeckoEditableListener.java
+++ b/mobile/android/base/GeckoEditableListener.java
@@ -1,22 +1,26 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
+
 /**
  * Interface for the Editable to listen on the Gecko thread, as well as for the IC thread to listen
  * to the Editable.
  */
 interface GeckoEditableListener {
     // IME notification type for notifyIME(), corresponding to NotificationToIME enum in Gecko
+    @WrapForJNI
     int NOTIFY_IME_OPEN_VKB = -2;
+    @WrapForJNI
     int NOTIFY_IME_REPLY_EVENT = -1;
     int NOTIFY_IME_OF_FOCUS = 1;
     int NOTIFY_IME_OF_BLUR = 2;
     int NOTIFY_IME_TO_COMMIT_COMPOSITION = 8;
     int NOTIFY_IME_TO_CANCEL_COMPOSITION = 9;
     // IME enabled state for notifyIMEContext()
     int IME_STATE_DISABLED = 0;
     int IME_STATE_ENABLED = 1;
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -68,17 +68,16 @@ public class GeckoEvent {
     // Make sure to keep these values in sync with the enum in
     // AndroidGeckoEvent in widget/android/AndroidJavaWrappers.h
     @JNITarget
     private enum NativeGeckoEvent {
         NATIVE_POKE(0),
         KEY_EVENT(1),
         MOTION_EVENT(2),
         SENSOR_EVENT(3),
-        PROCESS_OBJECT(4),
         LOCATION_EVENT(5),
         IME_EVENT(6),
         SIZE_CHANGED(8),
         APP_BACKGROUNDING(9),
         APP_FOREGROUNDING(10),
         LOAD_URI(12),
         NOOP(15),
         BROADCAST(19),
@@ -154,18 +153,16 @@ public class GeckoEvent {
     public static final int ACTION_MAGNIFY_END = 13;
 
     public static final int ACTION_GAMEPAD_ADDED = 1;
     public static final int ACTION_GAMEPAD_REMOVED = 2;
 
     public static final int ACTION_GAMEPAD_BUTTON = 1;
     public static final int ACTION_GAMEPAD_AXES = 2;
 
-    public static final int ACTION_OBJECT_LAYER_CLIENT = 1;
-
     private final int mType;
     private int mAction;
     private boolean mAckNeeded;
     private long mTime;
     private Point[] mPoints;
     private int[] mPointIndicies;
     private int mPointerIndex; // index of the point that has changed
     private float[] mOrientations;
@@ -217,18 +214,16 @@ public class GeckoEvent {
     private int mHeight;
 
     private int mID;
     private int mGamepadButton;
     private boolean mGamepadButtonPressed;
     private float mGamepadButtonValue;
     private float[] mGamepadValues;
 
-    private Object mObject;
-
     private GeckoEvent(NativeGeckoEvent event) {
         mType = event.value;
     }
 
     public static GeckoEvent createAppBackgroundingEvent() {
         return GeckoEvent.get(NativeGeckoEvent.APP_BACKGROUNDING);
     }
 
@@ -579,23 +574,16 @@ public class GeckoEvent {
             event.mY = s.values[1];
             event.mZ = s.values[2];
             event.mW = s.values[3];
             break;
         }
         return event;
     }
 
-    public static GeckoEvent createObjectEvent(final int action, final Object object) {
-        GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.PROCESS_OBJECT);
-        event.mAction = action;
-        event.mObject = object;
-        return event;
-    }
-
     public static GeckoEvent createLocationEvent(Location l) {
         GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.LOCATION_EVENT);
         event.mLocation = l;
         return event;
     }
 
     public static GeckoEvent createIMEEvent(ImeAction action) {
         GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.IME_EVENT);
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -6,17 +6,16 @@
 package org.mozilla.gecko;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.concurrent.SynchronousQueue;
 
 import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.gfx.InputConnectionHandler;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
 
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -35,17 +34,17 @@ import android.view.inputmethod.BaseInpu
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 
 class GeckoInputConnection
     extends BaseInputConnection
-    implements InputConnectionHandler, GeckoEditableListener {
+    implements InputConnectionListener, GeckoEditableListener {
 
     private static final boolean DEBUG = false;
     protected static final String LOGTAG = "GeckoInputConnection";
 
     private static final String CUSTOM_HANDLER_TEST_METHOD = "testInputConnection";
     private static final String CUSTOM_HANDLER_TEST_CLASS =
         "org.mozilla.gecko.tests.components.GeckoViewComponent$TextInput";
 
@@ -1002,17 +1001,17 @@ final class DebugGeckoInputConnection
                                       GeckoEditableClient editable) {
         super(targetView, editable);
         mCallLevel = new StringBuilder();
     }
 
     public static GeckoEditableListener create(View targetView,
                                                GeckoEditableClient editable) {
         final Class<?>[] PROXY_INTERFACES = { InputConnection.class,
-                InputConnectionHandler.class,
+                InputConnectionListener.class,
                 GeckoEditableListener.class };
         DebugGeckoInputConnection dgic =
                 new DebugGeckoInputConnection(targetView, editable);
         dgic.mProxy = (InputConnection)Proxy.newProxyInstance(
                 GeckoInputConnection.class.getClassLoader(),
                 PROXY_INTERFACES, dgic);
         return (GeckoEditableListener)dgic.mProxy;
     }
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -160,52 +160,64 @@ public class GeckoThread extends Thread 
             throw new UnsupportedOperationException("Cannot make call", e.getCause());
         }
     }
 
     // Queue a call to the given method.
     private static void queueNativeCallLocked(final Class<?> cls, final String methodName,
                                               final Object obj, final Object[] args,
                                               final State state) {
-        final Class<?>[] argTypes = new Class<?>[args.length];
+        final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
+        final ArrayList<Object> argValues = new ArrayList<>(args.length);
+
         for (int i = 0; i < args.length; i++) {
+            if (args[i] instanceof Class) {
+                argTypes.add((Class<?>) args[i]);
+                argValues.add(args[++i]);
+                continue;
+            }
             Class<?> argType = args[i].getClass();
             if (argType == Boolean.class) argType = Boolean.TYPE;
             else if (argType == Byte.class) argType = Byte.TYPE;
             else if (argType == Character.class) argType = Character.TYPE;
             else if (argType == Double.class) argType = Double.TYPE;
             else if (argType == Float.class) argType = Float.TYPE;
             else if (argType == Integer.class) argType = Integer.TYPE;
             else if (argType == Long.class) argType = Long.TYPE;
             else if (argType == Short.class) argType = Short.TYPE;
-            argTypes[i] = argType;
+            argTypes.add(argType);
+            argValues.add(args[i]);
         }
         final Method method;
         try {
-            method = cls.getDeclaredMethod(methodName, argTypes);
+            method = cls.getDeclaredMethod(
+                    methodName, argTypes.toArray(new Class<?>[argTypes.size()]));
         } catch (final NoSuchMethodException e) {
             throw new UnsupportedOperationException("Cannot find method", e);
         }
 
         if (QUEUED_CALLS.size() == 0 && isStateAtLeast(state)) {
-            invokeMethod(method, obj, args);
+            invokeMethod(method, obj, argValues.toArray());
             return;
         }
-        QUEUED_CALLS.add(new QueuedCall(method, obj, args, state));
+
+        QUEUED_CALLS.add(new QueuedCall(
+                method, obj, argValues.toArray(), state));
     }
 
     /**
      * Queue a call to the given static method until Gecko is in the given state.
      *
      * @param state The Gecko state in which the native call could be executed.
      *              Default is State.RUNNING, which means this queued call will
      *              run when Gecko is at or after RUNNING state.
      * @param cls Class that declares the static method.
      * @param methodName Name of the static method.
-     * @param args Args to call the static method with.
+     * @param args Args to call the static method with; to specify a parameter type,
+     *             pass in a Class instance first, followed by the value.
      */
     public static void queueNativeCallUntil(final State state, final Class<?> cls,
                                             final String methodName, final Object... args) {
         synchronized (QUEUED_CALLS) {
             queueNativeCallLocked(cls, methodName, null, args, state);
         }
     }
 
@@ -220,17 +232,18 @@ public class GeckoThread extends Thread 
     }
 
     /**
      * Queue a call to the given instance method until Gecko is in the given state.
      *
      * @param state The Gecko state in which the native call could be executed.
      * @param obj Object that declares the instance method.
      * @param methodName Name of the instance method.
-     * @param args Args to call the instance method with.
+     * @param args Args to call the instance method with; to specify a parameter type,
+     *             pass in a Class instance first, followed by the value.
      */
     public static void queueNativeCallUntil(final State state, final Object obj,
                                             final String methodName, final Object... args) {
         synchronized (QUEUED_CALLS) {
             queueNativeCallLocked(obj.getClass(), methodName, obj, args, state);
         }
     }
 
--- a/mobile/android/base/GeckoView.java
+++ b/mobile/android/base/GeckoView.java
@@ -25,30 +25,36 @@ import org.mozilla.gecko.util.NativeJSOb
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.TypedArray;
 import android.os.Bundle;
+import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
 
 public class GeckoView extends LayerView
     implements ContextGetter {
 
     private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
     private static final String LOGTAG = "GeckoView";
 
     private ChromeDelegate mChromeDelegate;
     private ContentDelegate mContentDelegate;
 
+    private InputConnectionListener mInputConnectionListener;
+
     private final GeckoEventListener mGeckoEventListener = new GeckoEventListener() {
         @Override
         public void handleMessage(final String event, final JSONObject message) {
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         if (event.equals("Gecko:Ready")) {
@@ -104,17 +110,18 @@ public class GeckoView extends LayerView
             } catch (Exception e) {
                 Log.w(LOGTAG, "handleMessage threw for " + event, e);
             }
         }
     };
 
     @WrapForJNI
     private static final class Window extends JNIObject {
-        static native void open(Window instance, int width, int height);
+        static native void open(Window instance, GeckoView view, int width, int height);
+        static native void setLayerClient(Object client);
         @Override protected native void disposeNative();
     }
 
     private final Window window = new Window();
 
     public GeckoView(Context context) {
         super(context);
         init(context, null, true);
@@ -137,18 +144,24 @@ public class GeckoView extends LayerView
             setGeckoInterface(new BaseGeckoInterface(context));
             GeckoAppShell.setContextGetter(this);
         }
 
         // Perform common initialization for Fennec/GeckoView.
         GeckoAppShell.setLayerView(this);
 
         initializeView(EventDispatcher.getInstance());
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createObjectEvent(
-                GeckoEvent.ACTION_OBJECT_LAYER_CLIENT, getLayerClientObject()));
+
+        if (GeckoThread.isStateAtLeast(GeckoThread.State.JNI_READY)) {
+            Window.setLayerClient(getLayerClientObject());
+        } else {
+            GeckoThread.queueNativeCallUntil(GeckoThread.State.JNI_READY,
+                    Window.class, "setLayerClient",
+                    Object.class, getLayerClientObject());
+        }
 
         // TODO: Fennec currently takes care of its own initialization, so this
         // flag is a hack used in Fennec to prevent GeckoView initialization.
         // This should go away once Fennec also uses GeckoView for
         // initialization.
         if (!doInit)
             return;
 
@@ -207,30 +220,101 @@ public class GeckoView extends LayerView
     @Override
     public void onAttachedToWindow()
     {
         super.onAttachedToWindow();
 
         final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            Window.open(window, metrics.widthPixels, metrics.heightPixels);
+            Window.open(window, this, metrics.widthPixels, metrics.heightPixels);
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, Window.class,
-                    "open", window, metrics.widthPixels, metrics.heightPixels);
+                    "open", window, GeckoView.class, this,
+                    metrics.widthPixels, metrics.heightPixels);
         }
     }
 
     @Override
     public void onDetachedFromWindow()
     {
         super.onDetachedFromWindow();
         window.disposeNative();
     }
 
+    /* package */ void setInputConnectionListener(final InputConnectionListener icl) {
+        mInputConnectionListener = icl;
+    }
+
+    @Override
+    public Handler getHandler() {
+        if (mInputConnectionListener != null) {
+            return mInputConnectionListener.getHandler(super.getHandler());
+        }
+        return super.getHandler();
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        if (mInputConnectionListener != null) {
+            return mInputConnectionListener.onCreateInputConnection(outAttrs);
+        }
+        return null;
+    }
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (super.onKeyPreIme(keyCode, event)) {
+            return true;
+        }
+        return mInputConnectionListener != null &&
+                mInputConnectionListener.onKeyPreIme(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (super.onKeyUp(keyCode, event)) {
+            return true;
+        }
+        return mInputConnectionListener != null &&
+                mInputConnectionListener.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (super.onKeyDown(keyCode, event)) {
+            return true;
+        }
+        return mInputConnectionListener != null &&
+                mInputConnectionListener.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+        if (super.onKeyLongPress(keyCode, event)) {
+            return true;
+        }
+        return mInputConnectionListener != null &&
+                mInputConnectionListener.onKeyLongPress(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+        if (super.onKeyMultiple(keyCode, repeatCount, event)) {
+            return true;
+        }
+        return mInputConnectionListener != null &&
+                mInputConnectionListener.onKeyMultiple(keyCode, repeatCount, event);
+    }
+
+    /* package */ boolean isIMEEnabled() {
+        return mInputConnectionListener != null &&
+                mInputConnectionListener.isIMEEnabled();
+    }
+
     /**
     * Add a Browser to the GeckoView container.
     * @param url The URL resource to load into the new Browser.
     */
     public Browser addBrowser(String url) {
         Tab tab = Tabs.getInstance().loadUrl(url, Tabs.LOADURL_NEW_TAB);
         if (tab != null) {
             return new Browser(tab.getId());
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/InputConnectionListener.java
@@ -0,0 +1,25 @@
+/* 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/. */
+
+package org.mozilla.gecko;
+
+import android.os.Handler;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+/**
+ * Interface for interacting with GeckoInputConnection from GeckoView.
+ */
+interface InputConnectionListener
+{
+    Handler getHandler(Handler defHandler);
+    InputConnection onCreateInputConnection(EditorInfo outAttrs);
+    boolean onKeyPreIme(int keyCode, KeyEvent event);
+    boolean onKeyDown(int keyCode, KeyEvent event);
+    boolean onKeyLongPress(int keyCode, KeyEvent event);
+    boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
+    boolean onKeyUp(int keyCode, KeyEvent event);
+    boolean isIMEEnabled();
+}
deleted file mode 100644
--- a/mobile/android/base/gfx/InputConnectionHandler.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/* 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/. */
-
-package org.mozilla.gecko.gfx;
-
-import android.os.Handler;
-import android.view.KeyEvent;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-public interface InputConnectionHandler
-{
-    Handler getHandler(Handler defHandler);
-    InputConnection onCreateInputConnection(EditorInfo outAttrs);
-    boolean onKeyPreIme(int keyCode, KeyEvent event);
-    boolean onKeyDown(int keyCode, KeyEvent event);
-    boolean onKeyLongPress(int keyCode, KeyEvent event);
-    boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
-    boolean onKeyUp(int keyCode, KeyEvent event);
-    boolean isIMEEnabled();
-}
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -26,43 +26,39 @@ import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
-import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
 import android.view.InputDevice;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
 /**
  * A view rendered by the layer compositor.
  */
 public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener {
     private static final String LOGTAG = "GeckoLayerView";
 
     private GeckoLayerClient mLayerClient;
     private PanZoomController mPanZoomController;
     private DynamicToolbarAnimator mToolbarAnimator;
     private final GLController mGLController;
-    private InputConnectionHandler mInputConnectionHandler;
     private LayerRenderer mRenderer;
     /* Must be a PAINT_xxx constant */
     private int mPaintState;
     private int mBackgroundColor;
     private FullScreenState mFullScreenState;
 
     private SurfaceView mSurfaceView;
     private TextureView mTextureView;
@@ -131,17 +127,16 @@ public class LayerView extends ScrollVie
         if (mOverscroll != null) {
             mLayerClient.setOverscrollHandler(mOverscroll);
         }
 
         mPanZoomController = mLayerClient.getPanZoomController();
         mToolbarAnimator = mLayerClient.getDynamicToolbarAnimator();
 
         mRenderer = new LayerRenderer(this);
-        mInputConnectionHandler = null;
 
         setFocusable(true);
         setFocusableInTouchMode(true);
 
         GeckoAccessibility.setDelegate(this);
         GeckoAccessibility.setAccessibilityStateChangeListener(getContext());
     }
 
@@ -359,81 +354,21 @@ public class LayerView extends ScrollVie
     public void setZoomConstraints(ZoomConstraints constraints) {
         mLayerClient.setZoomConstraints(constraints);
     }
 
     public void setIsRTL(boolean aIsRTL) {
         mLayerClient.setIsRTL(aIsRTL);
     }
 
-    public void setInputConnectionHandler(InputConnectionHandler inputConnectionHandler) {
-        mInputConnectionHandler = inputConnectionHandler;
-    }
-
-    @Override
-    public Handler getHandler() {
-        if (mInputConnectionHandler != null)
-            return mInputConnectionHandler.getHandler(super.getHandler());
-        return super.getHandler();
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        if (mInputConnectionHandler != null)
-            return mInputConnectionHandler.onCreateInputConnection(outAttrs);
-        return null;
-    }
-
-    @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyPreIme(keyCode, event)) {
-            return true;
-        }
-        return false;
-    }
-
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (mPanZoomController != null && mPanZoomController.onKeyEvent(event)) {
             return true;
         }
-        if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyDown(keyCode, event)) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-        if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyLongPress(keyCode, event)) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
-        if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event)) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyUp(keyCode, event)) {
-            return true;
-        }
-        return false;
-    }
-
-    public boolean isIMEEnabled() {
-        if (mInputConnectionHandler != null) {
-            return mInputConnectionHandler.isIMEEnabled();
-        }
         return false;
     }
 
     public void requestRender() {
         if (mListener != null) {
             mListener.renderRequested();
         }
     }
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -283,17 +283,16 @@ gbjar.sources += [
     'gfx/DisplayPortMetrics.java',
     'gfx/DrawTimingQueue.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
     'gfx/GeckoLayerClient.java',
     'gfx/GLController.java',
     'gfx/ImmutableViewportMetrics.java',
-    'gfx/InputConnectionHandler.java',
     'gfx/IntSize.java',
     'gfx/JavaPanZoomController.java',
     'gfx/Layer.java',
     'gfx/LayerRenderer.java',
     'gfx/LayerView.java',
     'gfx/NativePanZoomController.java',
     'gfx/Overscroll.java',
     'gfx/OverscrollEdgeEffect.java',
@@ -376,16 +375,17 @@ gbjar.sources += [
     'home/TabMenuStrip.java',
     'home/TabMenuStripLayout.java',
     'home/TopSitesGridItemView.java',
     'home/TopSitesGridView.java',
     'home/TopSitesPanel.java',
     'home/TopSitesThumbnailView.java',
     'home/TransitionAwareCursorLoaderCallbacks.java',
     'home/TwoLinePageRow.java',
+    'InputConnectionListener.java',
     'InputMethods.java',
     'IntentHelper.java',
     'javaaddons/JavaAddonManager.java',
     'javaaddons/JavaAddonManagerV1.java',
     'LayoutInterceptor.java',
     'LocaleManager.java',
     'Locales.java',
     'lwt/LightweightTheme.java',
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -2881,17 +2881,20 @@ Http2Session::ProcessSlowConsumer(Http2S
   nsresult rv = slowConsumer->WriteSegments(this, count, countWritten);
   mSegmentWriter = nullptr;
   LOG3(("Http2Session::ProcessSlowConsumer Writesegments %p 0x%X rv %X %d\n",
         this, slowConsumer->StreamID(), rv, *countWritten));
   if (NS_SUCCEEDED(rv) && !*countWritten && slowConsumer->RecvdFin()) {
     rv = NS_BASE_STREAM_CLOSED;
   }
 
-  if (NS_SUCCEEDED(rv)) {
+  if (NS_SUCCEEDED(rv) && (*countWritten > 0)) {
+    // There have been buffered bytes successfully fed into the
+    // formerly blocked consumer. Repeat until buffer empty or
+    // consumer is blocked again.
     UpdateLocalRwin(slowConsumer, 0);
     ConnectSlowConsumer(slowConsumer);
   }
 
   if (rv == NS_BASE_STREAM_CLOSED) {
     CleanupStream(slowConsumer, NS_OK, CANCEL_ERROR);
     rv = NS_OK;
   }
@@ -3184,17 +3187,18 @@ Http2Session::OnWriteSegment(char *buf,
   nsresult rv;
 
   if (!mSegmentWriter) {
     // the only way this could happen would be if Close() were called on the
     // stack with WriteSegments()
     return NS_ERROR_FAILURE;
   }
 
-  if (mDownstreamState == NOT_USING_NETWORK) {
+  if (mDownstreamState == NOT_USING_NETWORK ||
+      mDownstreamState == BUFFERING_FRAME_HEADER) {
     return NS_BASE_STREAM_WOULD_BLOCK;
   }
 
   if (mDownstreamState == PROCESSING_DATA_FRAME) {
 
     if (mInputFrameFinal &&
         mInputFrameDataRead == mInputFrameDataSize) {
       *countWritten = 0;
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -319,18 +319,30 @@ Http2Stream::WriteSegments(nsAHttpSegmen
   mSegmentWriter = writer;
   nsresult rv = mTransaction->WriteSegments(this, count, countWritten);
 
   if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
     // consuming transaction won't take data. but we need to read it into a buffer so that it
     // won't block other streams. but we should not advance the flow control window
     // so that we'll eventually push back on the sender.
 
+    // with tunnels you need to make sure that this is an underlying connction established
+    // that can be meaningfully giving this signal
+    bool doBuffer = true;
+    if (mIsTunnel) {
+      nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
+      if (qiTrans) {
+        doBuffer = qiTrans->ConnectedReadyForInput();
+      }
+    }
     // stash this data
-    rv = BufferInput(count, countWritten);
+    if (doBuffer) {
+      rv = BufferInput(count, countWritten);
+      LOG3(("Http2Stream::WriteSegments %p Buffered %X %d\n", this, rv, *countWritten));
+    }
   }
   mSegmentWriter = nullptr;
   return rv;
 }
 
 nsresult
 Http2Stream::MakeOriginURL(const nsACString &origin, nsRefPtr<nsStandardURL> &url)
 {
--- a/netwerk/protocol/http/TunnelUtils.cpp
+++ b/netwerk/protocol/http/TunnelUtils.cpp
@@ -1258,16 +1258,22 @@ SpdyConnectTransaction::WriteSegments(ns
        "goodput %p out %llu\n", this, mTunneledConn.get(),
        mTunneledConn->ContentBytesWritten()));
   if (NS_SUCCEEDED(rv) && !mTunneledConn->ContentBytesWritten()) {
     mTunnelStreamOut->AsyncWait(mTunnelStreamOut->mCallback, 0, 0, nullptr);
   }
   return rv;
 }
 
+bool
+SpdyConnectTransaction::ConnectedReadyForInput()
+{
+  return mTunneledConn && mTunnelStreamIn->mCallback;
+}
+
 nsHttpRequestHead *
 SpdyConnectTransaction::RequestHead()
 {
   return mRequestHead;
 }
 
 void
 SpdyConnectTransaction::Close(nsresult code)
--- a/netwerk/protocol/http/TunnelUtils.h
+++ b/netwerk/protocol/http/TunnelUtils.h
@@ -196,16 +196,20 @@ public:
 
   nsresult ReadSegments(nsAHttpSegmentReader *reader,
                         uint32_t count, uint32_t *countRead) override final;
   nsresult WriteSegments(nsAHttpSegmentWriter *writer,
                          uint32_t count, uint32_t *countWritten) override final;
   nsHttpRequestHead *RequestHead() override final;
   void Close(nsresult reason) override final;
 
+  // ConnectedReadyForInput() tests whether the spdy connect transaction is attached to
+  // an nsHttpConnection that can properly deal with flow control, etc..
+  bool ConnectedReadyForInput();
+
 private:
   friend class InputStreamShim;
   friend class OutputStreamShim;
 
   nsresult Flush(uint32_t count, uint32_t *countRead);
   void CreateShimError(nsresult code);
 
   nsCString             mConnectString;
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -2109,19 +2109,27 @@ nsHttpConnection::GetInterface(const nsI
         return callbacks->GetInterface(iid, result);
     return NS_ERROR_NO_INTERFACE;
 }
 
 void
 nsHttpConnection::CheckForTraffic(bool check)
 {
     if (check) {
+        LOG((" CheckForTraffic conn %p\n", this));
         if (mSpdySession) {
-            // Send a ping to verify it is still alive
-            mSpdySession->SendPing();
+            if (PR_IntervalToMilliseconds(IdleTime()) >= 500) {
+                // Send a ping to verify it is still alive if it has been idle
+                // more than half a second, the network changed events are
+                // rate-limited to one per 1000 ms.
+                LOG((" SendPing\n"));
+                mSpdySession->SendPing();
+            } else {
+                LOG((" SendPing skipped due to network activity\n"));
+            }
         } else {
             // If not SPDY, Store snapshot amount of data right now
             mTrafficCount = mTotalBytesWritten + mTotalBytesRead;
             mTrafficStamp = true;
         }
     } else {
         // mark it as not checked
         mTrafficStamp = false;
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -535,17 +535,17 @@ nsHttpTransaction::OnTransportStatus(nsI
 
     // If the timing is enabled, and we are not using a persistent connection
     // then the requestStart timestamp will be null, so we mark the timestamps
     // for domainLookupStart/End and connectStart/End
     // If we are using a persistent connection they will remain null,
     // and the correct value will be returned in nsPerformance.
     if (TimingEnabled() && GetRequestStart().IsNull()) {
         if (status == NS_NET_STATUS_RESOLVING_HOST) {
-            SetDomainLookupStart(TimeStamp::Now());
+            SetDomainLookupStart(TimeStamp::Now(), true);
         } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
             SetDomainLookupEnd(TimeStamp::Now());
         } else if (status == NS_NET_STATUS_CONNECTING_TO) {
             SetConnectStart(TimeStamp::Now());
         } else if (status == NS_NET_STATUS_CONNECTED_TO) {
             SetConnectEnd(TimeStamp::Now());
         }
     }
--- a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp
+++ b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp
@@ -31,16 +31,20 @@
 
 #ifdef MOZ_WIDGET_GONK
 #include <cutils/properties.h>
 #endif
 
 /* a shorter name that better explains what it does */
 #define EINTR_RETRY(x) MOZ_TEMP_FAILURE_RETRY(x)
 
+// period during which to absorb subsequent network change events, in
+// milliseconds
+static const unsigned int kNetworkChangeCoalescingPeriod  = 1000;
+
 using namespace mozilla;
 
 static PRLogModuleInfo *gNotifyAddrLog = nullptr;
 #define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args)
 
 #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
 
 NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
@@ -48,16 +52,17 @@ NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
                   nsIRunnable,
                   nsIObserver)
 
 nsNotifyAddrListener::nsNotifyAddrListener()
     : mLinkUp(true)  // assume true by default
     , mStatusKnown(false)
     , mAllowChangedEvent(true)
     , mChildThreadShutdown(false)
+    , mCoalescingActive(false)
 {
     mShutdownPipe[0] = -1;
     mShutdownPipe[1] = -1;
 }
 
 nsNotifyAddrListener::~nsNotifyAddrListener()
 {
     MOZ_ASSERT(!mThread, "nsNotifyAddrListener thread shutdown failed")