Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 23 Apr 2015 17:10:28 -0400
changeset 240781 22a157f7feb7bec54cff9bfcf61f6e96ec2903d0
parent 240718 3a9891b7ffffd6f3b936ac232456eac7b4956a15 (current diff)
parent 240780 9fa01890600fbf0218a2f5a295057655e7e32707 (diff)
child 240793 c6d66d6b26061f413701bb40d1b8006a9a222b0d
child 240813 0db6f8666c53d59e3e19a288e2c2bd7d8ddcd49a
child 240864 2a1ddf08a4b4ee8914e99139efcaa64b11318c0b
push id28644
push userryanvm@gmail.com
push dateThu, 23 Apr 2015 21:10:29 +0000
treeherdermozilla-central@22a157f7feb7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
22a157f7feb7 / 40.0a1 / 20150424030204 / files
nightly linux64
22a157f7feb7 / 40.0a1 / 20150424030204 / files
nightly mac
22a157f7feb7 / 40.0a1 / 20150424030204 / files
nightly win32
22a157f7feb7 / 40.0a1 / 20150424030204 / files
nightly win64
22a157f7feb7 / 40.0a1 / 20150424030204 / 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 inbound to m-c. a=merge
browser/base/content/browser.js
dom/workers/test/serviceworkers/test_workerUnregister.html
dom/workers/test/serviceworkers/test_workerUpdate.html
dom/workers/test/serviceworkers/unregister/unregister.html
dom/workers/test/serviceworkers/workerUpdate/update.html
dom/workers/test/serviceworkers/worker_unregister.js
dom/workers/test/serviceworkers/worker_update.js
layout/reftests/transform-3d/animate-cube-ref.html
layout/reftests/transform-3d/animate-cube-zoom-ref.html
layout/reftests/transform-3d/animate-cube-zoom.html
layout/reftests/transform-3d/animate-cube.html
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -948,16 +948,17 @@ var gBrowserInit = {
     BrowserOnClick.init();
     DevEdition.init();
     AboutPrivateBrowsingListener.init();
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
     mm.loadFrameScript("chrome://browser/content/content.js", true);
     mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
+    mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);
 
     window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
 
     // initialize observers and listeners
     // and give C++ access to gBrowser
     XULBrowserWindow.init();
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(nsIWebNavigation)
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -158,16 +158,53 @@ class BlobURLsReporter final : public ns
 
     blob = do_QueryInterface(aInfo->mObject);
     if (blob) {
       envp->mRefCounts.Put(blob, envp->mRefCounts.Get(blob) + 1);
     }
     return PL_DHASH_NEXT;
   }
 
+  static void BuildPath(nsAutoCString& path,
+                        nsCStringHashKey::KeyType aKey,
+                        DataInfo* aInfo,
+                        bool anonymize)
+  {
+    nsCOMPtr<nsIURI> principalURI;
+    nsAutoCString url, owner;
+    if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) &&
+        principalURI != nullptr &&
+        NS_SUCCEEDED(principalURI->GetSpec(owner)) &&
+        !owner.IsEmpty()) {
+      owner.ReplaceChar('/', '\\');
+      path += "owner(";
+      if (anonymize) {
+        path += "<anonymized>";
+      } else {
+        path += owner;
+      }
+      path += ")";
+    } else {
+      path += "owner unknown";
+    }
+    path += "/";
+    if (anonymize) {
+      path += "<anonymized-stack>";
+    } else {
+      path += aInfo->mStack;
+    }
+    url = aKey;
+    url.ReplaceChar('/', '\\');
+    if (anonymize) {
+      path += "<anonymized-url>";
+    } else {
+      path += url;
+    }
+  }
+
   static PLDHashOperator ReportCallback(nsCStringHashKey::KeyType aKey,
                                         DataInfo* aInfo,
                                         void* aUserArg)
   {
     EnumArg* envp = static_cast<EnumArg*>(aUserArg);
     nsCOMPtr<nsIDOMBlob> blob;
 
     blob = do_QueryInterface(aInfo->mObject);
@@ -190,44 +227,18 @@ class BlobURLsReporter final : public ns
 
       if (isMemoryFile) {
         if (NS_FAILED(blob->GetSize(&size))) {
           size = 0;
         }
       }
 
       path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/";
-      if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) &&
-          principalURI != nullptr &&
-          NS_SUCCEEDED(principalURI->GetSpec(owner)) &&
-          !owner.IsEmpty()) {
-        owner.ReplaceChar('/', '\\');
-        path += "owner(";
-        if (envp->mAnonymize) {
-          path += "<anonymized>";
-        } else {
-          path += owner;
-        }
-        path += ")";
-      } else {
-        path += "owner unknown";
-      }
-      path += "/";
-      if (envp->mAnonymize) {
-        path += "<anonymized-stack>";
-      } else {
-        path += aInfo->mStack;
-      }
-      url = aKey;
-      url.ReplaceChar('/', '\\');
-      if (envp->mAnonymize) {
-        path += "<anonymized-url>";
-      } else {
-        path += url;
-      }
+      BuildPath(path, aKey, aInfo, envp->mAnonymize);
+
       if (refCount > 1) {
         nsAutoCString addrStr;
 
         addrStr = "0x";
         addrStr.AppendInt((uint64_t)(nsIDOMBlob*)blob, 16);
 
         path += " ";
         path.AppendInt(refCount);
@@ -263,17 +274,37 @@ class BlobURLsReporter final : public ns
         envp->mCallback->Callback(EmptyCString(),
             path,
             KIND_OTHER,
             UNITS_COUNT,
             1,
             descString,
             envp->mData);
       }
+    } else {
+      // Just report the path for the DOMMediaStream or MediaSource.
+      nsCOMPtr<mozilla::dom::MediaSource> ms(do_QueryInterface(aInfo->mObject));
+      nsAutoCString path;
+      path = ms ? "media-source-urls/" : "dom-media-stream-urls/";
+      BuildPath(path, aKey, aInfo, envp->mAnonymize);
+
+      NS_NAMED_LITERAL_CSTRING
+        (desc, "An object URL allocated with URL.createObjectURL; the referenced "
+               "data cannot be freed until all URLs for it have been explicitly "
+               "invalidated with URL.revokeObjectURL.");
+
+      envp->mCallback->Callback(EmptyCString(),
+          path,
+          KIND_OTHER,
+          UNITS_COUNT,
+          1,
+          desc,
+          envp->mData);
     }
+
     return PL_DHASH_NEXT;
   }
 };
 
 NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
 
 }
 
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -3190,16 +3190,26 @@ nsObjectLoadingContent::ShouldPlay(Fallb
   if (!sPrefsInitialized) {
     Preferences::AddUintVarCache(&sSessionTimeoutMinutes,
                                  "plugin.sessionPermissionNow.intervalInMinutes", 60);
     Preferences::AddUintVarCache(&sPersistentTimeoutDays,
                                  "plugin.persistentPermissionAlways.intervalInDays", 90);
     sPrefsInitialized = true;
   }
 
+  if (XRE_GetProcessType() == GeckoProcessType_Default &&
+      (Preferences::GetBool("browser.tabs.remote.autostart", false) ||
+       Preferences::GetBool("browser.tabs.remote.autostart.1", false) ||
+       Preferences::GetBool("browser.tabs.remote.autostart.2", false))) {
+    // Plugins running OOP from the chrome process along with plugins running
+    // OOP from the content process will hang. Let's prevent that situation.
+    aReason = eFallbackDisabled;
+    return false;
+  }
+
   nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
 
   nsCOMPtr<nsIPluginPlayPreviewInfo> playPreviewInfo;
   bool isPlayPreviewSpecified = NS_SUCCEEDED(pluginHost->GetPlayPreviewInfo(
     mContentType, getter_AddRefs(playPreviewInfo)));
   if (isPlayPreviewSpecified) {
     // Checking PlayPreview whitelist as well.
     nsCString uriSpec, baseSpec;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -969,16 +969,25 @@ DOMInterfaces = {
     'headerFile': 'mozilla/dom/workers/bindings/ServiceWorker.h',
 },
 
 'ServiceWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'workers': True,
 },
 
+'ServiceWorkerRegistration': [{
+    'nativeType': 'mozilla::dom::ServiceWorkerRegistrationMainThread',
+    'headerFile': 'mozilla/dom/ServiceWorkerRegistration.h',
+}, {
+    'workers': True,
+    'nativeType': 'mozilla::dom::ServiceWorkerRegistrationWorkerThread',
+    'headerFile': 'mozilla/dom/ServiceWorkerRegistration.h',
+}],
+
 'SharedWorker': {
     'nativeType': 'mozilla::dom::workers::SharedWorker',
     'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
     'implicitJSContext': [ 'constructor' ],
 },
 
 'SharedWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -471,16 +471,17 @@ class CGDOMJSClass(CGThing):
               { "${name}",
                 ${flags},
                 ${addProperty}, /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 ${enumerate}, /* enumerate */
                 ${resolve}, /* resolve */
+                nullptr,               /* mayResolve */
                 nullptr,               /* convert */
                 ${finalize}, /* finalize */
                 ${call}, /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 ${trace}, /* trace */
                 JS_NULL_CLASS_SPEC,
                 $*{classExtensionAndObjectOps}
@@ -611,16 +612,17 @@ class CGPrototypeJSClass(CGThing):
                 "${name}Prototype",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 nullptr,               /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 nullptr,               /* enumerate */
                 nullptr,               /* resolve */
+                nullptr,               /* mayResolve */
                 nullptr,               /* convert */
                 nullptr,               /* finalize */
                 nullptr,               /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 nullptr,               /* trace */
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
@@ -704,16 +706,17 @@ class CGInterfaceObjectJSClass(CGThing):
                 "Function",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 nullptr,               /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 nullptr,               /* enumerate */
                 nullptr,               /* resolve */
+                nullptr,               /* mayResolve */
                 nullptr,               /* convert */
                 nullptr,               /* finalize */
                 ${ctorname}, /* call */
                 ${hasInstance}, /* hasInstance */
                 ${ctorname}, /* construct */
                 nullptr,               /* trace */
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -20120,16 +20120,17 @@ const JSClass CreateIndexOp::ThreadLocal
   "IndexedDBTransactionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   /* addProperty */ nullptr,
   /* delProperty */ nullptr,
   /* getProperty */ nullptr,
   /* setProperty */ nullptr,
   /* enumerate */ nullptr,
   /* resolve */ nullptr,
+  /* mayResolve */ nullptr,
   /* convert */ nullptr,
   /* finalize */ nullptr,
   /* call */ nullptr,
   /* hasInstance */ nullptr,
   /* construct */ nullptr,
   /* trace */ JS_GlobalObjectTraceHook
 };
 
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -28,17 +28,17 @@ interface nsIServiceWorkerInfo : nsISupp
   readonly attribute DOMString scope;
   readonly attribute DOMString scriptSpec;
   readonly attribute DOMString currentWorkerURL;
 
   readonly attribute DOMString activeCacheName;
   readonly attribute DOMString waitingCacheName;
 };
 
-[scriptable, builtinclass, uuid(c05b3b45-7f39-458c-8097-afafc7d69b01)]
+[scriptable, builtinclass, uuid(ff6e13ae-ae34-4941-a81e-a82f3e0e7b6b)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
@@ -71,20 +71,16 @@ interface nsIServiceWorkerManager : nsIS
 
   // Returns true if a given document is currently controlled by a ServiceWorker
   bool isControlled(in nsIDocument aDocument);
 
   // Cause a fetch event to be dispatched to the worker global associated with the given document.
   void dispatchFetchEvent(in nsIDocument aDoc, in nsIInterceptedChannel aChannel,
                           in boolean aIsReload);
 
-  // aTarget MUST be a ServiceWorkerRegistration.
-  [noscript] void AddRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
-  [noscript] void RemoveRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
-
   /**
    * Call this to request that document `aDoc` be controlled by a ServiceWorker
    * if a registration exists for it's scope.
    *
    * This MUST only be called once per document!
    */
   [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
 
@@ -106,19 +102,19 @@ interface nsIServiceWorkerManager : nsIS
   [noscript] nsISupports GetActive(in nsIDOMWindow aWindow, in DOMString aScope);
 
   /*
    * Returns a ServiceWorker.
    */
   [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow);
 
   /*
-   * This implements the update algorithm.
+   * This implements the soft update algorithm.
    */
-  void update(in DOMString aScope);
+  void softUpdate(in DOMString aScope);
 
   // Testing
   DOMString getScopeForUrl(in DOMString path);
 
   // This is meant to be used only by about:serviceworkers. It returns an array
   // of nsIServiceWorkerInfo.
   nsIArray getAllRegistrations();
 
--- a/dom/ipc/jar.mn
+++ b/dom/ipc/jar.mn
@@ -4,11 +4,12 @@
 
 toolkit.jar:
         content/global/test-ipc.xul (test.xul)
         content/global/remote-test-ipc.js (remote-test.js)
         content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
         content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
         content/global/BrowserElementPanning.js (../browser-element/BrowserElementPanning.js)
 *       content/global/BrowserElementPanningAPZDisabled.js (../browser-element/BrowserElementPanningAPZDisabled.js)
+        content/global/manifestMessages.js (manifestMessages.js)
         content/global/PushServiceChildPreload.js (../push/PushServiceChildPreload.js)
         content/global/preload.js (preload.js)
         content/global/post-fork-preload.js (post-fork-preload.js)
new file mode 100644
--- /dev/null
+++ b/dom/ipc/manifestMessages.js
@@ -0,0 +1,119 @@
+/* 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/.*/
+/*
+ * Manifest obtainer frame script implementation of:
+ * http://w3c.github.io/manifest/#obtaining
+ *
+ * It searches a top-level browsing context for
+ * a <link rel=manifest> element. Then fetches
+ * and processes the linked manifest.
+ *
+ * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
+ * exported ManifestObtainer
+ */
+/*globals content, ManifestProcessor, XPCOMUtils, sendAsyncMessage, addMessageListener, Components*/
+'use strict';
+const {
+  utils: Cu
+} = Components;
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+XPCOMUtils.defineLazyModuleGetter(this, 'ManifestProcessor',
+  'resource://gre/modules/ManifestProcessor.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'ManifestObtainer',
+  'resource://gre/modules/ManifestObtainer.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'BrowserUtils',
+  'resource://gre/modules/BrowserUtils.jsm');
+
+addMessageListener('DOM:ManifestObtainer:Obtain', (aMsg) => {
+  fetchManifest()
+    .then(
+      manifest => sendAsyncMessage('DOM:ManifestObtainer:Obtain', {
+        success: true,
+        result: manifest,
+        msgId: aMsg.data.msgId
+      }),
+      error => sendAsyncMessage('DOM:ManifestObtainer:Obtain', {
+        success: false,
+        result: cloneError(error),
+        msgId: aMsg.data.msgId
+      })
+    );
+});
+
+function cloneError(aError) {
+  const clone = {
+    'fileName': String(aError.fileName),
+    'lineNumber': String(aError.lineNumber),
+    'columnNumber': String(aError.columnNumber),
+    'stack': String(aError.stack),
+    'message': String(aError.message),
+    'name': String(aError.name)
+  };
+  return clone;
+}
+
+function fetchManifest() {
+  const manifestQuery = 'link[rel~="manifest"]';
+  return new Promise((resolve, reject) => {
+    if (!content || content.top !== content) {
+      let msg = 'Content window must be a top-level browsing context.';
+      return reject(new Error(msg));
+    }
+    const elem = content.document.querySelector(manifestQuery);
+    if (!elem || !elem.getAttribute('href')) {
+      let msg = 'No manifest to fetch.';
+      return reject(new Error(msg));
+    }
+    // Will throw on "about:blank" and possibly other invalid URIs.
+    const manifestURL = new content.URL(elem.href, elem.baseURI);
+    const reqInit = {};
+    switch (elem.crossOrigin) {
+      case 'use-credentials':
+        reqInit.credentials = 'include';
+        reqInit.mode = 'cors';
+        break;
+      case 'anonymous':
+        reqInit.credentials = 'omit';
+        reqInit.mode = 'cors';
+        break;
+      default:
+        reqInit.credentials = 'same-origin';
+        reqInit.mode = 'no-cors';
+        break;
+    }
+    const req = new content.Request(manifestURL, reqInit);
+    req.setContext('manifest');
+    content
+      .fetch(req)
+      .then(resp => processResponse(resp, content))
+      .then(resolve)
+      .catch(reject);
+  });
+}
+
+function processResponse(aResp, aContentWindow) {
+  const manifestURL = aResp.url;
+  return new Promise((resolve, reject) => {
+    const badStatus = aResp.status < 200 || aResp.status >= 300;
+    if (aResp.type === 'error' || badStatus) {
+      let msg =
+        `Fetch error: ${aResp.status} - ${aResp.statusText} at ${aResp.url}`;
+      return reject(new Error(msg));
+    }
+    aResp
+      .text()
+      .then((text) => {
+        const args = {
+          jsonText: text,
+          manifestURL: manifestURL,
+          docURL: aContentWindow.location.href
+        };
+        const processor = new ManifestProcessor();
+        const manifest = processor.process(args);
+        resolve(Cu.cloneInto(manifest, content));
+      }, reject);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/manifest/ManifestObtainer.jsm
@@ -0,0 +1,85 @@
+/* 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/.
+ *
+ * ManifestObtainer is an implementation of:
+ * http://w3c.github.io/manifest/#obtaining
+ *
+ * Exposes public method `.obtainManifest(browserWindow)`, which returns
+ * a promise. If successful, you get back a manifest (string).
+ *
+ * For e10s compat, this JSM relies on the following to do
+ * the nessesary IPC:
+ *   dom/ipc/manifestMessages.js
+ *
+ * whose internal URL is:
+ *   'chrome://global/content/manifestMessages.js'
+ *
+ * Which is injected into every browser instance via browser.js.
+ *
+ * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
+ * exported ManifestObtainer
+ */
+'use strict';
+this.EXPORTED_SYMBOLS = ['ManifestObtainer'];
+
+const MSG_KEY = 'DOM:ManifestObtainer:Obtain';
+let messageCounter = 0;
+// FIXME: Ideally, we would store a reference to the
+//        message manager in a weakmap instead of needing a
+//        browserMap. However, trying to store a messageManager
+//        results in a TypeError because of:
+//        https://bugzilla.mozilla.org/show_bug.cgi?id=888600
+const browsersMap = new WeakMap();
+
+function ManifestObtainer() {}
+
+ManifestObtainer.prototype = {
+  obtainManifest(aBrowserWindow) {
+    if (!aBrowserWindow) {
+      const err = new TypeError('Invalid input. Expected xul browser.');
+      return Promise.reject(err);
+    }
+    const mm = aBrowserWindow.messageManager;
+    const onMessage = function(aMsg) {
+      const msgId = aMsg.data.msgId;
+      const {
+        resolve, reject
+      } = browsersMap.get(aBrowserWindow).get(msgId);
+      browsersMap.get(aBrowserWindow).delete(msgId);
+      // If we we've processed all messages,
+      // stop listening.
+      if (!browsersMap.get(aBrowserWindow).size) {
+        browsersMap.delete(aBrowserWindow);
+        mm.removeMessageListener(MSG_KEY, onMessage);
+      }
+      if (aMsg.data.success) {
+        return resolve(aMsg.data.result);
+      }
+      reject(toError(aMsg.data.result));
+    };
+    // If we are not already listening for messages
+    // start listening.
+    if (!browsersMap.has(aBrowserWindow)) {
+      browsersMap.set(aBrowserWindow, new Map());
+      mm.addMessageListener(MSG_KEY, onMessage);
+    }
+    return new Promise((resolve, reject) => {
+      const msgId = messageCounter++;
+      browsersMap.get(aBrowserWindow).set(msgId, {
+        resolve: resolve,
+        reject: reject
+      });
+      mm.sendAsyncMessage(MSG_KEY, {
+        msgId: msgId
+      });
+    });
+
+    function toError(aErrorClone) {
+      const error = new Error();
+      Object.getOwnPropertyNames(aErrorClone)
+        .forEach(name => error[name] = aErrorClone[name]);
+      return error;
+    }
+  }
+};
--- a/dom/manifest/moz.build
+++ b/dom/manifest/moz.build
@@ -1,12 +1,13 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_JS_MODULES += [
+    'ManifestObtainer.jsm',
     'ManifestProcessor.jsm',
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
-# BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/manifest/test/browser.ini
@@ -0,0 +1,2 @@
+[DEFAULT]
+[browser_ManifestObtainer_obtain.js]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/manifest/test/browser_ManifestObtainer_obtain.js
@@ -0,0 +1,210 @@
+//Used by JSHint:
+/*global Cu, ManifestObtainer, BrowserTestUtils, add_task, SpecialPowers, todo_is, gBrowser, Assert*/
+'use strict';
+Cu.import('resource://gre/modules/ManifestObtainer.jsm', this);
+requestLongerTimeout(4); // e10s tests take time.
+const defaultURL =
+  'http://example.org/tests/dom/manifest/test/resource.sjs';
+const remoteURL =
+  'http://mochi.test:8888/tests/dom/manifest/test/resource.sjs';
+const tests = [
+  // Fetch tests.
+  {
+    expected: 'Manifest is first `link` where @rel contains token manifest.',
+    get tabURL() {
+      let query = [
+        `body=<h1>${this.expected}</h1>`,
+        'Content-Type=text/html; charset=utf-8',
+      ];
+      const URL = `${defaultURL}?${query.join('&')}`;
+      return URL;
+    },
+    run(manifest) {
+      Assert.strictEqual(manifest.name, 'pass-1', this.expected);
+    },
+    testData: `
+      <link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>
+      <link rel="foo bar manifest bar test" href='${defaultURL}?body={"name":"pass-1"}'>
+      <link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
+  }, {
+    expected: 'Manifest is first `link` where @rel contains token manifest.',
+    get tabURL() {
+      let query = [
+        `body=<h1>${this.expected}</h1>`,
+        'Content-Type=text/html; charset=utf-8',
+      ];
+      const URL = `${defaultURL}?${query.join('&')}`;
+      return URL;
+    },
+    run(manifest) {
+      Assert.strictEqual(manifest.name, 'pass-2', this.expected);
+    },
+    testData: `
+      <link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-2"}'>
+      <link rel="manifest" href='resource.sjs?body={"name":"fail"}'>
+      <link rel="manifest foo bar test" href='resource.sjs?body={"name":"fail"}'>`
+  }, {
+    expected: 'By default, manifest load cross-origin.',
+    get tabURL() {
+      let query = [
+        `body=<h1>${this.expected}</h1>`,
+        'Content-Type=text/html; charset=utf-8',
+      ];
+      const URL = `${defaultURL}?${query.join('&')}`;
+      return URL;
+    },
+    run(manifest) {
+      // Waiting on https://bugzilla.mozilla.org/show_bug.cgi?id=1130924
+      todo_is(manifest.name, 'pass-3', this.expected);
+    },
+    testData: `<link rel="manifest" href='${remoteURL}?body={"name":"pass-3"}'>`
+  },
+  // CORS Tests.
+  {
+    expected: 'CORS enabled, manifest must be fetched.',
+    get tabURL() {
+      let query = [
+        `body=<h1>${this.expected}</h1>`,
+        'Content-Type=text/html; charset=utf-8',
+      ];
+      const URL = `${defaultURL}?${query.join('&')}`;
+      return URL;
+    },
+    run(manifest) {
+      Assert.strictEqual(manifest.name, 'pass-4', this.expected);
+    },
+    get testData() {
+      const body = 'body={"name": "pass-4"}';
+      const CORS =
+        `Access-Control-Allow-Origin=${new URL(this.tabURL).origin}`;
+      const link =
+        `<link
+        crossorigin=anonymous
+        rel="manifest"
+        href='${remoteURL}?${body}&${CORS}'>`;
+      return link;
+    }
+  }, {
+    expected: 'Fetch blocked by CORS - origin does not match.',
+    get tabURL() {
+      let query = [
+        `body=<h1>${this.expected}</h1>`,
+        'Content-Type=text/html; charset=utf-8',
+      ];
+      const URL = `${defaultURL}?${query.join('&')}`;
+      return URL;
+    },
+    run(err) {
+      Assert.strictEqual(err.name, 'TypeError', this.expected);
+    },
+    get testData() {
+      const body = 'body={"name": "fail"}';
+      const CORS = 'Access-Control-Allow-Origin=http://not-here';
+      const link =
+        `<link
+        crossorigin
+        rel="manifest"
+        href='${remoteURL}?${body}&${CORS}'>`;
+      return link;
+    }
+  },
+];
+
+add_task(function*() {
+  yield new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({
+      'set': [
+        ['dom.fetch.enabled', true]
+      ]
+    }, resolve);
+  });
+  for (let test of tests) {
+    let tabOptions = {
+      gBrowser: gBrowser,
+      url: test.tabURL,
+    };
+    yield BrowserTestUtils.withNewTab(
+      tabOptions,
+      browser => testObtainingManifest(browser, test)
+    );
+  }
+
+  function* testObtainingManifest(aBrowser, aTest) {
+    const obtainer = new ManifestObtainer();
+    aBrowser.contentWindowAsCPOW.document.head.innerHTML = aTest.testData;
+    try {
+      const manifest = yield obtainer.obtainManifest(aBrowser);
+      aTest.run(manifest);
+    } catch (e) {
+      aTest.run(e);
+    }
+  }
+});
+
+/*
+ * e10s race condition tests
+ * Open a bunch of tabs and load manifests
+ * in each tab. They should all return pass.
+ */
+add_task(function*() {
+  const obtainer = new ManifestObtainer();
+  const defaultPath = '/tests/dom/manifest/test/manifestLoader.html';
+  const tabURLs = [
+    `http://test:80${defaultPath}`,
+    `http://mochi.test:8888${defaultPath}`,
+    `http://test1.mochi.test:8888${defaultPath}`,
+    `http://sub1.test1.mochi.test:8888${defaultPath}`,
+    `http://sub2.xn--lt-uia.mochi.test:8888${defaultPath}`,
+    `http://test2.mochi.test:8888${defaultPath}`,
+    `http://example.org:80${defaultPath}`,
+    `http://test1.example.org:80${defaultPath}`,
+    `http://test2.example.org:80${defaultPath}`,
+    `http://sub1.test1.example.org:80${defaultPath}`,
+    `http://sub1.test2.example.org:80${defaultPath}`,
+    `http://sub2.test1.example.org:80${defaultPath}`,
+    `http://sub2.test2.example.org:80${defaultPath}`,
+    `http://example.org:8000${defaultPath}`,
+    `http://test1.example.org:8000${defaultPath}`,
+    `http://test2.example.org:8000${defaultPath}`,
+    `http://sub1.test1.example.org:8000${defaultPath}`,
+    `http://sub1.test2.example.org:8000${defaultPath}`,
+    `http://sub2.test1.example.org:8000${defaultPath}`,
+    `http://sub2.test2.example.org:8000${defaultPath}`,
+    `http://example.com:80${defaultPath}`,
+    `http://www.example.com:80${defaultPath}`,
+    `http://test1.example.com:80${defaultPath}`,
+    `http://test2.example.com:80${defaultPath}`,
+    `http://sub1.test1.example.com:80${defaultPath}`,
+    `http://sub1.test2.example.com:80${defaultPath}`,
+    `http://sub2.test1.example.com:80${defaultPath}`,
+    `http://sub2.test2.example.com:80${defaultPath}`,
+  ];
+  // Open tabs an collect corresponding browsers
+  let browsers = [
+    for (url of tabURLs) gBrowser.addTab(url).linkedBrowser
+  ];
+  // Once all the pages have loaded, run a bunch of tests in "parallel".
+  yield Promise.all((
+    for (browser of browsers) BrowserTestUtils.browserLoaded(browser)
+  ));
+  // Flood random browsers with requests. Once promises settle, check that
+  // responses all pass.
+  const results = yield Promise.all((
+    for (browser of randBrowsers(browsers, 1000)) obtainer.obtainManifest(browser)
+  ));
+  const expected = 'Expect every manifest to have name equal to `pass`.';
+  const pass = results.every(manifest => manifest.name === 'pass');
+  Assert.ok(pass, expected);
+  //cleanup
+  browsers
+    .map(browser => gBrowser.getTabForBrowser(browser))
+    .forEach(tab => gBrowser.removeTab(tab));
+
+  //Helper generator, spits out random browsers
+  function* randBrowsers(aBrowsers, aMax) {
+    for (let i = 0; i < aMax; i++) {
+      const randNum = Math.round(Math.random() * (aBrowsers.length - 1));
+      yield aBrowsers[randNum];
+    }
+  }
+});
--- a/dom/manifest/test/common.js
+++ b/dom/manifest/test/common.js
@@ -1,17 +1,17 @@
 /**
  * Common infrastructure for manifest tests.
  **/
 
 'use strict';
 const bsp = SpecialPowers.Cu.import('resource://gre/modules/ManifestProcessor.jsm'),
   processor = new bsp.ManifestProcessor(),
   manifestURL = new URL(document.location.origin + '/manifest.json'),
-  docURL = new URL('', document.location.origin),
+  docURL = document.location,
   seperators = '\u2028\u2029\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000',
   lineTerminators = '\u000D\u000A\u2028\u2029',
   whiteSpace = `${seperators}${lineTerminators}`,
   typeTests = [1, null, {},
     [], false
   ],
   data = {
     jsonText: '{}',
new file mode 100644
--- /dev/null
+++ b/dom/manifest/test/manifestLoader.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset=utf-8>
+<!--
+Uses resource.sjs to load a Web Manifest that can be loaded cross-origin.
+-->
+<link rel="manifest" href='resource.sjs?body={"name":"pass"}&amp;Access-Control-Allow-Origin=*'>
+<h1>Manifest loader</h1>
+<p>Uses resource.sjs to load a Web Manifest that can be loaded cross-origin. The manifest looks like this:</p>
+<pre>
+{
+	"name":"pass"
+}
+</pre>
--- a/dom/manifest/test/mochitest.ini
+++ b/dom/manifest/test/mochitest.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
-
 support-files =
 	common.js
+	resource.sjs
+	manifestLoader.html
 
 [test_IconsProcessor_density.html]
 [test_IconsProcessor_sizes.html]
 [test_IconsProcessor_src.html]
 [test_IconsProcessor_type.html]
 [test_ManifestProcessor_display.html]
 [test_ManifestProcessor_icons.html]
 [test_ManifestProcessor_JSON.html]
new file mode 100644
--- /dev/null
+++ b/dom/manifest/test/resource.sjs
@@ -0,0 +1,93 @@
+/* Generic responder that composes a response from
+ * the query string of a request.
+ *
+ * It reserves some special prop names:
+ *  - body: get's used as the response body
+ *  - statusCode: override the 200 OK response code
+ *    (response text is set automatically)
+ *
+ * Any property names it doesn't know about get converted into
+ * HTTP headers.
+ *
+ * For example:
+ *  http://test/resource.sjs?Content-Type=text/html&body=<h1>hello</h1>&Hello=hi
+ *
+ * Outputs:
+ * HTTP/1.1 200 OK
+ * Content-Type: text/html
+ * Hello: hi
+ * <h1>hello</h1>
+ */
+//global handleRequest
+'use strict';
+const HTTPStatus = new Map([
+  [100, 'Continue'],
+  [101, 'Switching Protocol'],
+  [200, 'OK'],
+  [201, 'Created'],
+  [202, 'Accepted'],
+  [203, 'Non-Authoritative Information'],
+  [204, 'No Content'],
+  [205, 'Reset Content'],
+  [206, 'Partial Content'],
+  [300, 'Multiple Choice'],
+  [301, 'Moved Permanently'],
+  [302, 'Found'],
+  [303, 'See Other'],
+  [304, 'Not Modified'],
+  [305, 'Use Proxy'],
+  [306, 'unused'],
+  [307, 'Temporary Redirect'],
+  [308, 'Permanent Redirect'],
+  [400, 'Bad Request'],
+  [401, 'Unauthorized'],
+  [402, 'Payment Required'],
+  [403, 'Forbidden'],
+  [404, 'Not Found'],
+  [405, 'Method Not Allowed'],
+  [406, 'Not Acceptable'],
+  [407, 'Proxy Authentication Required'],
+  [408, 'Request Timeout'],
+  [409, 'Conflict'],
+  [410, 'Gone'],
+  [411, 'Length Required'],
+  [412, 'Precondition Failed'],
+  [413, 'Request Entity Too Large'],
+  [414, 'Request-URI Too Long'],
+  [415, 'Unsupported Media Type'],
+  [416, 'Requested Range Not Satisfiable'],
+  [417, 'Expectation Failed'],
+  [500, 'Internal Server Error'],
+  [501, 'Not Implemented'],
+  [502, 'Bad Gateway'],
+  [503, 'Service Unavailable'],
+  [504, 'Gateway Timeout'],
+  [505, 'HTTP Version Not Supported']
+]);
+
+function handleRequest(request, response) {
+  const queryMap = createQueryMap(request);
+  if (queryMap.has('statusCode')) {
+    let statusCode = parseInt(queryMap.get('statusCode'));
+    let statusText = HTTPStatus.get(statusCode);
+    queryMap.delete('statusCode');
+    response.setStatusLine('1.1', statusCode, statusText);
+  }
+  if (queryMap.has('body')) {
+    let body = queryMap.get('body') || '';
+    queryMap.delete('body');
+    response.write(body);
+  }
+  for (let [key, value] of queryMap) {
+    response.setHeader(key, value);
+  }
+
+  function createQueryMap(request) {
+    const queryMap = new Map();
+    request.queryString.split('&')
+      //split on first "="
+      .map((component) => component.split(/=(.+)?/))
+      .forEach(pair => queryMap.set(pair[0], decodeURIComponent(pair[1])));
+    return queryMap;
+  }
+}
--- a/dom/media/AudioSegment.h
+++ b/dom/media/AudioSegment.h
@@ -8,16 +8,17 @@
 
 #include "MediaSegment.h"
 #include "AudioSampleFormat.h"
 #include "SharedBuffer.h"
 #include "WebAudioUtils.h"
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/TimeStamp.h"
 #endif
+#include <float.h>
 
 namespace mozilla {
 
 template<typename T>
 class SharedChannelArrayBuffer : public ThreadSharedObject {
 public:
   explicit SharedChannelArrayBuffer(nsTArray<nsTArray<T> >* aBuffers)
   {
@@ -123,16 +124,35 @@ struct AudioChunk {
   void SetNull(StreamTime aDuration)
   {
     mBuffer = nullptr;
     mChannelData.Clear();
     mDuration = aDuration;
     mVolume = 1.0f;
     mBufferFormat = AUDIO_FORMAT_SILENCE;
   }
+
+  bool IsSilentOrSubnormal() const
+  {
+    if (!mBuffer) {
+      return true;
+    }
+
+    for (uint32_t i = 0, length = mChannelData.Length(); i < length; ++i) {
+      const float* channel = static_cast<const float*>(mChannelData[i]);
+      for (StreamTime frame = 0; frame < mDuration; ++frame) {
+        if (fabs(channel[frame]) >= FLT_MIN) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+
   int ChannelCount() const { return mChannelData.Length(); }
 
   bool IsMuted() const { return mVolume == 0.0f; }
 
   size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
   {
     return SizeOfExcludingThis(aMallocSizeOf, true);
   }
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -308,17 +308,18 @@ MediaDecoder::DecodedStreamData::Decoded
     mNextAudioTime(-1),
     mDecoder(aDecoder),
     mStreamInitialized(false),
     mHaveSentFinish(false),
     mHaveSentFinishAudio(false),
     mHaveSentFinishVideo(false),
     mStream(aStream),
     mHaveBlockedForPlayState(false),
-    mHaveBlockedForStateMachineNotPlaying(false)
+    mHaveBlockedForStateMachineNotPlaying(false),
+    mEOSVideoCompensation(false)
 {
   mListener = new DecodedStreamGraphListener(mStream, this);
   mStream->AddListener(mListener);
 }
 
 MediaDecoder::DecodedStreamData::~DecodedStreamData()
 {
   mListener->Forget();
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -450,16 +450,19 @@ public:
     // Can be read from any thread.
     nsRefPtr<DecodedStreamGraphListener> mListener;
     // True when we've explicitly blocked this stream because we're
     // not in PLAY_STATE_PLAYING. Used on the main thread only.
     bool mHaveBlockedForPlayState;
     // We also have an explicit blocker on the stream when
     // mDecoderStateMachine is non-null and MediaDecoderStateMachine is false.
     bool mHaveBlockedForStateMachineNotPlaying;
+    // True if we need to send a compensation video frame to ensure the
+    // StreamTime going forward.
+    bool mEOSVideoCompensation;
   };
 
   class DecodedStreamGraphListener : public MediaStreamListener {
   public:
     DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
     virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) override;
     virtual void NotifyEvent(MediaStreamGraph* aGraph,
                              MediaStreamListener::MediaStreamGraphEvent event) override;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -385,16 +385,26 @@ static void WriteVideoToMediaStream(Medi
 {
   nsRefPtr<layers::Image> image = aImage;
   StreamTime duration =
       aStream->MicrosecondsToStreamTimeRoundDown(aEndMicroseconds) -
       aStream->MicrosecondsToStreamTimeRoundDown(aStartMicroseconds);
   aOutput->AppendFrame(image.forget(), duration, aIntrinsicSize);
 }
 
+static bool ZeroDurationAtLastChunk(VideoSegment& aInput)
+{
+  // Get the last video frame's start time in VideoSegment aInput.
+  // If the start time is equal to the duration of aInput, means the last video
+  // frame's duration is zero.
+  StreamTime lastVideoStratTime;
+  aInput.GetLastFrame(&lastVideoStratTime);
+  return lastVideoStratTime == aInput.GetDuration();
+}
+
 void MediaDecoderStateMachine::SendStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
 
@@ -495,20 +505,35 @@ void MediaDecoderStateMachine::SendStrea
           stream->mNextVideoTime = v->GetEndTime();
           stream->mLastVideoImage = v->mImage;
           stream->mLastVideoImageDisplaySize = v->mDisplay;
         } else {
           VERBOSE_LOG("skipping writing video frame %lldus (end %lldus) to MediaStream",
                       v->mTime, v->GetEndTime());
         }
       }
+      // Check the output is not empty.
+      if (output.GetLastFrame()) {
+        stream->mEOSVideoCompensation = ZeroDurationAtLastChunk(output);
+      }
       if (output.GetDuration() > 0) {
         mediaStream->AppendToTrack(videoTrackId, &output);
       }
       if (VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
+        if (stream->mEOSVideoCompensation) {
+          VideoSegment endSegment;
+          // Calculate the deviation clock time from DecodedStream.
+          int64_t deviation_usec = mediaStream->StreamTimeToMicroseconds(1);
+          WriteVideoToMediaStream(mediaStream, stream->mLastVideoImage,
+            stream->mNextVideoTime + deviation_usec, stream->mNextVideoTime,
+            stream->mLastVideoImageDisplaySize, &endSegment);
+          stream->mNextVideoTime += deviation_usec;
+          MOZ_ASSERT(endSegment.GetDuration() > 0);
+          mediaStream->AppendToTrack(videoTrackId, &endSegment);
+        }
         mediaStream->EndTrack(videoTrackId);
         stream->mHaveSentFinishVideo = true;
       }
       endPosition = std::max(endPosition,
           mediaStream->MicrosecondsToStreamTimeRoundDown(
               stream->mNextVideoTime - stream->mInitialTime));
     }
 
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -363,21 +363,22 @@ class MediaRecorder::Session: public nsI
   friend class EncoderErrorNotifierRunnable;
   friend class PushBlobRunnable;
   friend class ExtractRunnable;
   friend class DestroyRunnable;
   friend class TracksAvailableCallback;
 
 public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
-    : mRecorder(aRecorder),
-      mTimeSlice(aTimeSlice),
-      mStopIssued(false),
-      mCanRetrieveData(false),
-      mIsRegisterProfiler(false)
+    : mRecorder(aRecorder)
+    , mTimeSlice(aTimeSlice)
+    , mStopIssued(false)
+    , mCanRetrieveData(false)
+    , mIsRegisterProfiler(false)
+    , mNeedSessionEndTask(true)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory",
                                            MAX_ALLOW_MEMORY_BUFFER);
     mEncodedBufferCache = new EncodedBufferCache(maxMem);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
@@ -391,16 +392,21 @@ public:
   }
 
   void Stop()
   {
     LOG(PR_LOG_DEBUG, ("Session.Stop %p", this));
     MOZ_ASSERT(NS_IsMainThread());
     mStopIssued = true;
     CleanupStreams();
+    if (mNeedSessionEndTask) {
+      LOG(PR_LOG_DEBUG, ("Session.Stop mNeedSessionEndTask %p", this));
+      // End the Session directly if there is no ExtractRunnable.
+      DoSessionEndTask(NS_OK);
+    }
     nsContentUtils::UnregisterShutdownObserver(this);
   }
 
   nsresult Pause()
   {
     LOG(PR_LOG_DEBUG, ("Session.Pause"));
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -573,56 +579,69 @@ private:
     return perm == nsIPermissionManager::ALLOW_ACTION;
   }
 
   void InitEncoder(uint8_t aTrackTypes)
   {
     LOG(PR_LOG_DEBUG, ("Session.InitEncoder %p", this));
     MOZ_ASSERT(NS_IsMainThread());
 
+    if (!mRecorder) {
+      LOG(PR_LOG_DEBUG, ("Session.InitEncoder failure, mRecorder is null %p", this));
+      return;
+    }
     // Allocate encoder and bind with union stream.
     // At this stage, the API doesn't allow UA to choose the output mimeType format.
 
     // Make sure the application has permission to assign AUDIO_3GPP
     if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && Check3gppPermission()) {
       mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP), aTrackTypes);
     } else {
       mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes);
     }
 
     if (!mEncoder) {
+      LOG(PR_LOG_DEBUG, ("Session.InitEncoder !mEncoder %p", this));
       DoSessionEndTask(NS_ERROR_ABORT);
       return;
     }
 
     // Media stream is ready but UA issues a stop method follow by start method.
     // The Session::stop would clean the mTrackUnionStream. If the AfterTracksAdded
     // comes after stop command, this function would crash.
     if (!mTrackUnionStream) {
+      LOG(PR_LOG_DEBUG, ("Session.InitEncoder !mTrackUnionStream %p", this));
       DoSessionEndTask(NS_OK);
       return;
     }
     mTrackUnionStream->AddListener(mEncoder);
     // Create a thread to read encode media data from MediaEncoder.
     if (!mReadThread) {
       nsresult rv = NS_NewNamedThread("Media_Encoder", getter_AddRefs(mReadThread));
       if (NS_FAILED(rv)) {
+        LOG(PR_LOG_DEBUG, ("Session.InitEncoder !mReadThread %p", this));
         DoSessionEndTask(rv);
         return;
       }
     }
 
-    // In case source media stream does not notify track end, recieve
+    // In case source media stream does not notify track end, receive
     // shutdown notification and stop Read Thread.
     nsContentUtils::RegisterShutdownObserver(this);
 
     nsCOMPtr<nsIRunnable> event = new ExtractRunnable(this);
     if (NS_FAILED(mReadThread->Dispatch(event, NS_DISPATCH_NORMAL))) {
       NS_WARNING("Failed to dispatch ExtractRunnable at beginning");
+      LOG(PR_LOG_DEBUG, ("Session.InitEncoder !ReadThread->Dispatch %p", this));
+      DoSessionEndTask(NS_ERROR_ABORT);
     }
+    // Set mNeedSessionEndTask to false because the
+    // ExtractRunnable/DestroyRunnable will take the response to
+    // end the session.
+    mNeedSessionEndTask = false;
   }
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
     if (NS_FAILED(rv)) {
       mRecorder->NotifyError(rv);
     }
@@ -632,16 +651,17 @@ private:
       MOZ_ASSERT(false, "NS_DispatchToMainThread EncoderErrorNotifierRunnable failed");
     }
     if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
     }
     if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
     }
+    mNeedSessionEndTask = false;
   }
   void CleanupStreams()
   {
     if (mInputPort.get()) {
       mInputPort->Destroy();
       mInputPort = nullptr;
     }
 
@@ -705,16 +725,20 @@ private:
   // by calling requestData API.
   const int32_t mTimeSlice;
   // Indicate this session's stop has been called.
   bool mStopIssued;
   // Indicate session has encoded data. This can be changed in recording thread.
   bool mCanRetrieveData;
   // The register flag for "Media_Encoder" thread to profiler
   bool mIsRegisterProfiler;
+  // False if the InitEncoder called successfully, ensure the
+  // ExtractRunnable/DestroyRunnable will end the session.
+  // Main thread only.
+  bool mNeedSessionEndTask;
 };
 
 NS_IMPL_ISUPPORTS(MediaRecorder::Session, nsIObserver)
 
 MediaRecorder::~MediaRecorder()
 {
   if (mPipeStream != nullptr) {
     mInputPort->Destroy();
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -421,18 +421,21 @@ MediaCodecReader::DecodeAudioDataSync()
       } else {
         return;
       }
     } else {
       return;
     }
   }
 
-  if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 &&
-      bufferInfo.mBuffer->data() != nullptr) {
+  if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
+      (status == ERROR_END_OF_STREAM)) {
+    AudioQueue().Finish();
+  } else if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 &&
+             bufferInfo.mBuffer->data() != nullptr) {
     // This is the approximate byte position in the stream.
     int64_t pos = mDecoder->GetResource()->Tell();
 
     uint32_t frames = bufferInfo.mSize /
                       (mInfo.mAudio.mChannels * sizeof(AudioDataValue));
 
     mAudioCompactor.Push(
       pos,
@@ -440,23 +443,17 @@ MediaCodecReader::DecodeAudioDataSync()
       mInfo.mAudio.mRate,
       frames,
       mInfo.mAudio.mChannels,
       AudioCompactor::NativeCopy(
         bufferInfo.mBuffer->data() + bufferInfo.mOffset,
         bufferInfo.mSize,
         mInfo.mAudio.mChannels));
   }
-
-  if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
-      (status == ERROR_END_OF_STREAM)) {
-    AudioQueue().Finish();
-  }
   mAudioTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
-
 }
 
 void
 MediaCodecReader::DecodeAudioDataTask()
 {
   if (AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished()) {
     DecodeAudioDataSync();
   }
@@ -913,16 +910,23 @@ MediaCodecReader::DecodeVideoFrameSync(i
       } else {
         return;
       }
     } else {
       return;
     }
   }
 
+  if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
+      (status == ERROR_END_OF_STREAM)) {
+    VideoQueue().Finish();
+    mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
+    return;
+  }
+
   nsRefPtr<VideoData> v;
   RefPtr<TextureClient> textureClient;
   sp<GraphicBuffer> graphicBuffer;
   if (bufferInfo.mBuffer != nullptr) {
     // This is the approximate byte position in the stream.
     int64_t pos = mDecoder->GetResource()->Tell();
 
     if (mVideoTrack.mNativeWindow != nullptr &&
@@ -1009,21 +1013,16 @@ MediaCodecReader::DecodeVideoFrameSync(i
       // Notify mDecoder that we have decoded a video frame.
       mDecoder->NotifyDecodedFrames(0, 1, 0);
       VideoQueue().Push(v);
     } else {
       NS_WARNING("Unable to create VideoData");
     }
   }
 
-  if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
-      (status == ERROR_END_OF_STREAM)) {
-    VideoQueue().Finish();
-  }
-
   if (v != nullptr && textureClient != nullptr && graphicBuffer != nullptr) {
     MutexAutoLock al(mTextureClientIndexesLock);
     mTextureClientIndexes.Put(textureClient.get(), bufferInfo.mIndex);
     textureClient->SetRecycleCallback(MediaCodecReader::TextureClientRecycleCallback, this);
   } else {
     mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
   }
 }
--- a/dom/media/webaudio/DelayNode.cpp
+++ b/dom/media/webaudio/DelayNode.cpp
@@ -78,17 +78,17 @@ public:
   virtual void ProcessBlock(AudioNodeStream* aStream,
                             const AudioChunk& aInput,
                             AudioChunk* aOutput,
                             bool* aFinished) override
   {
     MOZ_ASSERT(mSource == aStream, "Invalid source stream");
     MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
 
-    if (!aInput.IsNull()) {
+    if (!aInput.IsSilentOrSubnormal()) {
       if (mLeftOverData <= 0) {
         nsRefPtr<PlayingRefChanged> refchanged =
           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
         aStream->Graph()->
           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
       }
       mLeftOverData = mBuffer.MaxDelayTicks();
     } else if (mLeftOverData > 0) {
--- a/dom/media/webaudio/test/chrome.ini
+++ b/dom/media/webaudio/test/chrome.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g'
 
 [test_AudioNodeDevtoolsAPI.html]
+[test_bug1027864.html]
 [test_AudioParamDevtoolsAPI.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_bug1027864.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test bug 1027864</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+Components.utils.import('resource://gre/modules/Services.jsm');
+SimpleTest.waitForExplicitFinish();
+
+function observer(subject, topic, data) {
+  var id = parseInt(data);
+  var index = ids.indexOf(id);
+  if (index != -1) {
+    ok(true, "Dropping id " + id + " at index " + index);
+    ids.splice(index, 1);
+    if (ids.length == 0) {
+      SimpleTest.executeSoon(function() {
+        SimpleTest.finish();
+      });
+    }
+  }
+}
+
+Services.obs.addObserver(observer, "webaudio-node-demise", false);
+
+SimpleTest.registerCleanupFunction(function() {
+  Services.obs.removeObserver(observer, "webaudio-node-demise");
+});
+
+var ac = new AudioContext();
+var ids;
+
+(function() {
+  var delay = ac.createDelay();
+  delay.delayTime.value = 0.03;
+
+  var gain = ac.createGain();
+  gain.gain.value = 0.6;
+
+  delay.connect(gain);
+  gain.connect(delay);
+
+  gain.connect(ac.destination);
+
+  var source = ac.createOscillator();
+
+  source.connect(gain);
+  source.start(ac.currentTime);
+  source.stop(ac.currentTime + 0.1);
+
+  ids = [ delay.id, gain.id, source.id ];
+})();
+
+setInterval(function() {
+  forceCC();
+}, 200);
+
+function forceCC() {
+  SpecialPowers.DOMWindowUtils.cycleCollect();
+  SpecialPowers.DOMWindowUtils.garbageCollect();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -208,16 +208,17 @@ const static js::Class sNPObjectJSWrappe
     NPRUNTIME_JSCLASS_NAME,
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     NPObjWrapper_AddProperty,
     NPObjWrapper_DelProperty,
     NPObjWrapper_GetProperty,
     NPObjWrapper_SetProperty,
     nullptr,
     NPObjWrapper_Resolve,
+    nullptr,                                                /* mayResolve */
     NPObjWrapper_Convert,
     NPObjWrapper_Finalize,
     NPObjWrapper_Call,
     nullptr,                                                /* hasInstance */
     NPObjWrapper_Construct,
     nullptr,                                                /* trace */
     JS_NULL_CLASS_SPEC,
     {
@@ -260,17 +261,17 @@ NPObjectMember_Call(JSContext *cx, unsig
 
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
 static const JSClass sNPObjectMemberClass =
   {
     "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, NPObjectMember_Convert,
+    nullptr, nullptr, nullptr, NPObjectMember_Convert,
     NPObjectMember_Finalize, NPObjectMember_Call,
     nullptr, nullptr, NPObjectMember_Trace
   };
 
 static void
 OnWrapperDestroyed();
 
 static void
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -1107,34 +1107,27 @@ PluginModuleChromeParent::AnnotateHang(m
                 }
             }
             aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginIsWhitelistedForShumway"),
                                        isWhitelistedForShumway);
         }
     }
 }
 
-#ifdef MOZ_CRASHREPORTER_INJECTOR
+#ifdef MOZ_CRASHREPORTER
 static bool
-CreateFlashMinidump(DWORD processId, ThreadId childThread,
-                    nsIFile* parentMinidump, const nsACString& name)
+CreatePluginMinidump(base::ProcessId processId, ThreadId childThread,
+                     nsIFile* parentMinidump, const nsACString& name)
 {
-  if (processId == 0) {
+  mozilla::ipc::ScopedProcessHandle handle;
+  if (processId == 0 ||
+      !base::OpenPrivilegedProcessHandle(processId, &handle.rwget())) {
     return false;
   }
-
-  base::ProcessHandle handle;
-  if (!base::OpenPrivilegedProcessHandle(processId, &handle)) {
-    return false;
-  }
-
-  bool res = CreateAdditionalChildMinidump(handle, 0, parentMinidump, name);
-  base::CloseProcessHandle(handle);
-
-  return res;
+  return CreateAdditionalChildMinidump(handle, 0, parentMinidump, name);
 }
 #endif
 
 bool
 PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
 {
     if (mIsFlashPlugin) {
         MessageLoop::current()->PostTask(
@@ -1196,42 +1189,47 @@ PluginModuleChromeParent::TerminateChild
         if (hangUIDuration) {
             nsPrintfCString strHangUIDuration("%u", hangUIDuration);
             crashReporter->AnnotateCrashReport(
                     NS_LITERAL_CSTRING("PluginHangUIDuration"),
                     strHangUIDuration);
         }
     }
 #endif // XP_WIN
+    // Generate base report, includes plugin and browser process minidumps.
     if (crashReporter->GeneratePairedMinidump(this)) {
         mPluginDumpID = crashReporter->ChildDumpID();
         PLUGIN_LOG_DEBUG(
                 ("generated paired browser/plugin minidumps: %s)",
                  NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
-
         nsAutoCString additionalDumps("browser");
-
-#ifdef MOZ_CRASHREPORTER_INJECTOR
         nsCOMPtr<nsIFile> pluginDumpFile;
-
         if (GetMinidumpForID(mPluginDumpID, getter_AddRefs(pluginDumpFile)) &&
             pluginDumpFile) {
-          nsCOMPtr<nsIFile> childDumpFile;
-
-          if (CreateFlashMinidump(mFlashProcess1, 0, pluginDumpFile,
-                                  NS_LITERAL_CSTRING("flash1"))) {
-            additionalDumps.AppendLiteral(",flash1");
-          }
-          if (CreateFlashMinidump(mFlashProcess2, 0, pluginDumpFile,
-                                  NS_LITERAL_CSTRING("flash2"))) {
-            additionalDumps.AppendLiteral(",flash2");
-          }
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+            // If we have handles to the flash sandbox processes on Windows,
+            // include those minidumps as well.
+            if (CreatePluginMinidump(mFlashProcess1, 0, pluginDumpFile,
+                                     NS_LITERAL_CSTRING("flash1"))) {
+                additionalDumps.AppendLiteral(",flash1");
+            }
+            if (CreatePluginMinidump(mFlashProcess2, 0, pluginDumpFile,
+                                     NS_LITERAL_CSTRING("flash2"))) {
+                additionalDumps.AppendLiteral(",flash2");
+            }
+#endif
+            if (mContentParent) {
+                // Include the content process minidump
+                if (CreatePluginMinidump(mContentParent->OtherPid(), 0,
+                                         pluginDumpFile,
+                                         NS_LITERAL_CSTRING("content"))) {
+                    additionalDumps.AppendLiteral(",content");
+                }
+            }
         }
-#endif
-
         crashReporter->AnnotateCrashReport(
             NS_LITERAL_CSTRING("additional_minidumps"),
             additionalDumps);
     } else {
         NS_WARNING("failed to capture paired minidumps from hang");
     }
 #endif
 
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -31,16 +31,17 @@
  *   Josh Aas <josh@mozilla.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nptest.h"
 #include "nptest_utils.h"
 #include "nptest_platform.h"
 
+#include "mozilla/ArrayUtils.h"
 #include "mozilla/IntentionalCrash.h"
 
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <iostream>
 #include <string>
 #include <sstream>
@@ -56,17 +57,16 @@
 #else
 #include <unistd.h>
 #include <pthread.h>
 #endif
 
 using namespace std;
 
 #define PLUGIN_VERSION     "1.0.0.0"
-#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))
 
 extern const char *sPluginName;
 extern const char *sPluginDescription;
 static char sPluginVersion[] = PLUGIN_VERSION;
 
 //
 // Intentional crash
 //
@@ -230,17 +230,17 @@ static const NPUTF8* sPluginMethodIdenti
   "setSitesWithData",
   "setSitesWithDataCapabilities",
   "getLastKeyText",
   "getNPNVdocumentOrigin",
   "getMouseUpEventCount",
   "queryContentsScaleFactor",
   "echoString",
 };
-static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
+static NPIdentifier sPluginMethodIdentifiers[MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames)];
 static const ScriptableFunction sPluginMethodFunctions[] = {
   npnEvaluateTest,
   npnInvokeTest,
   npnInvokeDefaultTest,
   setUndefinedValueTest,
   identifierToStringTest,
   timerTest,
   queryPrivateModeState,
@@ -298,25 +298,25 @@ static const ScriptableFunction sPluginM
   setSitesWithDataCapabilities,
   getLastKeyText,
   getNPNVdocumentOrigin,
   getMouseUpEventCount,
   queryContentsScaleFactor,
   echoString,
 };
 
-static_assert(ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
-              ARRAY_LENGTH(sPluginMethodFunctions),
+static_assert(MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
+              MOZ_ARRAY_LENGTH(sPluginMethodFunctions),
               "Arrays should have the same size");
 
 static const NPUTF8* sPluginPropertyIdentifierNames[] = {
   "propertyAndMethod"
 };
-static NPIdentifier sPluginPropertyIdentifiers[ARRAY_LENGTH(sPluginPropertyIdentifierNames)];
-static NPVariant sPluginPropertyValues[ARRAY_LENGTH(sPluginPropertyIdentifierNames)];
+static NPIdentifier sPluginPropertyIdentifiers[MOZ_ARRAY_LENGTH(sPluginPropertyIdentifierNames)];
+static NPVariant sPluginPropertyValues[MOZ_ARRAY_LENGTH(sPluginPropertyIdentifierNames)];
 
 struct URLNotifyData
 {
   const char* cookie;
   NPObject* writeCallback;
   NPObject* notifyCallback;
   NPObject* redirectCallback;
   bool allowRedirects;
@@ -383,35 +383,35 @@ struct siteData {
 };
 static list<siteData>* sSitesWithData;
 static bool sClearByAgeSupported;
 
 static void initializeIdentifiers()
 {
   if (!sIdentifiersInitialized) {
     NPN_GetStringIdentifiers(sPluginMethodIdentifierNames,
-        ARRAY_LENGTH(sPluginMethodIdentifierNames), sPluginMethodIdentifiers);
+        MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames), sPluginMethodIdentifiers);
     NPN_GetStringIdentifiers(sPluginPropertyIdentifierNames,
-        ARRAY_LENGTH(sPluginPropertyIdentifierNames), sPluginPropertyIdentifiers);
+        MOZ_ARRAY_LENGTH(sPluginPropertyIdentifierNames), sPluginPropertyIdentifiers);
 
     sIdentifiersInitialized = true;
 
     // Check whether nullptr is handled in NPN_GetStringIdentifiers
     NPIdentifier IDList[2];
     static char const *const kIDNames[2] = { nullptr, "setCookie" };
     NPN_GetStringIdentifiers(const_cast<const NPUTF8**>(kIDNames), 2, IDList);
   }
 }
 
 static void clearIdentifiers()
 {
   memset(sPluginMethodIdentifiers, 0,
-      ARRAY_LENGTH(sPluginMethodIdentifiers) * sizeof(NPIdentifier));
+      MOZ_ARRAY_LENGTH(sPluginMethodIdentifiers) * sizeof(NPIdentifier));
   memset(sPluginPropertyIdentifiers, 0,
-      ARRAY_LENGTH(sPluginPropertyIdentifiers) * sizeof(NPIdentifier));
+      MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers) * sizeof(NPIdentifier));
 
   sIdentifiersInitialized = false;
 }
 
 static void addRange(InstanceData* instanceData, const char* range)
 {
   char rangestr[16];
   strncpy(rangestr, range, sizeof(rangestr));
@@ -680,17 +680,17 @@ NPError OSCALL NP_Initialize(NPNetscapeF
 #elif defined(XP_UNIX)
 NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs)
 #endif
 {
   sBrowserFuncs = bFuncs;
 
   initializeIdentifiers();
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH(sPluginPropertyValues); i++) {
+  for (unsigned int i = 0; i < MOZ_ARRAY_LENGTH(sPluginPropertyValues); i++) {
     VOID_TO_NPVARIANT(sPluginPropertyValues[i]);
   }
 
   memset(&sNPClass, 0, sizeof(NPClass));
   sNPClass.structVersion =  NP_CLASS_STRUCT_VERSION;
   sNPClass.allocate =       (NPAllocateFunctionPtr)scriptableAllocate;
   sNPClass.deallocate =     (NPDeallocateFunctionPtr)scriptableDeallocate;
   sNPClass.invalidate =     (NPInvalidateFunctionPtr)scriptableInvalidate;
@@ -731,17 +731,17 @@ NPError OSCALL NP_GetEntryPoints(NPPlugi
 #if defined(XP_UNIX)
 NP_EXPORT(NPError) NP_Shutdown()
 #elif defined(XP_WIN)
 NPError OSCALL NP_Shutdown()
 #endif
 {
   clearIdentifiers();
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH(sPluginPropertyValues); i++) {
+  for (unsigned int i = 0; i < MOZ_ARRAY_LENGTH(sPluginPropertyValues); i++) {
     NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
   }
 
   return NPERR_NO_ERROR;
 }
 
 NPError
 NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved)
@@ -1846,17 +1846,17 @@ scriptableDeallocate(NPObject* npobj)
 void
 scriptableInvalidate(NPObject* npobj)
 {
 }
 
 bool
 scriptableHasMethod(NPObject* npobj, NPIdentifier name)
 {
-  for (int i = 0; i < int(ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) {
+  for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) {
     if (name == sPluginMethodIdentifiers[i])
       return true;
   }
   return false;
 }
 
 bool
 scriptableInvoke(NPObject* npobj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result)
@@ -1872,17 +1872,17 @@ scriptableInvoke(NPObject* npobj, NPIden
       for (uint32_t i = 0; i < argCount; i++) {
         const NPString* argstr = &NPVARIANT_TO_STRING(args[i]);
         NPN_SetException(npobj, argstr->UTF8Characters);
       }
     }
     return false;
   }
 
-  for (int i = 0; i < int(ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) {
+  for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) {
     if (name == sPluginMethodIdentifiers[i])
       return sPluginMethodFunctions[i](npobj, args, argCount, result);
   }
   return false;
 }
 
 bool
 scriptableInvokeDefault(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
@@ -1941,76 +1941,76 @@ scriptableHasProperty(NPObject* npobj, N
     }
     NPN_MemFree(asUTF8);
   }
   else {
     if (NPN_GetIntIdentifier(NPN_IntFromIdentifier(name)) != name) {
       Crash();
     }
   }
-  for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+  for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
     if (name == sPluginPropertyIdentifiers[i]) {
       return true;
     }
   }
   return false;
 }
 
 bool
 scriptableGetProperty(NPObject* npobj, NPIdentifier name, NPVariant* result)
 {
-  for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+  for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
     if (name == sPluginPropertyIdentifiers[i]) {
       DuplicateNPVariant(*result, sPluginPropertyValues[i]);
       return true;
     }
   }
   return false;
 }
 
 bool
 scriptableSetProperty(NPObject* npobj, NPIdentifier name, const NPVariant* value)
 {
-  for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+  for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
     if (name == sPluginPropertyIdentifiers[i]) {
       NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
       DuplicateNPVariant(sPluginPropertyValues[i], *value);
       return true;
     }
   }
   return false;
 }
 
 bool
 scriptableRemoveProperty(NPObject* npobj, NPIdentifier name)
 {
-  for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+  for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
     if (name == sPluginPropertyIdentifiers[i]) {
       NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
 
       // Avoid double frees (see test_propertyAndMethod.html, which deletes a
       // property that doesn't exist).
       VOID_TO_NPVARIANT(sPluginPropertyValues[i]);
       return true;
     }
   }
   return false;
 }
 
 bool
 scriptableEnumerate(NPObject* npobj, NPIdentifier** identifier, uint32_t* count)
 {
-  const int bufsize = sizeof(NPIdentifier) * ARRAY_LENGTH(sPluginMethodIdentifierNames);
+  const int bufsize = sizeof(NPIdentifier) * MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames);
   NPIdentifier* ids = (NPIdentifier*) NPN_MemAlloc(bufsize);
   if (!ids)
     return false;
 
   memcpy(ids, sPluginMethodIdentifiers, bufsize);
   *identifier = ids;
-  *count = ARRAY_LENGTH(sPluginMethodIdentifierNames);
+  *count = MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames);
   return true;
 }
 
 bool
 scriptableConstruct(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
 {
   return false;
 }
--- a/dom/webidl/ServiceWorker.webidl
+++ b/dom/webidl/ServiceWorker.webidl
@@ -7,17 +7,17 @@
  * http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-obj
  *
  */
 
 // Still unclear what should be subclassed.
 // https://github.com/slightlyoff/ServiceWorker/issues/189
 [Func="mozilla::dom::workers::ServiceWorkerVisible",
  // FIXME(nsm): Bug 1113522. This is exposed to satisfy webidl constraints, but it won't actually work.
- Exposed=(ServiceWorker,Window)]
+ Exposed=(Worker,Window)]
 interface ServiceWorker : EventTarget {
   readonly attribute USVString scriptURL;
   readonly attribute ServiceWorkerState state;
 
   attribute EventHandler onstatechange;
 
   // FIXME(catalinb): Should inherit this from Worker.
   [Throws]
--- a/dom/webidl/ServiceWorkerGlobalScope.webidl
+++ b/dom/webidl/ServiceWorkerGlobalScope.webidl
@@ -9,21 +9,17 @@
  * You are granted a license to use, reproduce and create derivative works of
  * this document.
  */
 
 [Global=(Worker,ServiceWorker),
  Exposed=ServiceWorker]
 interface ServiceWorkerGlobalScope : WorkerGlobalScope {
   readonly attribute Clients clients;
-
-  void update();
-
-  [Throws]
-  Promise<boolean> unregister();
+  readonly attribute ServiceWorkerRegistration registration;
 
   attribute EventHandler oninstall;
   attribute EventHandler onactivate;
   attribute EventHandler onfetch;
   attribute EventHandler onbeforeevicted;
   attribute EventHandler onevicted;
 
   // The event.source of these MessageEvents are instances of Client
--- a/dom/webidl/ServiceWorkerRegistration.webidl
+++ b/dom/webidl/ServiceWorkerRegistration.webidl
@@ -3,30 +3,32 @@
  * 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/.
  *
  * The origin of this IDL file is
  * http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
  *
  */
 
-[Pref="dom.serviceWorkers.enabled",
- Exposed=Window]
+[Func="mozilla::dom::ServiceWorkerRegistrationVisible",
+ Exposed=(Window,Worker)]
 interface ServiceWorkerRegistration : EventTarget {
   [Unforgeable] readonly attribute ServiceWorker? installing;
   [Unforgeable] readonly attribute ServiceWorker? waiting;
   [Unforgeable] readonly attribute ServiceWorker? active;
 
   readonly attribute USVString scope;
 
+  void update();
+
   [Throws]
   Promise<boolean> unregister();
 
   // event
   attribute EventHandler onupdatefound;
 };
 
 partial interface ServiceWorkerRegistration {
 #ifndef MOZ_SIMPLEPUSH
-  [Throws, Pref="dom.push.enabled"]
+  [Throws, Exposed=Window, Pref="dom.push.enabled"]
   readonly attribute PushManager pushManager;
 #endif
 };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -155,16 +155,17 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 #define DUMP_CONTROLLED_BY_PREF 1
 #define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled"
 #endif
 
 #define PREF_DOM_CACHES_ENABLED        "dom.caches.enabled"
 #define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion"
 #define PREF_INTL_ACCEPT_LANGUAGES     "intl.accept_languages"
+#define PREF_SERVICEWORKERS_ENABLED    "dom.serviceWorkers.enabled"
 
 namespace {
 
 const uint32_t kNoIndex = uint32_t(-1);
 
 const JS::ContextOptions kRequiredContextOptions =
   JS::ContextOptions().setDontReportUncaught(true);
 
@@ -1928,16 +1929,20 @@ RuntimeService::Init()
                                   WorkerPrefChanged,
                                   PREF_DOM_WINDOW_DUMP_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DUMP))) ||
 #endif
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES))) ||
+      NS_FAILED(Preferences::RegisterCallbackAndCall(
+                                  WorkerPrefChanged,
+                                  PREF_SERVICEWORKERS_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS))) ||
       NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
                                               PREF_JS_OPTIONS_PREFIX,
                                               nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                                    LoadRuntimeOptions,
                                                    PREF_WORKERS_OPTIONS_PREFIX,
                                                    nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(PrefLanguagesChanged,
@@ -2125,16 +2130,20 @@ RuntimeService::Cleanup()
         NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeOptions,
                                                   PREF_JS_OPTIONS_PREFIX,
                                                   nullptr)) ||
         NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeOptions,
                                                   PREF_WORKERS_OPTIONS_PREFIX,
                                                   nullptr)) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
+                                  PREF_SERVICEWORKERS_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS))) ||
+        NS_FAILED(Preferences::UnregisterCallback(
+                                  WorkerPrefChanged,
                                   PREF_DOM_CACHES_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES))) ||
 #if DUMP_CONTROLLED_BY_PREF
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_DOM_WINDOW_DUMP_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DUMP))) ||
 #endif
@@ -2667,16 +2676,20 @@ RuntimeService::WorkerPrefChanged(const 
       Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false);
   }
 #endif
 
   if (key == WORKERPREF_DOM_CACHES) {
     key = WORKERPREF_DOM_CACHES;
     sDefaultPreferences[WORKERPREF_DOM_CACHES] =
       Preferences::GetBool(PREF_DOM_CACHES_ENABLED, false);
+  } else if (key == WORKERPREF_SERVICEWORKERS) {
+    key = WORKERPREF_SERVICEWORKERS;
+    sDefaultPreferences[WORKERPREF_SERVICEWORKERS] =
+      Preferences::GetBool(PREF_SERVICEWORKERS_ENABLED, false);
   }
   // This function should never be registered as a callback for a preference it
   // does not handle.
   MOZ_ASSERT(key != WORKERPREF_COUNT);
 
   RuntimeService* rts = RuntimeService::GetService();
   if (rts) {
     rts->UpdateAllWorkerPreference(key, sDefaultPreferences[key]);
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -378,19 +378,19 @@ public:
     : mWindow(aWindow)
     , mPromise(aPromise)
   {
   }
 
   void
   UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
   {
-    nsRefPtr<ServiceWorkerRegistration> swr =
-      new ServiceWorkerRegistration(mWindow,
-          NS_ConvertUTF8toUTF16(aInfo->mScope));
+    nsRefPtr<ServiceWorkerRegistrationMainThread> swr =
+      new ServiceWorkerRegistrationMainThread(mWindow,
+                                              NS_ConvertUTF8toUTF16(aInfo->mScope));
     mPromise->MaybeResolve(swr);
   }
 
   void
   UpdateFailed(nsresult aStatus) override
   {
     mPromise->MaybeReject(aStatus);
   }
@@ -697,19 +697,21 @@ public:
 
     mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget();
     mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
 
     Succeed();
 
     // Step 4.6 "Queue a task..." for updatefound.
     nsCOMPtr<nsIRunnable> upr =
-      NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
-                                                                  &ServiceWorkerManager::FireUpdateFound,
-                                                                  mRegistration);
+      NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
+        swm,
+        &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
+        mRegistration);
+
     NS_DispatchToMainThread(upr);
 
     nsRefPtr<ServiceWorker> serviceWorker;
     nsresult rv = swm->CreateServiceWorker(mRegistration->mPrincipal,
                                            mRegistration->mInstallingWorker,
                                            getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1242,17 +1244,17 @@ public:
     }
 
     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
     if (!principal) {
       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
       return NS_OK;
     }
 
-    nsTArray<nsRefPtr<ServiceWorkerRegistration>> array;
+    nsTArray<nsRefPtr<ServiceWorkerRegistrationMainThread>> array;
 
     bool isNullPrincipal = true;
     nsresult rv = principal->GetIsNullPrincipal(&isNullPrincipal);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (nsContentUtils::IsSystemPrincipal(principal) || isNullPrincipal) {
@@ -1271,18 +1273,18 @@ public:
       }
 
       rv = principal->CheckMayLoad(scopeURI, true /* report */,
                                    false /* allowIfInheritsPrincipal */);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         continue;
       }
 
-      nsRefPtr<ServiceWorkerRegistration> swr =
-        new ServiceWorkerRegistration(mWindow, scope);
+      nsRefPtr<ServiceWorkerRegistrationMainThread> swr =
+        new ServiceWorkerRegistrationMainThread(mWindow, scope);
 
       array.AppendElement(swr);
     }
 
     mPromise->MaybeResolve(array);
     return NS_OK;
   }
 };
@@ -1374,18 +1376,18 @@ public:
       swm->GetServiceWorkerRegistrationInfo(uri);
 
     if (!registration) {
       mPromise->MaybeResolve(JS::UndefinedHandleValue);
       return NS_OK;
     }
 
     NS_ConvertUTF8toUTF16 scope(registration->mScope);
-    nsRefPtr<ServiceWorkerRegistration> swr =
-      new ServiceWorkerRegistration(mWindow, scope);
+    nsRefPtr<ServiceWorkerRegistrationMainThread> swr =
+      new ServiceWorkerRegistrationMainThread(mWindow, scope);
     mPromise->MaybeResolve(swr);
 
     return NS_OK;
   }
 };
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
@@ -1674,18 +1676,18 @@ bool
 ServiceWorkerManager::CheckReadyPromise(nsPIDOMWindow* aWindow,
                                         nsIURI* aURI, Promise* aPromise)
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(aURI);
 
   if (registration && registration->mActiveWorker) {
     NS_ConvertUTF8toUTF16 scope(registration->mScope);
-    nsRefPtr<ServiceWorkerRegistration> swr =
-      new ServiceWorkerRegistration(aWindow, scope);
+    nsRefPtr<ServiceWorkerRegistrationMainThread> swr =
+      new ServiceWorkerRegistrationMainThread(aWindow, scope);
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
 already_AddRefed<ServiceWorker>
@@ -2179,73 +2181,69 @@ ServiceWorkerManager::GetScopeForUrl(con
       return NS_ERROR_FAILURE;
   }
 
   aScope = NS_ConvertUTF8toUTF16(r->mScope);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ServiceWorkerManager::AddRegistrationEventListener(const nsAString& aScope, nsIDOMEventTarget* aListener)
+ServiceWorkerManager::AddRegistrationEventListener(const nsAString& aScope,
+                                                   ServiceWorkerRegistrationListener* aListener)
 {
   AssertIsOnMainThread();
-  nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
-
-  // TODO: this is very very bad:
-  ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
-  MOZ_ASSERT(!mServiceWorkerRegistrations.Contains(registration));
+  MOZ_ASSERT(aListener);
 #ifdef DEBUG
   // Ensure a registration is only listening for it's own scope.
   nsAutoString regScope;
-  registration->GetScope(regScope);
+  aListener->GetScope(regScope);
   MOZ_ASSERT(!regScope.IsEmpty());
-  MOZ_ASSERT(scope.Equals(NS_ConvertUTF16toUTF8(regScope)));
+  MOZ_ASSERT(aScope.Equals(regScope));
 #endif
-  mServiceWorkerRegistrations.AppendElement(registration);
+
+  MOZ_ASSERT(!mServiceWorkerRegistrationListeners.Contains(aListener));
+  mServiceWorkerRegistrationListeners.AppendElement(aListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ServiceWorkerManager::RemoveRegistrationEventListener(const nsAString& aScope, nsIDOMEventTarget* aListener)
+ServiceWorkerManager::RemoveRegistrationEventListener(const nsAString& aScope,
+                                                      ServiceWorkerRegistrationListener* aListener)
 {
   AssertIsOnMainThread();
-  nsCString scope = NS_ConvertUTF16toUTF8(aScope);
-  ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
-  MOZ_ASSERT(mServiceWorkerRegistrations.Contains(registration));
+  MOZ_ASSERT(aListener);
 #ifdef DEBUG
   // Ensure a registration is unregistering for it's own scope.
   nsAutoString regScope;
-  registration->GetScope(regScope);
+  aListener->GetScope(regScope);
   MOZ_ASSERT(!regScope.IsEmpty());
-  MOZ_ASSERT(scope.Equals(NS_ConvertUTF16toUTF8(regScope)));
+  MOZ_ASSERT(aScope.Equals(regScope));
 #endif
-  mServiceWorkerRegistrations.RemoveElement(registration);
+
+  MOZ_ASSERT(mServiceWorkerRegistrationListeners.Contains(aListener));
+  mServiceWorkerRegistrationListeners.RemoveElement(aListener);
   return NS_OK;
 }
 
 void
-ServiceWorkerManager::FireEventOnServiceWorkerRegistrations(
-  ServiceWorkerRegistrationInfo* aRegistration,
-  const nsAString& aName)
+ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations(
+  ServiceWorkerRegistrationInfo* aRegistration)
 {
   AssertIsOnMainThread();
 
-  nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(mServiceWorkerRegistrations);
+  nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
   while (it.HasMore()) {
-    nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
+    nsRefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
     nsAutoString regScope;
     target->GetScope(regScope);
     MOZ_ASSERT(!regScope.IsEmpty());
 
     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
     if (utf8Scope.Equals(aRegistration->mScope)) {
-      nsresult rv = target->DispatchTrustedEvent(aName);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        // Warn only.
-      }
+      target->UpdateFound();
     }
   }
 }
 
 /*
  * This is used for installing, waiting and active.
  */
 NS_IMETHODIMP
@@ -2759,57 +2757,72 @@ ServiceWorkerManager::CreateServiceWorke
   return NS_OK;
 }
 
 void
 ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                                                 WhichServiceWorker aWhichOnes)
 {
   AssertIsOnMainThread();
-  nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(mServiceWorkerRegistrations);
+  nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
   while (it.HasMore()) {
-    nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
+    nsRefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
     nsAutoString regScope;
     target->GetScope(regScope);
     MOZ_ASSERT(!regScope.IsEmpty());
 
     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
 
     if (utf8Scope.Equals(aRegistration->mScope)) {
-      target->InvalidateWorkerReference(aWhichOnes);
+      target->InvalidateWorkers(aWhichOnes);
     }
   }
 }
 
 NS_IMETHODIMP
-ServiceWorkerManager::Update(const nsAString& aScope)
+ServiceWorkerManager::SoftUpdate(const nsAString& aScope)
 {
+  AssertIsOnMainThread();
   NS_ConvertUTF16toUTF8 scope(aScope);
 
   nsRefPtr<ServiceWorkerRegistrationInfo> registration;
   mServiceWorkerRegistrationInfos.Get(scope, getter_AddRefs(registration));
   if (NS_WARN_IF(!registration)) {
     return NS_OK;
   }
 
-  // FIXME(nsm): Bug 1089889 Refactor this into SoftUpdate.
+  // "If registration's uninstalling flag is set, abort these steps."
   if (registration->mPendingUninstall) {
     return NS_OK;
   }
 
+  // "If registration's installing worker is not null, abort these steps."
   if (registration->mInstallingWorker) {
     return NS_OK;
   }
 
+  // "Let newestWorker be the result of running Get Newest Worker algorithm
+  // passing registration as its argument.
+  // If newestWorker is null, abort these steps."
+  nsRefPtr<ServiceWorkerInfo> newest = registration->Newest();
+  if (!newest) {
+    return NS_OK;
+  }
+
+  // "Set registration's registering script url to newestWorker's script url."
+  registration->mScriptSpec = newest->ScriptSpec();
+
   ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scope);
   MOZ_ASSERT(queue);
 
   nsRefPtr<ServiceWorkerUpdateFinishCallback> cb =
     new ServiceWorkerUpdateFinishCallback();
 
+  // "Invoke Update algorithm, or its equivalent, with client, registration as
+  // its argument."
   nsRefPtr<ServiceWorkerRegisterJob> job =
     new ServiceWorkerRegisterJob(queue, registration, cb);
   queue->Append(job);
   return NS_OK;
 }
 
 namespace {
 
@@ -3098,17 +3111,17 @@ ServiceWorkerManager::GetAllRegistration
 }
 
 static PLDHashOperator
 UpdateEachRegistration(const nsACString& aKey,
                        ServiceWorkerRegistrationInfo* aInfo,
                        void* aUserArg) {
   auto This = static_cast<ServiceWorkerManager*>(aUserArg);
   MOZ_ASSERT(!aInfo->mScope.IsEmpty());
-  nsresult res = This->Update(NS_ConvertUTF8toUTF16(aInfo->mScope));
+  nsresult res = This->SoftUpdate(NS_ConvertUTF8toUTF16(aInfo->mScope));
   unused << NS_WARN_IF(NS_FAILED(res));
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::UpdateAllRegistrations()
 {
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -31,17 +31,17 @@
 namespace mozilla {
 
 namespace ipc {
 class BackgroundChild;
 }
 
 namespace dom {
 
-class ServiceWorkerRegistration;
+class ServiceWorkerRegistrationListener;
 
 namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerClientInfo;
 class ServiceWorkerInfo;
 
 class ServiceWorkerJobQueue;
@@ -333,17 +333,17 @@ public:
   // wrong this should be replaced with a better structure to avoid the
   // memmoves associated with inserting stuff in the middle of the array.
   nsTArray<nsCString> mOrderedScopes;
 
   // Scope to registration. 
   // The scope should be a fully qualified valid URL.
   nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mServiceWorkerRegistrationInfos;
 
-  nsTObserverArray<ServiceWorkerRegistration*> mServiceWorkerRegistrations;
+  nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
 
   nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
 
   // Maps scopes to job queues.
   nsClassHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
 
   nsDataHashtable<nsCStringHashKey, bool> mSetOfScopesBeingUpdated;
 
@@ -391,16 +391,23 @@ public:
                 nsTArray<ServiceWorkerClientInfo>& aControlledDocuments);
 
   static already_AddRefed<ServiceWorkerManager>
   GetInstance();
 
  void LoadRegistrations(
                  const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
 
+  NS_IMETHOD
+  AddRegistrationEventListener(const nsAString& aScope,
+                               ServiceWorkerRegistrationListener* aListener);
+
+  NS_IMETHOD
+  RemoveRegistrationEventListener(const nsAString& aScope,
+                                  ServiceWorkerRegistrationListener* aListener);
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
   void
   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
@@ -450,25 +457,17 @@ private:
   static void
   RemoveScope(nsTArray<nsCString>& aList, const nsACString& aScope);
 
   void
   QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
                                              const nsAString& aName);
 
   void
-  FireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
-                                        const nsAString& aName);
-
-  void
-  FireUpdateFound(ServiceWorkerRegistrationInfo* aRegistration)
-  {
-    FireEventOnServiceWorkerRegistrations(aRegistration,
-                                          NS_LITERAL_STRING("updatefound"));
-  }
+  FireUpdateFoundOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration);
 
   void
   FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
 
   void
   StorePendingReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise);
 
   void
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -2,126 +2,277 @@
 /* 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 "ServiceWorkerRegistration.h"
 
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
 #include "mozilla/Services.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "ServiceWorker.h"
 
 #include "nsIDocument.h"
 #include "nsIServiceWorkerManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsPIDOMWindow.h"
 
+#include "Workers.h"
+
 #ifndef MOZ_SIMPLEPUSH
 #include "mozilla/dom/PushManagerBinding.h"
 #endif
 
 using namespace mozilla::dom::workers;
 
 namespace mozilla {
 namespace dom {
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration)
+bool
+ServiceWorkerRegistrationVisible(JSContext* aCx, JSObject* aObj)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("dom.serviceWorkers.enabled", false);
+  }
+
+  // Otherwise check the pref via the work private helper
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+  if (!workerPrivate) {
+    return false;
+  }
+
+  return workerPrivate->ServiceWorkersEnabled();
+}
+
+NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationBase, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationBase, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationBase)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationBase,
+                                   DOMEventTargetHelper, mCCDummy);
 
-#ifdef MOZ_SIMPLEPUSH
+ServiceWorkerRegistrationBase::ServiceWorkerRegistrationBase(nsPIDOMWindow* aWindow,
+                                                             const nsAString& aScope)
+  : DOMEventTargetHelper(aWindow)
+  , mScope(aScope)
+{}
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration,
-                                   DOMEventTargetHelper,
-                                   mInstallingWorker,
-                                   mWaitingWorker,
-                                   mActiveWorker)
+////////////////////////////////////////////////////
+// Main Thread implementation
+NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistrationBase)
+NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistrationBase)
 
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationMainThread)
+NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistrationBase)
+
+#ifndef MOZ_SIMPLEPUSH
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistrationBase,
+                                   mPushManager,
+                                   mInstallingWorker, mWaitingWorker, mActiveWorker);
 #else
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration,
-                                   DOMEventTargetHelper,
-                                   mInstallingWorker,
-                                   mWaitingWorker,
-                                   mActiveWorker,
-                                   mPushManager)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistrationBase,
+                                   mInstallingWorker, mWaitingWorker, mActiveWorker);
 #endif
 
-ServiceWorkerRegistration::ServiceWorkerRegistration(nsPIDOMWindow* aWindow,
-                                                     const nsAString& aScope)
-  : DOMEventTargetHelper(aWindow)
-  , mScope(aScope)
+ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow,
+                                                                         const nsAString& aScope)
+  : ServiceWorkerRegistrationBase(aWindow, aScope)
   , mListeningForEvents(false)
 {
+  AssertIsOnMainThread();
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsInnerWindow());
-
   StartListeningForEvents();
 }
 
-ServiceWorkerRegistration::~ServiceWorkerRegistration()
+ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread()
 {
   StopListeningForEvents();
+  MOZ_ASSERT(!mListeningForEvents);
+}
+
+
+already_AddRefed<workers::ServiceWorker>
+ServiceWorkerRegistrationMainThread::GetWorkerReference(WhichServiceWorker aWhichOne)
+{
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if (!window) {
+    return nullptr;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIServiceWorkerManager> swm =
+    do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsISupports> serviceWorker;
+  switch(aWhichOne) {
+    case WhichServiceWorker::INSTALLING_WORKER:
+      rv = swm->GetInstalling(window, mScope, getter_AddRefs(serviceWorker));
+      break;
+    case WhichServiceWorker::WAITING_WORKER:
+      rv = swm->GetWaiting(window, mScope, getter_AddRefs(serviceWorker));
+      break;
+    case WhichServiceWorker::ACTIVE_WORKER:
+      rv = swm->GetActive(window, mScope, getter_AddRefs(serviceWorker));
+      break;
+    default:
+      MOZ_CRASH("Invalid enum value");
+  }
+
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv) || rv == NS_ERROR_DOM_NOT_FOUND_ERR,
+                   "Unexpected error getting service worker instance from ServiceWorkerManager");
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+
+  nsRefPtr<ServiceWorker> ref =
+    static_cast<ServiceWorker*>(serviceWorker.get());
+  return ref.forget();
+}
+
+// XXXnsm, maybe this can be optimized to only add when a event handler is
+// registered.
+void
+ServiceWorkerRegistrationMainThread::StartListeningForEvents()
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!mListeningForEvents);
+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (swm) {
+    swm->AddRegistrationEventListener(mScope, this);
+    mListeningForEvents = true;
+  }
 }
 
 void
-ServiceWorkerRegistration::DisconnectFromOwner()
+ServiceWorkerRegistrationMainThread::StopListeningForEvents()
 {
-  StopListeningForEvents();
-  DOMEventTargetHelper::DisconnectFromOwner();
+  AssertIsOnMainThread();
+  if (!mListeningForEvents) {
+    return;
+  }
+
+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (swm) {
+    swm->RemoveRegistrationEventListener(mScope, this);
+  }
+  mListeningForEvents = false;
 }
 
 JSObject*
-ServiceWorkerRegistration::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+ServiceWorkerRegistrationMainThread::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
+  AssertIsOnMainThread();
   return ServiceWorkerRegistrationBinding::Wrap(aCx, this, aGivenProto);
 }
 
 already_AddRefed<workers::ServiceWorker>
-ServiceWorkerRegistration::GetInstalling()
+ServiceWorkerRegistrationMainThread::GetInstalling()
 {
+  AssertIsOnMainThread();
   if (!mInstallingWorker) {
     mInstallingWorker = GetWorkerReference(WhichServiceWorker::INSTALLING_WORKER);
   }
 
   nsRefPtr<ServiceWorker> ret = mInstallingWorker;
   return ret.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
-ServiceWorkerRegistration::GetWaiting()
+ServiceWorkerRegistrationMainThread::GetWaiting()
 {
+  AssertIsOnMainThread();
   if (!mWaitingWorker) {
     mWaitingWorker = GetWorkerReference(WhichServiceWorker::WAITING_WORKER);
   }
 
   nsRefPtr<ServiceWorker> ret = mWaitingWorker;
   return ret.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
-ServiceWorkerRegistration::GetActive()
+ServiceWorkerRegistrationMainThread::GetActive()
 {
+  AssertIsOnMainThread();
   if (!mActiveWorker) {
     mActiveWorker = GetWorkerReference(WhichServiceWorker::ACTIVE_WORKER);
   }
 
   nsRefPtr<ServiceWorker> ret = mActiveWorker;
   return ret.forget();
 }
 
+void
+ServiceWorkerRegistrationMainThread::UpdateFound()
+{
+  DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
+}
+
+void
+ServiceWorkerRegistrationMainThread::InvalidateWorkers(WhichServiceWorker aWhichOnes)
+{
+  AssertIsOnMainThread();
+  if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) {
+    mInstallingWorker = nullptr;
+  }
+
+  if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
+    mWaitingWorker = nullptr;
+  }
+
+  if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
+    mActiveWorker = nullptr;
+  }
+}
+
 namespace {
 
+void
+UpdateInternal(const nsAString& aScope)
+{
+  AssertIsOnMainThread();
+  nsCOMPtr<nsIServiceWorkerManager> swm =
+    mozilla::services::GetServiceWorkerManager();
+  MOZ_ASSERT(swm);
+  // The spec defines ServiceWorkerRegistration.update() exactly as Soft Update.
+  swm->SoftUpdate(aScope);
+}
+
+class UpdateRunnable final : public nsRunnable
+{
+public:
+  explicit UpdateRunnable(const nsAString& aScope)
+    : mScope(aScope)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+    UpdateInternal(mScope);
+    return NS_OK;
+  }
+
+private:
+  ~UpdateRunnable()
+  {}
+
+  const nsString mScope;
+};
+
 class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
 {
   nsRefPtr<Promise> mPromise;
 
 public:
   NS_DECL_ISUPPORTS
 
   explicit UnregisterCallback(Promise* aPromise)
@@ -138,34 +289,186 @@ public:
     return NS_OK;
   }
 
   NS_IMETHODIMP
   UnregisterFailed() override
   {
     AssertIsOnMainThread();
 
-    AutoJSAPI api;
-    api.Init(mPromise->GetParentObject());
-    mPromise->MaybeReject(api.cx(), JS::UndefinedHandleValue);
+    mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return NS_OK;
   }
 
 private:
   ~UnregisterCallback()
   { }
 };
 
 NS_IMPL_ISUPPORTS(UnregisterCallback, nsIServiceWorkerUnregisterCallback)
 
+class FulfillUnregisterPromiseRunnable final : public WorkerRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+  Maybe<bool> mState;
+public:
+  FulfillUnregisterPromiseRunnable(WorkerPrivate* aWorkerPrivate,
+                                   PromiseWorkerProxy* aProxy,
+                                   Maybe<bool> aState)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mPromiseWorkerProxy(aProxy)
+    , mState(aState)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mPromiseWorkerProxy);
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    Promise* promise = mPromiseWorkerProxy->GetWorkerPromise();
+    MOZ_ASSERT(promise);
+    if (mState.isSome()) {
+      promise->MaybeResolve(mState.value());
+    } else {
+      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    }
+
+    mPromiseWorkerProxy->CleanUp(aCx);
+    return true;
+  }
+};
+
+class WorkerUnregisterCallback final : public nsIServiceWorkerUnregisterCallback
+{
+  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy)
+    : mPromiseWorkerProxy(aProxy)
+  {
+  }
+
+  NS_IMETHODIMP
+  UnregisterSucceeded(bool aState) override
+  {
+    AssertIsOnMainThread();
+    Finish(Some(aState));
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  UnregisterFailed() override
+  {
+    AssertIsOnMainThread();
+    Finish(Nothing());
+    return NS_OK;
+  }
+
+private:
+  ~WorkerUnregisterCallback()
+  { }
+
+  void
+  Finish(Maybe<bool> aState)
+  {
+    AssertIsOnMainThread();
+    if (!mPromiseWorkerProxy) {
+      return;
+    }
+
+    MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock());
+    if (mPromiseWorkerProxy->IsClean()) {
+      return;
+    }
+
+    nsRefPtr<WorkerRunnable> r =
+      new FulfillUnregisterPromiseRunnable(mPromiseWorkerProxy->GetWorkerPrivate(),
+                                           mPromiseWorkerProxy, aState);
+
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    if (!r->Dispatch(jsapi.cx())) {
+      nsRefPtr<WorkerControlRunnable> cr =
+        new PromiseWorkerProxyControlRunnable(
+          mPromiseWorkerProxy->GetWorkerPrivate(),
+          mPromiseWorkerProxy);
+      cr->Dispatch(jsapi.cx());
+    }
+  }
+};
+
+NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback);
+
+/*
+ * If the worker goes away, we still continue to unregister, but we don't try to
+ * resolve the worker Promise (which doesn't exist by that point).
+ */
+class StartUnregisterRunnable final : public nsRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+  const nsString mScope;
+
+public:
+  StartUnregisterRunnable(WorkerPrivate* aWorker, Promise* aPromise,
+                          const nsAString& aScope)
+    : mPromiseWorkerProxy(PromiseWorkerProxy::Create(aWorker, aPromise))
+    , mScope(aScope)
+  {
+    // mPromiseWorkerProxy may be null if AddFeature failed.
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+
+    nsRefPtr<WorkerUnregisterCallback> cb = new WorkerUnregisterCallback(mPromiseWorkerProxy);
+
+    // XXXnsm: There is a rare chance of this failing if the worker gets
+    // destroyed. In that case, unregister() called from a SW is no longer
+    // guaranteed to run. We should fix this by having a main thread proxy
+    // maintain a strongref to ServiceWorkerRegistrationInfo and use its
+    // principal. Can that be trusted?
+    nsCOMPtr<nsIPrincipal> principal;
+    {
+      MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock());
+      if (mPromiseWorkerProxy->IsClean()) {
+        return NS_OK;
+      }
+
+      WorkerPrivate* worker = mPromiseWorkerProxy->GetWorkerPrivate();
+      MOZ_ASSERT(worker);
+      principal = worker->GetPrincipal();
+    }
+    MOZ_ASSERT(principal);
+
+    nsCOMPtr<nsIServiceWorkerManager> swm =
+      mozilla::services::GetServiceWorkerManager();
+    nsresult rv = swm->Unregister(principal, cb, mScope);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      cb->UnregisterFailed();
+    }
+
+    return NS_OK;
+  }
+};
 } // anonymous namespace
 
+void
+ServiceWorkerRegistrationMainThread::Update()
+{
+  UpdateInternal(mScope);
+}
+
 already_AddRefed<Promise>
-ServiceWorkerRegistration::Unregister(ErrorResult& aRv)
+ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv)
 {
+  AssertIsOnMainThread();
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
   if (!go) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   // Although the spec says that the same-origin checks should also be done
   // asynchronously, we do them in sync because the Promise created by the
@@ -197,21 +500,17 @@ ServiceWorkerRegistration::Unregister(Er
 
   nsAutoCString uriSpec;
   aRv = scopeURI->GetSpecIgnoringRef(uriSpec);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIServiceWorkerManager> swm =
-    do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRv.Throw(rv);
-    return nullptr;
-  }
+    mozilla::services::GetServiceWorkerManager();
 
   nsRefPtr<Promise> promise = Promise::Create(go, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsRefPtr<UnregisterCallback> cb = new UnregisterCallback(promise);
 
@@ -219,101 +518,18 @@ ServiceWorkerRegistration::Unregister(Er
   aRv = swm->Unregister(documentPrincipal, cb, scope);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   return promise.forget();
 }
 
-already_AddRefed<workers::ServiceWorker>
-ServiceWorkerRegistration::GetWorkerReference(WhichServiceWorker aWhichOne)
-{
-  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
-  if (!window) {
-    return nullptr;
-  }
-
-  nsresult rv;
-  nsCOMPtr<nsIServiceWorkerManager> swm =
-    do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsISupports> serviceWorker;
-  switch(aWhichOne) {
-    case WhichServiceWorker::INSTALLING_WORKER:
-      rv = swm->GetInstalling(window, mScope, getter_AddRefs(serviceWorker));
-      break;
-    case WhichServiceWorker::WAITING_WORKER:
-      rv = swm->GetWaiting(window, mScope, getter_AddRefs(serviceWorker));
-      break;
-    case WhichServiceWorker::ACTIVE_WORKER:
-      rv = swm->GetActive(window, mScope, getter_AddRefs(serviceWorker));
-      break;
-    default:
-      MOZ_CRASH("Invalid enum value");
-  }
-
-  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv) || rv == NS_ERROR_DOM_NOT_FOUND_ERR,
-                   "Unexpected error getting service worker instance from ServiceWorkerManager");
-  if (NS_FAILED(rv)) {
-    return nullptr;
-  }
-
-  nsRefPtr<ServiceWorker> ref =
-    static_cast<ServiceWorker*>(serviceWorker.get());
-  return ref.forget();
-}
-
-void
-ServiceWorkerRegistration::InvalidateWorkerReference(WhichServiceWorker aWhichOnes)
-{
-  if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) {
-    mInstallingWorker = nullptr;
-  }
-
-  if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
-    mWaitingWorker = nullptr;
-  }
-
-  if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
-    mActiveWorker = nullptr;
-  }
-}
-
-// XXXnsm, maybe this can be optimized to only add when a event handler is
-// registered.
-void
-ServiceWorkerRegistration::StartListeningForEvents()
-{
-  nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
-  if (swm) {
-    swm->AddRegistrationEventListener(mScope, this);
-    mListeningForEvents = true;
-  }
-}
-
-void
-ServiceWorkerRegistration::StopListeningForEvents()
-{
-  if (!mListeningForEvents) {
-    return;
-  }
-
-  nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
-  if (swm) {
-    swm->RemoveRegistrationEventListener(mScope, this);
-    mListeningForEvents = false;
-  }
-}
-
 already_AddRefed<PushManager>
-ServiceWorkerRegistration::GetPushManager(ErrorResult& aRv)
+ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv)
 {
   AssertIsOnMainThread();
 
 #ifdef MOZ_SIMPLEPUSH
   return nullptr;
 #else
 
   if (!mPushManager) {
@@ -352,10 +568,364 @@ ServiceWorkerRegistration::GetPushManage
   }
 
   nsRefPtr<PushManager> ret = mPushManager;
   return ret.forget();
 
   #endif /* ! MOZ_SIMPLEPUSH */
 }
 
+////////////////////////////////////////////////////
+// Worker Thread implementation
+class WorkerListener final : public ServiceWorkerRegistrationListener
+{
+  // Accessed on the main thread.
+  WorkerPrivate* mWorkerPrivate;
+  nsString mScope;
+  bool mListeningForEvents;
+
+  // Accessed on the worker thread.
+  ServiceWorkerRegistrationWorkerThread* mRegistration;
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override)
+
+  WorkerListener(WorkerPrivate* aWorkerPrivate,
+                 ServiceWorkerRegistrationWorkerThread* aReg)
+    : mWorkerPrivate(aWorkerPrivate)
+    , mListeningForEvents(false)
+    , mRegistration(aReg)
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(mRegistration);
+    // Copy scope so we can return it on the main thread.
+    mRegistration->GetScope(mScope);
+  }
+
+  void
+  StartListeningForEvents()
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(!mListeningForEvents);
+    MOZ_ASSERT(mWorkerPrivate);
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (swm) {
+      // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
+      swm->AddRegistrationEventListener(mScope, this);
+      mListeningForEvents = true;
+    }
+  }
+
+  void
+  StopListeningForEvents()
+  {
+    AssertIsOnMainThread();
+
+    MOZ_ASSERT(mListeningForEvents);
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+    // We aren't going to need this anymore and we shouldn't hold on since the
+    // worker will go away soon.
+    mWorkerPrivate = nullptr;
+
+    if (swm) {
+      // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
+      swm->RemoveRegistrationEventListener(mScope, this);
+      mListeningForEvents = false;
+    }
+  }
+
+  // ServiceWorkerRegistrationListener
+  void
+  UpdateFound() override;
+
+  void
+  InvalidateWorkers(WhichServiceWorker aWhichOnes) override
+  {
+    AssertIsOnMainThread();
+    // FIXME(nsm);
+  }
+
+  void
+  GetScope(nsAString& aScope) const override
+  {
+    aScope = mScope;
+  }
+
+  ServiceWorkerRegistrationWorkerThread*
+  GetRegistration() const
+  {
+    if (mWorkerPrivate) {
+      mWorkerPrivate->AssertIsOnWorkerThread();
+    }
+    return mRegistration;
+  }
+
+  void
+  ClearRegistration()
+  {
+    if (mWorkerPrivate) {
+      mWorkerPrivate->AssertIsOnWorkerThread();
+    }
+    mRegistration = nullptr;
+  }
+
+private:
+  ~WorkerListener()
+  {
+    MOZ_ASSERT(!mListeningForEvents);
+  }
+};
+
+NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistrationBase)
+NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistrationBase)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationWorkerThread)
+NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistrationBase)
+
+// Expanded macros since we need special behaviour to release the proxy.
+NS_IMPL_CYCLE_COLLECTION_CLASS(ServiceWorkerRegistrationWorkerThread)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread,
+                                                  ServiceWorkerRegistrationBase)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread,
+                                                ServiceWorkerRegistrationBase)
+  tmp->ReleaseListener(RegistrationIsGoingAway);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
+                                                                             const nsAString& aScope)
+  : ServiceWorkerRegistrationBase(nullptr, aScope)
+  , mWorkerPrivate(aWorkerPrivate)
+{
+  InitListener();
+}
+
+ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread()
+{
+  ReleaseListener(RegistrationIsGoingAway);
+  MOZ_ASSERT(!mListener);
+}
+
+JSObject*
+ServiceWorkerRegistrationWorkerThread::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return ServiceWorkerRegistrationBinding_workers::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<workers::ServiceWorker>
+ServiceWorkerRegistrationWorkerThread::GetInstalling()
+{
+  // FIXME(nsm): Will be implemented after Bug 1113522.
+  return nullptr;
+}
+
+already_AddRefed<workers::ServiceWorker>
+ServiceWorkerRegistrationWorkerThread::GetWaiting()
+{
+  // FIXME(nsm): Will be implemented after Bug 1113522.
+  return nullptr;
+}
+
+already_AddRefed<workers::ServiceWorker>
+ServiceWorkerRegistrationWorkerThread::GetActive()
+{
+  // FIXME(nsm): Will be implemented after Bug 1113522.
+  return nullptr;
+}
+
+void
+ServiceWorkerRegistrationWorkerThread::Update()
+{
+#ifdef DEBUG
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+#endif
+  nsCOMPtr<nsIRunnable> r = new UpdateRunnable(mScope);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
+{
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  if (!worker->IsServiceWorker()) {
+    // For other workers, the registration probably originated from
+    // getRegistration(), so we may have to validate origin etc. Let's do this
+    // this later.
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsRefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(worker, promise, mScope);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+
+  return promise.forget();
+}
+
+class StartListeningRunnable final : public nsRunnable
+{
+  nsRefPtr<WorkerListener> mListener;
+public:
+  explicit StartListeningRunnable(WorkerListener* aListener)
+    : mListener(aListener)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    mListener->StartListeningForEvents();
+    return NS_OK;
+  }
+};
+
+void
+ServiceWorkerRegistrationWorkerThread::InitListener()
+{
+  MOZ_ASSERT(!mListener);
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  mListener = new WorkerListener(worker, this);
+  if (!worker->AddFeature(worker->GetJSContext(), this)) {
+    mListener = nullptr;
+    NS_WARNING("Could not add feature");
+    return;
+  }
+
+  nsRefPtr<StartListeningRunnable> r =
+    new StartListeningRunnable(mListener);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+}
+
+class AsyncStopListeningRunnable final : public nsRunnable
+{
+  nsRefPtr<WorkerListener> mListener;
+public:
+  explicit AsyncStopListeningRunnable(WorkerListener* aListener)
+    : mListener(aListener)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    mListener->StopListeningForEvents();
+    return NS_OK;
+  }
+};
+
+class SyncStopListeningRunnable final : public WorkerMainThreadRunnable
+{
+  nsRefPtr<WorkerListener> mListener;
+public:
+  SyncStopListeningRunnable(WorkerPrivate* aWorkerPrivate,
+                            WorkerListener* aListener)
+    : WorkerMainThreadRunnable(aWorkerPrivate)
+    , mListener(aListener)
+  {}
+
+  bool
+  MainThreadRun() override
+  {
+    mListener->StopListeningForEvents();
+    return true;
+  }
+};
+
+void
+ServiceWorkerRegistrationWorkerThread::ReleaseListener(Reason aReason)
+{
+  if (!mListener) {
+    return;
+  }
+
+  // We can assert worker here, because:
+  // 1) We always AddFeature, so if the worker has shutdown already, we'll have
+  //    received Notify and removed it. If AddFeature had failed, mListener will
+  //    be null and we won't reach here.
+  // 2) Otherwise, worker is still around even if we are going away.
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
+
+  mListener->ClearRegistration();
+
+  if (aReason == RegistrationIsGoingAway) {
+    nsRefPtr<AsyncStopListeningRunnable> r =
+      new AsyncStopListeningRunnable(mListener);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+  } else if (aReason == WorkerIsGoingAway) {
+    nsRefPtr<SyncStopListeningRunnable> r =
+      new SyncStopListeningRunnable(mWorkerPrivate, mListener);
+    if (!r->Dispatch(nullptr)) {
+      NS_ERROR("Failed to dispatch stop listening runnable!");
+    }
+  } else {
+    MOZ_CRASH("Bad reason");
+  }
+  mListener = nullptr;
+  mWorkerPrivate = nullptr;
+}
+
+bool
+ServiceWorkerRegistrationWorkerThread::Notify(JSContext* aCx, workers::Status aStatus)
+{
+  ReleaseListener(WorkerIsGoingAway);
+  return true;
+}
+
+class FireUpdateFoundRunnable final : public WorkerRunnable
+{
+  nsRefPtr<WorkerListener> mListener;
+public:
+  FireUpdateFoundRunnable(WorkerPrivate* aWorkerPrivate,
+                          WorkerListener* aListener)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mListener(aListener)
+  {
+    // Need this assertion for now since runnables which modify busy count can
+    // only be dispatched from parent thread to worker thread and we don't deal
+    // with nested workers. SW threads can't be nested.
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    ServiceWorkerRegistrationWorkerThread* reg = mListener->GetRegistration();
+    if (reg) {
+      reg->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
+    }
+    return true;
+  }
+};
+
+void
+WorkerListener::UpdateFound()
+{
+  AssertIsOnMainThread();
+  if (mWorkerPrivate) {
+    nsRefPtr<FireUpdateFoundRunnable> r =
+      new FireUpdateFoundRunnable(mWorkerPrivate, this);
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    }
+  }
+}
 } // dom namespace
 } // mozilla namespace
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -5,103 +5,233 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ServiceWorkerRegistration_h
 #define mozilla_dom_ServiceWorkerRegistration_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/ServiceWorkerBinding.h"
 #include "mozilla/dom/ServiceWorkerCommon.h"
+#include "mozilla/dom/workers/bindings/WorkerFeature.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class PushManager;
+class WorkerListener;
 
 namespace workers {
 class ServiceWorker;
+class WorkerPrivate;
 }
 
-class ServiceWorkerRegistration final : public DOMEventTargetHelper
+bool
+ServiceWorkerRegistrationVisible(JSContext* aCx, JSObject* aObj);
+
+// This class exists solely so that we can satisfy some WebIDL Func= attribute
+// constraints. Func= converts the function name to a header file to include, in
+// this case "ServiceWorkerRegistration.h".
+class ServiceWorkerRegistration final
 {
 public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistration,
-                                           DOMEventTargetHelper)
-
-  IMPL_EVENT_HANDLER(updatefound)
-
-  ServiceWorkerRegistration(nsPIDOMWindow* aWindow,
-                            const nsAString& aScope);
-
-  JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  already_AddRefed<workers::ServiceWorker>
-  GetInstalling();
-
-  already_AddRefed<workers::ServiceWorker>
-  GetWaiting();
-
-  already_AddRefed<workers::ServiceWorker>
-  GetActive();
-
-  void
-  GetScope(nsAString& aScope) const
-  {
-    aScope = mScope;
-  }
-
-  already_AddRefed<Promise>
-  Unregister(ErrorResult& aRv);
-
-  // Useful methods for ServiceWorkerManager:
-  void
-  InvalidateWorkerReference(WhichServiceWorker aWhichOnes);
-
-  // DOMEventTargethelper
-  virtual void DisconnectFromOwner() override;
-
-  already_AddRefed<PushManager>
-  GetPushManager(ErrorResult& aRv);
-
   // Something that we can feed into the Func webidl property to ensure that
   // SetScope is never exposed to the user.
   static bool
   WebPushMethodHider(JSContext* unusedContext, JSObject* unusedObject) {
     return false;
   }
 
+};
+
+// Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of
+// updatefound event and invalidating ServiceWorker instances.
+class ServiceWorkerRegistrationListener
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+  virtual void
+  UpdateFound() = 0;
+
+  virtual void
+  InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0;
+
+  virtual void
+  GetScope(nsAString& aScope) const = 0;
+};
+
+class ServiceWorkerRegistrationBase : public DOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationBase,
+                                           DOMEventTargetHelper)
+
+  IMPL_EVENT_HANDLER(updatefound)
+
+  ServiceWorkerRegistrationBase(nsPIDOMWindow* aWindow,
+                                const nsAString& aScope);
+
+  JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
+
+  virtual already_AddRefed<workers::ServiceWorker>
+  GetInstalling() = 0;
+
+  virtual already_AddRefed<workers::ServiceWorker>
+  GetWaiting() = 0;
+
+  virtual already_AddRefed<workers::ServiceWorker>
+  GetActive() = 0;
+
+protected:
+  virtual ~ServiceWorkerRegistrationBase()
+  { }
+
+  const nsString mScope;
 private:
-  ~ServiceWorkerRegistration();
+  nsCOMPtr<nsISupports> mCCDummy;
+};
+
+class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistrationBase,
+                                                  public ServiceWorkerRegistrationListener
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationMainThread,
+                                           ServiceWorkerRegistrationBase)
+
+  ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow,
+                                      const nsAString& aScope);
+
+  void
+  Update();
+
+  already_AddRefed<Promise>
+  Unregister(ErrorResult& aRv);
+
+  JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<workers::ServiceWorker>
+  GetInstalling() override;
+
+  already_AddRefed<workers::ServiceWorker>
+  GetWaiting() override;
+  
+  already_AddRefed<workers::ServiceWorker>
+  GetActive() override;
+  
+  already_AddRefed<PushManager>
+  GetPushManager(ErrorResult& aRv);
+
+  // DOMEventTargethelper
+  void DisconnectFromOwner() override
+  {
+    StopListeningForEvents();
+    ServiceWorkerRegistrationBase::DisconnectFromOwner();
+  }
+
+  // ServiceWorkerRegistrationListener
+  void
+  UpdateFound() override;
+
+  void
+  InvalidateWorkers(WhichServiceWorker aWhichOnes) override;
+
+  void
+  GetScope(nsAString& aScope) const override
+  {
+    aScope = mScope;
+  }
+
+private:
+  ~ServiceWorkerRegistrationMainThread();
 
   already_AddRefed<workers::ServiceWorker>
   GetWorkerReference(WhichServiceWorker aWhichOne);
 
   void
   StartListeningForEvents();
 
   void
   StopListeningForEvents();
 
+  bool mListeningForEvents;
+
   // The following properties are cached here to ensure JS equality is satisfied
   // instead of acquiring a new worker instance from the ServiceWorkerManager
   // for every access. A null value is considered a cache miss.
   // These three may change to a new worker at any time.
   nsRefPtr<workers::ServiceWorker> mInstallingWorker;
   nsRefPtr<workers::ServiceWorker> mWaitingWorker;
   nsRefPtr<workers::ServiceWorker> mActiveWorker;
 
 #ifndef MOZ_SIMPLEPUSH
   nsRefPtr<PushManager> mPushManager;
 #endif
+};
 
-  const nsString mScope;
-  bool mListeningForEvents;
+class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistrationBase
+                                                  , public workers::WorkerFeature
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationWorkerThread,
+                                           ServiceWorkerRegistrationBase)
+
+  ServiceWorkerRegistrationWorkerThread(workers::WorkerPrivate* aWorkerPrivate,
+                                        const nsAString& aScope);
+
+  void
+  Update();
+
+  already_AddRefed<Promise>
+  Unregister(ErrorResult& aRv);
+
+  JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<workers::ServiceWorker>
+  GetInstalling() override;
+
+  already_AddRefed<workers::ServiceWorker>
+  GetWaiting() override;
+
+  already_AddRefed<workers::ServiceWorker>
+  GetActive() override;
+
+  void
+  GetScope(nsAString& aScope) const
+  {
+    aScope = mScope;
+  }
+
+  bool
+  Notify(JSContext* aCx, workers::Status aStatus) override;
+
+private:
+  enum Reason
+  {
+    RegistrationIsGoingAway = 0,
+    WorkerIsGoingAway,
+  };
+
+  ~ServiceWorkerRegistrationWorkerThread();
+
+  void
+  InitListener();
+
+  void
+  ReleaseListener(Reason aReason);
+
+  workers::WorkerPrivate* mWorkerPrivate;
+  nsRefPtr<WorkerListener> mListener;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif /* mozilla_dom_ServiceWorkerRegistration_h */
\ No newline at end of file
+#endif /* mozilla_dom_ServiceWorkerRegistration_h */
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1190,16 +1190,23 @@ public:
   bool
   DOMCachesEnabled() const
   {
     AssertIsOnWorkerThread();
     return mPreferences[WORKERPREF_DOM_CACHES];
   }
 
   bool
+  ServiceWorkersEnabled() const
+  {
+    AssertIsOnWorkerThread();
+    return mPreferences[WORKERPREF_SERVICEWORKERS];
+  }
+
+  bool
   OnLine() const
   {
     AssertIsOnWorkerThread();
     return mOnLine;
   }
 
   void
   StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult);
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -34,16 +34,17 @@
 #include "Navigator.h"
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "Performance.h"
 #include "ServiceWorkerClients.h"
+#include "ServiceWorkerRegistration.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
 
 using mozilla::dom::cache::CacheStorage;
 using mozilla::dom::indexedDB::IDBFactory;
 using mozilla::ipc::PrincipalInfo;
@@ -425,17 +426,17 @@ SharedWorkerGlobalScope::WrapGlobalObjec
   mWorkerPrivate->CopyJSCompartmentOptions(options);
 
   return SharedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this, options,
                                                       GetWorkerPrincipal(),
                                                       true, aReflector);
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope,
-                                   mClients)
+                                   mClients, mRegistration)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope)
 NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope)
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope)
 
 ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
                                                    const nsACString& aScope)
@@ -468,16 +469,27 @@ ServiceWorkerGlobalScope::Clients()
 {
   if (!mClients) {
     mClients = new ServiceWorkerClients(this);
   }
 
   return mClients;
 }
 
+ServiceWorkerRegistrationWorkerThread*
+ServiceWorkerGlobalScope::Registration()
+{
+  if (!mRegistration) {
+    mRegistration =
+      new ServiceWorkerRegistrationWorkerThread(mWorkerPrivate, mScope);
+  }
+
+  return mRegistration;
+}
+
 WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
                                                   WorkerPrivate* aWorkerPrivate)
 : mWorkerPrivate(aWorkerPrivate)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
 WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope()
@@ -594,16 +606,17 @@ const js::Class workerdebuggersandbox_cl
     "workerdebuggersandbox",
     JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     workerdebuggersandbox_enumerate,
     workerdebuggersandbox_resolve,
+    nullptr, /* mayResolve */
     workerdebuggersandbox_convert,
     workerdebuggersandbox_finalize,
     nullptr,
     nullptr,
     nullptr,
     JS_GlobalObjectTraceHook,
     JS_NULL_CLASS_SPEC, {
       nullptr,
@@ -778,240 +791,9 @@ IsDebuggerSandbox(JSObject* object)
 
 bool
 GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
   JS_ReportErrorNumber(aCx, js::GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
   return false;
 }
 
-namespace {
-
-class WorkerScopeUnregisterRunnable;
-class UnregisterResultRunnable final : public WorkerRunnable
-{
-public:
-  enum State { Succeeded, Failed };
-
-  UnregisterResultRunnable(WorkerPrivate* aWorkerPrivate,
-                           WorkerScopeUnregisterRunnable* aRunnable,
-                           State aState, bool aValue)
-    : WorkerRunnable(aWorkerPrivate,
-                     WorkerThreadUnchangedBusyCount)
-    , mRunnable(aRunnable), mState(aState), mValue(aValue)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(mRunnable);
-  }
-
-  virtual bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
-
-private:
-  nsRefPtr<WorkerScopeUnregisterRunnable> mRunnable;
-  State mState;
-  bool mValue;
-};
-
-class WorkerScopeUnregisterRunnable final : public nsRunnable
-                                          , public nsIServiceWorkerUnregisterCallback
-                                          , public WorkerFeature
-{
-  WorkerPrivate* mWorkerPrivate;
-  nsString mScope;
-
-  // Worker thread only.
-  nsRefPtr<Promise> mWorkerPromise;
-  bool mCleanedUp;
-
-  ~WorkerScopeUnregisterRunnable()
-  {
-    MOZ_ASSERT(mCleanedUp);
-  }
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  WorkerScopeUnregisterRunnable(WorkerPrivate* aWorkerPrivate,
-                                Promise* aWorkerPromise,
-                                const nsAString& aScope)
-    : mWorkerPrivate(aWorkerPrivate)
-    , mScope(aScope)
-    , mWorkerPromise(aWorkerPromise)
-    , mCleanedUp(false)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(aWorkerPromise);
-
-    if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
-      MOZ_ASSERT(false, "cannot add the worker feature!");
-      mWorkerPromise = nullptr;
-      mCleanedUp = true;
-      return;
-    }
-  }
-
-  Promise*
-  WorkerPromise() const
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(!mCleanedUp);
-    return mWorkerPromise;
-  }
-
-  NS_IMETHODIMP
-  UnregisterSucceeded(bool aState) override
-  {
-    AssertIsOnMainThread();
-
-    nsRefPtr<UnregisterResultRunnable> runnable =
-      new UnregisterResultRunnable(mWorkerPrivate, this,
-                                   UnregisterResultRunnable::Succeeded, aState);
-    runnable->Dispatch(nullptr);
-    return NS_OK;
-  }
-
-  NS_IMETHODIMP
-  UnregisterFailed() override
-  {
-    AssertIsOnMainThread();
-
-    nsRefPtr<UnregisterResultRunnable> runnable =
-      new UnregisterResultRunnable(mWorkerPrivate, this,
-                                   UnregisterResultRunnable::Failed, false);
-    runnable->Dispatch(nullptr);
-    return NS_OK;
-  }
-
-  void
-  CleanUp(JSContext* aCx)
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (mCleanedUp) {
-      return;
-    }
-
-    mWorkerPrivate->RemoveFeature(aCx, this);
-    mWorkerPromise = nullptr;
-    mCleanedUp = true;
-  }
-
-  NS_IMETHODIMP
-  Run() override
-  {
-    AssertIsOnMainThread();
-
-    nsresult rv;
-    nsCOMPtr<nsIServiceWorkerManager> swm =
-      do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      UnregisterFailed();
-      return NS_OK;
-    }
-
-    // We don't need to check if the principal can load this mScope because a
-    // ServiceWorkerGlobalScope can always unregister itself.
-
-    rv = swm->Unregister(mWorkerPrivate->GetPrincipal(), this, mScope);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      UnregisterFailed();
-      return NS_OK;
-    }
-
-    return NS_OK;
-  }
-
-  virtual bool Notify(JSContext* aCx, workers::Status aStatus) override
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(aStatus > workers::Running);
-    CleanUp(aCx);
-    return true;
-  }
-};
-
-NS_IMPL_ISUPPORTS_INHERITED(WorkerScopeUnregisterRunnable, nsRunnable,
-                            nsIServiceWorkerUnregisterCallback)
-
-bool
-UnregisterResultRunnable::WorkerRun(JSContext* aCx,
-                                    WorkerPrivate* aWorkerPrivate)
-{
-  if (mState == Failed) {
-    mRunnable->WorkerPromise()->MaybeReject(aCx, JS::UndefinedHandleValue);
-  } else {
-    mRunnable->WorkerPromise()->MaybeResolve(mValue);
-  }
-
-  mRunnable->CleanUp(aCx);
-  return true;
-}
-
-} // anonymous namespace
-
-already_AddRefed<Promise>
-ServiceWorkerGlobalScope::Unregister(ErrorResult& aRv)
-{
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
-
-  nsRefPtr<Promise> promise = Promise::Create(this, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  nsRefPtr<WorkerScopeUnregisterRunnable> runnable =
-    new WorkerScopeUnregisterRunnable(mWorkerPrivate, promise, mScope);
-
-  // Ensure the AddFeature succeeded before dispatching.
-  // Otherwise we let the promise remain pending since script is going to stop
-  // soon anyway.
-  if (runnable->WorkerPromise()) {
-    NS_DispatchToMainThread(runnable);
-  }
-
-  return promise.forget();
-}
-
-namespace {
-
-class UpdateRunnable final : public nsRunnable
-{
-  nsString mScope;
-
-public:
-  explicit UpdateRunnable(const nsAString& aScope)
-    : mScope(aScope)
-  { }
-
-  NS_IMETHODIMP
-  Run()
-  {
-    AssertIsOnMainThread();
-
-    nsresult rv;
-    nsCOMPtr<nsIServiceWorkerManager> swm =
-      do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return NS_OK;
-    }
-
-    swm->Update(mScope);
-    return NS_OK;
-  }
-};
-
-} //anonymous namespace
-
-void
-ServiceWorkerGlobalScope::Update()
-{
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
-
-  nsRefPtr<UpdateRunnable> runnable =
-    new UpdateRunnable(mScope);
-  NS_DispatchToMainThread(runnable);
-}
-
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -14,16 +14,17 @@
 
 namespace mozilla {
 namespace dom {
 
 class Console;
 class Function;
 class Promise;
 class RequestOrUSVString;
+class ServiceWorkerRegistrationWorkerThread;
 
 namespace cache {
 
 class CacheStorage;
 
 } // namespace cache
 
 namespace indexedDB {
@@ -193,16 +194,17 @@ public:
 
   IMPL_EVENT_HANDLER(connect)
 };
 
 class ServiceWorkerGlobalScope final : public WorkerGlobalScope
 {
   const nsString mScope;
   nsRefPtr<ServiceWorkerClients> mClients;
+  nsRefPtr<ServiceWorkerRegistrationWorkerThread> mRegistration;
 
   ~ServiceWorkerGlobalScope();
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope,
                                            WorkerGlobalScope)
 
@@ -213,25 +215,22 @@ public:
                    JS::MutableHandle<JSObject*> aReflector) override;
 
   void
   GetScope(nsString& aScope) const
   {
     aScope = mScope;
   }
 
-  void
-  Update();
-
-  already_AddRefed<Promise>
-  Unregister(ErrorResult& aRv);
-
   ServiceWorkerClients*
   Clients();
 
+  ServiceWorkerRegistrationWorkerThread*
+  Registration();
+
   IMPL_EVENT_HANDLER(activate)
   IMPL_EVENT_HANDLER(beforeevicted)
   IMPL_EVENT_HANDLER(evicted)
   IMPL_EVENT_HANDLER(fetch)
   IMPL_EVENT_HANDLER(install)
   IMPL_EVENT_HANDLER(message)
 };
 
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -190,16 +190,17 @@ struct JSSettings
     return false;
   }
 };
 
 enum WorkerPreference
 {
   WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
   WORKERPREF_DOM_CACHES, // dom.caches.enabled
+  WORKERPREF_SERVICEWORKERS, // dom.serviceWorkers.enabled
   WORKERPREF_COUNT
 };
 
 // Implemented in WorkerPrivate.cpp
 
 struct WorkerLoadInfo
 {
   // All of these should be released in WorkerPrivateParent::ForgetMainThreadObjects.
--- a/dom/workers/test/serviceworkers/importscript_worker.js
+++ b/dom/workers/test/serviceworkers/importscript_worker.js
@@ -8,16 +8,16 @@ importScripts(['importscript.sjs']);
 
 onmessage = function(e) {
   self.clients.matchAll().then(function(res) {
     if (!res.length) {
       dump("ERROR: no clients are currently controlled.\n");
     }
 
     try {
-      importScript(['importscript.sjs']);
+      importScripts(['importscript.sjs']);
       res[0].postMessage("KO");
       return;
     } catch(e) {}
 
     res[0].postMessage(counter == 2 ? "OK" : "KO");
   });
 };
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -63,16 +63,19 @@ support-files =
   source_message_posting_worker.js
   scope/scope_worker.js
   redirect_serviceworker.sjs
   importscript.sjs
   importscript_worker.js
   client_focus_worker.js
   bug1151916_worker.js
   bug1151916_driver.html
+  worker_updatefoundevent.js
+  worker_updatefoundevent2.js
+  updatefoundevent.html
   empty.js
   periodic.sjs
   periodic/frame.html
   periodic/register.html
   periodic/unregister.html
 
 [test_unregister.html]
 [test_installation_simple.html]
@@ -81,25 +84,26 @@ skip-if = os != "linux" # Bug 1136780
 [test_https_fetch.html]
 [test_https_fetch_cloned_response.html]
 [test_match_all.html]
 [test_match_all_advanced.html]
 [test_install_event.html]
 [test_navigator.html]
 [test_scopes.html]
 [test_controller.html]
+[test_workerUnregister.html]
 [test_workerUpdate.html]
-[test_workerUnregister.html]
 [test_post_message.html]
 [test_post_message_advanced.html]
 [test_post_message_source.html]
 [test_match_all_client_properties.html]
 [test_close.html]
 [test_serviceworker_interfaces.html]
 [test_serviceworker_not_sharedworker.html]
 [test_match_all_client_id.html]
 [test_sandbox_intercept.html]
 [test_request_context.html]
 [test_importscript.html]
 [test_client_focus.html]
 [test_bug1151916.html]
+[test_workerupdatefoundevent.html]
 [test_empty_serviceworker.html]
 [test_periodic_update.html]
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -154,16 +154,18 @@ var interfaceNamesInGlobalScope =
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerGlobalScope",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "ServiceWorkerRegistration",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "TextDecoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextEncoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequestEventTarget",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
@@ -0,0 +1,84 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  var registration;
+  var promise;
+
+  function start() {
+    return navigator.serviceWorker.register("worker_updatefoundevent.js",
+                                            { scope: "./updatefoundevent.html" })
+      .then((swr) => registration = swr);
+  }
+
+  function startWaitForUpdateFound() {
+    registration.onupdatefound = function(e) {
+    }
+
+    promise = new Promise(function(resolve, reject) {
+      window.onmessage = function(e) {
+
+        if (e.data == "finish") {
+          ok(true, "Received updatefound");
+          resolve();
+        }
+      }
+    });
+
+    content = document.getElementById("content");
+    iframe = document.createElement("iframe");
+    content.appendChild(iframe);
+    iframe.setAttribute("src", "./updatefoundevent.html");
+
+    return Promise.resolve();
+  }
+
+  function registerNext() {
+    return navigator.serviceWorker.register("worker_updatefoundevent2.js",
+                                            { scope: "./updatefoundevent.html" });
+  }
+
+  function waitForUpdateFound() {
+    return promise;
+  }
+
+  function unregister() {
+    return registration.unregister().then(function(result) {
+      ok(result, "Unregister should return true.");
+    });
+  }
+
+  function runTest() {
+     start()
+      .then(startWaitForUpdateFound)
+      .then(registerNext)
+      .then(waitForUpdateFound)
+      .then(unregister)
+      .catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+      }).then(SimpleTest.finish);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/updatefoundevent.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title>
+</head>
+<body>
+<script>
+  navigator.serviceWorker.onmessage = function(e) {
+    dump("NSM iframe got message " + e.data + "\n");
+    window.parent.postMessage(e.data, "*");
+  };
+</script>
+</body>
--- a/dom/workers/test/serviceworkers/worker_unregister.js
+++ b/dom/workers/test/serviceworkers/worker_unregister.js
@@ -1,16 +1,16 @@
 onmessage = function(e) {
   clients.matchAll().then(function(c) {
-    if (c.length == 0) {
+    if (c.length === 0) {
       // We cannot proceed.
       return;
     }
 
-    unregister().then(function() {
+    registration.unregister().then(function() {
       c[0].postMessage('DONE');
     }, function() {
       c[0].postMessage('ERROR');
     }).then(function() {
       c[0].postMessage('FINISH');
     });
   });
 }
--- a/dom/workers/test/serviceworkers/worker_update.js
+++ b/dom/workers/test/serviceworkers/worker_update.js
@@ -1,13 +1,13 @@
 // For now this test only calls update to verify that our registration
 // job queueing works properly when called from the worker thread. We should
 // test actual update scenarios with a SJS test.
 onmessage = function(e) {
-  self.update();
+  self.registration.update();
   clients.matchAll().then(function(c) {
     if (c.length == 0) {
       // We cannot proceed.
       return;
     }
 
     c[0].postMessage('FINISH');
   });
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker_updatefoundevent.js
@@ -0,0 +1,23 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+onactivate = function(e) {
+  e.waitUntil(new Promise(function(resolve, reject) {
+    registration.onupdatefound = function(e) {
+      clients.matchAll().then(function(clients) {
+        if (!clients.length) {
+          reject("No clients found");
+        }
+
+        if (registration.scope.match(/updatefoundevent\.html$/)) {
+          clients[0].postMessage("finish");
+          resolve();
+        } else {
+          dump("Scope did not match");
+        }
+      }, reject);
+    }
+  }));
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker_updatefoundevent2.js
@@ -0,0 +1,1 @@
+// Not useful.
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -142,16 +142,18 @@ var interfaceNamesInGlobalScope =
     "Performance",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "ServiceWorkerRegistration", pref: "dom.serviceWorkers.enabled" },
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "TextDecoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextEncoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequestEventTarget",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -88,17 +88,17 @@ XBLEnumerate(JSContext *cx, JS::Handle<J
 }
 
 static const JSClass gPrototypeJSClass = {
     "XBL prototype JSClass",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     // Our one reserved slot holds the relevant nsXBLPrototypeBinding
     JSCLASS_HAS_RESERVED_SLOTS(1),
     nullptr, nullptr, nullptr, nullptr,
-    XBLEnumerate, nullptr,
+    XBLEnumerate, nullptr, nullptr,
     nullptr, XBLFinalize,
     nullptr, nullptr, nullptr, nullptr
 };
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -2592,33 +2592,52 @@ nsEditor::CreateTxnForJoinNode(nsINode& 
 }
 
 
 // END nsEditor core implementation
 
 
 // BEGIN nsEditor public helper methods
 
+struct SavedRange {
+  nsRefPtr<Selection> mSelection;
+  nsCOMPtr<nsINode> mStartNode;
+  nsCOMPtr<nsINode> mEndNode;
+  int32_t mStartOffset;
+  int32_t mEndOffset;
+};
+
 nsresult
 nsEditor::SplitNodeImpl(nsIContent& aExistingRightNode,
                         int32_t aOffset,
                         nsIContent& aNewLeftNode)
 {
-  // Get selection
-  nsRefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-
-  // Remember some selection points, if selection is set
-  nsCOMPtr<nsINode> selStartNode, selEndNode;
-  int32_t selStartOffset = 0, selEndOffset = 0;
-  if (selection->GetRangeAt(0)) {
-    selStartNode = selection->GetRangeAt(0)->GetStartParent();
-    selStartOffset = selection->GetRangeAt(0)->StartOffset();
-    selEndNode = selection->GetRangeAt(0)->GetEndParent();
-    selEndOffset = selection->GetRangeAt(0)->EndOffset();
+  // Remember all selection points.
+  nsAutoTArray<SavedRange, 10> savedRanges;
+  for (size_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES - 1; ++i) {
+    SelectionType type(1 << i);
+    SavedRange range;
+    range.mSelection = GetSelection(type);
+    if (type == nsISelectionController::SELECTION_NORMAL) {
+      NS_ENSURE_TRUE(range.mSelection, NS_ERROR_NULL_POINTER);
+    } else if (!range.mSelection) {
+      // For non-normal selections, skip over the non-existing ones.
+      continue;
+    }
+
+    for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
+      nsRefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
+      MOZ_ASSERT(r->IsPositioned());
+      range.mStartNode = r->GetStartParent();
+      range.mStartOffset = r->StartOffset();
+      range.mEndNode = r->GetEndParent();
+      range.mEndOffset = r->EndOffset();
+
+      savedRanges.AppendElement(range);
+    }
   }
 
   nsCOMPtr<nsINode> parent = aExistingRightNode.GetParentNode();
   NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
 
   ErrorResult rv;
   parent->InsertBefore(aNewLeftNode, &aExistingRightNode, rv);
   NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
@@ -2659,51 +2678,74 @@ nsEditor::SplitNodeImpl(nsIContent& aExi
   }
 
   // Handle selection
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   if (ps) {
     ps->FlushPendingNotifications(Flush_Frames);
   }
 
-  if (GetShouldTxnSetSelection()) {
-    // Editor wants us to set selection at split point
-    selection->Collapse(&aNewLeftNode, aOffset);
-  } else if (selStartNode) {
-    // Else adjust the selection if needed.  If selStartNode is null, then
-    // there was no selection.
-    if (selStartNode == &aExistingRightNode) {
-      if (selStartOffset < aOffset) {
-        selStartNode = &aNewLeftNode;
+  bool shouldSetSelection = GetShouldTxnSetSelection();
+
+  nsRefPtr<Selection> previousSelection;
+  for (size_t i = 0; i < savedRanges.Length(); ++i) {
+    // Adjust the selection if needed.
+    SavedRange& range = savedRanges[i];
+
+    // If we have not seen the selection yet, clear all of its ranges.
+    if (range.mSelection != previousSelection) {
+      nsresult rv = range.mSelection->RemoveAllRanges();
+      NS_ENSURE_SUCCESS(rv, rv);
+      previousSelection = range.mSelection;
+    }
+
+    if (shouldSetSelection &&
+        range.mSelection->Type() ==
+          nsISelectionController::SELECTION_NORMAL) {
+      // If the editor should adjust the selection, don't bother restoring
+      // the ranges for the normal selection here.
+      continue;
+    }
+
+    // Split the selection into existing node and new node.
+    if (range.mStartNode == &aExistingRightNode) {
+      if (range.mStartOffset < aOffset) {
+        range.mStartNode = &aNewLeftNode;
       } else {
-        selStartOffset -= aOffset;
+        range.mStartOffset -= aOffset;
       }
     }
-    if (selEndNode == &aExistingRightNode) {
-      if (selEndOffset < aOffset) {
-        selEndNode = &aNewLeftNode;
+
+    if (range.mEndNode == &aExistingRightNode) {
+      if (range.mEndOffset < aOffset) {
+        range.mEndNode = &aNewLeftNode;
       } else {
-        selEndOffset -= aOffset;
+        range.mEndOffset -= aOffset;
       }
     }
-    selection->Collapse(selStartNode, selStartOffset);
-    selection->Extend(selEndNode, selEndOffset);
+
+    nsRefPtr<nsRange> newRange;
+    nsresult rv = nsRange::CreateRange(range.mStartNode, range.mStartOffset,
+                                       range.mEndNode, range.mEndOffset,
+                                       getter_AddRefs(newRange));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = range.mSelection->AddRange(newRange);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (shouldSetSelection) {
+    // Editor wants us to set selection at split point.
+    nsRefPtr<Selection> selection = GetSelection();
+    NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
+    selection->Collapse(&aNewLeftNode, aOffset);
   }
 
   return NS_OK;
 }
 
-struct SavedRange {
-  nsRefPtr<Selection> mSelection;
-  nsCOMPtr<nsINode> mStartNode;
-  nsCOMPtr<nsINode> mEndNode;
-  int32_t mStartOffset;
-  int32_t mEndOffset;
-};
-
 nsresult
 nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
                         nsINode* aNodeToJoin,
                         nsINode* aParent)
 {
   MOZ_ASSERT(aNodeToKeep);
   MOZ_ASSERT(aNodeToJoin);
   MOZ_ASSERT(aParent);
@@ -2725,19 +2767,17 @@ nsEditor::JoinNodesImpl(nsINode* aNodeTo
       NS_ENSURE_TRUE(range.mSelection, NS_ERROR_NULL_POINTER);
     } else if (!range.mSelection) {
       // For non-normal selections, skip over the non-existing ones.
       continue;
     }
 
     for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
       nsRefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
-      if (!r->IsPositioned()) {
-        continue;
-      }
+      MOZ_ASSERT(r->IsPositioned());
       range.mStartNode = r->GetStartParent();
       range.mStartOffset = r->StartOffset();
       range.mEndNode = r->GetEndParent();
       range.mEndOffset = r->EndOffset();
 
       // If selection endpoint is between the nodes, remember it as being
       // in the one that is going away instead.  This simplifies later selection
       // adjustment logic at end of this method.
--- a/editor/libeditor/tests/chrome.ini
+++ b/editor/libeditor/tests/chrome.ini
@@ -19,16 +19,17 @@ skip-if = buildapp == 'mulet'
 [test_bug636465.xul]
 [test_bug646194.xul]
 [test_bug780908.xul]
 [test_bug830600.html]
 [test_bug1053048.html]
 [test_bug1100966.html]
 [test_bug1102906.html]
 [test_bug1101392.html]
+[test_bug1154791.html]
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_text_input_handling.html]
 [test_dragdrop.html]
 skip-if = buildapp == 'mulet'
 [test_htmleditor_keyevent_handling.html]
 [test_selection_move_commands.xul]
 [test_texteditor_keyevent_handling.html]
 skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1154791.html
@@ -0,0 +1,64 @@
+<!DOCTYPE>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1154791
+-->
+<head>
+  <title>Test for Bug 1154791</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="display">
+</div>
+
+<div id="content" contenteditable>
+<tt>thiss onee is stilll a</tt>
+</div>
+
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 1154791 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  var div = document.getElementById("content");
+  div.focus();
+  synthesizeMouseAtCenter(div, {});
+  synthesizeKey("VK_LEFT", {});
+  synthesizeKey("VK_LEFT", {});
+
+  setTimeout(function() {
+    synthesizeKey("VK_BACK_SPACE", {});
+    setTimeout(function() {
+      synthesizeKey(" ", {});
+
+      var sel = getSpellCheckSelection();
+      is(sel.rangeCount, 2, "We should have two misspelled words");
+      is(String(sel.getRangeAt(0)), "thiss", "Correct misspelled word");
+      is(String(sel.getRangeAt(1)), "onee", "Correct misspelled word");
+
+      SimpleTest.finish();
+    },0);
+  },0);
+
+});
+
+function getSpellCheckSelection() {
+  var Ci = Components.interfaces;
+  var editingSession = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIWebNavigation)
+                             .QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIEditingSession);
+  var editor = editingSession.getEditorForWindow(window);
+  var selcon = editor.selectionController;
+  return selcon.getSelection(selcon.SELECTION_SPELLCHECK);
+}
+
+</script>
+</body>
+
+</html>
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1669,16 +1669,19 @@ GLContext::InitExtensions()
             MarkExtensionUnsupported(OES_EGL_sync);
         }
 
         if (Renderer() == GLRenderer::AndroidEmulator) {
             // the Android emulator, which we use to run B2G reftests on,
             // doesn't expose the OES_rgb8_rgba8 extension, but it seems to
             // support it (tautologically, as it only runs on desktop GL).
             MarkExtensionSupported(OES_rgb8_rgba8);
+            // there seems to be a similar issue for EXT_texture_format_BGRA8888
+            // on the Android 4.3 emulator
+            MarkExtensionSupported(EXT_texture_format_BGRA8888);
         }
 
         if (Vendor() == GLVendor::VMware &&
             Renderer() == GLRenderer::GalliumLlvmpipe)
         {
             // The llvmpipe driver that is used on linux try servers appears to have
             // buggy support for s3tc/dxt1 compressed textures.
             // See Bug 975824.
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -332,17 +332,17 @@ public:
                           gfx::Rect* aClipRectOut = nullptr,
                           gfx::Rect* aRenderBoundsOut = nullptr) = 0;
 
   /**
    * Flush the current frame to the screen and tidy up.
    */
   virtual void EndFrame() = 0;
 
-  virtual void SetFBAcquireFence(Layer* aLayer) {}
+  virtual void SetDispAcquireFence(Layer* aLayer) {}
 
   virtual FenceHandle GetReleaseFence()
   {
     return FenceHandle();
   }
 
   /**
    * Post-rendering stuff if the rendering is done outside of this Compositor
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -262,69 +262,75 @@ Layer::ClearAnimationsForNextTransaction
   // Ensure we have a non-null mPendingAnimations to mark a future clear.
   if (!mPendingAnimations) {
     mPendingAnimations = new AnimationArray;
   }
 
   mPendingAnimations->Clear();
 }
 
+static inline void
+SetCSSAngle(const CSSAngle& aAngle, nsCSSValue& aValue)
+{
+  aValue.SetFloatValue(aAngle.value(), nsCSSUnit(aAngle.unit()));
+}
+
 static nsCSSValueSharedList*
 CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
 {
   nsAutoPtr<nsCSSValueList> result;
   nsCSSValueList** resultTail = getter_Transfers(result);
   for (uint32_t i = 0; i < aFunctions.Length(); i++) {
     nsRefPtr<nsCSSValue::Array> arr;
     switch (aFunctions[i].type()) {
       case TransformFunction::TRotationX:
       {
-        float theta = aFunctions[i].get_RotationX().radians();
+        const CSSAngle& angle = aFunctions[i].get_RotationX().angle();
         arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatex,
                                                            resultTail);
-        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        SetCSSAngle(angle, arr->Item(1));
         break;
       }
       case TransformFunction::TRotationY:
       {
-        float theta = aFunctions[i].get_RotationY().radians();
+        const CSSAngle& angle = aFunctions[i].get_RotationY().angle();
         arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatey,
                                                            resultTail);
-        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        SetCSSAngle(angle, arr->Item(1));
         break;
       }
       case TransformFunction::TRotationZ:
       {
-        float theta = aFunctions[i].get_RotationZ().radians();
+        const CSSAngle& angle = aFunctions[i].get_RotationZ().angle();
         arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatez,
                                                            resultTail);
-        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        SetCSSAngle(angle, arr->Item(1));
         break;
       }
       case TransformFunction::TRotation:
       {
-        float theta = aFunctions[i].get_Rotation().radians();
+        const CSSAngle& angle = aFunctions[i].get_Rotation().angle();
         arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate,
                                                            resultTail);
-        arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
+        SetCSSAngle(angle, arr->Item(1));
         break;
       }
       case TransformFunction::TRotation3D:
       {
         float x = aFunctions[i].get_Rotation3D().x();
         float y = aFunctions[i].get_Rotation3D().y();
         float z = aFunctions[i].get_Rotation3D().z();
-        float theta = aFunctions[i].get_Rotation3D().radians();
+        const CSSAngle& angle = aFunctions[i].get_Rotation3D().angle();
         arr =
           StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate3d,
                                                        resultTail);
         arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
         arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
         arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
-        arr->Item(4).SetFloatValue(theta, eCSSUnit_Radian);
+        SetCSSAngle(angle, arr->Item(4));
         break;
       }
       case TransformFunction::TScale:
       {
         arr =
           StyleAnimationValue::AppendTransformFunction(eCSSKeyword_scale3d,
                                                        resultTail);
         arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
@@ -339,36 +345,38 @@ CreateCSSValueList(const InfallibleTArra
                                                        resultTail);
         arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
         arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
         arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
         break;
       }
       case TransformFunction::TSkewX:
       {
-        float x = aFunctions[i].get_SkewX().x();
+        const CSSAngle& x = aFunctions[i].get_SkewX().x();
         arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewx,
                                                            resultTail);
-        arr->Item(1).SetFloatValue(x, eCSSUnit_Radian);
+        SetCSSAngle(x, arr->Item(1));
         break;
       }
       case TransformFunction::TSkewY:
       {
-        float y = aFunctions[i].get_SkewY().y();
+        const CSSAngle& y = aFunctions[i].get_SkewY().y();
         arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewy,
                                                            resultTail);
-        arr->Item(1).SetFloatValue(y, eCSSUnit_Radian);
+        SetCSSAngle(y, arr->Item(1));
         break;
       }
       case TransformFunction::TSkew:
       {
+        const CSSAngle& x = aFunctions[i].get_Skew().x();
+        const CSSAngle& y = aFunctions[i].get_Skew().y();
         arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skew,
                                                            resultTail);
-        arr->Item(1).SetFloatValue(aFunctions[i].get_Skew().x(), eCSSUnit_Radian);
-        arr->Item(2).SetFloatValue(aFunctions[i].get_Skew().y(), eCSSUnit_Radian);
+        SetCSSAngle(x, arr->Item(1));
+        SetCSSAngle(y, arr->Item(2));
         break;
       }
       case TransformFunction::TTransformMatrix:
       {
         arr =
           StyleAnimationValue::AppendTransformFunction(eCSSKeyword_matrix3d,
                                                        resultTail);
         const gfx::Matrix4x4& matrix = aFunctions[i].get_TransformMatrix().value();
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -763,17 +763,17 @@ LayerManagerComposite::Render()
   // Debugging
   RenderDebugOverlay(actualBounds);
 
   {
     PROFILER_LABEL("LayerManagerComposite", "EndFrame",
       js::ProfileEntry::Category::GRAPHICS);
 
     mCompositor->EndFrame();
-    mCompositor->SetFBAcquireFence(mRoot);
+    mCompositor->SetDispAcquireFence(mRoot);
   }
 
   if (composer2D) {
     composer2D->Render();
   }
 
   mCompositor->GetWidget()->PostRender(this);
 
@@ -940,17 +940,17 @@ LayerManagerComposite::RenderToPresentat
   egl->fClearColor(0.0, 0.0, 0.0, 0.0);
   egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
 
   const nsIntRect clipRect = nsIntRect(0, 0, (int)(scale * pageWidth), actualHeight);
   RootLayer()->Prepare(RenderTargetPixel::FromUntyped(clipRect));
   RootLayer()->RenderLayer(clipRect);
 
   mCompositor->EndFrame();
-  mCompositor->SetFBAcquireFence(mRoot);
+  mCompositor->SetDispAcquireFence(mRoot);
 }
 #endif
 
 static void
 SubtractTransformedRegion(nsIntRegion& aRegion,
                           const nsIntRegion& aRegionToSubtract,
                           const Matrix4x4& aTransform)
 {
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -89,36 +89,43 @@ struct StepFunction {
   int type;
 };
 
 union TimingFunction {
   CubicBezierFunction;
   StepFunction;
 };
 
+// Send the angle with units rather than sending all angles in radians
+// to avoid having floating point error introduced by unit switching.
+struct CSSAngle {
+  float value;
+  int unit; // an nsCSSUnit that is valid for angles
+};
+
 struct LayerColor { gfxRGBA value; };
 struct Perspective { float value; };
-struct RotationX { float radians; };
-struct RotationY { float radians; };
-struct RotationZ { float radians; };
-struct Rotation { float radians; };
+struct RotationX { CSSAngle angle; };
+struct RotationY { CSSAngle angle; };
+struct RotationZ { CSSAngle angle; };
+struct Rotation { CSSAngle angle; };
 struct Rotation3D {
   float x;
   float y;
   float z;
-  float radians;
+  CSSAngle angle;
 };
 struct Scale {
   float x;
   float y;
   float z;
 };
-struct Skew { float x; float y; };
-struct SkewX { float x; };
-struct SkewY { float y; };
+struct Skew { CSSAngle x; CSSAngle y; };
+struct SkewX { CSSAngle x; };
+struct SkewY { CSSAngle y; };
 struct TransformMatrix { Matrix4x4 value; };
 struct Translation {
   float x;
   float y;
   float z;
 };
 
 union TransformFunction {
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1279,30 +1279,30 @@ CompositorOGL::EndFrame()
   mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
   if (!mGLContext->IsGLES()) {
     mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
   }
 }
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 void
-CompositorOGL::SetFBAcquireFence(Layer* aLayer)
+CompositorOGL::SetDispAcquireFence(Layer* aLayer)
 {
   // OpenGL does not provide ReleaseFence for rendering.
-  // Instead use FBAcquireFence as layer buffer's ReleaseFence
+  // Instead use DispAcquireFence as layer buffer's ReleaseFence
   // to prevent flickering and tearing.
-  // FBAcquireFence is FramebufferSurface's AcquireFence.
+  // DispAcquireFence is DisplaySurface's AcquireFence.
   // AcquireFence will be signaled when a buffer's content is available.
   // See Bug 974152.
 
   if (!aLayer) {
     return;
   }
 
-  android::sp<android::Fence> fence = new android::Fence(GetGonkDisplay()->GetPrevFBAcquireFd());
+  android::sp<android::Fence> fence = new android::Fence(GetGonkDisplay()->GetPrevDispAcquireFd());
   if (fence.get() && fence->isValid()) {
     FenceHandle handle = FenceHandle(fence);
     mReleaseFenceHandle.Merge(handle);
   }
 }
 
 FenceHandle
 CompositorOGL::GetReleaseFence()
@@ -1310,17 +1310,17 @@ CompositorOGL::GetReleaseFence()
   if (!mReleaseFenceHandle.IsValid()) {
     return FenceHandle();
   }
   return FenceHandle(new android::Fence(mReleaseFenceHandle.mFence->dup()));
 }
 
 #else
 void
-CompositorOGL::SetFBAcquireFence(Layer* aLayer)
+CompositorOGL::SetDispAcquireFence(Layer* aLayer)
 {
 }
 
 FenceHandle
 CompositorOGL::GetReleaseFence()
 {
   return FenceHandle();
 }
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -231,17 +231,17 @@ public:
 
   virtual void DrawQuad(const gfx::Rect& aRect,
                         const gfx::Rect& aClipRect,
                         const EffectChain &aEffectChain,
                         gfx::Float aOpacity,
                         const gfx::Matrix4x4 &aTransform) override;
 
   virtual void EndFrame() override;
-  virtual void SetFBAcquireFence(Layer* aLayer) override;
+  virtual void SetDispAcquireFence(Layer* aLayer) override;
   virtual FenceHandle GetReleaseFence() override;
   virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
 
   virtual bool SupportsPartialTextureUpdate() override;
 
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
   {
     if (!mGLContext)
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -518,16 +518,17 @@ HasNonOpaqueColor(gfxContext *aContext, 
         
     return false;
 }
 
 // helper class for double-buffering drawing with non-opaque color
 struct BufferAlphaColor {
     explicit BufferAlphaColor(gfxContext *aContext)
         : mContext(aContext)
+        , mAlpha(0.0)
     {
 
     }
 
     ~BufferAlphaColor() {}
 
     void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, uint32_t appsPerDevUnit)
     {
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -59,16 +59,17 @@ UNIFIED_SOURCES += [
     'src/base/tracked.cc',
     'src/base/tracked_objects.cc',
     'src/chrome/common/child_process.cc',
     'src/chrome/common/child_process_host.cc',
     'src/chrome/common/child_process_info.cc',
     'src/chrome/common/child_thread.cc',
     'src/chrome/common/chrome_switches.cc',
     'src/chrome/common/env_vars.cc',
+    'src/chrome/common/ipc_channel.cc',
     'src/chrome/common/ipc_channel_proxy.cc',
     'src/chrome/common/ipc_message.cc',
     'src/chrome/common/ipc_sync_channel.cc',
     'src/chrome/common/ipc_sync_message.cc',
     'src/chrome/common/message_router.cc',
     'src/chrome/common/notification_service.cc',
 ]
 
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/base/atomic_sequence_num.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ATOMIC_SEQUENCE_NUM_H_
+#define BASE_ATOMIC_SEQUENCE_NUM_H_
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+
+namespace base {
+
+class AtomicSequenceNumber;
+
+// Static (POD) AtomicSequenceNumber that MUST be used in global scope (or
+// non-function scope) ONLY. This implementation does not generate any static
+// initializer.  Note that it does not implement any constructor which means
+// that its fields are not initialized except when it is stored in the global
+// data section (.data in ELF). If you want to allocate an atomic sequence
+// number on the stack (or heap), please use the AtomicSequenceNumber class
+// declared below.
+class StaticAtomicSequenceNumber {
+ public:
+  inline int GetNext() {
+    return static_cast<int>(
+        base::subtle::NoBarrier_AtomicIncrement(&seq_, 1) - 1);
+  }
+
+ private:
+  friend class AtomicSequenceNumber;
+
+  inline void Reset() {
+    base::subtle::Release_Store(&seq_, 0);
+  }
+
+  base::subtle::Atomic32 seq_;
+};
+
+// AtomicSequenceNumber that can be stored and used safely (i.e. its fields are
+// always initialized as opposed to StaticAtomicSequenceNumber declared above).
+// Please use StaticAtomicSequenceNumber if you want to declare an atomic
+// sequence number in the global scope.
+class AtomicSequenceNumber {
+ public:
+  AtomicSequenceNumber() {
+    seq_.Reset();
+  }
+
+  inline int GetNext() {
+    return seq_.GetNext();
+  }
+
+ private:
+  StaticAtomicSequenceNumber seq_;
+  DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber);
+};
+
+}  // namespace base
+
+#endif  // BASE_ATOMIC_SEQUENCE_NUM_H_
--- a/ipc/chromium/src/chrome/common/child_process_host.cc
+++ b/ipc/chromium/src/chrome/common/child_process_host.cc
@@ -69,17 +69,17 @@ ChildProcessHost::~ChildProcessHost() {
     // Above call took ownership, so don't want WaitableEvent to assert because
     // the handle isn't valid anymore.
     process_event_->Release();
 #endif
   }
 }
 
 bool ChildProcessHost::CreateChannel() {
-  channel_id_ = GenerateRandomChannelID(this);
+  channel_id_ = IPC::Channel::GenerateVerifiedChannelID(std::wstring());
   channel_.reset(new IPC::Channel(
       channel_id_, IPC::Channel::MODE_SERVER, &listener_));
   if (!channel_->Connect())
     return false;
 
   opening_channel_ = true;
 
   return true;
--- a/ipc/chromium/src/chrome/common/child_process_info.cc
+++ b/ipc/chromium/src/chrome/common/child_process_info.cc
@@ -2,19 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "chrome/common/child_process_info.h"
 
 #include <limits>
 
 #include "base/logging.h"
-#include "base/process_util.h"
-#include "base/rand_util.h"
-#include "base/string_util.h"
 
 std::wstring ChildProcessInfo::GetTypeNameInEnglish(
     ChildProcessInfo::ProcessType type) {
   switch (type) {
     case BROWSER_PROCESS:
       return L"Browser";
     case RENDER_PROCESS:
       return L"Tab";
@@ -39,21 +36,9 @@ ChildProcessInfo::ChildProcessInfo(Proce
   // just a simple object that contains information about it.  So add it to our
   // list of running processes.
   type_ = type;
   pid_ = -1;
 }
 
 
 ChildProcessInfo::~ChildProcessInfo() {
-}
-
-std::wstring ChildProcessInfo::GenerateRandomChannelID(void* instance) {
-  // Note: the string must start with the current process id, this is how
-  // child processes determine the pid of the parent.
-  // Build the channel ID.  This is composed of a unique identifier for the
-  // parent browser process, an identifier for the child instance, and a random
-  // component. We use a random component so that a hacked child process can't
-  // cause denial of service by causing future named pipe creation to fail.
-  return StringPrintf(L"%d.%x.%d",
-                      base::GetCurrentProcId(), instance,
-                      base::RandInt(0, std::numeric_limits<int>::max()));
-}
+}
\ No newline at end of file
--- a/ipc/chromium/src/chrome/common/child_process_info.h
+++ b/ipc/chromium/src/chrome/common/child_process_info.h
@@ -73,20 +73,16 @@ class ChildProcessInfo {
       return process_ .handle() < rhs.process_.handle();
     return false;
   }
 
   bool operator ==(const ChildProcessInfo& rhs) const {
     return process_.handle() == rhs.process_.handle();
   }
 
-  // Generates a unique channel name for a child renderer/plugin process.
-  // The "instance" pointer value is baked into the channel id.
-  static std::wstring GenerateRandomChannelID(void* instance);
-
  protected:
   void set_type(ProcessType type) { type_ = type; }
   void set_name(const std::wstring& name) { name_ = name; }
   void set_handle(base::ProcessHandle handle) {
     process_.set_handle(handle);
     pid_ = -1;
   }
 
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_channel.h"
+
+#include <limits>
+
+#include "base/atomic_sequence_num.h"
+#include "base/process_util.h"
+#include "base/rand_util.h"
+#include "base/string_util.h"
+
+namespace {
+
+// Global atomic used to guarantee channel IDs are unique.
+base::StaticAtomicSequenceNumber g_last_id;
+
+}  // namespace
+
+namespace IPC {
+
+// static
+std::wstring Channel::GenerateUniqueRandomChannelID() {
+  // Note: the string must start with the current process id, this is how
+  // some child processes determine the pid of the parent.
+  //
+  // This is composed of a unique incremental identifier, the process ID of
+  // the creator, an identifier for the child instance, and a strong random
+  // component. The strong random component prevents other processes from
+  // hijacking or squatting on predictable channel names.
+
+  return StringPrintf(L"%d.%u.%d",
+      base::GetCurrentProcId(),
+      g_last_id.GetNext(),
+      base::RandInt(0, std::numeric_limits<int32_t>::max()));
+}
+
+}  // namespace IPC
\ No newline at end of file
--- a/ipc/chromium/src/chrome/common/ipc_channel.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -1,15 +1,17 @@
 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef CHROME_COMMON_IPC_CHANNEL_H_
 #define CHROME_COMMON_IPC_CHANNEL_H_
 
+#include <string>
+
 #include <queue>
 #include "chrome/common/ipc_message.h"
 
 namespace IPC {
 
 //------------------------------------------------------------------------------
 
 class Channel : public Message::Sender {
@@ -126,16 +128,25 @@ class Channel : public Message::Sender {
   // Close the client side of the socketpair.
   void CloseClientFileDescriptor();
 
 #elif defined(OS_WIN)
   // Return the server pipe handle.
   void* GetServerPipeHandle() const;
 #endif  // defined(OS_POSIX)
 
+  // Generates a channel ID that's non-predictable and unique.
+  static std::wstring GenerateUniqueRandomChannelID();
+
+  // Generates a channel ID that, if passed to the client as a shared secret,
+  // will validate that the client's authenticity. On platforms that do not
+  // require additional validation this is simply calls GenerateUniqueRandomChannelID().
+  // For portability the prefix should not include the \ character.
+  static std::wstring GenerateVerifiedChannelID(const std::wstring& prefix);
+
  private:
   // PIMPL to which all channel calls are delegated.
   class ChannelImpl;
   ChannelImpl *channel_impl_;
 
   enum {
 #if defined(OS_MACOSX)
     // If the channel receives a message that contains file descriptors, then
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -1051,9 +1051,21 @@ void Channel::CloseClientFileDescriptor(
 bool Channel::Unsound_IsClosed() const {
   return channel_impl_->Unsound_IsClosed();
 }
 
 uint32_t Channel::Unsound_NumQueuedMessages() const {
   return channel_impl_->Unsound_NumQueuedMessages();
 }
 
+// static
+std::wstring Channel::GenerateVerifiedChannelID(const std::wstring& prefix) {
+  // A random name is sufficient validation on posix systems, so we don't need
+  // an additional shared secret.
+
+  std::wstring id = prefix;
+  if (!id.empty())
+    id.append(L".");
+
+  return id.append(GenerateUniqueRandomChannelID());
+}
+
 }  // namespace IPC
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -4,16 +4,19 @@
 
 #include "chrome/common/ipc_channel_win.h"
 
 #include <windows.h>
 #include <sstream>
 
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/process_util.h"
+#include "base/rand_util.h"
+#include "base/string_util.h"
 #include "base/non_thread_safe.h"
 #include "base/win_util.h"
 #include "chrome/common/ipc_message_utils.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
 namespace IPC {
 //------------------------------------------------------------------------------
 
@@ -28,32 +31,36 @@ Channel::ChannelImpl::State::~State() {
 }
 
 //------------------------------------------------------------------------------
 
 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
                               Listener* listener)
     : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
+      shared_secret_(0),
+      waiting_for_shared_secret_(false) {
   Init(mode, listener);
 
   if (!CreatePipe(channel_id, mode)) {
     // The pipe may have been closed already.
     CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
                              "\" in " << (mode == 0 ? "server" : "client") << " mode.";
   }
 }
 
 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id,
                                   HANDLE server_pipe,
                                   Mode mode, Listener* listener)
     : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
+      shared_secret_(0),
+      waiting_for_shared_secret_(false) {
   Init(mode, listener);
 
   if (mode == MODE_SERVER) {
     // Use the existing handle that was dup'd to us
     pipe_ = server_pipe;
     EnqueueHelloMessage();
   } else {
     // Take the normal init path to connect to the server pipe
@@ -148,28 +155,41 @@ bool Channel::ChannelImpl::Send(Message*
         return false;
     }
   }
 
   return true;
 }
 
 const std::wstring Channel::ChannelImpl::PipeName(
-    const std::wstring& channel_id) const {
+    const std::wstring& channel_id, int32_t* secret) const {
+  MOZ_ASSERT(secret);
+
   std::wostringstream ss;
-  // XXX(darin): get application name from somewhere else
-  ss << L"\\\\.\\pipe\\chrome." << channel_id;
+  ss << L"\\\\.\\pipe\\chrome.";
+
+  // Prevent the shared secret from ending up in the pipe name.
+  size_t index = channel_id.find_first_of(L'\\');
+  if (index != std::string::npos) {
+    StringToInt(channel_id.substr(index + 1), secret);
+    ss << channel_id.substr(0, index - 1);
+  } else {
+    // This case is here to support predictable named pipes in tests.
+    *secret = 0;
+    ss << channel_id;
+  }
   return ss.str();
 }
 
 bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
                                       Mode mode) {
   DCHECK(pipe_ == INVALID_HANDLE_VALUE);
-  const std::wstring pipe_name = PipeName(channel_id);
+  const std::wstring pipe_name = PipeName(channel_id, &shared_secret_);
   if (mode == MODE_SERVER) {
+    waiting_for_shared_secret_ = !!shared_secret_;
     pipe_ = CreateNamedPipeW(pipe_name.c_str(),
                              PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
                                 FILE_FLAG_FIRST_PIPE_INSTANCE,
                              PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
                              1,         // number of pipe instances
                              // output buffer size (XXX tune)
                              Channel::kReadBufferSize,
                              // input buffer size (XXX tune)
@@ -195,17 +215,25 @@ bool Channel::ChannelImpl::CreatePipe(co
   // Create the Hello message to be sent when Connect is called
   return EnqueueHelloMessage();
 }
 
 bool Channel::ChannelImpl::EnqueueHelloMessage() {
   mozilla::UniquePtr<Message> m = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE,
                                                                HELLO_MESSAGE_TYPE,
                                                                IPC::Message::PRIORITY_NORMAL);
-  if (!m->WriteInt(GetCurrentProcessId())) {
+
+  // If we're waiting for our shared secret from the other end's hello message
+  // then don't give the game away by sending it in ours.
+  int32_t secret = waiting_for_shared_secret_ ? 0 : shared_secret_;
+
+  // Also, don't send if the value is zero (for IPC backwards compatability).
+  if (!m->WriteInt(GetCurrentProcessId()) ||
+      (secret && !m->WriteUInt32(secret)))
+  {
     CloseHandle(pipe_);
     pipe_ = INVALID_HANDLE_VALUE;
     return false;
   }
 
   OutputQueuePush(m.release());
   return true;
 }
@@ -333,18 +361,29 @@ bool Channel::ChannelImpl::ProcessIncomi
         int len = static_cast<int>(message_tail - p);
         const Message m(p, len);
 #ifdef IPC_MESSAGE_DEBUG_EXTRA
         DLOG(INFO) << "received message on channel @" << this <<
                       " with type " << m.type();
 #endif
         if (m.routing_id() == MSG_ROUTING_NONE &&
             m.type() == HELLO_MESSAGE_TYPE) {
-          // The Hello message contains only the process id.
-          listener_->OnChannelConnected(MessageIterator(m).NextInt());
+          // The Hello message contains the process id and must include the
+          // shared secret, if we are waiting for it.
+          MessageIterator it = MessageIterator(m);
+          int32_t claimed_pid = it.NextInt();
+          if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
+            NOTREACHED();
+            // Something went wrong. Abort connection.
+            Close();
+            listener_->OnChannelError();
+            return false;
+          }
+          waiting_for_shared_secret_ = false;
+          listener_->OnChannelConnected(claimed_pid);
         } else {
           listener_->OnMessageReceived(m);
         }
         p = message_tail;
       } else {
         // Last message is partial.
         break;
       }
@@ -497,9 +536,26 @@ bool Channel::Send(Message* message) {
 bool Channel::Unsound_IsClosed() const {
   return channel_impl_->Unsound_IsClosed();
 }
 
 uint32_t Channel::Unsound_NumQueuedMessages() const {
   return channel_impl_->Unsound_NumQueuedMessages();
 }
 
+// static
+std::wstring Channel::GenerateVerifiedChannelID(const std::wstring& prefix) {
+  // Windows pipes can be enumerated by low-privileged processes. So, we
+  // append a strong random value after the \ character. This value is not
+  // included in the pipe name, but sent as part of the client hello, to
+  // prevent hijacking the pipe name to spoof the client.
+  std::wstring id = prefix;
+  if (!id.empty())
+    id.append(L".");
+  int secret;
+  do {  // Guarantee we get a non-zero value.
+    secret = base::RandInt(0, std::numeric_limits<int>::max());
+  } while (secret == 0);
+  id.append(GenerateUniqueRandomChannelID());
+  return id.append(StringPrintf(L"\\%d", secret));
+}
+
 }  // namespace IPC
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h
@@ -44,17 +44,18 @@ class Channel::ChannelImpl : public Mess
   uint32_t Unsound_NumQueuedMessages() const;
 
  private:
   void Init(Mode mode, Listener* listener);
 
   void OutputQueuePush(Message* msg);
   void OutputQueuePop();
 
-  const std::wstring PipeName(const std::wstring& channel_id) const;
+  const std::wstring PipeName(const std::wstring& channel_id,
+                              int32_t* secret) const;
   bool CreatePipe(const std::wstring& channel_id, Mode mode);
   bool EnqueueHelloMessage();
 
   bool ProcessConnection();
   bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_read);
   bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_written);
@@ -103,16 +104,26 @@ class Channel::ChannelImpl : public Mess
   // This variable is updated so it matches output_queue_.size(), except we can
   // read output_queue_length_ from any thread (if we're OK getting an
   // occasional out-of-date or bogus value).  We use output_queue_length_ to
   // implement Unsound_NumQueuedMessages.
   size_t output_queue_length_;
 
   ScopedRunnableMethodFactory<ChannelImpl> factory_;
 
+  // This is a unique per-channel value used to authenticate the client end of
+  // a connection. If the value is non-zero, the client passes it in the hello
+  // and the host validates. (We don't send the zero value to preserve IPC
+  // compatibility with existing clients that don't validate the channel.)
+  int32_t shared_secret_;
+
+  // In server-mode, we wait for the channel at the other side of the pipe to
+  // send us back our shared secret, if we are using one.
+  bool waiting_for_shared_secret_;
+
   mozilla::UniquePtr<NonThreadSafe> thread_check_;
 
   DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
 };
 
 }  // namespace IPC
 
 #endif  // CHROME_COMMON_IPC_CHANNEL_WIN_H_
--- a/ipc/glue/Transport_posix.cpp
+++ b/ipc/glue/Transport_posix.cpp
@@ -20,20 +20,17 @@ using base::ProcessHandle;
 
 namespace mozilla {
 namespace ipc {
 
 bool
 CreateTransport(base::ProcessId /*unused*/,
                 TransportDescriptor* aOne, TransportDescriptor* aTwo)
 {
-  // Gecko doesn't care about this random ID, and the argument to this
-  // function isn't really necessary, it can be just any random
-  // pointer value
-  wstring id = ChildProcessInfo::GenerateRandomChannelID(aOne);
+  wstring id = IPC::Channel::GenerateVerifiedChannelID(std::wstring());
   // Use MODE_SERVER to force creation of the socketpair
   Transport t(id, Transport::MODE_SERVER, nullptr);
   int fd1 = t.GetFileDescriptor();
   int fd2, dontcare;
   t.GetClientFileDescriptorMapping(&fd2, &dontcare);
   if (fd1 < 0 || fd2 < 0) {
     return false;
   }
--- a/ipc/glue/Transport_win.cpp
+++ b/ipc/glue/Transport_win.cpp
@@ -17,19 +17,17 @@ using base::ProcessHandle;
 
 namespace mozilla {
 namespace ipc {
 
 bool
 CreateTransport(base::ProcessId aProcIdOne,
                 TransportDescriptor* aOne, TransportDescriptor* aTwo)
 {
-  // This id is used to name the IPC pipe.  The pointer passed to this
-  // function isn't significant.
-  wstring id = ChildProcessInfo::GenerateRandomChannelID(aOne);
+  wstring id = IPC::Channel::GenerateVerifiedChannelID(std::wstring());
   // Use MODE_SERVER to force creation of the pipe
   Transport t(id, Transport::MODE_SERVER, nullptr);
   HANDLE serverPipe = t.GetServerPipeHandle();
   if (!serverPipe) {
     return false;
   }
 
   // NB: we create the server pipe immediately, instead of just
--- a/js/ipc/CPOWTimer.cpp
+++ b/js/ipc/CPOWTimer.cpp
@@ -14,12 +14,12 @@ CPOWTimer::~CPOWTimer()
     JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
     if (!cx)
         return;
 
     JSRuntime* runtime = JS_GetRuntime(cx);
     if (!js::IsStopwatchActive(runtime))
         return;
 
-    js::PerformanceData *performance = js::GetPerformanceData(runtime);
+    js::PerformanceData* performance = js::GetPerformanceData(runtime);
     uint64_t duration = PR_IntervalToMicroseconds(PR_IntervalNow() - startInterval);
     performance->totalCPOWTime += duration;
 }
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -19,16 +19,17 @@
 
 /*
  * A JSClass acts as a vtable for JS objects that allows JSAPI clients to
  * control various aspects of the behavior of an object like property lookup.
  * js::Class is an engine-private extension that allows more control over
  * object behavior and, e.g., allows custom slow layout.
  */
 
+struct JSAtomState;
 struct JSFreeOp;
 struct JSFunctionSpec;
 
 namespace js {
 
 struct Class;
 class FreeOp;
 class Shape;
@@ -276,16 +277,28 @@ typedef bool
 // JS looks for a property in an object, and if not found, tries to resolve
 // the given id. *resolvedp should be set to true iff the property was
 // was defined on |obj|.
 //
 typedef bool
 (* JSResolveOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                 bool* resolvedp);
 
+// A class with a resolve hook can optionally have a mayResolve hook. This hook
+// must have no side effects and must return true for a given id if the resolve
+// hook may resolve this id. This is useful when we're doing a "pure" lookup: if
+// mayResolve returns false, we know we don't have to call the effectful resolve
+// hook.
+//
+// maybeObj, if non-null, is the object on which we're doing the lookup. This
+// can be nullptr: during JIT compilation we sometimes know the Class but not
+// the object.
+typedef bool
+(* JSMayResolveOp)(const JSAtomState& names, jsid id, JSObject* maybeObj);
+
 // Convert obj to the given type, returning true with the resulting value in
 // *vp on success, and returning false on error or exception.
 typedef bool
 (* JSConvertOp)(JSContext* cx, JS::HandleObject obj, JSType type,
                 JS::MutableHandleValue vp);
 
 // Finalize obj, which the garbage collector has determined to be unreachable
 // from other live objects or from GC roots.  Obviously, finalizers must never
@@ -415,16 +428,17 @@ typedef void
                                                                               \
     /* Function pointer members (may be null). */                             \
     JSAddPropertyOp     addProperty;                                          \
     JSDeletePropertyOp  delProperty;                                          \
     JSGetterOp          getProperty;                                          \
     JSSetterOp          setProperty;                                          \
     JSEnumerateOp       enumerate;                                            \
     JSResolveOp         resolve;                                              \
+    JSMayResolveOp      mayResolve;                                           \
     JSConvertOp         convert;                                              \
     FinalizeOpType      finalize;                                             \
     JSNative            call;                                                 \
     JSHasInstanceOp     hasInstance;                                          \
     JSNative            construct;                                            \
     JSTraceOp           trace
 
 // Callback for the creation of constructor and prototype objects.
@@ -683,16 +697,18 @@ static_assert(offsetof(JSClass, delPrope
 static_assert(offsetof(JSClass, getProperty) == offsetof(Class, getProperty),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, setProperty) == offsetof(Class, setProperty),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, enumerate) == offsetof(Class, enumerate),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, resolve) == offsetof(Class, resolve),
               "Class and JSClass must be consistent");
+static_assert(offsetof(JSClass, mayResolve) == offsetof(Class, mayResolve),
+              "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, convert) == offsetof(Class, convert),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, finalize) == offsetof(Class, finalize),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, call) == offsetof(Class, call),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, construct) == offsetof(Class, construct),
               "Class and JSClass must be consistent");
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -214,17 +214,17 @@ class JS_PUBLIC_API(CallbackTracer) : pu
     friend class AutoOriginalTraceLocation;
     void*const* contextRealLocation_;
 };
 
 // Set the name portion of the tracer's context for the current edge.
 class AutoTracingName
 {
     CallbackTracer* trc_;
-    const char *prior_;
+    const char* prior_;
 
   public:
     AutoTracingName(CallbackTracer* trc, const char* name) : trc_(trc), prior_(trc->contextName_) {
         MOZ_ASSERT(name);
         trc->contextName_ = name;
     }
     ~AutoTracingName() {
         MOZ_ASSERT(trc_->contextName_);
@@ -285,17 +285,17 @@ class AutoTracingDetails
 // Some dynamic analyses depend on knowing the edge source location as it
 // exists in the object graph. When marking some types of things, e.g. Value
 // edges, it is necessary to copy into a temporary on the stack. This class
 // records the original location if we need to copy the tracee, so that the
 // relevant analyses can continue to operate correctly.
 class AutoOriginalTraceLocation
 {
 #ifdef JS_GC_ZEAL
-    CallbackTracer *trc_;
+    CallbackTracer* trc_;
 
   public:
     template <typename T>
     AutoOriginalTraceLocation(JSTracer* trc, T*const* realLocation) : trc_(nullptr) {
         if (trc->isCallbackTracer() && trc->asCallbackTracer()->contextRealLocation_ == nullptr) {
             trc_ = trc->asCallbackTracer();
             trc_->contextRealLocation_ = reinterpret_cast<void*const*>(realLocation);
         }
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -475,16 +475,23 @@ OnDetached()
 
 static void
 OnOutOfBounds()
 {
     JSContext* cx = JSRuntime::innermostAsmJSActivation()->cx();
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
 }
 
+static void
+OnImpreciseConversion()
+{
+    JSContext* cx = JSRuntime::innermostAsmJSActivation()->cx();
+    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SIMD_FAILED_CONVERSION);
+}
+
 static bool
 AsmJSHandleExecutionInterrupt()
 {
     AsmJSActivation* act = JSRuntime::innermostAsmJSActivation();
     act->module().setInterrupted(true);
     bool ret = CheckForInterrupt(act->cx());
     act->module().setInterrupted(false);
     return ret;
@@ -671,16 +678,18 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
       case AsmJSImm_StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case AsmJSImm_ReportOverRecursed:
         return RedirectCall(FuncCast(AsmJSReportOverRecursed), Args_General0);
       case AsmJSImm_OnDetached:
         return RedirectCall(FuncCast(OnDetached), Args_General0);
       case AsmJSImm_OnOutOfBounds:
         return RedirectCall(FuncCast(OnOutOfBounds), Args_General0);
+      case AsmJSImm_OnImpreciseConversion:
+        return RedirectCall(FuncCast(OnImpreciseConversion), Args_General0);
       case AsmJSImm_HandleExecutionInterrupt:
         return RedirectCall(FuncCast(AsmJSHandleExecutionInterrupt), Args_General0);
       case AsmJSImm_InvokeFromAsmJS_Ignore:
         return RedirectCall(FuncCast(InvokeFromAsmJS_Ignore), Args_General3);
       case AsmJSImm_InvokeFromAsmJS_ToInt32:
         return RedirectCall(FuncCast(InvokeFromAsmJS_ToInt32), Args_General3);
       case AsmJSImm_InvokeFromAsmJS_ToNumber:
         return RedirectCall(FuncCast(InvokeFromAsmJS_ToNumber), Args_General3);
@@ -993,16 +1002,17 @@ const Class AsmJSModuleObject::class_ = 
     JSCLASS_IS_ANONYMOUS | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     AsmJSModuleObject_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     AsmJSModuleObject_trace
 };
 
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1400,16 +1400,17 @@ class MOZ_STACK_CLASS ModuleCompiler
     MathNameMap                    standardLibraryMathNames_;
     AtomicsNameMap                 standardLibraryAtomicsNames_;
     SimdOperationNameMap           standardLibrarySimdOpNames_;
     NonAssertingLabel              stackOverflowLabel_;
     NonAssertingLabel              asyncInterruptLabel_;
     NonAssertingLabel              syncInterruptLabel_;
     NonAssertingLabel              onDetachedLabel_;
     NonAssertingLabel              onOutOfBoundsLabel_;
+    NonAssertingLabel              onConversionErrorLabel_;
 
     UniquePtr<char[], JS::FreePolicy> errorString_;
     uint32_t                       errorOffset_;
     bool                           errorOverRecursed_;
 
     int64_t                        usecBefore_;
     SlowFunctionVector             slowFunctions_;
 
@@ -1619,16 +1620,17 @@ class MOZ_STACK_CLASS ModuleCompiler
     AsmJSParser& parser() const { return parser_; }
     TokenStream& tokenStream() const { return parser_.tokenStream; }
     MacroAssembler& masm() { return masm_; }
     Label& stackOverflowLabel() { return stackOverflowLabel_; }
     Label& asyncInterruptLabel() { return asyncInterruptLabel_; }
     Label& syncInterruptLabel() { return syncInterruptLabel_; }
     Label& onDetachedLabel() { return onDetachedLabel_; }
     Label& onOutOfBoundsLabel() { return onOutOfBoundsLabel_; }
+    Label& onConversionErrorLabel() { return onConversionErrorLabel_; }
     bool hasError() const { return errorString_ != nullptr; }
     const AsmJSModule& module() const { return *module_.get(); }
     bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); }
     bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); }
     bool supportsSimd() const { return supportsSimd_; }
 
     ParseNode* moduleFunctionNode() const { return moduleFunctionNode_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
@@ -2520,16 +2522,17 @@ class FunctionCompiler
         graph_  = lifo_.new_<MIRGraph>(alloc_);
         info_   = lifo_.new_<CompileInfo>(locals_.count());
         const OptimizationInfo* optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
         const JitCompileOptions options;
         mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()),
                                            options, alloc_,
                                            graph_, info_, optimizationInfo,
                                            &m().onOutOfBoundsLabel(),
+                                           &m().onConversionErrorLabel(),
                                            m().usesSignalHandlersForOOB());
 
         if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_))
             return false;
 
         for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
             MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
             curBlock_->add(ins);
@@ -5945,20 +5948,20 @@ CheckSimdOperationCall(FunctionCompiler&
         return CheckSimdWith(f, call, opType, SimdLane::LaneY, def, type);
       case AsmJSSimdOperation_withZ:
         return CheckSimdWith(f, call, opType, SimdLane::LaneZ, def, type);
       case AsmJSSimdOperation_withW:
         return CheckSimdWith(f, call, opType, SimdLane::LaneW, def, type);
 
       case AsmJSSimdOperation_fromInt32x4:
         return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_int32x4, opType, def, type);
+      case AsmJSSimdOperation_fromFloat32x4:
+        return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_float32x4, opType, def, type);
       case AsmJSSimdOperation_fromInt32x4Bits:
         return CheckSimdCast<MSimdReinterpretCast>(f, call, AsmJSSimdType_int32x4, opType, def, type);
-      case AsmJSSimdOperation_fromFloat32x4:
-        return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_float32x4, opType, def, type);
       case AsmJSSimdOperation_fromFloat32x4Bits:
         return CheckSimdCast<MSimdReinterpretCast>(f, call, AsmJSSimdType_float32x4, opType, def, type);
 
       case AsmJSSimdOperation_shiftLeftByScalar:
         return CheckSimdBinary(f, call, opType, MSimdShift::lsh, def, type);
       case AsmJSSimdOperation_shiftRightArithmeticByScalar:
         return CheckSimdBinary(f, call, opType, MSimdShift::rsh, def, type);
       case AsmJSSimdOperation_shiftRightLogicalByScalar:
@@ -9030,31 +9033,31 @@ GenerateOnDetachedLabelExit(ModuleCompil
     // For now, OnDetached always throws (see OnDetached comment).
     masm.call(AsmJSImmPtr(AsmJSImm_OnDetached));
     masm.jump(throwLabel);
 
     return m.finishGeneratingInlineStub(&m.onDetachedLabel()) && !masm.oom();
 }
 
 static bool
-GenerateOnOutOfBoundsLabelExit(ModuleCompiler& m, Label* throwLabel)
+GenerateExceptionLabelExit(ModuleCompiler& m, Label* throwLabel, Label* exit, AsmJSImmKind func)
 {
     MacroAssembler& masm = m.masm();
-    masm.bind(&m.onOutOfBoundsLabel());
+    masm.bind(exit);
 
     // sp can be anything at this point, so ensure it is aligned when calling
     // into C++.  We unconditionally jump to throw so don't worry about restoring sp.
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
 
     // OnOutOfBounds always throws.
     masm.assertStackAlignment(ABIStackAlignment);
-    masm.call(AsmJSImmPtr(AsmJSImm_OnOutOfBounds));
+    masm.call(AsmJSImmPtr(func));
     masm.jump(throwLabel);
 
-    return m.finishGeneratingInlineStub(&m.onOutOfBoundsLabel()) && !masm.oom();
+    return m.finishGeneratingInlineStub(exit) && !masm.oom();
 }
 
 static const LiveRegisterSet AllRegsExceptSP(
     GeneralRegisterSet(Registers::AllMask&
                        ~(uint32_t(1) << Registers::StackPointer)),
     FloatRegisterSet(FloatRegisters::AllMask));
 
 // The async interrupt-callback exit is called from arbitrarily-interrupted asm.js
@@ -9285,17 +9288,19 @@ GenerateStubs(ModuleCompiler& m)
     }
 
     if (m.stackOverflowLabel().used() && !GenerateStackOverflowExit(m, &throwLabel))
         return false;
 
     if (m.onDetachedLabel().used() && !GenerateOnDetachedLabelExit(m, &throwLabel))
         return false;
 
-    if (!GenerateOnOutOfBoundsLabelExit(m, &throwLabel))
+    if (!GenerateExceptionLabelExit(m, &throwLabel, &m.onOutOfBoundsLabel(), AsmJSImm_OnOutOfBounds))
+        return false;
+    if (!GenerateExceptionLabelExit(m, &throwLabel, &m.onConversionErrorLabel(), AsmJSImm_OnImpreciseConversion))
         return false;
 
     if (!GenerateAsyncInterruptExit(m, &throwLabel))
         return false;
     if (m.syncInterruptLabel().used() && !GenerateSyncInterruptExit(m, &throwLabel))
         return false;
 
     if (!GenerateThrowStub(m, &throwLabel))
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -560,16 +560,17 @@ static const Class CollatorClass = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     collator_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 collator_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1052,16 +1053,17 @@ static const Class NumberFormatClass = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     numberFormat_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1511,16 +1513,17 @@ static const Class DateTimeFormatClass =
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     dateTimeFormat_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 dateTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -875,16 +875,17 @@ const Class MapIteratorObject::class_ = 
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     MapIteratorObject::finalize
 };
 
 const JSFunctionSpec MapIteratorObject::methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
@@ -1019,16 +1020,17 @@ const Class MapObject::class_ = {
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
+    nullptr, // mayResolve
     nullptr, // convert
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
@@ -1611,16 +1613,17 @@ const Class SetIteratorObject::class_ = 
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     SetIteratorObject::finalize
 };
 
 const JSFunctionSpec SetIteratorObject::methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
@@ -1751,16 +1754,17 @@ const Class SetObject::class_ = {
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
+    nullptr, // mayResolve
     nullptr, // convert
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1184,16 +1184,17 @@ const Class PlainObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     nullptr,  /* addProperty */
     nullptr,  /* delProperty */
     nullptr,  /* getProperty */
     nullptr,  /* setProperty */
     nullptr,  /* enumerate */
     nullptr,  /* resolve */
+    nullptr,  /* mayResolve */
     nullptr,  /* convert */
     nullptr,  /* finalize */
     nullptr,  /* call */
     nullptr,  /* hasInstance */
     nullptr,  /* construct */
     nullptr,  /* trace */
     {
         CreateObjectConstructor,
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -9,16 +9,17 @@
  * Specification matches polyfill:
  * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
  * The objects float32x4 and int32x4 are installed on the SIMD pseudo-module.
  */
 
 #include "builtin/SIMD.h"
 
 #include "mozilla/IntegerTypeTraits.h"
+
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "builtin/TypedObject.h"
 #include "js/Value.h"
 
 #include "jsobjinlines.h"
 
@@ -175,16 +176,17 @@ const Class SimdTypeDescr::class_ = {
     "SIMD",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     call
 };
 
 namespace {
 
 // These classes just exist to group together various properties and so on.
@@ -483,136 +485,145 @@ js::CreateSimd(JSContext* cx, const type
 template JSObject* js::CreateSimd<Float32x4>(JSContext* cx, const Float32x4::Elem* data);
 template JSObject* js::CreateSimd<Float64x2>(JSContext* cx, const Float64x2::Elem* data);
 template JSObject* js::CreateSimd<Int32x4>(JSContext* cx, const Int32x4::Elem* data);
 
 namespace js {
 // Unary SIMD operators
 template<typename T>
 struct Identity {
-    static inline T apply(T x) { return x; }
+    static T apply(T x) { return x; }
 };
 template<typename T>
 struct Abs {
-    static inline T apply(T x) { return mozilla::Abs(x); }
+    static T apply(T x) { return mozilla::Abs(x); }
 };
 template<typename T>
 struct Neg {
-    static inline T apply(T x) { return -1 * x; }
+    static T apply(T x) { return -1 * x; }
 };
 template<typename T>
 struct Not {
-    static inline T apply(T x) { return ~x; }
+    static T apply(T x) { return ~x; }
 };
 template<typename T>
 struct RecApprox {
-    static inline T apply(T x) { return 1 / x; }
+    static T apply(T x) { return 1 / x; }
 };
 template<typename T>
 struct RecSqrtApprox {
-    static inline T apply(T x) { return 1 / sqrt(x); }
+    static T apply(T x) { return 1 / sqrt(x); }
 };
 template<typename T>
 struct Sqrt {
-    static inline T apply(T x) { return sqrt(x); }
+    static T apply(T x) { return sqrt(x); }
 };
 
 // Binary SIMD operators
 template<typename T>
 struct Add {
-    static inline T apply(T l, T r) { return l + r; }
+    static T apply(T l, T r) { return l + r; }
 };
 template<typename T>
 struct Sub {
-    static inline T apply(T l, T r) { return l - r; }
+    static T apply(T l, T r) { return l - r; }
 };
 template<typename T>
 struct Div {
-    static inline T apply(T l, T r) { return l / r; }
+    static T apply(T l, T r) { return l / r; }
 };
 template<typename T>
 struct Mul {
-    static inline T apply(T l, T r) { return l * r; }
+    static T apply(T l, T r) { return l * r; }
 };
 template<typename T>
 struct Minimum {
-    static inline T apply(T l, T r) { return math_min_impl(l, r); }
+    static T apply(T l, T r) { return math_min_impl(l, r); }
 };
 template<typename T>
 struct MinNum {
-    static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); }
+    static T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); }
 };
 template<typename T>
 struct Maximum {
-    static inline T apply(T l, T r) { return math_max_impl(l, r); }
+    static T apply(T l, T r) { return math_max_impl(l, r); }
 };
 template<typename T>
 struct MaxNum {
-    static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); }
+    static T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); }
 };
 template<typename T>
 struct LessThan {
-    static inline int32_t apply(T l, T r) { return l < r ? 0xFFFFFFFF : 0x0; }
+    static int32_t apply(T l, T r) { return l < r ? 0xFFFFFFFF : 0x0; }
 };
 template<typename T>
 struct LessThanOrEqual {
-    static inline int32_t apply(T l, T r) { return l <= r ? 0xFFFFFFFF : 0x0; }
+    static int32_t apply(T l, T r) { return l <= r ? 0xFFFFFFFF : 0x0; }
 };
 template<typename T>
 struct GreaterThan {
-    static inline int32_t apply(T l, T r) { return l > r ? 0xFFFFFFFF : 0x0; }
+    static int32_t apply(T l, T r) { return l > r ? 0xFFFFFFFF : 0x0; }
 };
 template<typename T>
 struct GreaterThanOrEqual {
-    static inline int32_t apply(T l, T r) { return l >= r ? 0xFFFFFFFF : 0x0; }
+    static int32_t apply(T l, T r) { return l >= r ? 0xFFFFFFFF : 0x0; }
 };
 template<typename T>
 struct Equal {
-    static inline int32_t apply(T l, T r) { return l == r ? 0xFFFFFFFF : 0x0; }
+    static int32_t apply(T l, T r) { return l == r ? 0xFFFFFFFF : 0x0; }
 };
 template<typename T>
 struct NotEqual {
-    static inline int32_t apply(T l, T r) { return l != r ? 0xFFFFFFFF : 0x0; }
+    static int32_t apply(T l, T r) { return l != r ? 0xFFFFFFFF : 0x0; }
 };
 template<typename T>
 struct Xor {
-    static inline T apply(T l, T r) { return l ^ r; }
+    static T apply(T l, T r) { return l ^ r; }
 };
 template<typename T>
 struct And {
-    static inline T apply(T l, T r) { return l & r; }
+    static T apply(T l, T r) { return l & r; }
 };
 template<typename T>
 struct Or {
-    static inline T apply(T l, T r) { return l | r; }
+    static T apply(T l, T r) { return l | r; }
 };
 template<typename T>
 struct WithX {
-    static inline T apply(int32_t lane, T scalar, T x) { return lane == 0 ? scalar : x; }
+    static T apply(int32_t lane, T scalar, T x) { return lane == 0 ? scalar : x; }
 };
 template<typename T>
 struct WithY {
-    static inline T apply(int32_t lane, T scalar, T x) { return lane == 1 ? scalar : x; }
+    static T apply(int32_t lane, T scalar, T x) { return lane == 1 ? scalar : x; }
 };
 template<typename T>
 struct WithZ {
-    static inline T apply(int32_t lane, T scalar, T x) { return lane == 2 ? scalar : x; }
+    static T apply(int32_t lane, T scalar, T x) { return lane == 2 ? scalar : x; }
 };
 template<typename T>
 struct WithW {
-    static inline T apply(int32_t lane, T scalar, T x) { return lane == 3 ? scalar : x; }
+    static T apply(int32_t lane, T scalar, T x) { return lane == 3 ? scalar : x; }
 };
+// For the following three operators, if the value v we're trying to shift is
+// such that v << bits can't fit in the int32 range, then we have undefined
+// behavior, according to C++11 [expr.shift]p2.
 struct ShiftLeft {
-    static inline int32_t apply(int32_t v, int32_t bits) { return v << bits; }
+    static int32_t apply(int32_t v, int32_t bits) {
+        return uint32_t(bits) >= 32 ? 0 : v << bits;
+    }
 };
-struct ShiftRight {
-    static inline int32_t apply(int32_t v, int32_t bits) { return v >> bits; }
+struct ShiftRightArithmetic {
+    static int32_t apply(int32_t v, int32_t bits) {
+        return v >> (uint32_t(bits) >= 32 ? 31 : bits);
+    }
 };
 struct ShiftRightLogical {
-    static inline int32_t apply(int32_t v, int32_t bits) { return uint32_t(v) >> (bits & 31); }
+    static int32_t apply(int32_t v, int32_t bits) {
+        return uint32_t(bits) >= 32 ? 0 : uint32_t(v) >> bits;
+    }
 };
 }
 
 static inline bool
 ErrorBadArgs(JSContext* cx)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
     return false;
@@ -807,31 +818,98 @@ CompareFunc(JSContext* cx, unsigned argc
     for (unsigned i = 0; i < Int32x4::lanes; i++) {
         unsigned j = (i * In::lanes) / Int32x4::lanes;
         result[i] = Op<InElem>::apply(left[j], right[j]);
     }
 
     return StoreResult<Int32x4>(cx, args, result);
 }
 
+// This struct defines whether we should throw during a conversion attempt,
+// when trying to convert a value of type from From to the type To.  This
+// happens whenever a C++ conversion would have undefined behavior (and perhaps
+// be platform-dependent).
+template<typename From, typename To>
+struct ThrowOnConvert;
+
+struct NeverThrow
+{
+    static bool value(int32_t v) {
+        return false;
+    }
+};
+
+// While int32 to float conversions can be lossy, these conversions have
+// defined behavior in C++, so we don't need to care about them here. In practice,
+// this means round to nearest, tie with even (zero bit in significand).
+template<>
+struct ThrowOnConvert<int32_t, float> : public NeverThrow {};
+
+// All int32 can be safely converted to doubles.
+template<>
+struct ThrowOnConvert<int32_t, double> : public NeverThrow {};
+
+// All floats can be safely converted to doubles.
+template<>
+struct ThrowOnConvert<float, double> : public NeverThrow {};
+
+// Double to float conversion for inputs which aren't in the float range are
+// undefined behavior in C++, but they're defined in IEEE754.
+template<>
+struct ThrowOnConvert<double, float> : public NeverThrow {};
+
+// Float to integer conversions have undefined behavior if the float value
+// is out of the representable integer range (on x86, will yield the undefined
+// value pattern, namely 0x80000000; on arm, will clamp the input value), so
+// check this here.
+template<typename From, typename IntegerType>
+struct ThrowIfNotInRange
+{
+    static_assert(mozilla::IsIntegral<IntegerType>::value, "bad destination type");
+
+    static bool value(From v) {
+        double d(v);
+        return mozilla::IsNaN(d) ||
+               d < double(mozilla::MinValue<IntegerType>::value) ||
+               d > double(mozilla::MaxValue<IntegerType>::value);
+    }
+};
+
+template<>
+struct ThrowOnConvert<double, int32_t> : public ThrowIfNotInRange<double, int32_t> {};
+
+template<>
+struct ThrowOnConvert<float, int32_t> : public ThrowIfNotInRange<float, int32_t> {};
+
 template<typename V, typename Vret>
 static bool
 FuncConvert(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename V::Elem Elem;
     typedef typename Vret::Elem RetElem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     Elem* val = TypedObjectMemory<Elem*>(args[0]);
+
     RetElem result[Vret::lanes];
-    for (unsigned i = 0; i < Vret::lanes; i++)
-        result[i] = i < V::lanes ? ConvertScalar<RetElem>(val[i]) : 0;
+    for (unsigned i = 0; i < Min(V::lanes, Vret::lanes); i++) {
+        if (ThrowOnConvert<Elem, RetElem>::value(val[i])) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                                 JSMSG_SIMD_FAILED_CONVERSION);
+            return false;
+        }
+        result[i] = ConvertScalar<RetElem>(val[i]);
+    }
+
+    // Fill remaining lanes with 0
+    for (unsigned i = V::lanes; i < Vret::lanes; i++)
+        result[i] = 0;
 
     return StoreResult<Vret>(cx, args, result);
 }
 
 template<typename V, typename Vret>
 static bool
 FuncConvertBits(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -152,17 +152,17 @@
   V(loadXYZ, (Load<Int32x4, 3>), 2)                                                   \
   V(loadXY,  (Load<Int32x4, 2>), 2)                                                   \
   V(loadX,   (Load<Int32x4, 1>), 2)                                                   \
   V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2)                                      \
   V(notEqual, (CompareFunc<Int32x4, NotEqual>), 2)                                    \
   V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2)                                        \
   V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2)                                      \
   V(shiftLeftByScalar, (Int32x4BinaryScalar<ShiftLeft>), 2)                           \
-  V(shiftRightArithmeticByScalar, (Int32x4BinaryScalar<ShiftRight>), 2)               \
+  V(shiftRightArithmeticByScalar, (Int32x4BinaryScalar<ShiftRightArithmetic>), 2)     \
   V(shiftRightLogicalByScalar, (Int32x4BinaryScalar<ShiftRightLogical>), 2)           \
   V(store,    (Store<Int32x4, 4>), 3)                                                 \
   V(storeXYZ, (Store<Int32x4, 3>), 3)                                                 \
   V(storeXY,  (Store<Int32x4, 2>), 3)                                                 \
   V(storeX,   (Store<Int32x4, 1>), 3)                                                 \
   V(withX, (FuncWith<Int32x4, WithX>), 2)                                             \
   V(withY, (FuncWith<Int32x4, WithY>), 2)                                             \
   V(withZ, (FuncWith<Int32x4, WithZ>), 2)                                             \
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -20,16 +20,17 @@ const Class SymbolObject::class_ = {
     "Symbol",
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     convert
 };
 
 SymbolObject*
 SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol)
 {
     JSObject* obj = NewBuiltinClassInstance(cx, &class_);
     if (!obj)
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -206,17 +206,17 @@ GetBuildConfiguration(JSContext* cx, uns
 #ifdef MOZ_MEMORY
     value = BooleanValue(true);
 #else
     value = BooleanValue(false);
 #endif
     if (!JS_SetProperty(cx, info, "moz-memory", value))
         return false;
 
-    value.setInt32(sizeof(void *));
+    value.setInt32(sizeof(void*));
     if (!JS_SetProperty(cx, info, "pointer-byte-size", value))
         return false;
 
     args.rval().setObject(*info);
     return true;
 }
 
 static bool
@@ -1149,16 +1149,17 @@ finalize_counter_finalize(JSFreeOp* fop,
 static const JSClass FinalizeCounterClass = {
     "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     finalize_counter_finalize
 };
 
 static bool
 MakeFinalizeObserver(JSContext* cx, unsigned argc, jsval* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1830,16 +1831,17 @@ class CloneBufferObject : public NativeO
 const Class CloneBufferObject::class_ = {
     "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     Finalize
 };
 
 const JSPropertySpec CloneBufferObject::props_[] = {
     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
     JS_PS_END
 };
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -219,16 +219,17 @@ const Class js::ScalarTypeDescr::class_ 
     "Scalar",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     ScalarTypeDescr::call
 };
 
 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
@@ -316,16 +317,17 @@ const Class js::ReferenceTypeDescr::clas
     "Reference",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     ReferenceTypeDescr::call
 };
 
 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
@@ -495,16 +497,17 @@ const Class ArrayTypeDescr::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
@@ -722,16 +725,17 @@ const Class StructTypeDescr::class_ = {
     "StructType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
@@ -2241,16 +2245,17 @@ OutlineTransparentTypedObject::getOrCrea
         # Name,                                          \
         Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS, \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* getProperty */                \
         nullptr,        /* setProperty */                \
         nullptr,        /* enumerate   */                \
         nullptr,        /* resolve     */                \
+        nullptr,        /* mayResolve  */                \
         nullptr,        /* convert     */                \
         nullptr,        /* finalize    */                \
         nullptr,        /* call        */                \
         nullptr,        /* hasInstance */                \
         nullptr,        /* construct   */                \
         Trace,                                           \
         JS_NULL_CLASS_SPEC,                              \
         JS_NULL_CLASS_EXT,                               \
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -528,49 +528,49 @@ static const JSClass sCABIClass = {
 };
 
 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
 // This exists to give said prototypes a class of "CType", and to provide
 // reserved slots for stashing various other prototype objects.
 static const JSClass sCTypeProtoClass = {
   "CType",
   JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
   ConstructAbstract, nullptr, ConstructAbstract
 };
 
 // Class representing ctypes.CData.prototype and the 'prototype' properties
 // of CTypes. This exists to give said prototypes a class of "CData".
 static const JSClass sCDataProtoClass = {
   "CData",
   0
 };
 
 static const JSClass sCTypeClass = {
   "CType",
   JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CType::Finalize,
   CType::ConstructData, CType::HasInstance, CType::ConstructData,
   CType::Trace
 };
 
 static const JSClass sCDataClass = {
   "CData",
   JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
   nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
-  nullptr, nullptr, nullptr, CData::Finalize,
+  nullptr, nullptr, nullptr, nullptr, CData::Finalize,
   FunctionType::Call, nullptr, FunctionType::Call
 };
 
 static const JSClass sCClosureClass = {
   "CClosure",
   JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CClosure::Finalize,
   nullptr, nullptr, nullptr, CClosure::Trace
 };
 
 /*
  * Class representing the prototype of CDataFinalizer.
  */
 static const JSClass sCDataFinalizerProtoClass = {
@@ -582,17 +582,17 @@ static const JSClass sCDataFinalizerProt
  * Class representing instances of CDataFinalizer.
  *
  * Instances of CDataFinalizer have both private data (with type
  * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
  */
 static const JSClass sCDataFinalizerClass = {
   "CDataFinalizer",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CDataFinalizer::Finalize
 };
 
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CTYPESCTOR_FLAGS \
@@ -768,24 +768,24 @@ static const JSClass sInt64ProtoClass = 
 static const JSClass sUInt64ProtoClass = {
   "UInt64",
   0
 };
 
 static const JSClass sInt64Class = {
   "Int64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Int64Base::Finalize
 };
 
 static const JSClass sUInt64Class = {
   "UInt64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Int64Base::Finalize
 };
 
 static const JSFunctionSpec sInt64StaticFunctions[] = {
   JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
   JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
   JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
   // "join" is defined specially; see InitInt64Class.
@@ -4024,39 +4024,44 @@ CType::Finalize(JSFreeOp* fop, JSObject*
   }
   default:
     // Nothing to do here.
     break;
   }
 }
 
 void
+TraceFieldInfoHash(JSTracer* trc, FieldInfoHash* fields)
+{
+  for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) {
+    JSString* key = e.front().key();
+    JS_CallUnbarrieredStringTracer(trc, &key, "fieldName");
+    if (key != e.front().key())
+      e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key));
+    JS_CallObjectTracer(trc, &e.front().value().mType, "fieldType");
+  }
+}
+
+void
 CType::Trace(JSTracer* trc, JSObject* obj)
 {
   // Make sure our TypeCode slot is legit. If it's not, bail.
   jsval slot = obj->as<NativeObject>().getSlot(SLOT_TYPECODE);
   if (slot.isUndefined())
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(slot.toInt32())) {
   case TYPE_struct: {
     slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
     if (slot.isUndefined())
       return;
 
     FieldInfoHash* fields = static_cast<FieldInfoHash*>(slot.toPrivate());
-    for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) {
-      JSString* key = e.front().key();
-      JS_CallUnbarrieredStringTracer(trc, &key, "fieldName");
-      if (key != e.front().key())
-          e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key));
-      JS_CallObjectTracer(trc, &e.front().value().mType, "fieldType");
-    }
-
+    TraceFieldInfoHash(trc, fields);
     break;
   }
   case TYPE_function: {
     // Check if we have a FunctionInfo.
     slot = obj->as<NativeObject>().getReservedSlot(SLOT_FNINFO);
     if (slot.isUndefined())
       return;
 
@@ -5452,16 +5457,41 @@ PostBarrierCallback(JSTracer* trc, JSStr
                     SystemAllocPolicy> UnbarrieredFieldInfoHash;
 
     UnbarrieredFieldInfoHash* table = reinterpret_cast<UnbarrieredFieldInfoHash*>(data);
     JSString* prior = key;
     JS_CallUnbarrieredStringTracer(trc, &key, "CType fieldName");
     table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key));
 }
 
+// Holds a pointer to a FieldInfoHash while it is being constructed, tracing it
+// on GC and destroying it when it dies unless release() has been called first.
+class FieldInfoHolder : public JS::CustomAutoRooter
+{
+  public:
+    FieldInfoHolder(JSContext* cx, FieldInfoHash* fields)
+      : CustomAutoRooter(cx), fields_(fields) {}
+    ~FieldInfoHolder() {
+      delete fields_;
+    }
+    virtual void trace(JSTracer* trc) override {
+      if (fields_)
+        TraceFieldInfoHash(trc, fields_);
+    }
+    FieldInfoHash* operator->() { return fields_; }
+    operator FieldInfoHash*() { return fields_; }
+    FieldInfoHash* release() {
+      FieldInfoHash* result = fields_;
+      fields_ = nullptr;
+      return result;
+    }
+  private:
+    FieldInfoHash* fields_;
+};
+
 bool
 StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_)
 {
   RootedObject typeObj(cx, typeObj_);
   RootedObject fieldsObj(cx, fieldsObj_);
 
   uint32_t len;
   ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
@@ -5482,43 +5512,37 @@ StructType::DefineInternal(JSContext* cx
   if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
                          JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
   // Create a FieldInfoHash to stash on the type object, and an array to root
   // its constituents. (We cannot simply stash the hash in a reserved slot now
   // to get GC safety for free, since if anything in this function fails we
   // do not want to mutate 'typeObj'.)
-  auto fields = cx->make_unique<FieldInfoHash>();
+  FieldInfoHolder fields(cx, cx->new_<FieldInfoHash>());
   if (!fields || !fields->init(len)) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
-  JS::AutoValueVector fieldRoots(cx);
-  if (!fieldRoots.resize(len)) {
-    JS_ReportOutOfMemory(cx);
-    return false;
-  }
 
   // Process the field types.
   size_t structSize, structAlign;
   if (len != 0) {
     structSize = 0;
     structAlign = 0;
 
     for (uint32_t i = 0; i < len; ++i) {
       RootedValue item(cx);
       if (!JS_GetElement(cx, fieldsObj, i, &item))
         return false;
 
       RootedObject fieldType(cx, nullptr);
       Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, &fieldType));
       if (!name)
         return false;
-      fieldRoots[i].setObject(*fieldType);
 
       // Make sure each field name is unique
       FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
       if (entryPtr) {
         JS_ReportError(cx, "struct fields must have unique names");
         return false;
       }
 
@@ -5558,21 +5582,21 @@ StructType::DefineInternal(JSContext* cx
       // checking fieldOffset for overflow.
       if (fieldOffset + fieldSize < structSize) {
         JS_ReportError(cx, "size overflow");
         return false;
       }
 
       // Add field name to the hash
       FieldInfo info;
-      info.mType = nullptr; // Value of fields are not yet traceable here.
+      info.mType = fieldType;
       info.mIndex = i;
       info.mOffset = fieldOffset;
       ASSERT_OK(fields->add(entryPtr, name, info));
-      JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields.get());
+      JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields);
 
       structSize = fieldOffset + fieldSize;
 
       if (fieldAlign > structAlign)
         structAlign = fieldAlign;
     }
 
     // Pad the struct tail according to struct alignment.
@@ -5591,22 +5615,16 @@ StructType::DefineInternal(JSContext* cx
     structSize = 1;
     structAlign = 1;
   }
 
   RootedValue sizeVal(cx);
   if (!SizeTojsval(cx, structSize, &sizeVal))
     return false;
 
-  for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
-    FieldInfo& field = r.front().value();
-    MOZ_ASSERT(field.mIndex < fieldRoots.length());
-    field.mType = &fieldRoots[field.mIndex].toObject();
-  }
-
   JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.release()));
 
   JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
   JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign));
   //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
   //  return false;
   JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
   return true;
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -276,16 +276,19 @@ struct FieldHashPolicy : DefaultHasher<J
       return false;
 
     return EqualChars(k, l);
   }
 };
 
 typedef HashMap<JSFlatString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash;
 
+void
+TraceFieldInfoHash(JSTracer* trc, FieldInfoHash* fields);
+
 // Descriptor of ABI, return type, argument types, and variadicity for a
 // FunctionType.
 struct FunctionInfo
 {
   // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in
   // FunctionType::Call, when mIsVariadic. Not always consistent with
   // mFFITypes, due to lazy initialization when mIsVariadic.
   ffi_cif mCIF;
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -30,17 +30,17 @@ namespace Library
 ** JSObject implementation
 *******************************************************************************/
 
 typedef Rooted<JSFlatString*>    RootedFlatString;
 
 static const JSClass sLibraryClass = {
   "Library",
   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Library::Finalize
 };
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 static const JSFunctionSpec sLibraryFunctions[] = {
   JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -399,17 +399,17 @@ struct InternalGCMethods<jsid>
   private:
     static JSRuntime* runtimeFromAnyThread(jsid id) {
         MOZ_ASSERT(JSID_IS_GCTHING(id));
         return JSID_TO_GCTHING(id).asCell()->runtimeFromAnyThread();
     }
     static JS::shadow::Runtime* shadowRuntimeFromAnyThread(jsid id) {
         return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread(id));
     }
-    static void preBarrierImpl(Zone *zone, jsid id) {
+    static void preBarrierImpl(Zone* zone, jsid id) {
         JS::shadow::Zone* shadowZone = JS::shadow::Zone::asShadowZone(zone);
         if (shadowZone->needsIncrementalBarrier()) {
             jsid tmp(id);
             js::gc::MarkIdForBarrier(shadowZone->barrierTracer(), &tmp, "id write barrier");
             MOZ_ASSERT(tmp == id);
         }
     }
 
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -12,17 +12,17 @@
 
 using namespace JS;
 
 /* The class of the global object. */
 const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 template<typename T>
 static inline T*
 checkPtr(T* ptr)
 {
   if (! ptr)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/lib/class.js
@@ -0,0 +1,1 @@
+load(libdir + "../../tests/ecma_6/Class/shell.js");
--- a/js/src/jit-test/tests/SIMD/convert.js
+++ b/js/src/jit-test/tests/SIMD/convert.js
@@ -1,11 +1,11 @@
 load(libdir + 'simd.js');
 
-setJitCompilerOption("ion.warmup.trigger", 50);
+setJitCompilerOption("ion.warmup.trigger", 30);
 
 var cast = (function() {
     var i32 = new Int32Array(1);
     var f32 = new Float32Array(i32.buffer);
     return {
         fromInt32Bits(x) {
             i32[0] = x;
             return f32[0];
@@ -14,21 +14,57 @@ var cast = (function() {
         fromFloat32Bits(x) {
             f32[0] = x;
             return i32[0];
         }
     }
 })();
 
 function f() {
+    // No bailout here.
     var f4 = SIMD.float32x4(1, 2, 3, 4);
     var i4 = SIMD.int32x4(1, 2, 3, 4);
     var BitOrZero = (x) => x | 0;
     for (var i = 0; i < 150; i++) {
         assertEqX4(SIMD.float32x4.fromInt32x4(i4), unaryX4(BitOrZero, f4, Math.fround));
         assertEqX4(SIMD.float32x4.fromInt32x4Bits(i4), unaryX4(cast.fromInt32Bits, f4, Math.fround));
         assertEqX4(SIMD.int32x4.fromFloat32x4(f4), unaryX4(Math.fround, i4, BitOrZero));
         assertEqX4(SIMD.int32x4.fromFloat32x4Bits(f4), unaryX4(cast.fromFloat32Bits, i4, BitOrZero));
     }
 }
 
+function uglyDuckling(val) {
+    // We bail out when i == 149 because the conversion will return
+    // 0x80000000 and the input actually wasn't in bounds.
+    print('entering uglyDuckling');
+    val = Math.fround(val);
+    for (var i = 0; i < 150; i++) {
+        var caught = false;
+        try {
+            var v = SIMD.float32x4(i < 149 ? 0 : val, 0, 0, 0)
+            SIMD.int32x4.fromFloat32x4(v);
+        } catch(e) {
+            assertEq(e instanceof RangeError, true);
+            assertEq(i, 149);
+            caught = true;
+        }
+        assertEq(i < 149 || caught, true);
+    }
+}
+
+function dontBail() {
+    print('entering dontbail');
+    // On x86, the conversion will return 0x80000000, which will imply that we
+    // check the input values. However, we shouldn't bail out in this case.
+    for (var i = 0; i < 150; i++) {
+        var v = SIMD.float32x4(i < 149 ? 0 : -Math.pow(2, 31), 0, 0, 0)
+        SIMD.int32x4.fromFloat32x4(v);
+    }
+}
+
 f();
 
+dontBail();
+dontBail();
+
+uglyDuckling(Math.pow(2, 31));
+uglyDuckling(NaN);
+uglyDuckling(-Math.pow(2, 32));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/SIMD/shift.js
@@ -0,0 +1,57 @@
+load(libdir + 'simd.js');
+
+setJitCompilerOption("ion.warmup.trigger", 50);
+
+function curry(f, arg) { return f.bind(null, arg); }
+
+function binaryLsh(count, v) { if (count>>>0 >= 32) return 0; return (v << count) | 0; }
+function lsh(count) { return curry(binaryLsh, count); }
+
+function binaryRsh(count, v) { if (count>>>0 >= 32) count = 31; return (v >> count) | 0; }
+function rsh(count) { return curry(binaryRsh, count); }
+
+function binaryUrsh(count, v) { if (count>>>0 >= 32) return 0; return (v >>> count) | 0; }
+function ursh(count) { return curry(binaryUrsh, count); }
+
+function f() {
+    var v = SIMD.int32x4(1, 2, -3, 4);
+    var a = [1, 2, -3, 4];
+    var zeros = [0,0,0,0];
+
+    var shifts = [-1, 0, 1, 31, 32];
+
+    var r;
+    for (var i = 0; i < 150; i++) {
+        // Constant shift counts
+        assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, -1), a.map(lsh(-1)));
+        assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 0),  a.map(lsh(0)));
+        assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 1),  a.map(lsh(1)));
+        assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 2),  a.map(lsh(2)));
+        assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 31), a.map(lsh(31)));
+        assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 32), a.map(lsh(32)));
+
+        assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, -1), a.map(rsh(31)));
+        assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 0),  a.map(rsh(0)));
+        assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 1),  a.map(rsh(1)));
+        assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 2),  a.map(rsh(2)));
+        assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 31), a.map(rsh(31)));
+        assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 32), a.map(rsh(31)));
+
+        assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, -1), a.map(ursh(-1)));
+        assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 0),  a.map(ursh(0)));
+        assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 1),  a.map(ursh(1)));
+        assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 2),  a.map(ursh(2)));
+        assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 31), a.map(ursh(31)));
+        assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 32), a.map(ursh(32)));
+
+        // Non constant shift counts
+        var c = shifts[i % shifts.length];
+        assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, c), a.map(lsh(c)));
+        assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, c), a.map(rsh(c)));
+        assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, c), a.map(ursh(c)));
+    }
+    return r;
+}
+
+f();
+
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -1,9 +1,10 @@
 load(libdir + "asm.js");
+load(libdir + "asserts.js");
 var heap = new ArrayBuffer(0x10000);
 
 // Set to true to see more JS debugging spew
 const DEBUG = false;
 
 if (!isSimdAvailable() || typeof SIMD === 'undefined') {
     DEBUG && print("won't run tests as simd extensions aren't activated yet");
     quit(0);
@@ -724,24 +725,29 @@ assertAsmTypeFail('glob', USE_ASM + I32 
 assertAsmTypeFail('glob', USE_ASM + F32 + "var cvt=f4.fromFloat32x4; return {}");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=f4(1,2,3,4); x=cvt(x);} return f");
 
 var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + CI32 + CVTIF + 'function f(x){x=ci4(x); var y=f4(0,0,0,0); y=cvt(x); return cf4(y);} return f'), this);
 assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4]);
 assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, Math.fround(INT32_MIN), Math.fround(INT32_MAX), -1]);
 
-// TODO amend tests once int32x4.fromFloat32x4 is fully specified, when float
-// values can't be converted into an int32 without overflowing.  In these
-// tests, we assume x86/x64, so a conversion which failed will return the
-// undefined int32 value. See also bug 1068028.
-const UNDEFINED_INT32 = 0x80000000 | 0;
 var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + F32 + CF32 + CVTFI + 'function f(x){x=cf4(x); var y=i4(0,0,0,0); y=cvt(x); return ci4(y);} return f'), this);
 assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4]);
-assertEqX4(f(SIMD.float32x4(NaN,Infinity,-Infinity,-0)), [UNDEFINED_INT32, UNDEFINED_INT32, UNDEFINED_INT32, 0]);
+// Test that INT32_MIN (exactly representable as an float32) and the first
+// integer representable as an float32 can be converted.
+assertEqX4(f(SIMD.float32x4(INT32_MIN, INT32_MAX - 64, -0, 0)), [INT32_MIN, INT32_MAX - 64, 0, 0].map(Math.fround));
+// Test boundaries: first integer less than INT32_MIN and representable as a float32
+assertThrowsInstanceOf(() => f(SIMD.float32x4(INT32_MIN - 129, 0, 0, 0)), RangeError);
+// INT_MAX + 1
+assertThrowsInstanceOf(() => f(SIMD.float32x4(Math.pow(2, 31), 0, 0, 0)), RangeError);
+// Special values
+assertThrowsInstanceOf(() => f(SIMD.float32x4(NaN, 0, 0, 0)), RangeError);
+assertThrowsInstanceOf(() => f(SIMD.float32x4(Infinity, 0, 0, 0)), RangeError);
+assertThrowsInstanceOf(() => f(SIMD.float32x4(-Infinity, 0, 0, 0)), RangeError);
 
 // Cast operators
 const CVTIFB = 'var cvt=f4.fromInt32x4Bits;';
 const CVTFIB = 'var cvt=i4.fromFloat32x4Bits;';
 
 var cast = (function() {
     var i32 = new Int32Array(1);
     var f32 = new Float32Array(i32.buffer);
--- a/js/src/jit-test/tests/basic/semicolon-less-return.js
+++ b/js/src/jit-test/tests/basic/semicolon-less-return.js
@@ -1,11 +1,13 @@
 // Warning should be shown for expression-like statement after semicolon-less
 // return (bug 1005110).
 
+load(libdir + "class.js");
+
 if (options().indexOf("werror") == -1)
   options("werror");
 
 function testWarn(code, lineNumber, columnNumber) {
   var caught = false;
   try {
     eval(code);
   } catch (e) {
@@ -234,22 +236,24 @@ function f() {
 testWarn(`
 function* f() {
   return
     yield 1;
 }
 `, 4, 4);
 
 // TOK_CLASS
-testWarn(`
+if (classesEnabled()) {
+  testWarn(`
 function f() {
   return
     class A { constructor() {} };
 }
 `, 4, 4);
+}
 
 // TOK_ADD
 testWarn(`
 function f() {
   return
     +1;
 }
 `, 4, 4);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -346,31 +346,31 @@ ICStub::trace(JSTracer* trc)
         break;
       }
       case ICStub::TypeUpdate_ObjectGroup: {
         ICTypeUpdate_ObjectGroup* updateStub = toTypeUpdate_ObjectGroup();
         TraceEdge(trc, &updateStub->group(), "baseline-update-group");
         break;
       }
       case ICStub::In_Native: {
-        ICIn_Native *inStub = toIn_Native();
+        ICIn_Native* inStub = toIn_Native();
         TraceEdge(trc, &inStub->shape(), "baseline-innative-stub-shape");
         TraceEdge(trc, &inStub->name(), "baseline-innative-stub-name");
         break;
       }
       case ICStub::In_NativePrototype: {
-        ICIn_NativePrototype *inStub = toIn_NativePrototype();
+        ICIn_NativePrototype* inStub = toIn_NativePrototype();
         TraceEdge(trc, &inStub->shape(), "baseline-innativeproto-stub-shape");
         TraceEdge(trc, &inStub->name(), "baseline-innativeproto-stub-name");
         TraceEdge(trc, &inStub->holder(), "baseline-innativeproto-stub-holder");
         TraceEdge(trc, &inStub->holderShape(), "baseline-innativeproto-stub-holdershape");
         break;
       }
       case ICStub::In_NativeDoesNotExist: {
-        ICIn_NativeDoesNotExist *inStub = toIn_NativeDoesNotExist();
+        ICIn_NativeDoesNotExist* inStub = toIn_NativeDoesNotExist();
         TraceEdge(trc, &inStub->name(), "baseline-innativedoesnotexist-stub-name");
         JS_STATIC_ASSERT(ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
         switch (inStub->protoChainDepth()) {
           case 0: inStub->toImpl<0>()->traceShapes(trc); break;
           case 1: inStub->toImpl<1>()->traceShapes(trc); break;
           case 2: inStub->toImpl<2>()->traceShapes(trc); break;
           case 3: inStub->toImpl<3>()->traceShapes(trc); break;
           case 4: inStub->toImpl<4>()->traceShapes(trc); break;
@@ -378,17 +378,17 @@ ICStub::trace(JSTracer* trc)
           case 6: inStub->toImpl<6>()->traceShapes(trc); break;
           case 7: inStub->toImpl<7>()->traceShapes(trc); break;
           case 8: inStub->toImpl<8>()->traceShapes(trc); break;
           default: MOZ_CRASH("Invalid proto stub.");
         }
         break;
       }
       case ICStub::In_Dense: {
-        ICIn_Dense *inStub = toIn_Dense();
+        ICIn_Dense* inStub = toIn_Dense();
         TraceEdge(trc, &inStub->shape(), "baseline-in-dense-shape");
         break;
       }
       case ICStub::GetName_Global: {
         ICGetName_Global* globalStub = toGetName_Global();
         TraceEdge(trc, &globalStub->shape(), "baseline-global-stub-shape");
         break;
       }
@@ -3801,17 +3801,17 @@ ArgumentsGetElemStubExists(ICGetElem_Fal
             continue;
         if (iter->toGetElem_Arguments()->which() == which)
             return true;
     }
     return false;
 }
 
 static bool
-IsOptimizableElementPropertyName(JSContext *cx, HandleValue key, MutableHandleId idp)
+IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId idp)
 {
     if (!key.isString())
         return false;
 
     // Convert to interned property name.
     if (!ValueToId<CanGC>(cx, key, idp))
         return false;
 
@@ -5851,38 +5851,38 @@ ICSetElem_TypedArray::Compiler::generate
     return true;
 }
 
 //
 // In_Fallback
 //
 
 static bool
-TryAttachDenseInStub(JSContext *cx, HandleScript script, ICIn_Fallback *stub,
-                     HandleValue key, HandleObject obj, bool *attached)
+TryAttachDenseInStub(JSContext* cx, HandleScript script, ICIn_Fallback* stub,
+                     HandleValue key, HandleObject obj, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
     if (!IsNativeDenseElementAccess(obj, key))
         return true;
 
     JitSpew(JitSpew_BaselineIC, "  Generating In(Native[Int32] dense) stub");
     ICIn_Dense::Compiler compiler(cx, obj->as<NativeObject>().lastProperty());
-    ICStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
+    ICStub* denseStub = compiler.getStub(compiler.getStubSpace(script));
     if (!denseStub)
         return false;
 
     *attached = true;
     stub->addNewStub(denseStub);
     return true;
 }
 
 static bool
-TryAttachNativeInStub(JSContext *cx, HandleScript script, ICIn_Fallback *stub,
-                      HandleValue key, HandleObject obj, bool *attached)
+TryAttachNativeInStub(JSContext* cx, HandleScript script, ICIn_Fallback* stub,
+                      HandleValue key, HandleObject obj, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
     RootedId id(cx);
     if (!IsOptimizableElementPropertyName(cx, key, &id))
         return true;
 
     RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
@@ -5892,32 +5892,32 @@ TryAttachNativeInStub(JSContext *cx, Han
         return false;
 
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         ICStub::Kind kind = (obj == holder) ? ICStub::In_Native
                                             : ICStub::In_NativePrototype;
         JitSpew(JitSpew_BaselineIC, "  Generating In(Native %s) stub",
                     (obj == holder) ? "direct" : "prototype");
         ICInNativeCompiler compiler(cx, kind, obj, holder, name);
-        ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
+        ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         *attached = true;
         stub->addNewStub(newStub);
         return true;
     }
 
     return true;
 }
 
 static bool
-TryAttachNativeInDoesNotExistStub(JSContext *cx, HandleScript script,
-                                  ICIn_Fallback *stub, HandleValue key,
-                                  HandleObject obj, bool *attached)
+TryAttachNativeInDoesNotExistStub(JSContext* cx, HandleScript script,
+                                  ICIn_Fallback* stub, HandleValue key,
+                                  HandleObject obj, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
     RootedId id(cx);
     if (!IsOptimizableElementPropertyName(cx, key, &id))
         return true;
 
     // Check if does-not-exist can be confirmed on property.
@@ -5929,27 +5929,27 @@ TryAttachNativeInDoesNotExistStub(JSCont
     MOZ_ASSERT(protoChainDepth < SIZE_MAX);
 
     if (protoChainDepth > ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH)
         return true;
 
     // Confirmed no-such-property. Add stub.
     JitSpew(JitSpew_BaselineIC, "  Generating In_NativeDoesNotExist stub");
     ICInNativeDoesNotExistCompiler compiler(cx, obj, name, protoChainDepth);
-    ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
+    ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
 
     *attached = true;
     stub->addNewStub(newStub);
     return true;
 }
 
 static bool
-DoInFallback(JSContext *cx, BaselineFrame *frame, ICIn_Fallback *stub_,
+DoInFallback(JSContext* cx, BaselineFrame* frame, ICIn_Fallback* stub_,
              HandleValue key, HandleValue objValue, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICIn_Fallback*> stub(frame, stub_);
 
     FallbackICSpew(cx, stub, "In");
 
     if (!objValue.isObject()) {
@@ -5988,17 +5988,17 @@ DoInFallback(JSContext *cx, BaselineFram
             if (attached)
                 return true;
         }
     }
 
     return true;
 }
 
-typedef bool (*DoInFallbackFn)(JSContext *, BaselineFrame *, ICIn_Fallback *, HandleValue,
+typedef bool (*DoInFallbackFn)(JSContext*, BaselineFrame*, ICIn_Fallback*, HandleValue,
                                HandleValue, MutableHandleValue);
 static const VMFunction DoInFallbackInfo =
     FunctionInfo<DoInFallbackFn>(DoInFallback, TailCall, PopValues(2));
 
 bool
 ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
 {
     EmitRestoreTailCallReg(masm);
@@ -6012,17 +6012,17 @@ ICIn_Fallback::Compiler::generateStubCod
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     return tailCallVM(DoInFallbackInfo, masm);
 }
 
 bool
-ICInNativeCompiler::generateStubCode(MacroAssembler &masm)
+ICInNativeCompiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure, failurePopR0Scratch;
 
     masm.branchTestString(Assembler::NotEqual, R0, &failure);
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratch = regs.takeAny();
@@ -6056,29 +6056,29 @@ ICInNativeCompiler::generateStubCode(Mac
     // Failure case - jump to next stub
     masm.bind(&failurePopR0Scratch);
     masm.pop(R0.scratchReg());
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
-ICStub *
-ICInNativeDoesNotExistCompiler::getStub(ICStubSpace *space)
+ICStub*
+ICInNativeDoesNotExistCompiler::getStub(ICStubSpace* space)
 {
     AutoShapeVector shapes(cx);
     if (!shapes.append(obj_->as<NativeObject>().lastProperty()))
         return nullptr;
 
     if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
         return nullptr;
 
     JS_STATIC_ASSERT(ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
 
-    ICStub *stub = nullptr;
+    ICStub* stub = nullptr;
     switch (protoChainDepth_) {
       case 0: stub = getStubSpecific<0>(space, &shapes); break;
       case 1: stub = getStubSpecific<1>(space, &shapes); break;
       case 2: stub = getStubSpecific<2>(space, &shapes); break;
       case 3: stub = getStubSpecific<3>(space, &shapes); break;
       case 4: stub = getStubSpecific<4>(space, &shapes); break;
       case 5: stub = getStubSpecific<5>(space, &shapes); break;
       case 6: stub = getStubSpecific<6>(space, &shapes); break;
@@ -6087,17 +6087,17 @@ ICInNativeDoesNotExistCompiler::getStub(
       default: MOZ_CRASH("ProtoChainDepth too high.");
     }
     if (!stub)
         return nullptr;
     return stub;
 }
 
 bool
-ICInNativeDoesNotExistCompiler::generateStubCode(MacroAssembler &masm)
+ICInNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure, failurePopR0Scratch;
 
     masm.branchTestString(Assembler::NotEqual, R0, &failure);
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratch = regs.takeAny();
@@ -6144,17 +6144,17 @@ ICInNativeDoesNotExistCompiler::generate
     masm.bind(&failurePopR0Scratch);
     masm.pop(R0.scratchReg());
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
-ICIn_Dense::Compiler::generateStubCode(MacroAssembler &masm)
+ICIn_Dense::Compiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure;
 
     masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratch = regs.takeAny();
@@ -7330,20 +7330,20 @@ TryAttachPrimitiveGetPropStub(JSContext*
         return false;
 
     stub->addNewStub(newStub);
     *attached = true;
     return true;
 }
 
 static bool
-TryAttachNativeGetPropDoesNotExistStub(JSContext *cx, HandleScript script,
-                                       jsbytecode *pc, ICGetProp_Fallback *stub,
+TryAttachNativeGetPropDoesNotExistStub(JSContext* cx, HandleScript script,
+                                       jsbytecode* pc, ICGetProp_Fallback* stub,
                                        HandlePropertyName name, HandleValue val,
-                                       bool *attached)
+                                       bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
     if (!val.isObject())
         return true;
 
     RootedObject obj(cx, &val.toObject());
 
@@ -9785,17 +9785,17 @@ GetTemplateObjectForNative(JSContext* cx
 
     if (JitSupportsSimd()) {
 #define ADD_INT32X4_SIMD_OP_NAME_(OP) || native == js::simd_int32x4_##OP
 #define ADD_FLOAT32X4_SIMD_OP_NAME_(OP) || native == js::simd_float32x4_##OP
        if (false
            ION_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)
            COMP_COMMONX4_TO_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)
            COMP_COMMONX4_TO_INT32X4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)
-           CONVERSION_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)
+           FOREACH_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)
            ION_ONLY_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_))
        {
             Rooted<SimdTypeDescr*> descr(cx, &cx->global()->int32x4TypeDescr().as<SimdTypeDescr>());
             res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr));
             return !!res;
        }
        if (false
            FOREACH_FLOAT32X4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)
@@ -12367,32 +12367,32 @@ ICSetElem_TypedArray::ICSetElem_TypedArr
   : ICStub(SetElem_TypedArray, stubCode),
     shape_(shape)
 {
     extra_ = uint8_t(type);
     MOZ_ASSERT(extra_ == type);
     extra_ |= (static_cast<uint16_t>(expectOutOfBounds) << 8);
 }
 
-ICInNativeStub::ICInNativeStub(ICStub::Kind kind, JitCode *stubCode, HandleShape shape,
+ICInNativeStub::ICInNativeStub(ICStub::Kind kind, JitCode* stubCode, HandleShape shape,
                                HandlePropertyName name)
   : ICStub(kind, stubCode),
     shape_(shape),
     name_(name)
 { }
 
-ICIn_NativePrototype::ICIn_NativePrototype(JitCode *stubCode, HandleShape shape,
+ICIn_NativePrototype::ICIn_NativePrototype(JitCode* stubCode, HandleShape shape,
                                            HandlePropertyName name, HandleObject holder,
                                            HandleShape holderShape)
   : ICInNativeStub(In_NativePrototype, stubCode, shape, name),
     holder_(holder),
     holderShape_(holderShape)
 { }
 
-ICIn_NativeDoesNotExist::ICIn_NativeDoesNotExist(JitCode *stubCode, size_t protoChainDepth,
+ICIn_NativeDoesNotExist::ICIn_NativeDoesNotExist(JitCode* stubCode, size_t protoChainDepth,
                                                  HandlePropertyName name)
   : ICStub(In_NativeDoesNotExist, stubCode),
     name_(name)
 {
     MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
     extra_ = protoChainDepth;
 }
 
@@ -12402,35 +12402,35 @@ ICIn_NativeDoesNotExist::offsetOfShape(s
     MOZ_ASSERT(ICIn_NativeDoesNotExistImpl<0>::offsetOfShape(idx) ==
                ICIn_NativeDoesNotExistImpl<
                     ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH>::offsetOfShape(idx));
     return ICIn_NativeDoesNotExistImpl<0>::offsetOfShape(idx);
 }
 
 template <size_t ProtoChainDepth>
 ICIn_NativeDoesNotExistImpl<ProtoChainDepth>::ICIn_NativeDoesNotExistImpl(
-        JitCode *stubCode, const AutoShapeVector *shapes, HandlePropertyName name)
+        JitCode* stubCode, const AutoShapeVector* shapes, HandlePropertyName name)
   : ICIn_NativeDoesNotExist(stubCode, ProtoChainDepth, name)
 {
     MOZ_ASSERT(shapes->length() == NumShapes);
     for (size_t i = 0; i < NumShapes; i++)
         shapes_[i].init((*shapes)[i]);
 }
 
 ICInNativeDoesNotExistCompiler::ICInNativeDoesNotExistCompiler(
-        JSContext *cx, HandleObject obj, HandlePropertyName name, size_t protoChainDepth)
+        JSContext* cx, HandleObject obj, HandlePropertyName name, size_t protoChainDepth)
   : ICStubCompiler(cx, ICStub::In_NativeDoesNotExist),
     obj_(cx, obj),
     name_(cx, name),
     protoChainDepth_(protoChainDepth)
 {
     MOZ_ASSERT(protoChainDepth_ <= ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH);
 }
 
-ICIn_Dense::ICIn_Dense(JitCode *stubCode, HandleShape shape)
+ICIn_Dense::ICIn_Dense(JitCode* stubCode, HandleShape shape)
   : ICStub(In_Dense, stubCode),
     shape_(shape)
 { }
 
 ICGetName_Global::ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape,
                                    uint32_t slot)
   : ICMonitoredStub(GetName_Global, stubCode, firstMonitorStub),
     shape_(shape),
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -3417,63 +3417,63 @@ class ICIn_Fallback : public ICFallbackS
 
 // Base class for In_Native and In_NativePrototype stubs.
 class ICInNativeStub : public ICStub
 {
     HeapPtrShape shape_;
     HeapPtrPropertyName name_;
 
   protected:
-    ICInNativeStub(ICStub::Kind kind, JitCode *stubCode, HandleShape shape,
+    ICInNativeStub(ICStub::Kind kind, JitCode* stubCode, HandleShape shape,
                    HandlePropertyName name);
 
   public:
-    HeapPtrShape &shape() {
+    HeapPtrShape& shape() {
         return shape_;
     }
     static size_t offsetOfShape() {
         return offsetof(ICInNativeStub, shape_);
     }
 
-    HeapPtrPropertyName &name() {
+    HeapPtrPropertyName& name() {
         return name_;
     }
     static size_t offsetOfName() {
         return offsetof(ICInNativeStub, name_);
     }
 };
 
 // Stub for confirming an own property on a native object.
 class ICIn_Native : public ICInNativeStub
 {
     friend class ICStubSpace;
 
-    ICIn_Native(JitCode *stubCode, HandleShape shape, HandlePropertyName name)
+    ICIn_Native(JitCode* stubCode, HandleShape shape, HandlePropertyName name)
       : ICInNativeStub(In_Native, stubCode, shape, name)
     {}
 };
 
 // Stub for confirming a property on a native object's prototype. Note that due to
 // the shape teleporting optimization, we only have to guard on the object's shape
 // and the holder's shape.
 class ICIn_NativePrototype : public ICInNativeStub
 {
     friend class ICStubSpace;
 
     HeapPtrObject holder_;
     HeapPtrShape holderShape_;
 
-    ICIn_NativePrototype(JitCode *stubCode, HandleShape shape, HandlePropertyName name,
+    ICIn_NativePrototype(JitCode* stubCode, HandleShape shape, HandlePropertyName name,
                          HandleObject holder, HandleShape holderShape);
 
   public:
-    HeapPtrObject &holder() {
+    HeapPtrObject& holder() {
         return holder_;
     }
-    HeapPtrShape &holderShape() {
+    HeapPtrShape& holderShape() {
         return holderShape_;
     }
     static size_t offsetOfHolder() {
         return offsetof(ICIn_NativePrototype, holder_);
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICIn_NativePrototype, holderShape_);
     }
@@ -3481,28 +3481,28 @@ class ICIn_NativePrototype : public ICIn
 
 // Compiler for In_Native and In_NativePrototype stubs.
 class ICInNativeCompiler : public ICStubCompiler
 {
     RootedObject obj_;
     RootedObject holder_;
     RootedPropertyName name_;
 
-    bool generateStubCode(MacroAssembler &masm);
+    bool generateStubCode(MacroAssembler& masm);
 
   public:
-    ICInNativeCompiler(JSContext *cx, ICStub::Kind kind, HandleObject obj, HandleObject holder,
+    ICInNativeCompiler(JSContext* cx, ICStub::Kind kind, HandleObject obj, HandleObject holder,
                        HandlePropertyName name)
       : ICStubCompiler(cx, kind),
         obj_(cx, obj),
         holder_(cx, holder),
         name_(cx, name)
     {}
 
-    ICStub *getStub(ICStubSpace *space) {
+    ICStub* getStub(ICStubSpace* space) {
         RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
         if (kind == ICStub::In_Native) {
             MOZ_ASSERT(obj_ == holder_);
             return ICStub::New<ICIn_Native>(space, getStubCode(), shape, name_);
         }
 
         MOZ_ASSERT(obj_ != holder_);
         MOZ_ASSERT(kind == ICStub::In_NativePrototype);
@@ -3519,32 +3519,32 @@ class ICIn_NativeDoesNotExist : public I
     friend class ICStubSpace;
 
     HeapPtrPropertyName name_;
 
   public:
     static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
 
   protected:
-    ICIn_NativeDoesNotExist(JitCode *stubCode, size_t protoChainDepth,
+    ICIn_NativeDoesNotExist(JitCode* stubCode, size_t protoChainDepth,
                             HandlePropertyName name);
 
   public:
     size_t protoChainDepth() const {
         MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH);
         return extra_;
     }
-    HeapPtrPropertyName &name() {
+    HeapPtrPropertyName& name() {
         return name_;
     }
 
     template <size_t ProtoChainDepth>
-    ICIn_NativeDoesNotExistImpl<ProtoChainDepth> *toImpl() {
+    ICIn_NativeDoesNotExistImpl<ProtoChainDepth>* toImpl() {
         MOZ_ASSERT(ProtoChainDepth == protoChainDepth());
-        return static_cast<ICIn_NativeDoesNotExistImpl<ProtoChainDepth> *>(this);
+        return static_cast<ICIn_NativeDoesNotExistImpl<ProtoChainDepth>*>(this);
     }
 
     static size_t offsetOfShape(size_t idx);
     static size_t offsetOfName() {
         return offsetof(ICIn_NativeDoesNotExist, name_);
     }
 };
 
@@ -3555,21 +3555,21 @@ class ICIn_NativeDoesNotExistImpl : publ
 
   public:
     static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
     static const size_t NumShapes = ProtoChainDepth + 1;
 
   private:
     mozilla::Array<HeapPtrShape, NumShapes> shapes_;
 
-    ICIn_NativeDoesNotExistImpl(JitCode *stubCode, const AutoShapeVector *shapes,
+    ICIn_NativeDoesNotExistImpl(JitCode* stubCode, const AutoShapeVector* shapes,
                                 HandlePropertyName name);
 
   public:
-    void traceShapes(JSTracer *trc) {
+    void traceShapes(JSTracer* trc) {
         for (size_t i = 0; i < NumShapes; i++)
             TraceEdge(trc, &shapes_[i], "baseline-innativedoesnotexist-stub-shape");
     }
 
     static size_t offsetOfShape(size_t idx) {
         return offsetof(ICIn_NativeDoesNotExistImpl, shapes_) + (idx * sizeof(HeapPtrShape));
     }
 };
@@ -3580,60 +3580,60 @@ class ICInNativeDoesNotExistCompiler : p
     RootedPropertyName name_;
     size_t protoChainDepth_;
 
   protected:
     virtual int32_t getKey() const {
         return static_cast<int32_t>(kind) | (static_cast<int32_t>(protoChainDepth_) << 16);
     }
 
-    bool generateStubCode(MacroAssembler &masm);
+    bool generateStubCode(MacroAssembler& masm);
 
   public:
-    ICInNativeDoesNotExistCompiler(JSContext *cx, HandleObject obj, HandlePropertyName name,
+    ICInNativeDoesNotExistCompiler(JSContext* cx, HandleObject obj, HandlePropertyName name,
                                    size_t protoChainDepth);
 
     template <size_t ProtoChainDepth>
-    ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) {
+    ICStub* getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) {
         return ICStub::New<ICIn_NativeDoesNotExistImpl<ProtoChainDepth>>(space, getStubCode(),
                                                                          shapes, name_);
     }
 
-    ICStub *getStub(ICStubSpace *space);
+    ICStub* getStub(ICStubSpace* space);
 };
 
 class ICIn_Dense : public ICStub
 {
     friend class ICStubSpace;
 
     HeapPtrShape shape_;
 
-    ICIn_Dense(JitCode *stubCode, HandleShape shape);
+    ICIn_Dense(JitCode* stubCode, HandleShape shape);
 
   public:
-    HeapPtrShape &shape() {
+    HeapPtrShape& shape() {
         return shape_;
     }
     static size_t offsetOfShape() {
         return offsetof(ICIn_Dense, shape_);
     }
 
     class Compiler : public ICStubCompiler {
       RootedShape shape_;
 
       protected:
-        bool generateStubCode(MacroAssembler &masm);
+        bool generateStubCode(MacroAssembler& masm);
 
       public:
-        Compiler(JSContext *cx, Shape *shape)
+        Compiler(JSContext* cx, Shape* shape)
           : ICStubCompiler(cx, ICStub::In_Dense),
             shape_(cx, shape)
         {}
 
-        ICStub *getStub(ICStubSpace *space) {
+        ICStub* getStub(ICStubSpace* space) {
             return ICStub::New<ICIn_Dense>(space, getStubCode(), shape_);
         }
     };
 };
 
 // GetName
 //      JSOP_GETNAME
 //      JSOP_GETGNAME
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1018,17 +1018,17 @@ static const size_t RegExpReservedStack 
 
 static size_t
 RegExpPairsVectorStartOffset(size_t inputOutputDataStartOffset)
 {
     return inputOutputDataStartOffset + sizeof(irregexp::InputOutputData) + sizeof(MatchPairs);
 }
 
 static Address
-RegExpPairCountAddress(MacroAssembler &masm, size_t inputOutputDataStartOffset)
+RegExpPairCountAddress(MacroAssembler& masm, size_t inputOutputDataStartOffset)
 {
     return Address(masm.getStackPointer(), inputOutputDataStartOffset
                                            + sizeof(irregexp::InputOutputData)
                                            + MatchPairs::offsetOfPairCount());
 }
 
 // Prepare an InputOutputData and optional MatchPairs which space has been
 // allocated for on the stack, and try to execute a RegExp on a string input.
--- a/js/src/jit/EffectiveAddressAnalysis.cpp
+++ b/js/src/jit/EffectiveAddressAnalysis.cpp
@@ -97,17 +97,17 @@ AnalyzeLsh(TempAllocator& alloc, MLsh* l
 
     MEffectiveAddress* eaddr = MEffectiveAddress::New(alloc, base, index, scale, displacement);
     last->replaceAllUsesWith(eaddr);
     last->block()->insertAfter(last, eaddr);
 }
 
 template<typename MAsmJSHeapAccessType>
 bool
-EffectiveAddressAnalysis::tryAddDisplacement(MAsmJSHeapAccessType *ins, int32_t o)
+EffectiveAddressAnalysis::tryAddDisplacement(MAsmJSHeapAccessType* ins, int32_t o)
 {
     // Compute the new offset. Check for overflow and negative. In theory it
     // ought to be possible to support negative offsets, but it'd require
     // more elaborate bounds checking mechanisms than we currently have.
     MOZ_ASSERT(ins->offset() >= 0);
     int32_t newOffset = uint32_t(ins->offset()) + o;
     if (newOffset < 0)
         return false;
--- a/js/src/jit/EffectiveAddressAnalysis.h
+++ b/js/src/jit/EffectiveAddressAnalysis.h
@@ -13,23 +13,23 @@ namespace jit {
 class MIRGraph;
 
 class EffectiveAddressAnalysis
 {
     MIRGenerator* mir_;
     MIRGraph& graph_;
 
     template<typename MAsmJSHeapAccessType>
-    bool tryAddDisplacement(MAsmJSHeapAccessType *ins, int32_t o);
+    bool tryAddDisplacement(MAsmJSHeapAccessType* ins, int32_t o);
 
     template<typename MAsmJSHeapAccessType>
     void analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins);
 
   public:
-    EffectiveAddressAnalysis(MIRGenerator *mir, MIRGraph& graph)
+    EffectiveAddressAnalysis(MIRGenerator* mir, MIRGraph& graph)
       : mir_(mir), graph_(graph)
     {}
 
     bool analyze();
 };
 
 } /* namespace jit */
 } /* namespace js */
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -894,18 +894,17 @@ IonBuilder::build()
     }
 
     if (!maybeAddOsrTypeBarriers())
         return false;
 
     if (!processIterators())
         return false;
 
-    if (!abortedPreliminaryGroups().empty()) {
-        MOZ_ASSERT(!info().isAnalysis());
+    if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) {
         abortReason_ = AbortReason_PreliminaryObjects;
         return false;
     }
 
     if (shouldForceAbort()) {
         abortReason_ = AbortReason_Disable;
         return false;
     }
@@ -1052,18 +1051,17 @@ IonBuilder::buildInline(IonBuilder* call
     insertRecompileCheck();
 
     if (!traverseBytecode())
         return false;
 
     // Discard unreferenced & pre-allocated resume points.
     replaceMaybeFallbackFunctionGetter(nullptr);
 
-    if (!abortedPreliminaryGroups().empty()) {
-        MOZ_ASSERT(!info().isAnalysis());
+    if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) {
         abortReason_ = AbortReason_PreliminaryObjects;
         return false;
     }
 
     if (shouldForceAbort()) {
         abortReason_ = AbortReason_Disable;
         return false;
     }
@@ -6578,17 +6576,17 @@ IonBuilder::jsop_initelem_array()
 {
     MDefinition* value = current->pop();
     MDefinition* obj = current->peek(-1);
 
     // Make sure that arrays have the type being written to them by the
     // intializer, and that arrays are marked as non-packed when writing holes
     // to them during initialization.
     bool needStub = false;
-    if (obj->isUnknownValue()) {
+    if (obj->isUnknownValue() || shouldAbortOnPreliminaryGroups(obj)) {
         needStub = true;
     } else {
         TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0);
         if (value->type() == MIRType_MagicHole) {
             if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED))
                 needStub = true;
         } else if (!initializer->unknownProperties()) {
             HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
@@ -7099,28 +7097,18 @@ ObjectHasExtraOwnProperty(CompileCompart
 
     const Class* clasp = object->clasp();
 
     // Array |length| properties are not reflected in type information.
     if (clasp == &ArrayObject::class_)
         return name == comp->runtime()->names().length;
 
     // Resolve hooks can install new properties on objects on demand.
-    if (!clasp->resolve)
-        return false;
-
-    if (clasp->resolve == str_resolve) {
-        // str_resolve only resolves integers, not names.
-        return false;
-    }
-
-    if (clasp->resolve == fun_resolve)
-        return FunctionHasResolveHook(comp->runtime()->names(), NameToId(name));
-
-    return true;
+    JSObject* singleton = object->isSingleton() ? object->singleton() : nullptr;
+    return ClassMayResolveId(comp->runtime()->names(), clasp, NameToId(name), singleton);
 }
 
 void
 IonBuilder::insertRecompileCheck()
 {
     // No need for recompile checks if this is the highest optimization level.
     OptimizationLevel curLevel = optimizationInfo().level();
     if (js_IonOptimizations.isLastLevel(curLevel))
@@ -7705,17 +7693,17 @@ IonBuilder::jsop_getelem()
     MDefinition* index = current->pop();
     MDefinition* obj = current->pop();
 
     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
     trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
 
     // Always use a call if we are performing analysis and not actually
     // emitting code, to simplify later analysis.
-    if (info().isAnalysis()) {
+    if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
         MInstruction* ins = MCallGetElement::New(alloc(), obj, index);
 
         current->add(ins);
         current->push(ins);
 
         if (!resumeAfter(ins))
             return false;
 
@@ -8756,16 +8744,23 @@ IonBuilder::jsop_setelem()
     MDefinition* value = current->pop();
     MDefinition* index = current->pop();
     MDefinition* object = current->pop();
 
     trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet());
     trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
     trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
 
+    if (shouldAbortOnPreliminaryGroups(object)) {
+        MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
+        current->add(ins);
+        current->push(value);
+        return resumeAfter(ins);
+    }
+
     trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject);
     if (!setElemTryTypedObject(&emitted, object, index, value) || emitted)
         return emitted;
 
     trackOptimizationAttempt(TrackedStrategy::SetElem_TypedStatic);
     if (!setElemTryTypedStatic(&emitted, object, index, value) || emitted)
         return emitted;
 
@@ -9443,47 +9438,16 @@ uint32_t
 IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed,
                             BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
 {
     if (!types || types->unknownObject()) {
         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
         return UINT32_MAX;
     }
 
-    // Watch for groups which still have preliminary object information and
-    // have not had the new script properties other analyses performed on their
-    // preliminary objects. Normally this is done after a small number of the
-    // objects have been created, but if only a few have been created we can
-    // still perform the analysis with a smaller object population. The
-    // analysis can have side effects so abort the builder and retry later.
-    //
-    // We always check this, so that even if we aren't able to find a common
-    // slot we ensure that the new script analysis is performed on all accessed
-    // objects. Later, this will let us elide baseline IC stubs for preliminary
-    // objects, which often have a different number of fixed slots from
-    // subsequent objects.
-    for (size_t i = 0; i < types->getObjectCount(); i++) {
-        TypeSet::ObjectKey* key = types->getObject(i);
-        if (!key)
-            continue;
-
-        if (ObjectGroup* group = key->maybeGroup()) {
-            if (group->newScript() && !group->newScript()->analyzed()) {
-                addAbortedPreliminaryGroup(group);
-                trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo);
-                return UINT32_MAX;
-            }
-            if (group->maybePreliminaryObjects()) {
-                addAbortedPreliminaryGroup(group);
-                trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo);
-                return UINT32_MAX;
-            }
-        }
-    }
-
     uint32_t slot = UINT32_MAX;
 
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         TypeSet::ObjectKey* key = types->getObject(i);
         if (!key)
             continue;
 
         if (key->unknownProperties()) {
@@ -9944,16 +9908,47 @@ bool
 IonBuilder::storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool needsBarrier,
                       MIRType slotType /* = MIRType_None */)
 {
     MOZ_ASSERT(shape->writable());
     return storeSlot(obj, shape->slot(), shape->numFixedSlots(), value, needsBarrier, slotType);
 }
 
 bool
+IonBuilder::shouldAbortOnPreliminaryGroups(MDefinition *obj)
+{
+    // Watch for groups which still have preliminary object information and
+    // have not had the new script properties or unboxed layout analyses
+    // performed. Normally this is done after a small number of the objects
+    // have been created, but if only a few have been created we can still
+    // perform the analysis with a smaller object population. The analysis can
+    // have side effects so we will end up aborting compilation after building
+    // finishes and retrying later.
+    TemporaryTypeSet *types = obj->resultTypeSet();
+    if (!types || types->unknownObject())
+        return false;
+
+    bool preliminary = false;
+    for (size_t i = 0; i < types->getObjectCount(); i++) {
+        TypeSet::ObjectKey* key = types->getObject(i);
+        if (!key)
+            continue;
+
+        if (ObjectGroup* group = key->maybeGroup()) {
+            if (group->hasUnanalyzedPreliminaryObjects()) {
+                addAbortedPreliminaryGroup(group);
+                preliminary = true;
+            }
+        }
+    }
+
+    return preliminary;
+}
+
+bool
 IonBuilder::jsop_getprop(PropertyName* name)
 {
     bool emitted = false;
     startTrackingOptimizations();
 
     MDefinition* obj = current->pop();
     TemporaryTypeSet* types = bytecodeTypes(pc);
 
@@ -9984,17 +9979,17 @@ IonBuilder::jsop_getprop(PropertyName* n
     } else {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
     }
 
     // Always use a call if we are performing analysis and
     // not actually emitting code, to simplify later analysis. Also skip deeper
     // analysis if there are no known types for this operation, as it will
     // always invalidate when executing.
-    if (info().isAnalysis() || types->empty()) {
+    if (info().isAnalysis() || types->empty() || shouldAbortOnPreliminaryGroups(obj)) {
         if (types->empty()) {
             // Since no further optimizations will be tried, use the IC
             // strategy, which would have been the last one to be tried, as a
             // sentinel value for why everything failed.
             trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
             trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
         }
 
@@ -11091,17 +11086,17 @@ IonBuilder::jsop_setprop(PropertyName* n
 
     bool emitted = false;
     startTrackingOptimizations();
     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
     trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
 
     // Always use a call if we are doing the definite properties analysis and
     // not actually emitting code, to simplify later analysis.
-    if (info().isAnalysis()) {
+    if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
         bool strict = IsStrictSetPC(pc);
         MInstruction* ins = MCallSetProperty::New(alloc(), obj, value, name, strict);
         current->add(ins);
         current->push(value);
         return resumeAfter(ins);
     }
 
     // Try to inline a common property setter, or make a call.
@@ -12230,17 +12225,18 @@ IonBuilder::jsop_setaliasedvar(ScopeCoor
 }
 
 bool
 IonBuilder::jsop_in()
 {
     MDefinition* obj = current->peek(-1);
     MDefinition* id = current->peek(-2);
 
-    if (ElementAccessIsDenseNative(constraints(), obj, id) &&
+    if (!shouldAbortOnPreliminaryGroups(obj) &&
+        ElementAccessIsDenseNative(constraints(), obj, id) &&
         !ElementAccessHasExtraIndexedProperty(constraints(), obj))
     {
         return jsop_in_dense();
     }
 
     current->pop();
     current->pop();
     MIn* ins = MIn::New(alloc(), id, obj);
@@ -12878,17 +12874,17 @@ MConstant*
 IonBuilder::constant(const Value& v)
 {
     // For performance reason (TLS) and error code handling (AtomizeString), we
     // should prefer the specialized frunction constantMaybeAtomize instead of
     // constant.
     MOZ_ASSERT(!v.isString() || v.toString()->isAtom(),
                "To handle non-atomized strings, you should use constantMaybeAtomize instead of constant.");
     if (v.isString() && MOZ_UNLIKELY(!v.toString()->isAtom())) {
-        MConstant *cst = constantMaybeAtomize(v);
+        MConstant* cst = constantMaybeAtomize(v);
         if (!cst)
             js::CrashAtUnhandlableOOM("Use constantMaybeAtomize.");
         return cst;
     }
 
     MConstant* c = MConstant::New(alloc(), v, constraints());
     current->add(c);
     return c;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -417,16 +417,17 @@ class IonBuilder
                   BarrierKind barrier, TemporaryTypeSet* types);
     bool loadSlot(MDefinition* obj, Shape* shape, MIRType rvalType,
                   BarrierKind barrier, TemporaryTypeSet* types);
     bool storeSlot(MDefinition* obj, size_t slot, size_t nfixed,
                    MDefinition* value, bool needsBarrier,
                    MIRType slotType = MIRType_None);
     bool storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool needsBarrier,
                    MIRType slotType = MIRType_None);
+    bool shouldAbortOnPreliminaryGroups(MDefinition *obj);
 
     MDefinition* tryInnerizeWindow(MDefinition* obj);
 
     // jsop_getprop() helpers.
     bool checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs);
     bool getPropTryInferredConstant(bool* emitted, MDefinition* obj, PropertyName* name,
                                     TemporaryTypeSet* types);
     bool getPropTryArgumentsLength(bool* emitted, MDefinition* obj);
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -540,17 +540,29 @@ IsNullOrUndefined(MIRType type)
 {
     return type == MIRType_Null || type == MIRType_Undefined;
 }
 
 static inline bool
 IsSimdType(MIRType type)
 {
     return type == MIRType_Int32x4 || type == MIRType_Float32x4;
-};
+}
+
+static inline bool
+IsFloatingPointSimdType(MIRType type)
+{
+    return type == MIRType_Float32x4;
+}
+
+static inline bool
+IsIntegerSimdType(MIRType type)
+{
+    return type == MIRType_Int32x4;
+}
 
 static inline bool
 IsMagicType(MIRType type)
 {
     return type == MIRType_MagicHole ||
            type == MIRType_MagicOptimizedOut ||
            type == MIRType_MagicIsConstructing ||
            type == MIRType_MagicOptimizedArguments ||
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -523,17 +523,17 @@ BaselineFrameAndStackPointersFromTryNote
 {
     JSScript* script = frame.baselineFrame()->script();
     *framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
     *stackPointer = *framePointer - BaselineFrame::Size() -
                     (script->nfixed() + tn->stackDepth) * sizeof(Value);
 }
 
 static void
-SettleOnTryNote(JSContext* cx, JSTryNote *tn, const JitFrameIterator& frame,
+SettleOnTryNote(JSContext* cx, JSTryNote* tn, const JitFrameIterator& frame,
                 ScopeIter& si, ResumeFromException* rfe, jsbytecode** pc)
 {
     RootedScript script(cx, frame.baselineFrame()->script());
 
     // Unwind scope chain (pop block objects).
     if (cx->isExceptionPending())
         UnwindScope(cx, si, UnwindScopeToTryPc(script, tn));
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -561,16 +561,19 @@ class LSimdShift : public LInstructionHe
         return getOperand(0);
     }
     const LAllocation* value() {
         return getOperand(1);
     }
     MSimdShift::Operation operation() const {
         return mir_->toSimdShift()->operation();
     }
+    const char* extraName() const {
+        return MSimdShift::OperationName(operation());
+    }
     MSimdShift* mir() const {
         return mir_->toSimdShift();
     }
 };
 
 // SIMD selection of lanes from two int32x4 or float32x4 arguments based on a
 // int32x4 argument.
 class LSimdSelect : public LInstructionHelper<1, 3, 1>
@@ -3754,22 +3757,29 @@ class LInt32x4ToFloat32x4 : public LInst
 {
   public:
     LIR_HEADER(Int32x4ToFloat32x4);
     explicit LInt32x4ToFloat32x4(const LAllocation& input) {
         setOperand(0, input);
     }
 };
 
-class LFloat32x4ToInt32x4 : public LInstructionHelper<1, 1, 0>
+class LFloat32x4ToInt32x4 : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(Float32x4ToInt32x4);
-    explicit LFloat32x4ToInt32x4(const LAllocation& input) {
+    explicit LFloat32x4ToInt32x4(const LAllocation& input, const LDefinition& temp) {
         setOperand(0, input);
+        setTemp(0, temp);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const MSimdConvert* mir() const {
+        return mir_->toSimdConvert();
     }
 };
 
 // No-op instruction that is used to hold the entry snapshot. This simplifies
 // register allocation as it doesn't need to sniff the snapshot out of the
 // LIRGraph.
 class LStart : public LInstructionHelper<0, 0, 0>
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3885,21 +3885,23 @@ LIRGenerator::visitSimdConstant(MSimdCon
         MOZ_CRASH("Unknown SIMD kind when generating constant");
 }
 
 void
 LIRGenerator::visitSimdConvert(MSimdConvert* ins)
 {
     MOZ_ASSERT(IsSimdType(ins->type()));
     MDefinition* input = ins->input();
-    LUse use = useRegisterAtStart(input);
-
+    LUse use = useRegister(input);
     if (ins->type() == MIRType_Int32x4) {
         MOZ_ASSERT(input->type() == MIRType_Float32x4);
-        define(new(alloc()) LFloat32x4ToInt32x4(use), ins);
+        LFloat32x4ToInt32x4* lir = new(alloc()) LFloat32x4ToInt32x4(use, temp());
+        if (!gen->conversionErrorLabel())
+            assignSnapshot(lir, Bailout_BoundsCheck);
+        define(lir, ins);
     } else if (ins->type() == MIRType_Float32x4) {
         MOZ_ASSERT(input->type() == MIRType_Int32x4);
         define(new(alloc()) LInt32x4ToFloat32x4(use), ins);
     } else {
         MOZ_CRASH("Unknown SIMD kind when generating constant");
     }
 }
 
@@ -4096,16 +4098,18 @@ LIRGenerator::visitSimdBinaryBitwise(MSi
         MOZ_CRASH("Unknown SIMD kind when doing bitwise operations");
     }
 }
 
 void
 LIRGenerator::visitSimdShift(MSimdShift* ins)
 {
     MOZ_ASSERT(ins->type() == MIRType_Int32x4);
+    MOZ_ASSERT(ins->lhs()->type() == MIRType_Int32x4);
+    MOZ_ASSERT(ins->rhs()->type() == MIRType_Int32);
 
     LUse vector = useRegisterAtStart(ins->lhs());
     LAllocation value = useRegisterOrConstant(ins->rhs());
     LSimdShift* lir = new(alloc()) LSimdShift(vector, value);
     defineReuseInput(lir, ins, 0);
 }
 
 void
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -41,16 +41,23 @@ IonBuilder::inlineNativeCall(CallInfo& c
     if (!optimizationInfo().inlineNative()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
         return InliningStatus_NotInlined;
     }
 
     // Default failure reason is observing an unsupported type.
     trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
 
+    if (shouldAbortOnPreliminaryGroups(callInfo.thisArg()))
+        return InliningStatus_NotInlined;
+    for (size_t i = 0; i < callInfo.argc(); i++) {
+        if (shouldAbortOnPreliminaryGroups(callInfo.getArg(i)))
+            return InliningStatus_NotInlined;
+    }
+
     // Atomic natives.
     if (native == atomics_compareExchange)
         return inlineAtomicsCompareExchange(callInfo);
     if (native == atomics_load)
         return inlineAtomicsLoad(callInfo);
     if (native == atomics_store)
         return inlineAtomicsStore(callInfo);
     if (native == atomics_fence)
@@ -286,16 +293,23 @@ IonBuilder::inlineNativeCall(CallInfo& c
                                                     SimdTypeDescr::Int32x4);                     \
     if (native == js::simd_float32x4_##OP)                                                       \
         return inlineBinarySimd<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::OP##_, \
                                                     SimdTypeDescr::Float32x4);
 
     BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_)
 #undef INLINE_SIMD_BITWISE_
 
+    if (native == js::simd_int32x4_shiftLeftByScalar)
+        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4);
+    if (native == js::simd_int32x4_shiftRightArithmeticByScalar)
+        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4);
+    if (native == js::simd_int32x4_shiftRightLogicalByScalar)
+        return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4);
+
 #define INLINE_SIMD_COMPARISON_(OP)                                                                \
     if (native == js::simd_int32x4_##OP)                                                           \
         return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4);      \
     if (native == js::simd_float32x4_##OP)                                                         \
         return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4);
 
     COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_)
 #undef INLINE_SIMD_COMPARISON_
@@ -3104,17 +3118,17 @@ IonBuilder::inlineConstructTypedObject(C
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr)
 {
     // Generic constructor of SIMD valuesX4.
     MIRType simdType = SimdTypeDescrToMIRType(descr->type());
 
-    // TODO Happens for TYPE_FLOAT64 (Bug 1124205)
+    // TODO Happens for Float64x2 (Bug 1124205)
     if (simdType == MIRType_Undefined)
         return InliningStatus_NotInlined;
 
     // Take the templateObject out of Baseline ICs, such that we can box
     // SIMD value type in the same kind of objects.
     MOZ_ASSERT(size_t(descr->size(descr->type())) < InlineTypedObject::MaximumSize);
     JSObject* templateObject = inspector->getTemplateObjectForClassHook(pc, descr->getClass());
     if (!templateObject)
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1012,16 +1012,21 @@ MSimdUnaryArith::printOpcode(FILE* fp) c
 {
     PrintOpcodeOperation(this, fp);
 }
 void
 MSimdBinaryComp::printOpcode(FILE* fp) const
 {
     PrintOpcodeOperation(this, fp);
 }
+void
+MSimdShift::printOpcode(FILE* fp) const
+{
+    PrintOpcodeOperation(this, fp);
+}
 
 void
 MSimdInsertElement::printOpcode(FILE* fp) const
 {
     MDefinition::printOpcode(fp);
     fprintf(fp, " (%s)", MSimdInsertElement::LaneName(lane()));
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1526,19 +1526,24 @@ class MSimdConstant
 class MSimdConvert
   : public MUnaryInstruction,
     public SimdPolicy<0>::Data
 {
     MSimdConvert(MDefinition* obj, MIRType fromType, MIRType toType)
       : MUnaryInstruction(obj)
     {
         MOZ_ASSERT(IsSimdType(toType));
-        setMovable();
         setResultType(toType);
         specialization_ = fromType; // expects fromType as input
+
+        setMovable();
+        if (IsFloatingPointSimdType(fromType) && IsIntegerSimdType(toType)) {
+            // Does the extra range check => do not remove
+            setGuard();
+        }
     }
 
   public:
     INSTRUCTION_HEADER(SimdConvert)
     static MSimdConvert* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType fromType,
                                   MIRType toType)
     {
         MOZ_ASSERT(IsSimdType(obj->type()) && fromType == obj->type());
@@ -2266,50 +2271,68 @@ class MSimdBinaryBitwise
 
     void printOpcode(FILE* fp) const override;
 
     ALLOW_CLONE(MSimdBinaryBitwise)
 };
 
 class MSimdShift
   : public MBinaryInstruction,
-    public NoTypePolicy::Data
+    public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
 {
   public:
     enum Operation {
         lsh,
         rsh,
         ursh
     };
 
   private:
     Operation operation_;
 
     MSimdShift(MDefinition* left, MDefinition* right, Operation op)
       : MBinaryInstruction(left, right), operation_(op)
     {
-        MOZ_ASSERT(left->type() == MIRType_Int32x4 && right->type() == MIRType_Int32);
         setResultType(MIRType_Int32x4);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(SimdShift)
     static MSimdShift* NewAsmJS(TempAllocator& alloc, MDefinition* left,
                                 MDefinition* right, Operation op)
     {
+        MOZ_ASSERT(left->type() == MIRType_Int32x4 && right->type() == MIRType_Int32);
+        return new(alloc) MSimdShift(left, right, op);
+    }
+
+    static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                           Operation op, MIRType type)
+    {
+        MOZ_ASSERT(type == MIRType_Int32x4);
         return new(alloc) MSimdShift(left, right, op);
     }
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     Operation operation() const { return operation_; }
 
+    static const char* OperationName(Operation op) {
+        switch (op) {
+          case lsh:  return "lsh";
+          case rsh:  return "rsh-arithmetic";
+          case ursh: return "rhs-logical";
+        }
+        MOZ_CRASH("unexpected operation");
+    }
+
+    void printOpcode(FILE* fp) const override;
+
     bool congruentTo(const MDefinition* ins) const override {
         if (!binaryCongruentTo(ins))
             return false;
         return operation_ == ins->toSimdShift()->operation();
     }
 
     ALLOW_CLONE(MSimdShift)
 };
@@ -8085,17 +8108,17 @@ class MNot
         operandIsNeverNaN_(false)
     {
         setResultType(MIRType_Boolean);
         setMovable();
         if (constraints)
             cacheOperandMightEmulateUndefined(constraints);
     }
 
-    void cacheOperandMightEmulateUndefined(CompilerConstraintList *constraints);
+    void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
 
   public:
     static MNot* New(TempAllocator& alloc, MDefinition* elements,
                      CompilerConstraintList* constraints = nullptr)
     {
         return new(alloc) MNot(elements, constraints);
     }
     static MNot* NewAsmJS(TempAllocator& alloc, MDefinition* elements) {
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -33,17 +33,19 @@ class MIRGraph;
 class OptimizationInfo;
 
 class MIRGenerator
 {
   public:
     MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options,
                  TempAllocator* alloc, MIRGraph* graph,
                  CompileInfo* info, const OptimizationInfo* optimizationInfo,
-                 Label* outOfBoundsLabel = nullptr, bool usesSignalHandlersForAsmJSOOB = false);
+                 Label* outOfBoundsLabel = nullptr,
+                 Label* conversionErrorLabel = nullptr,
+                 bool usesSignalHandlersForAsmJSOOB = false);
 
     TempAllocator& alloc() {
         return *alloc_;
     }
     MIRGraph& graph() {
         return *graph_;
     }
     bool ensureBallast() {
@@ -197,16 +199,20 @@ class MIRGenerator
     // minor GC while compilation happens off-thread. This Vector should only
     // be accessed on the main thread (IonBuilder, nursery GC or
     // CodeGenerator::link).
     ObjectVector nurseryObjects_;
 
     void addAbortedPreliminaryGroup(ObjectGroup* group);
 
     Label* outOfBoundsLabel_;
+    // Label where we should jump in asm.js mode, in the case where we have an
+    // invalid conversion or a loss of precision (when converting from a
+    // floating point SIMD type into an integer SIMD type).
+    Label* conversionErrorLabel_;
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
     bool usesSignalHandlersForAsmJSOOB_;
 #endif
 
     void setForceAbort() {
         shouldForceAbort_ = true;
     }
     bool shouldForceAbort() {
@@ -224,17 +230,22 @@ class MIRGenerator
     const JitCompileOptions options;
 
     void traceNurseryObjects(JSTracer* trc);
 
     const ObjectVector& nurseryObjects() const {
         return nurseryObjects_;
     }
 
+    Label* conversionErrorLabel() const {
+        MOZ_ASSERT((conversionErrorLabel_ != nullptr) == compilingAsmJS());
+        return conversionErrorLabel_;
+    }
     Label* outOfBoundsLabel() const {
+        MOZ_ASSERT(compilingAsmJS());
         return outOfBoundsLabel_;
     }
     bool needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const;
     size_t foldableOffsetRange(const MAsmJSHeapAccess* access) const;
 };
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -15,17 +15,19 @@
 
 using namespace js;
 using namespace js::jit;
 using mozilla::Swap;
 
 MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options,
                            TempAllocator* alloc, MIRGraph* graph, CompileInfo* info,
                            const OptimizationInfo* optimizationInfo,
-                           Label* outOfBoundsLabel, bool usesSignalHandlersForAsmJSOOB)
+                           Label* outOfBoundsLabel,
+                           Label* conversionErrorLabel,
+                           bool usesSignalHandlersForAsmJSOOB)
   : compartment(compartment),
     info_(info),
     optimizationInfo_(optimizationInfo),
     alloc_(alloc),
     graph_(graph),
     abortReason_(AbortReason_NoAbort),
     shouldForceAbort_(false),
     abortedPreliminaryGroups_(*alloc_),
@@ -37,16 +39,17 @@ MIRGenerator::MIRGenerator(CompileCompar
     usesSimd_(false),
     usesSimdCached_(false),
     minAsmJSHeapLength_(0),
     modifiesFrameArguments_(false),
     instrumentedProfiling_(false),
     instrumentedProfilingIsCached_(false),
     nurseryObjects_(*alloc),
     outOfBoundsLabel_(outOfBoundsLabel),
+    conversionErrorLabel_(conversionErrorLabel),
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
     usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB),
 #endif
     options(options)
 { }
 
 bool
 MIRGenerator::usesSimd()
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -965,25 +965,25 @@ class MacroAssembler : public MacroAssem
     void loadStackPtr(T t) { loadPtr(t, getStackPointer()); }
     template <typename T>
     void storeStackPtr(T t) { storePtr(getStackPointer(), t); }
 
     // StackPointer testing functions.
     // On ARM64, sp can function as the zero register depending on context.
     // Code shared across platforms must use these functions to be valid.
     template <typename T>
-    void branchTestStackPtr(Condition cond, T t, Label *label) {
+    void branchTestStackPtr(Condition cond, T t, Label* label) {
         branchTestPtr(cond, getStackPointer(), t, label);
     }
     template <typename T>
-    void branchStackPtr(Condition cond, T rhs, Label *label) {
+    void branchStackPtr(Condition cond, T rhs, Label* label) {
         branchPtr(cond, getStackPointer(), rhs, label);
     }
     template <typename T>
-    void branchStackPtrRhs(Condition cond, T lhs, Label *label) {
+    void branchStackPtrRhs(Condition cond, T lhs, Label* label) {
         branchPtr(cond, lhs, getStackPointer(), label);
     }
 #endif // !JS_CODEGEN_ARM64
 
   private:
     // These two functions are helpers used around call sites throughout the
     // assembler. They are called from the above call wrappers to emit the
     // necessary instrumentation.
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -884,16 +884,17 @@ enum AsmJSImmKind
     AsmJSImm_PowD            = AsmJSExit::Builtin_PowD,
     AsmJSImm_ATan2D          = AsmJSExit::Builtin_ATan2D,
     AsmJSImm_Runtime,
     AsmJSImm_RuntimeInterruptUint32,
     AsmJSImm_StackLimit,
     AsmJSImm_ReportOverRecursed,
     AsmJSImm_OnDetached,
     AsmJSImm_OnOutOfBounds,
+    AsmJSImm_OnImpreciseConversion,
     AsmJSImm_HandleExecutionInterrupt,
     AsmJSImm_InvokeFromAsmJS_Ignore,
     AsmJSImm_InvokeFromAsmJS_ToInt32,
     AsmJSImm_InvokeFromAsmJS_ToNumber,
     AsmJSImm_CoerceInPlace_ToInt32,
     AsmJSImm_CoerceInPlace_ToNumber,
     AsmJSImm_Limit
 };
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -2170,17 +2170,68 @@ CodeGeneratorX86Shared::visitInt32x4ToFl
     masm.convertInt32x4ToFloat32x4(in, out);
 }
 
 void
 CodeGeneratorX86Shared::visitFloat32x4ToInt32x4(LFloat32x4ToInt32x4* ins)
 {
     FloatRegister in = ToFloatRegister(ins->input());
     FloatRegister out = ToFloatRegister(ins->output());
+    Register temp = ToRegister(ins->temp());
+
     masm.convertFloat32x4ToInt32x4(in, out);
+
+    OutOfLineSimdFloatToIntCheck *ool = new(alloc()) OutOfLineSimdFloatToIntCheck(temp, in, ins);
+    addOutOfLineCode(ool, ins->mir());
+
+    static const SimdConstant InvalidResult = SimdConstant::SplatX4(int32_t(-2147483648));
+
+    masm.loadConstantInt32x4(InvalidResult, ScratchSimdReg);
+    masm.packedEqualInt32x4(Operand(out), ScratchSimdReg);
+    // TODO (bug 1156228): If we have SSE4.1, we can use PTEST here instead of
+    // the two following instructions.
+    masm.vmovmskps(ScratchSimdReg, temp);
+    masm.cmp32(temp, Imm32(0));
+    masm.j(Assembler::NotEqual, ool->entry());
+
+    masm.bind(ool->rejoin());
+}
+
+void
+CodeGeneratorX86Shared::visitOutOfLineSimdFloatToIntCheck(OutOfLineSimdFloatToIntCheck *ool)
+{
+    static const SimdConstant Int32MaxX4 = SimdConstant::SplatX4(2147483647.f);
+    static const SimdConstant Int32MinX4 = SimdConstant::SplatX4(-2147483648.f);
+
+    Label bail;
+    Label* onConversionError = gen->conversionErrorLabel();
+    if (!onConversionError)
+        onConversionError = &bail;
+
+    FloatRegister input = ool->input();
+    Register temp = ool->temp();
+
+    masm.loadConstantFloat32x4(Int32MinX4, ScratchSimdReg);
+    masm.vcmpleps(Operand(input), ScratchSimdReg, ScratchSimdReg);
+    masm.vmovmskps(ScratchSimdReg, temp);
+    masm.cmp32(temp, Imm32(15));
+    masm.j(Assembler::NotEqual, onConversionError);
+
+    masm.loadConstantFloat32x4(Int32MaxX4, ScratchSimdReg);
+    masm.vcmpleps(Operand(input), ScratchSimdReg, ScratchSimdReg);
+    masm.vmovmskps(ScratchSimdReg, temp);
+    masm.cmp32(temp, Imm32(0));
+    masm.j(Assembler::NotEqual, onConversionError);
+
+    masm.jump(ool->rejoin());
+
+    if (bail.used()) {
+        masm.bind(&bail);
+        bailout(ool->ins()->snapshot());
+    }
 }
 
 void
 CodeGeneratorX86Shared::visitSimdValueInt32x4(LSimdValueInt32x4* ins)
 {
     MOZ_ASSERT(ins->mir()->type() == MIRType_Int32x4);
 
     FloatRegister output = ToFloatRegister(ins->output());
@@ -3099,25 +3150,22 @@ CodeGeneratorX86Shared::visitSimdBinaryB
 }
 
 void
 CodeGeneratorX86Shared::visitSimdShift(LSimdShift* ins)
 {
     FloatRegister out = ToFloatRegister(ins->output());
     MOZ_ASSERT(ToFloatRegister(ins->vector()) == out); // defineReuseInput(0);
 
-    // TODO: If the shift count is greater than 31, this will just zero all
-    // lanes by default for lsh and ursh, and set the count to 32 for rsh
-    // (which will just extend the sign bit to all bits). Plain JS doesn't do
-    // this: instead it only keeps the five low bits of the mask. Spec isn't
-    // clear about that topic so this might need to be fixed. See also bug
-    // 1068028.
+    // If the shift count is greater than 31, this will just zero all lanes by
+    // default for lsh and ursh, and for rsh extend the sign bit to all bits,
+    // per the SIMD.js spec (as of March 19th 2015).
     const LAllocation* val = ins->value();
     if (val->isConstant()) {
-        int32_t c = ToInt32(val);
+        uint32_t c = uint32_t(ToInt32(val));
         if (c > 31) {
             switch (ins->operation()) {
               case MSimdShift::lsh:
               case MSimdShift::ursh:
                 masm.zeroInt32x4(out);
                 return;
               default:
                 c = 31;
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -64,16 +64,38 @@ class CodeGeneratorX86Shared : public Co
         Label* outOfBounds() const { return outOfBounds_; }
         Register ptrReg() const { return ptrReg_; }
         int32_t offset() const { return offset_; }
         void accept(CodeGeneratorX86Shared* codegen) {
             codegen->visitOffsetBoundsCheck(this);
         }
     };
 
+    // Additional bounds check for vector Float to Int conversion, when the
+    // undefined pattern is seen. Might imply a bailout.
+    class OutOfLineSimdFloatToIntCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared>
+    {
+        Register temp_;
+        FloatRegister input_;
+        LInstruction* ins_;
+
+      public:
+        OutOfLineSimdFloatToIntCheck(Register temp, FloatRegister input, LInstruction *ins)
+          : temp_(temp), input_(input), ins_(ins)
+        {}
+
+        Register temp() const { return temp_; }
+        FloatRegister input() const { return input_; }
+        LInstruction* ins() const { return ins_; }
+
+        void accept(CodeGeneratorX86Shared* codegen) {
+            codegen->visitOutOfLineSimdFloatToIntCheck(this);
+        }
+    };
+
     // Functions for emitting bounds-checking code with branches.
     MOZ_WARN_UNUSED_RESULT
     uint32_t emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, const MInstruction* ins,
                                         Register ptr, Label* fail);
     void cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, Register ptr);
 
     // Label for the common return path.
     NonAssertingLabel returnLabel_;
@@ -277,16 +299,17 @@ class CodeGeneratorX86Shared : public Co
 
     // Out of line visitors.
     void visitOutOfLineBailout(OutOfLineBailout* ool);
     void visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation* ool);
     void visitMulNegativeZeroCheck(MulNegativeZeroCheck* ool);
     void visitModOverflowCheck(ModOverflowCheck* ool);
     void visitReturnZero(ReturnZero* ool);
     void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
+    void visitOutOfLineSimdFloatToIntCheck(OutOfLineSimdFloatToIntCheck* ool);
     void generateInvalidateEpilogue();
 };
 
 // An out-of-line bailout thunk.
 class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorX86Shared>
 {
     LSnapshot* snapshot_;
 
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
@@ -929,21 +929,20 @@ class MacroAssemblerX86Shared : public A
     void convertFloat32ToDouble(FloatRegister src, FloatRegister dest) {
         vcvtss2sd(src, dest, dest);
     }
     void convertDoubleToFloat32(FloatRegister src, FloatRegister dest) {
         vcvtsd2ss(src, dest, dest);
     }
 
     void convertFloat32x4ToInt32x4(FloatRegister src, FloatRegister dest) {
-        // TODO: Note that if the conversion failed (because the converted
+        // Note that if the conversion failed (because the converted
         // result is larger than the maximum signed int32, or less than the
         // least signed int32, or NaN), this will return the undefined integer
-        // value (0x8000000). Spec should define what to do in such cases. See
-        // also bug 1068020.
+        // value (0x8000000).
         vcvttps2dq(src, dest);
     }
     void convertInt32x4ToFloat32x4(FloatRegister src, FloatRegister dest) {
         vcvtdq2ps(src, dest);
     }
 
     void bitwiseAndX4(const Operand& src, FloatRegister dest) {
         // TODO Using the "ps" variant for all types incurs a domain crossing
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -435,23 +435,24 @@ MSG_DEF(JSMSG_NUMBERS_OUT_OF_ORDER,    0
 MSG_DEF(JSMSG_TOO_MANY_PARENS,         0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
 MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN,   0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
 MSG_DEF(JSMSG_UNTERM_CLASS,            0, JSEXN_SYNTAXERR, "unterminated character class")
 
 // Self-hosting
 MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR,    0, JSEXN_ERR, "internal error getting the default locale")
 MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1, JSEXN_ERR, "No such property on self-hosted object: {0}")
 
-// Typed object
+// Typed object / SIMD
 MSG_DEF(JSMSG_INVALID_PROTOTYPE,       0, JSEXN_TYPEERR, "prototype field is not an object")
 MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
+MSG_DEF(JSMSG_SIMD_FAILED_CONVERSION,  0, JSEXN_RANGEERR, "SIMD conversion loses precision")
 
 // Typed array
 MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX,   0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
 MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED,    0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -17,16 +17,17 @@ static const JSClass global_class = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JS::PersistentRootedObject trusted_glob;
 static JS::PersistentRootedObject trusted_fun;
 
 static bool
 CallTrusted(JSContext* cx, unsigned argc, jsval* vp)
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -20,17 +20,17 @@ GlobalResolve(JSContext* cx, JS::HandleO
 }
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
     static const JSClass cls = {
         "global", JSCLASS_GLOBAL_FLAGS,
         nullptr, nullptr, nullptr, nullptr,
         GlobalEnumerate, GlobalResolve, nullptr,
-        nullptr, nullptr, nullptr, nullptr,
+        nullptr, nullptr, nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
     options.setVersion(JSVERSION_LATEST);
     JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
     if (!g)
--- a/js/src/jsapi-tests/testGCNursery.cpp
+++ b/js/src/jsapi-tests/testGCNursery.cpp
@@ -22,16 +22,17 @@ static const js::Class TenuredClass = {
     "TenuredClass",
     0,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* ??? */
     _finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
@@ -42,16 +43,17 @@ static const js::Class NurseryClass = {
     "NurseryClass",
     JSCLASS_FINALIZE_FROM_NURSERY | JSCLASS_HAS_RESERVED_SLOTS(1),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* ??? */
     _finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -93,17 +93,17 @@ BEGIN_TEST(testNewObject_1)
     CHECK_SAME(v, INT_TO_JSVAL(N - 1));
 
     // With JSClass.construct.
     static const JSClass cls = {
         "testNewObject_1",
         0,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, constructHook
+        nullptr, nullptr, nullptr, constructHook
     };
     JS::RootedObject ctor(cx, JS_NewObject(cx, &cls));
     CHECK(ctor);
     JS::RootedValue rt2(cx, OBJECT_TO_JSVAL(ctor));
     obj = JS_New(cx, ctor, JS::HandleValueArray::subarray(argv, 0, 3));
     CHECK(obj);
     CHECK(JS_GetElement(cx, ctor, 0, &v));
     CHECK_SAME(v, JSVAL_ZERO);
--- a/js/src/jsapi-tests/testOps.cpp
+++ b/js/src/jsapi-tests/testOps.cpp
@@ -18,17 +18,17 @@ my_convert(JSContext* context, JS::Handl
     }
     return false;
 }
 
 static const JSClass myClass = {
     "MyClass",
     0,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, my_convert
+    nullptr, nullptr, nullptr, my_convert
 };
 
 static bool
 createMyObject(JSContext* context, unsigned argc, jsval* vp)
 {
     JS_BeginRequest(context);
 
     //JS_GC(context); //<- if we make GC here, all is ok
--- a/js/src/jsapi-tests/testPersistentRooted.cpp
+++ b/js/src/jsapi-tests/testPersistentRooted.cpp
@@ -24,16 +24,17 @@ const JSClass BarkWhenTracedClass::class
     "BarkWhenTracedClass", JSCLASS_IMPLEMENTS_BARRIERS,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     finalize,
     nullptr,
     nullptr,
     nullptr,
     trace
 };
 
 struct Kennel {
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -143,16 +143,17 @@ JSObject* newKey()
         "keyWithDelgate",
         JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
         nullptr, /* resolve */
+        nullptr, /* mayResolve */
         nullptr, /* convert */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         nullptr, /* trace */
         JS_NULL_CLASS_SPEC,
         {
@@ -199,16 +200,17 @@ JSObject* newDelegate()
         "delegate",
         JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
         nullptr, /* resolve */
+        nullptr, /* mayResolve */
         nullptr, /* convert */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         JS_GlobalObjectTraceHook,
         JS_NULL_CLASS_SPEC,
         {
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -225,17 +225,17 @@ class JSAPITest
 
     JSAPITestString messages() const { return msgs; }
 
     static const JSClass * basicGlobalClass() {
         static const JSClass c = {
             "global", JSCLASS_GLOBAL_FLAGS,
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr, nullptr,
-            nullptr, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr,
             JS_GlobalObjectTraceHook
         };
         return &c;
     }
 
   protected:
     static bool
     print(JSContext* cx, unsigned argc, jsval* vp)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1133,23 +1133,22 @@ JS_InitStandardClasses(JSContext* cx, Ha
 typedef struct JSStdName {
     size_t      atomOffset;     /* offset of atom pointer in JSAtomState */
     JSProtoKey  key;
     bool isDummy() const { return key == JSProto_Null; }
     bool isSentinel() const { return key == JSProto_LIMIT; }
 } JSStdName;
 
 static const JSStdName*
-LookupStdName(JSRuntime* rt, HandleString name, const JSStdName* table)
-{
-    MOZ_ASSERT(name->isAtom());
+LookupStdName(const JSAtomState& names, JSAtom* name, const JSStdName* table)
+{
     for (unsigned i = 0; !table[i].isSentinel(); i++) {
         if (table[i].isDummy())
             continue;
-        JSAtom* atom = AtomStateOffsetToName(*rt->commonNames, table[i].atomOffset);
+        JSAtom* atom = AtomStateOffsetToName(names, table[i].atomOffset);
         MOZ_ASSERT(atom);
         if (name == atom)
             return &table[i];
     }
 
     return nullptr;
 }
 
@@ -1215,33 +1214,32 @@ JS_ResolveStandardClass(JSContext* cx, H
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     *resolved = false;
 
     rt = cx->runtime();
     if (!rt->hasContexts() || !JSID_IS_ATOM(id))
         return true;
 
-    RootedString idstr(cx, JSID_TO_STRING(id));
-
     /* Check whether we're resolving 'undefined', and define it if so. */
+    JSAtom* idAtom = JSID_TO_ATOM(id);
     JSAtom* undefinedAtom = cx->names().undefined;
-    if (idstr == undefinedAtom) {
+    if (idAtom == undefinedAtom) {
         *resolved = true;
         return DefineProperty(cx, obj, undefinedAtom->asPropertyName(),
                               UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_PERMANENT | JSPROP_READONLY);
     }
 
     /* Try for class constructors/prototypes named by well-known atoms. */
-    stdnm = LookupStdName(rt, idstr, standard_class_names);
+    stdnm = LookupStdName(cx->names(), idAtom, standard_class_names);
 
     /* Try less frequently used top-level functions and constants. */
     if (!stdnm)
-        stdnm = LookupStdName(rt, idstr, builtin_property_names);
+        stdnm = LookupStdName(cx->names(), idAtom, builtin_property_names);
 
     // If this class is anonymous, then it doesn't exist as a global
     // property, so we won't resolve anything.
     JSProtoKey key = stdnm ? stdnm->key : JSProto_Null;
     if (key != JSProto_Null && !(ProtoKeyToClass(key)->flags & JSCLASS_IS_ANONYMOUS)) {
         if (!GlobalObject::ensureConstructor(cx, global, stdnm->key))
             return false;
 
@@ -1256,16 +1254,37 @@ JS_ResolveStandardClass(JSContext* cx, H
     // Object.prototype yet. Force it now.
     if (!global->getOrCreateObjectPrototype(cx))
         return false;
 
     return true;
 }
 
 JS_PUBLIC_API(bool)
+JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj)
+{
+    MOZ_ASSERT_IF(maybeObj, maybeObj->is<GlobalObject>());
+
+    // The global object's resolve hook is special: JS_ResolveStandardClass
+    // initializes the prototype chain lazily. Only attempt to optimize here
+    // if we know the prototype chain has been initialized.
+    if (!maybeObj || !maybeObj->getProto())
+        return true;
+
+    if (!JSID_IS_ATOM(id))
+        return false;
+
+    JSAtom* atom = JSID_TO_ATOM(id);
+
+    return atom == names.undefined ||
+           LookupStdName(names, atom, standard_class_names) ||
+           LookupStdName(names, atom, builtin_property_names);
+}
+
+JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     MOZ_ASSERT(obj->is<GlobalObject>());
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     return GlobalObject::initStandardClasses(cx, global);
@@ -1300,18 +1319,19 @@ ProtoKeyToId(JSContext* cx, JSProtoKey k
 JS_PUBLIC_API(JSProtoKey)
 JS_IdToProtoKey(JSContext* cx, HandleId id)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     if (!JSID_IS_ATOM(id))
         return JSProto_Null;
-    RootedString idstr(cx, JSID_TO_STRING(id));
-    const JSStdName* stdnm = LookupStdName(cx->runtime(), idstr, standard_class_names);
+
+    JSAtom* atom = JSID_TO_ATOM(id);
+    const JSStdName* stdnm = LookupStdName(cx->names(), atom, standard_class_names);
     if (!stdnm)
         return JSProto_Null;
 
     MOZ_ASSERT(MOZ_ARRAY_LENGTH(standard_class_names) == JSProto_LIMIT + 1);
     return static_cast<JSProtoKey>(stdnm - standard_class_names);
 }
 
 JS_PUBLIC_API(JSObject*)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -979,17 +979,17 @@ JS_ValueToFunction(JSContext* cx, JS::Ha
 
 extern JS_PUBLIC_API(JSFunction*)
 JS_ValueToConstructor(JSContext* cx, JS::HandleValue v);
 
 extern JS_PUBLIC_API(JSString*)
 JS_ValueToSource(JSContext* cx, JS::Handle<JS::Value> v);
 
 extern JS_PUBLIC_API(bool)
-JS_DoubleIsInt32(double d, int32_t *ip);
+JS_DoubleIsInt32(double d, int32_t* ip);
 
 extern JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext* cx, JS::Handle<JS::Value> v);
 
 extern JS_PUBLIC_API(bool)
 JS_StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* equal);
 
 extern JS_PUBLIC_API(bool)
@@ -1502,16 +1502,19 @@ JS_InitStandardClasses(JSContext* cx, JS
  * to define standard classes lazily.  The class's enumerate op should call
  * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in
  * loops any classes not yet resolved lazily.
  */
 extern JS_PUBLIC_API(bool)
 JS_ResolveStandardClass(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved);
 
 extern JS_PUBLIC_API(bool)
+JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj);
+
+extern JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext* cx, JS::HandleObject obj);
 
 extern JS_PUBLIC_API(bool)
 JS_GetClassObject(JSContext* cx, JSProtoKey key, JS::MutableHandle<JSObject*> objp);
 
 extern JS_PUBLIC_API(bool)
 JS_GetClassPrototype(JSContext* cx, JSProtoKey key, JS::MutableHandle<JSObject*> objp);
 
@@ -5425,24 +5428,24 @@ struct PerformanceGroup {
     // the performance of this performance group for this iteration
     // of the event loop, `false` otherwise.
     bool hasStopwatch(uint64_t iteration) const {
         return stopwatch_ != nullptr && iteration_ == iteration;
     }
 
     // Mark that an instance of `AutoStopwatch` is monitoring
     // the performance of this group for a given iteration.
-    void acquireStopwatch(uint64_t iteration, const AutoStopwatch *stopwatch) {
+    void acquireStopwatch(uint64_t iteration, const AutoStopwatch* stopwatch) {
         iteration_ = iteration;
         stopwatch_ = stopwatch;
     }
 
     // Mark that no `AutoStopwatch` is monitoring the
     // performance of this group for the iteration.
-    void releaseStopwatch(uint64_t iteration, const AutoStopwatch *stopwatch) {
+    void releaseStopwatch(uint64_t iteration, const AutoStopwatch* stopwatch) {
         if (iteration_ != iteration)
             return;
 
         MOZ_ASSERT(stopwatch == stopwatch_ || stopwatch_ == nullptr);
         stopwatch_ = nullptr;
     }
 
     PerformanceGroup()
@@ -5455,17 +5458,17 @@ struct PerformanceGroup {
         MOZ_ASSERT(refCount_ == 0);
     }
   private:
     PerformanceGroup& operator=(const PerformanceGroup&) = delete;
     PerformanceGroup(const PerformanceGroup&) = delete;
 
     // The stopwatch currently monitoring the group,
     // or `nullptr` if none. Used ony for comparison.
-    const AutoStopwatch *stopwatch_;
+    const AutoStopwatch* stopwatch_;
 
     // The current iteration of the event loop. If necessary,
     // may safely overflow.
     uint64_t iteration_;
 
     // Increment/decrement the refcounter, return the updated value.
     uint64_t incRefCount() {
         MOZ_ASSERT(refCount_ + 1 > 0);
@@ -5485,50 +5488,50 @@ struct PerformanceGroup {
 //
 // Indirection towards a PerformanceGroup.
 // This structure handles reference counting for instances of PerformanceGroup.
 //
 struct PerformanceGroupHolder {
     // Get the group.
     // On first call, this causes a single Hashtable lookup.
     // Successive calls do not require further lookups.
-    js::PerformanceGroup *getGroup();
+    js::PerformanceGroup* getGroup();
 
     // `true` if the this holder is currently associated to a
     // PerformanceGroup, `false` otherwise. Use this method to avoid
     // instantiating a PerformanceGroup if you only need to get
     // available performance data.
     inline bool isLinked() const {
         return group_ != nullptr;
     }
 
     // Remove the link to the PerformanceGroup. This method is designed
     // as an invalidation mechanism if the JSCompartment changes nature
     // (new values of `isSystem()`, `principals()` or `addonId`).
     void unlink();
 
-    PerformanceGroupHolder(JSRuntime *runtime, JSCompartment *compartment)
+    PerformanceGroupHolder(JSRuntime* runtime, JSCompartment* compartment)
       : runtime_(runtime)
       , compartment_(compartment)
       , group_(nullptr)
     {   }
     ~PerformanceGroupHolder();
 private:
     // Return the key representing this PerformanceGroup in
     // Runtime::Stopwatch.
     // Do not deallocate the key.
     void* getHashKey();
 
-    JSRuntime *runtime_;
-    JSCompartment *compartment_;
+    JSRuntime* runtime_;
+    JSCompartment* compartment_;
 
     // The PerformanceGroup held by this object.
     // Initially set to `nullptr` until the first cal to `getGroup`.
     // May be reset to `nullptr` by a call to `unlink`.
-    js::PerformanceGroup *group_;
+    js::PerformanceGroup* group_;
 };
 
 /**
  * Reset any stopwatch currently measuring.
  *
  * This function is designed to be called when we process a new event.
  */
 extern JS_PUBLIC_API(void)
@@ -5555,17 +5558,17 @@ GetPerformanceData(JSRuntime*);
  * Performance statistics for a performance group (a process, an
  * add-on, a webpage, the built-ins or a special compartment).
  */
 struct PerformanceStats {
     /**
      * If this group represents an add-on, the ID of the addon,
      * otherwise `nullptr`.
      */
-    JSAddonId *addonId;
+    JSAddonId* addonId;
 
     /**
      * If this group represents a webpage, the process itself or a special
      * compartment, a human-readable name. Unspecified for add-ons.
      */
     char name[1024];
 
     /**
@@ -5593,14 +5596,14 @@ typedef js::Vector<PerformanceStats, 0, 
     /**
  * Extract the performance statistics.
  *
  * After a successful call, `stats` holds the `PerformanceStats` for
  * all performance groups, and `global` holds a `PerformanceStats`
  * representing the entire process.
  */
 extern JS_PUBLIC_API(bool)
-GetPerformanceStats(JSRuntime *rt, js::PerformanceStatsVector &stats, js::PerformanceStats &global);
+GetPerformanceStats(JSRuntime* rt, js::PerformanceStatsVector& stats, js::PerformanceStats& global);
 
 } /* namespace js */
 
 
 #endif /* jsapi_h */
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3244,16 +3244,17 @@ const Class ArrayObject::class_ = {
     "Array",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
     array_addProperty,
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     {
         GenericCreateConstructor<ArrayConstructor, 1, JSFunction::FinalizeKind>,
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -759,19 +759,19 @@ js::ReportErrorNumberVA(JSContext* cx, u
         js_free((void*)report.messageArgs);
     }
     js_free((void*)report.ucmessage);
 
     return warning;
 }
 
 static bool
-ExpandErrorArguments(ExclusiveContext *cx, JSErrorCallback callback,
-                     void *userRef, const unsigned errorNumber,
-                     char **messagep, JSErrorReport *reportp,
+ExpandErrorArguments(ExclusiveContext* cx, JSErrorCallback callback,
+                     void* userRef, const unsigned errorNumber,
+                     char** messagep, JSErrorReport* reportp,
                      ErrorArgumentsType argumentsType, ...)
 {
     va_list ap;
     va_start(ap, argumentsType);
     bool expanded = js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
                                                messagep, reportp, argumentsType, ap);
     va_end(ap);
     return expanded;
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3039,16 +3039,17 @@ const Class DateObject::class_ = {
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     date_convert,
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     {
         GenericCreateConstructor<DateConstructor, MAXARGS, JSFunction::FinalizeKind>,
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -72,16 +72,17 @@ static const JSFunctionSpec exception_me
         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
         nullptr,                 /* addProperty */ \
         nullptr,                 /* delProperty */ \
         nullptr,                 /* getProperty */ \
         nullptr,                 /* setProperty */ \
         nullptr,                 /* enumerate */ \
         nullptr,                 /* resolve */ \
+        nullptr,                 /* mayResolve */ \
         nullptr,                 /* convert */ \
         exn_finalize, \
         nullptr,                 /* call        */ \
         nullptr,                 /* hasInstance */ \
         nullptr,                 /* construct   */ \
         nullptr,                 /* trace       */ \
         { \
             ErrorObject::createConstructor, \
@@ -103,16 +104,17 @@ ErrorObject::classes[JSEXN_LIMIT] = {
         JSCLASS_HAS_CACHED_PROTO(JSProto_Error) |
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
         nullptr,                 /* addProperty */
         nullptr,                 /* delProperty */
         nullptr,                 /* getProperty */
         nullptr,                 /* setProperty */
         nullptr,                 /* enumerate */
         nullptr,                 /* resolve */
+        nullptr,                 /* mayResolve */
         nullptr,                 /* convert */
         exn_finalize,
         nullptr,                 /* call        */
         nullptr,                 /* hasInstance */
         nullptr,                 /* construct   */
         nullptr,                 /* trace       */
         {
             ErrorObject::createConstructor,
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -314,16 +314,17 @@ namespace js {
             JSCLASS_IMPLEMENTS_BARRIERS |                                               \
             flags,                                                                      \
         nullptr,                 /* addProperty */                                      \
         nullptr,                 /* delProperty */                                      \
         nullptr,                 /* getProperty */                                      \
         nullptr,                 /* setProperty */                                      \
         nullptr,                 /* enumerate */                                        \
         nullptr,                 /* resolve */                                          \
+        nullptr,                 /* mayResolve */                                       \
         js::proxy_Convert,                                                              \
         js::proxy_Finalize,      /* finalize    */                                      \
         nullptr,                 /* call        */                                      \
         js::proxy_HasInstance,   /* hasInstance */                                      \
         nullptr,                 /* construct   */                                      \
         js::proxy_Trace,         /* trace       */                                      \
         JS_NULL_CLASS_SPEC,                                                             \
         ext,                                                                            \
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -414,28 +414,28 @@ ResolveInterpretedFunctionPrototype(JSCo
         RootedValue objVal(cx, ObjectValue(*obj));
         if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0))
             return nullptr;
     }
 
     return proto;
 }
 
-bool
-js::FunctionHasResolveHook(const JSAtomState& atomState, jsid id)
+static bool
+fun_mayResolve(const JSAtomState& names, jsid id, JSObject*)
 {
     if (!JSID_IS_ATOM(id))
         return false;
 
     JSAtom* atom = JSID_TO_ATOM(id);
-    return atom == atomState.prototype || atom == atomState.length || atom == atomState.name;
+    return atom == names.prototype || atom == names.length || atom == names.name;
 }
 
-bool
-js::fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+static bool
+fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
 {
     if (!JSID_IS_ATOM(id))
         return true;
 
     RootedFunction fun(cx, &obj->as<JSFunction>());
 
     if (JSID_IS_ATOM(id, cx->names().prototype)) {
         /*
@@ -886,17 +886,18 @@ const Class JSFunction::class_ = {
     js_Function_str,
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     fun_enumerate,
-    js::fun_resolve,
+    fun_resolve,
+    fun_mayResolve,
     nullptr,                 /* convert     */
     nullptr,                 /* finalize    */
     nullptr,                 /* call        */
     fun_hasInstance,
     nullptr,                 /* construct   */
     fun_trace,
     {
         CreateFunctionConstructor,
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -48,18 +48,18 @@ class JSFunction : public js::NativeObje
                                        Function() call (not a FunctionDeclaration or nonstandard
                                        function-statement) */
         SELF_HOSTED      = 0x0080,  /* function is self-hosted builtin and must not be
                                        decompilable nor constructible. */
         SELF_HOSTED_CTOR = 0x0100,  /* function is self-hosted builtin constructor and
                                        must be constructible but not decompilable. */
         HAS_REST         = 0x0200,  /* function has a rest (...) parameter */
         INTERPRETED_LAZY = 0x0400,  /* function is interpreted but doesn't have a script yet */
-        RESOLVED_LENGTH  = 0x0800,  /* f.length has been resolved (see js::fun_resolve). */
-        RESOLVED_NAME    = 0x1000,  /* f.name has been resolved (see js::fun_resolve). */
+        RESOLVED_LENGTH  = 0x0800,  /* f.length has been resolved (see fun_resolve). */
+        RESOLVED_NAME    = 0x1000,  /* f.name has been resolved (see fun_resolve). */
 
         FUNCTION_KIND_SHIFT = 13,
         FUNCTION_KIND_MASK  = 0x3 << FUNCTION_KIND_SHIFT,
 
         ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
         ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
 
         /* Derived Flags values for convenience: */
@@ -564,19 +564,16 @@ DefineFunction(JSContext* cx, HandleObje
                unsigned nargs, unsigned flags,
                gc::AllocKind allocKind = JSFunction::FinalizeKind,
                NewObjectKind newKind = GenericObject);
 
 bool
 FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
 
 extern bool
-fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
-
-extern bool
 fun_toString(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 fun_bind(JSContext* cx, unsigned argc, Value* vp);
 
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough do that efficient
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -983,16 +983,17 @@ const Class PropertyIteratorObject::clas
     JSCLASS_HAS_PRIVATE |
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     finalize,
     nullptr, /* call        */
     nullptr, /* hasInstance */
     nullptr, /* construct   */
     trace
 };
 
@@ -1370,16 +1371,17 @@ const Class StopIterationObject::class_ 
     "StopIteration",
     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     stopiter_hasInstance
 };
 
 /* static */ bool
 GlobalObject::initIteratorClasses(JSContext* cx, Handle<GlobalObject*> global)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2779,29 +2779,20 @@ js::LookupPropertyPure(ExclusiveContext*
             }
 
             if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
                 *objp = obj;
                 *propp = shape;
                 return true;
             }
 
-            // Fail if there's a resolve hook. We allow the JSFunction resolve hook
-            // if we know it will never add a property with this name or str_resolve
-            // with a non-integer property.
-            do {
-                const Class* clasp = obj->getClass();
-                if (!clasp->resolve)
-                    break;
-                if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id))
-                    break;
-                if (clasp->resolve == str_resolve && !JSID_IS_INT(id))
-                    break;
+            // Fail if there's a resolve hook, unless the mayResolve hook tells
+            // us the resolve hook won't define a property with this id.
+            if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
                 return false;
-            } while (0);
         } else if (obj->is<UnboxedPlainObject>()) {
             if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
                 *objp = obj;
                 MarkNonNativePropertyFound<NoGC>(propp);
                 return true;
             }
         } else if (obj->is<TypedObject>()) {
             if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
@@ -4036,39 +4027,39 @@ JSObject::addSizeOfExcludingThis(mozilla
 size_t
 JSObject::sizeOfIncludingThisInNursery() const
 {
     // This function doesn't concern itself yet with typed objects (bug 1133593)
     // nor unboxed objects (bug 1133592).
 
     MOZ_ASSERT(!isTenured());
 
-    const Nursery &nursery = compartment()->runtimeFromAnyThread()->gc.nursery;
+    const Nursery& nursery = compartment()->runtimeFromAnyThread()->gc.nursery;
     size_t size = Arena::thingSize(allocKindForTenure(nursery));
 
     if (is<NativeObject>()) {
-        const NativeObject &native = as<NativeObject>();
+        const NativeObject& native = as<NativeObject>();
 
         size += native.numFixedSlots() * sizeof(Value);
         size += native.numDynamicSlots() * sizeof(Value);
 
         if (native.hasDynamicElements()) {
-            js::ObjectElements &elements = *native.getElementsHeader();
+            js::ObjectElements& elements = *native.getElementsHeader();
             if (!elements.isCopyOnWrite() || elements.ownerObject() == this)
                 size += elements.capacity * sizeof(HeapSlot);
         }
     }
 
     return size;
 }
 
 size_t
 JS::ubi::Concrete<JSObject>::size(mozilla::MallocSizeOf mallocSizeOf) const
 {
-    JSObject &obj = get();
+    JSObject& obj = get();
 
     if (!obj.isTenured())
         return obj.sizeOfIncludingThisInNursery();
 
     JS::ClassInfo info;
     obj.addSizeOfExcludingThis(mallocSizeOf, &info);
     return obj.tenuredSizeOfThis() + info.sizeOfAllThings();
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -278,17 +278,17 @@ class JSObject : public js::gc::Cell
     }
     static MOZ_ALWAYS_INLINE void readBarrier(JSObject* obj);
     static MOZ_ALWAYS_INLINE void writeBarrierPre(JSObject* obj);
     static MOZ_ALWAYS_INLINE void writeBarrierPost(JSObject* obj, void* cellp);
     static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(JSObject* obj, void* cellp);
     static MOZ_ALWAYS_INLINE void writeBarrierPostRemove(JSObject* obj, void* cellp);
 
     /* Return the allocKind we would use if we were to tenure this object. */
-    js::gc::AllocKind allocKindForTenure(const js::Nursery &nursery) const;
+    js::gc::AllocKind allocKindForTenure(const js::Nursery& nursery) const;
 
     size_t tenuredSizeOfThis() const {
         MOZ_ASSERT(isTenured());
         return js::gc::Arena::thingSize(asTenured().getAllocKind());
     }
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info);
 
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -305,17 +305,17 @@ Shape::fixupAfterMovingGC()
 {
     if (inDictionary())
         fixupDictionaryShapeAfterMovingGC();
     else
         fixupShapeTreeAfterMovingGC();
 }
 
 void
-Shape::fixupGetterSetterForBarrier(JSTracer *trc)
+Shape::fixupGetterSetterForBarrier(JSTracer* trc)
 {
     // Relocating the getterObj or setterObj will change our location in our
     // parent's KidsHash, so remove ourself first if we're going to get moved.
     if (parent && !parent->inDictionary() && parent->kids.isHash()) {
         KidsHash* kh = parent->kids.toHash();
         MOZ_ASSERT(kh->lookup(StackShape(this)));
         kh->remove(StackShape(this));
     }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1356,16 +1356,17 @@ const Class ScriptSourceObject::class_ =
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     trace
 };
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -389,18 +389,25 @@ str_enumerate(JSContext* cx, HandleObjec
         value.setString(str1);
         if (!DefineElement(cx, obj, i, value, nullptr, nullptr, STRING_ELEMENT_ATTRS))
             return false;
     }
 
     return true;
 }
 
-bool
-js::str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+static bool
+str_mayResolve(const JSAtomState&, jsid id, JSObject*)
+{
+    // str_resolve ignores non-integer ids.
+    return JSID_IS_INT(id);
+}
+
+static bool
+str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
 {
     if (!JSID_IS_INT(id))
         return true;
 
     RootedString str(cx, obj->as<StringObject>().unbox());
 
     int32_t slot = JSID_TO_INT(id);
     if ((size_t)slot < str->length()) {
@@ -419,17 +426,18 @@ const Class StringObject::class_ = {
     js_String_str,
     JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_String),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     str_enumerate,
-    str_resolve
+    str_resolve,
+    str_mayResolve
 };
 
 /*
  * Returns a JSString * for the |this| value associated with 'call', or throws
  * a TypeError if |this| is null or undefined.  This algorithm is the same as
  * calling CheckObjectCoercible(this), then returning ToString(this), as all
  * String.prototype.* methods do (other than toString and valueOf).
  */
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -404,19 +404,16 @@ str_search(JSContext* cx, unsigned argc,
 
 bool
 str_split(JSContext* cx, unsigned argc, Value* vp);
 
 JSObject*
 str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep);
 
 bool
-str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
-
-bool
 str_replace_regexp_raw(JSContext* cx, HandleString string, HandleObject regexp,
                        HandleString replacement, MutableHandleValue rval);
 
 bool
 str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern,
                        HandleString replacement, MutableHandleValue rval);
 
 extern bool
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -611,16 +611,17 @@ const Class WeakMapObject::class_ = {
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     WeakMap_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WeakMap_mark
 };
 
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -158,17 +158,17 @@ static const struct pm_const {
 
 #undef CONSTANT
 
 static bool pm_construct(JSContext* cx, unsigned argc, jsval* vp);
 static void pm_finalize(JSFreeOp* fop, JSObject* obj);
 
 static const JSClass pm_class = {
     "PerfMeasurement", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, pm_finalize
 };
 
 // Constructor and destructor
 
 static bool
 pm_construct(JSContext* cx, unsigned argc, jsval* vp)
 {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2655,17 +2655,17 @@ sandbox_resolve(JSContext* cx, HandleObj
     return true;
 }
 
 static const JSClass sandbox_class = {
     "sandbox",
     JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
-    nullptr, nullptr,
+    nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JSObject*
 NewSandbox(JSContext* cx, bool lazy)
 {
     RootedObject obj(cx, JS_NewGlobalObject(cx, &sandbox_class, nullptr,
@@ -5114,20 +5114,26 @@ global_resolve(JSContext* cx, HandleObje
 {
 #ifdef LAZY_STANDARD_CLASSES
     if (!JS_ResolveStandardClass(cx, obj, id, resolvedp))
         return false;
 #endif
     return true;
 }
 
+static bool
+global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj)
+{
+    return JS_MayResolveStandardClass(names, id, maybeObj);
+}
+
 static const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
-    global_enumerate, global_resolve,
+    global_enumerate, global_resolve, global_mayResolve,
     nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 /*
  * Define a FakeDOMObject constructor. It returns an object with a getter,
  * setter and method with attached JitInfo. This object can be used to test
--- a/js/src/tests/ecma_7/SIMD/conversions.js
+++ b/js/src/tests/ecma_7/SIMD/conversions.js
@@ -14,16 +14,58 @@ function testFloat32x4FromFloat64x2() {
     [Infinity, -Infinity],
     [Math.pow(2, 25) - 1, -Math.pow(25)],
     [Math.pow(2, 1000), Math.pow(2, -1000)]
   ];
 
   for (var v of vals) {
     assertEqX4(float32x4.fromFloat64x2(float64x2(...v)), expected(v));
   }
+
+  // Test rounding to nearest, break tie with even
+  var f1 = makeFloat(0, 127, 0);
+  assertEq(f1, 1);
+
+  var f2 = makeFloat(0, 127, 1);
+  var d = makeDouble(0, 1023, 0x0000020000000);
+  assertEq(f2, d);
+
+  var mid = makeDouble(0, 1023, 0x0000010000000);
+  assertEq((1 + d) / 2, mid);
+
+  var nextMid = makeDouble(0, 1023, 0x0000010000001);
+
+  // mid is halfway between f1 and f2 => tie to even, which is f1
+  var v = float64x2(mid, nextMid);
+  assertEqX4(float32x4.fromFloat64x2(v), [f1, f2, 0, 0]);
+
+  var f3 = makeFloat(0, 127, 2);
+  var d = makeDouble(0, 1023, 0x0000040000000);
+  assertEq(f3, d);
+
+  mid = makeDouble(0, 1023, 0x0000030000000);
+  assertEq((f2 + f3) / 2, mid);
+
+  // same here. tie to even, which is f3 here
+  nextMid = makeDouble(0, 1023, 0x0000030000001);
+  var v = float64x2(mid, nextMid);
+  assertEqX4(float32x4.fromFloat64x2(v), [f3, f3, 0, 0]);
+
+  // Test boundaries
+  var biggestFloat = makeFloat(0, 127 + 127, 0x7fffff);
+  assertEq(makeDouble(0, 1023 + 127, 0xfffffe0000000), biggestFloat);
+
+  var lowestFloat = makeFloat(1, 127 + 127, 0x7fffff);
+  assertEq(makeDouble(1, 1023 + 127, 0xfffffe0000000), lowestFloat);
+
+  var v = float64x2(lowestFloat, biggestFloat);
+  assertEqX4(float32x4.fromFloat64x2(v), [lowestFloat, biggestFloat, 0, 0]);
+
+  var v = float64x2(makeDouble(0, 1023 + 127, 0xfffffe0000001), makeDouble(0, 1023 + 127, 0xffffff0000000));
+  assertEqX4(float32x4.fromFloat64x2(v), [biggestFloat, Infinity, 0, 0]);
 }
 
 function testFloat32x4FromFloat64x2Bits() {
   var valsExp = [
     [[2.000000473111868, 512.0001225471497], [1.0, 2.0, 3.0, 4.0]],
     [[-0, NaN], [0, -0, 0, NaN]],
     [[Infinity, -Infinity], [0, NaN, 0, NaN]]
   ];
@@ -40,16 +82,55 @@ function testFloat32x4FromInt32x4() {
   var vals = [
     [1, 2, 3, 4],
     [INT32_MIN, INT32_MAX, Math.pow(2, 30) - 1, -Math.pow(2, 30)]
   ];
 
   for (var v of vals) {
     assertEqX4(float32x4.fromInt32x4(int32x4(...v)), expected(v));
   }
+
+  // Check that rounding to nearest, even is applied.
+  {
+      var num = makeFloat(0, 150 + 2, 0);
+      var next = makeFloat(0, 150 + 2, 1);
+      assertEq(num + 4, next);
+
+      v = float32x4.fromInt32x4(int32x4(num, num + 1, num + 2, num + 3));
+      assertEqX4(v, [num, num, /* even */ num, next]);
+  }
+
+  {
+      var num = makeFloat(0, 150 + 2, 1);
+      var next = makeFloat(0, 150 + 2, 2);
+      assertEq(num + 4, next);
+
+      v = float32x4.fromInt32x4(int32x4(num, num + 1, num + 2, num + 3));
+      assertEqX4(v, [num, num, /* even */ next, next]);
+  }
+
+  {
+      var last = makeFloat(0, 157, 0x7fffff);
+
+      assertEq(last, Math.fround(last), "float");
+      assertEq(last < Math.pow(2, 31), true, "less than 2**31");
+      assertEq(last | 0, last, "it should be an integer, as exponent >= 150");
+
+      var diff = (Math.pow(2, 31) - 1) - last;
+      v = float32x4.fromInt32x4(int32x4(Math.pow(2, 31) - 1,
+                                        Math.pow(2, 30) + 1,
+                                        last + (diff / 2) | 0,      // nearest is last
+                                        last + (diff / 2) + 1 | 0   // nearest is Math.pow(2, 31)
+                                ));
+      assertEqX4(v, [Math.pow(2, 31),
+                     Math.pow(2, 30),
+                     last,
+                     Math.pow(2, 31)
+                    ]);
+  }
 }
 
 function testFloat32x4FromInt32x4Bits() {
   var valsExp = [
     [[100, 200, 300, 400], [1.401298464324817e-43, 2.802596928649634e-43, 4.203895392974451e-43, 5.605193857299268e-43]],
     [[INT32_MIN, INT32_MAX, 0, 0], [-0, NaN, 0, 0]]
   ];
 
@@ -108,48 +189,92 @@ function testFloat64x2FromInt32x4Bits() 
   ];
 
   for (var [v,w] of valsExp) {
     assertEqX2(float64x2.fromInt32x4Bits(int32x4(...v)), w);
   }
 }
 
 function testInt32x4FromFloat32x4() {
-  var valsExp = [
-    [[1.1, 2.2, 3.3, 4.6], [1, 2, 3, 4]],
-    [[NaN, -0, Infinity, -Infinity], [0, 0, 0, 0]]
-  ];
+  var d = float32x4(1.1, 2.2, 3.3, 4.6);
+  assertEqX4(int32x4.fromFloat32x4(d), [1, 2, 3, 4]);
+
+  var d = float32x4(NaN, 0, 0, 0);
+  assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError);
+
+  var d = float32x4(Infinity, 0, 0, 0);
+  assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError);
+
+  var d = float32x4(-Infinity, 0, 0, 0);
+  assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError);
 
-  for (var [v,w] of valsExp) {
-    assertEqX4(int32x4.fromFloat32x4(float32x4(...v)), w);
-  }
+  // Test high boundaries: float(0, 157, 0x7fffff) < INT32_MAX < float(0, 158, 0)
+  var d = float32x4(makeFloat(0, 127 + 31, 0), 0, 0, 0);
+  assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError);
+
+  var lastFloat = makeFloat(0, 127 + 30, 0x7FFFFF);
+  var d = float32x4(lastFloat, 0, 0, 0);
+  var e = SIMD.int32x4.fromFloat32x4(d);
+  assertEqX4(e, [lastFloat, 0, 0, 0]);
+
+  // Test low boundaries
+  assertEq(makeFloat(1, 127 + 31, 0), INT32_MIN);
+  var d = float32x4(makeFloat(1, 127 + 31, 0), 0, 0, 0);
+  var e = SIMD.int32x4.fromFloat32x4(d);
+  assertEqX4(e, [INT32_MIN, 0, 0, 0]);
+
+  var d = float32x4(makeFloat(1, 127 + 31, 1), 0, 0, 0);
+  assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError);
 }
 
 function testInt32x4FromFloat32x4Bits() {
   var valsExp = [
     [[1, 2, 3, 4], [0x3f800000 | 0, 0x40000000 | 0, 0x40400000 | 0, 0x40800000 | 0]],
     [[NaN, -0, Infinity, -Infinity], [0x7fc00000 | 0, 0x80000000 | 0, 0x7f800000 | 0, 0xff800000 | 0]]
   ];
 
   for (var [v,w] of valsExp) {
     assertEqX4(int32x4.fromFloat32x4Bits(float32x4(...v)), w);
   }
 }
 
 function testInt32x4FromFloat64x2() {
-  var valsExp = [
-    [[1, 2.2], [1, 2, 0, 0]],
-    [[NaN, -0], [0, 0, 0, 0]],
-    [[Infinity, -Infinity], [0, 0, 0, 0]],
-    [[Math.pow(2, 31), -Math.pow(2, 31) - 1], [INT32_MIN, INT32_MAX, 0, 0]]
-  ];
+  assertEqX4(int32x4.fromFloat64x2(float64x2(1, 2.2)), [1, 2, 0, 0]);
+
+  var g = float64x2(Infinity, 0);
+  assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError);
+
+  var g = float64x2(-Infinity, 0);
+  assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError);
+
+  var g = float64x2(NaN, 0);
+  assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError);
 
-  for (var [v,w] of valsExp) {
-    assertEqX4(int32x4.fromFloat64x2(float64x2(...v)), w);
-  }
+  // Testing high boundaries
+  // double(0, 1023 + 30, 0) < INT32_MAX < double(0, 1023 + 31, 0), so the
+  // lowest exactly representable quantity at this scale is 2**(-52 + 30) ==
+  // 2**-22.
+  assertEq(makeDouble(0, 1023 + 30, 0) + Math.pow(2, -22), makeDouble(0, 1023 + 30, 1));
+  assertEq(makeDouble(0, 1023 + 30, 0) + Math.pow(2, -23), makeDouble(0, 1023 + 30, 0));
+
+  var g = float64x2(INT32_MAX, 0);
+  assertEqX4(int32x4.fromFloat64x2(g), [INT32_MAX, 0, 0, 0]);
+
+  var g = float64x2(INT32_MAX + Math.pow(2, -22), 0);
+  assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError);
+
+  // Testing low boundaries
+  assertEq(makeDouble(1, 1023 + 31, 0), INT32_MIN);
+
+  var g = float64x2(makeDouble(1, 1023 + 31, 0), 0);
+  assertEqX4(int32x4.fromFloat64x2(g), [INT32_MIN, 0, 0, 0]);
+
+  var g = float64x2(makeDouble(1, 1023 + 31, 1), 0);
+  assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError);
+
 }
 
 function testInt32x4FromFloat64x2Bits() {
   var valsExp = [
     [[1.0, 2.0], [0x00000000, 0x3FF00000, 0x00000000, 0x40000000]],
     [[+Infinity, -Infinity], [0x00000000, 0x7ff00000, 0x00000000, -0x100000]],
     [[-0, NaN], [0x00000000, -0x80000000, 0x00000000, 0x7ff80000]],
     [[1.0000006400213732, 2.0000002532866263], [-0x543210ee, 0x3ff00000, 0x21fedcba, 0x40000000]]
--- a/js/src/tests/ecma_7/SIMD/shell.js
+++ b/js/src/tests/ecma_7/SIMD/shell.js
@@ -1,8 +1,39 @@
+function makeFloat(sign, exp, mantissa) {
+    assertEq(sign, sign & 0x1);
+    assertEq(exp, exp & 0xFF);
+    assertEq(mantissa, mantissa & 0x7FFFFF);
+
+    var i32 = new Int32Array(1);
+    var f32 = new Float32Array(i32.buffer);
+
+    i32[0] = (sign << 31) | (exp << 23) | mantissa;
+    return f32[0];
+}
+
+function makeDouble(sign, exp, mantissa) {
+    assertEq(sign, sign & 0x1);
+    assertEq(exp, exp & 0x7FF);
+
+    // Can't use bitwise operations on mantissa, as it might be a double
+    assertEq(mantissa <= 0xfffffffffffff, true);
+    var highBits = (mantissa / Math.pow(2, 32)) | 0;
+    var lowBits = mantissa - highBits * Math.pow(2, 32);
+
+    var i32 = new Int32Array(2);
+    var f64 = new Float64Array(i32.buffer);
+
+    // Note that this assumes little-endian order, which is the case on tier-1
+    // platforms.
+    i32[0] = lowBits;
+    i32[1] = (sign << 31) | (exp << 20) | highBits;
+    return f64[0];
+}
+
 function assertEqX2(v, arr) {
     try {
         assertEq(v.x, arr[0]);
         assertEq(v.y, arr[1]);
     } catch (e) {
         print("stack trace:", e.stack);
         throw e;
     }
--- a/js/src/tests/ecma_7/SIMD/shifts.js
+++ b/js/src/tests/ecma_7/SIMD/shifts.js
@@ -3,37 +3,37 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var int32x4 = SIMD.int32x4;
 
 function lsh(a, b) {
-    return (a << b) | 0;
+    return (b >>> 0) >= 32 ? 0 : (a << b) | 0;
 }
 function rsh(a, b) {
-    return (a >> b) | 0;
+    return (a >> Math.min(b >>> 0, 31)) | 0;
 }
 function ursh(a, b) {
-    return (a >>> b) | 0;
+    return (b >>> 0) >= 32 ? 0 : (a >>> b) | 0;
 }
 
 function test() {
   function TestError() {};
 
   var good = {valueOf: () => 21};
   var bad = {valueOf: () => {throw new TestError(); }};
 
   for (var v of [
             int32x4(-1, 2, -3, 4),
             int32x4(INT32_MAX, INT32_MIN, INT32_MAX - 1, INT32_MIN + 1)
        ])
   {
-      for (var bits = 0; bits < 32; bits++) {
+      for (var bits = -2; bits < 36; bits++) {
           testBinaryScalarFunc(v, bits, int32x4.shiftLeftByScalar, lsh);
           testBinaryScalarFunc(v, bits, int32x4.shiftRightArithmeticByScalar, rsh);
           testBinaryScalarFunc(v, bits, int32x4.shiftRightLogicalByScalar, ursh);
       }
       // Test that the shift count is coerced to an int32.
       testBinaryScalarFunc(v, undefined, int32x4.shiftLeftByScalar, lsh);
       testBinaryScalarFunc(v, 3.5, int32x4.shiftLeftByScalar, lsh);
       testBinaryScalarFunc(v, good, int32x4.shiftLeftByScalar, lsh);
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -555,16 +555,17 @@ const Class NormalArgumentsObject::class
     JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     args_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     args_enumerate,
     args_resolve,
+    nullptr,                 /* mayResolve  */
     nullptr,                 /* convert     */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
 
@@ -579,15 +580,16 @@ const Class StrictArgumentsObject::class
     JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     args_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     strictargs_enumerate,
     strictargs_resolve,
+    nullptr,                 /* mayResolve  */
     nullptr,                 /* convert     */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -103,16 +103,17 @@ const Class ArrayBufferObject::class_ = 
     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     nullptr,                 /* enumerate */
     nullptr,                 /* resolve */
+    nullptr,                 /* mayResolve */
     nullptr,                 /* convert */
     ArrayBufferObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::trace,
     JS_NULL_CLASS_SPEC,
     {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2400,17 +2400,17 @@ Debugger::finalize(FreeOp* fop, JSObject
         return;
     fop->delete_(dbg);
 }
 
 const Class Debugger::jsclass = {
     "Debugger",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, Debugger::finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     Debugger::traceObject
 };
 
 /* static */ Debugger*
@@ -4199,17 +4199,17 @@ DebuggerScript_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(script);
     }
 }
 
 const Class DebuggerScript_class = {
     "Script",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerScript_trace
 };
 
 JSObject*
@@ -5220,17 +5220,17 @@ DebuggerSource_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerSource_class = {
     "Source",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerSource_trace
 };
 
 JSObject*
@@ -5575,17 +5575,17 @@ DebuggerFrame_maybeDecrementFrameScriptS
 static void
 DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
 {
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
 }
 
 const Class DebuggerFrame_class = {
     "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, DebuggerFrame_finalize
 };
 
 static NativeObject*
 CheckThisFrame(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
 {
     if (!args.thisv().isObject()) {
         ReportObjectRequired(cx);
@@ -6337,17 +6337,17 @@ DebuggerObject_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerObject_class = {
     "Object",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerObject_trace
 };
 
 static NativeObject*
@@ -7247,17 +7247,17 @@ DebuggerEnv_trace(JSTracer* trc, JSObjec
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerEnv_class = {
     "Environment",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerEnv_trace
 };
 
 static NativeObject*
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -501,17 +501,17 @@ static void
 GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
 {
     fop->delete_((GlobalObject::DebuggerVector*) obj->as<NativeObject>().getPrivate());
 }
 
 static const Class
 GlobalDebuggees_class = {
     "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, GlobalDebuggees_finalize
 };
 
 GlobalObject::DebuggerVector*
 GlobalObject::getDebuggers()
 {
     Value debuggers = getReservedSlot(DEBUGGERS);
     if (debuggers.isUndefined())
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -187,17 +187,17 @@ js::CancelOffThreadIonCompile(JSCompartm
         builder = next;
     }
 }
 
 static const JSClass parseTaskGlobalClass = {
     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx,
                      const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -403,17 +403,17 @@ namespace js {
 // group performance stats).
 struct AutoStopwatch final
 {
     // If the stopwatch is active, constructing an instance of
     // AutoStopwatch causes it to become the current owner of the
     // stopwatch.
     //
     // Previous owner is restored upon destruction.
-    explicit inline AutoStopwatch(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : compartment_(nullptr)
       , runtime_(nullptr)
       , iteration_(0)
       , isActive_(false)
       , isTop_(false)
       , userTimeStart_(0)
       , systemTimeStart_(0)
       , CPOWTimeStart_(0)
@@ -423,17 +423,17 @@ struct AutoStopwatch final
         if (!runtime_->stopwatch.isActive())
             return;
         compartment_ = cx->compartment();
         MOZ_ASSERT(compartment_);
         if (compartment_->scheduledForDestruction)
             return;
         iteration_ = runtime_->stopwatch.iteration;
 
-        PerformanceGroup *group = compartment_->performanceMonitoring.getGroup();
+        PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
         MOZ_ASSERT(group);
 
         if (group->hasStopwatch(iteration_)) {
             // Someone is already monitoring this group during this
             // tick, no need for further monitoring.
             return;
         }
 
@@ -472,17 +472,17 @@ struct AutoStopwatch final
         }
 
         if (iteration_ != runtime_->stopwatch.iteration) {
             // We have entered a nested event loop at some point.
             // Any information we may have is obsolete.
             return;
         }
 
-        PerformanceGroup *group = compartment_->performanceMonitoring.getGroup();
+        PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
         MOZ_ASSERT(group);
 
         // Compute time spent.
         group->releaseStopwatch(iteration_, this);
         uint64_t userTimeEnd, systemTimeEnd;
         if (!this->getTimes(&userTimeEnd, &systemTimeEnd))
             return;
 
@@ -601,21 +601,21 @@ struct AutoStopwatch final
 #endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
 
         return true;
     }
 
   private:
     // The compartment with which this object was initialized.
     // Non-null.
-    JSCompartment *compartment_;
+    JSCompartment* compartment_;
 
     // The runtime with which this object was initialized.
     // Non-null.
-    JSRuntime *runtime_;
+    JSRuntime* runtime_;
 
     // An indication of the number of times we have entered the event
     // loop.  Used only for comparison.
     uint64_t iteration_;
 
     // `true` if this object is currently used to monitor performance,
     // `false` otherwise, i.e. if the stopwatch mechanism is off or if
     // another stopwatch is already in charge of monitoring for the
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -318,17 +318,17 @@ class MOZ_STACK_CLASS TryNoteIter
         settle();
     }
 
     bool done() const { return tn_ == tnEnd_; }
     JSTryNote* operator*() const { return tn_; }
 };
 
 bool
-HandleClosingGeneratorReturn(JSContext *cx, AbstractFramePtr frame, bool ok);
+HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame, bool ok);
 
 /************************************************************************/
 
 bool
 Throw(JSContext* cx, HandleValue v);
 
 bool
 ThrowingOperation(JSContext* cx, HandleValue v);
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -409,27 +409,54 @@ CallResolveOp(JSContext* cx, HandleNativ
 
     bool resolved = false;
     if (!obj->getClass()->resolve(cx, obj, id, &resolved))
         return false;
 
     if (!resolved)
         return true;
 
+    // Assert the mayResolve hook, if there is one, returns true for this
+    // property.
+    MOZ_ASSERT_IF(obj->getClass()->mayResolve,
+                  obj->getClass()->mayResolve(cx->names(), id, obj));
+
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
         MarkDenseOrTypedArrayElementFound<CanGC>(propp);
         return true;
     }
 
     MOZ_ASSERT(!IsAnyTypedArray(obj));
 
     propp.set(obj->lookup(cx, id));
     return true;
 }
 
+static MOZ_ALWAYS_INLINE bool
+ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
+{
+    MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
+
+    if (!clasp->resolve) {
+        // Sanity check: we should only have a mayResolve hook if we have a
+        // resolve hook.
+        MOZ_ASSERT(!clasp->mayResolve, "Class with mayResolve hook but no resolve hook");
+        return false;
+    }