Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 01 Sep 2015 21:01:50 -0400
changeset 260388 e2eb0442ece9d545487b137a68c9db13ea86d843
parent 260387 121e42feb6465337ed83ee777734bb997ef2eaba (current diff)
parent 260358 eb1f8cc289a474a61357ef67748d583ba15634a6 (diff)
child 260418 fb720c90eb49590ba55bf52a8a4826ffff9f528b
push id64495
push userryanvm@gmail.com
push dateWed, 02 Sep 2015 01:16:33 +0000
treeherdermozilla-inbound@e747377d86eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-frame-resource.https.html.ini
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5449,33 +5449,40 @@
         dt.mozCursor = "default";
 
         // Create a canvas to which we capture the current tab.
         // Until canvas is HiDPI-aware (bug 780362), we need to scale the desired
         // canvas size (in CSS pixels) to the window's backing resolution in order
         // to get a full-resolution drag image for use on HiDPI displays.
         let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
         let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
-        let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+        let canvas = this._dndCanvas ? this._dndCanvas
+                                     : document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
         canvas.mozOpaque = true;
         canvas.width = 160 * scale;
         canvas.height = 90 * scale;
         let toDrag;
         if (gMultiProcessBrowser) {
+          var context = canvas.getContext('2d');
+          context.fillStyle = "white";
+          context.fillRect(0, 0, canvas.width, canvas.height);
           // Create a panel to use it in setDragImage
           // which will tell xul to render a panel that follows
           // the pointer while a dnd session is on.
-          var panel = document.createElement("panel");
-          panel.setAttribute("type", "drag");
-          panel.appendChild(canvas);
-          document.documentElement.appendChild(panel);
+          if (!this._dndPanel) {
+            this._dndCanvas = canvas;
+            this._dndPanel = document.createElement("panel");
+            this._dndPanel.setAttribute("type", "drag");
+            this._dndPanel.appendChild(canvas);
+            document.documentElement.appendChild(this._dndPanel);
+          }
           // PageThumb is async with e10s but that's fine
           // since we can update the panel during the dnd.
           PageThumbs.captureToCanvas(browser, canvas);
-          toDrag = panel;
+          toDrag = this._dndPanel;
         } else {
           // For the non e10s case we can just use PageThumbs
           // sync. No need for xul magic, the native dnd will
           // be fine, so let's use the canvas for setDragImage.
           PageThumbs.captureToCanvas(browser, canvas);
           toDrag = canvas;
         }
         dt.setDragImage(toDrag, -16 * scale, -16 * scale);
--- a/browser/components/migration/nsEdgeReadingListExtractor.cpp
+++ b/browser/components/migration/nsEdgeReadingListExtractor.cpp
@@ -45,17 +45,17 @@ nsEdgeReadingListExtractor::Extract(cons
   // otherwise the compiler complains.
   nsCOMPtr<nsIMutableArray> items = do_CreateInstance(NS_ARRAY_CONTRACTID);
 
   // JET does not throw exceptions, and so error handling and ensuring we close
   // the DB is a bit finnicky. Keep track of how far we got so we guarantee closing
   // the right things
   bool instanceCreated, sessionCreated, dbOpened, tableOpened;
 
-  char16_t* dbPath = ToNewUnicode(aDBPath);
+  char16ptr_t dbPath = ToNewUnicode(aDBPath);
 
   // Check for the right page size and initialize with that
   unsigned long pageSize;
   err = JetGetDatabaseFileInfoW(dbPath, &pageSize, sizeof(pageSize), JET_DbInfoPageSize);
   NS_HANDLE_JET_ERROR(err)
   err = JetSetSystemParameter(&instance, NULL, JET_paramDatabasePageSize, pageSize, NULL);
   NS_HANDLE_JET_ERROR(err)
 
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -98,27 +98,33 @@ AudioChannelAgent::InitInternal(nsIDOMWi
              "Enum of channel on nsIAudioChannelAgent.idl should be the same with AudioChannelBinding.h");
 
   if (mAudioChannelType != AUDIO_AGENT_CHANNEL_ERROR ||
       aChannelType > AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION ||
       aChannelType < AUDIO_AGENT_CHANNEL_NORMAL) {
     return NS_ERROR_FAILURE;
   }
 
-  if (aWindow) {
-    nsCOMPtr<nsPIDOMWindow> pInnerWindow = do_QueryInterface(aWindow);
-    MOZ_ASSERT(pInnerWindow->IsInnerWindow());
-    mInnerWindowID = pInnerWindow->WindowID();
+  if (NS_WARN_IF(!aWindow)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> pInnerWindow = do_QueryInterface(aWindow);
+  MOZ_ASSERT(pInnerWindow->IsInnerWindow());
+  mInnerWindowID = pInnerWindow->WindowID();
 
-    nsCOMPtr<nsIDOMWindow> topWindow;
-    aWindow->GetScriptableTop(getter_AddRefs(topWindow));
-    mWindow = do_QueryInterface(topWindow);
-    if (mWindow) {
-      mWindow = mWindow->GetOuterWindow();
-    }
+  nsCOMPtr<nsIDOMWindow> topWindow;
+  aWindow->GetScriptableTop(getter_AddRefs(topWindow));
+  mWindow = do_QueryInterface(topWindow);
+  if (mWindow) {
+    mWindow = mWindow->GetOuterWindow();
+  }
+
+  if (!mWindow) {
+    return NS_ERROR_FAILURE;
   }
 
   mAudioChannelType = aChannelType;
 
   if (aUseWeakRef) {
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
@@ -129,16 +135,23 @@ AudioChannelAgent::InitInternal(nsIDOMWi
 
 NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(uint32_t aNotifyPlayback,
                                                       float *aVolume,
                                                       bool* aMuted)
 {
   MOZ_ASSERT(aVolume);
   MOZ_ASSERT(aMuted);
 
+  // Window-less AudioChannelAgents are muted by default.
+  if (!mWindow) {
+    *aVolume = 0;
+    *aMuted = true;
+    return NS_OK;
+  }
+
   nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   service->RegisterAudioChannelAgent(this, aNotifyPlayback,
     static_cast<AudioChannel>(mAudioChannelType));
--- a/dom/base/nsAttrAndChildArray.cpp
+++ b/dom/base/nsAttrAndChildArray.cpp
@@ -6,28 +6,31 @@
 
 /*
  * Storage of the children and attributes of a DOM node; storage for
  * the two is unified to minimize footprint.
  */
 
 #include "nsAttrAndChildArray.h"
 
+#include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "nsMappedAttributeElement.h"
 #include "nsString.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsRuleWalker.h"
 #include "nsMappedAttributes.h"
 #include "nsUnicharUtils.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h" // nsAutoScriptBlocker
 
+using mozilla::CheckedUint32;
+
 /*
 CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer.
 It should be small enough to not cause collisions between adjecent arrays, and
 large enough to make sure that all indexes are used. The size below is based
 on the size of the smallest possible element (currently 24[*] bytes) which is
 the smallest distance between two nsAttrAndChildArray. 24/(2^_5_) is 0.75.
 This means that two adjacent nsAttrAndChildArrays will overlap one in 4 times.
 However not all elements will have enough children to get cached. And any
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1586,16 +1586,28 @@ nsIDocument::~nsIDocument()
 
   if (mNodeInfoManager) {
     mNodeInfoManager->DropDocumentReference();
   }
 
   UnlinkOriginalDocumentIfStatic();
 }
 
+bool
+nsDocument::IsAboutPage()
+{
+  nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
+  nsCOMPtr<nsIURI> uri;
+  principal->GetURI(getter_AddRefs(uri));
+  bool isAboutScheme = true;
+  if (uri) {
+    uri->SchemeIs("about", &isAboutScheme);
+  }
+  return isAboutScheme;
+}
 
 nsDocument::~nsDocument()
 {
   if (gDocumentLeakPRLog)
     MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
            ("DOCUMENT %p destroyed", this));
 
   NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
@@ -1603,25 +1615,17 @@ nsDocument::~nsDocument()
   // Note: This assert is only non-fatal because mochitest-bc triggers
   // it... as well as the preceding assert about !mIsShowing.
   NS_ASSERTION(!mObservingAppThemeChanged,
                "Document leaked to shutdown, then the observer service dropped "
                "its ref to us so we were able to go away.");
 
   if (IsTopLevelContentDocument()) {
     //don't report for about: pages
-    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
-    nsCOMPtr<nsIURI> uri;
-    principal->GetURI(getter_AddRefs(uri));
-    bool isAboutScheme = true;
-    if (uri) {
-      uri->SchemeIs("about", &isAboutScheme);
-    }
-
-    if (!isAboutScheme) {
+    if (!IsAboutPage()) {
       // Record the page load
       uint32_t pageLoaded = 1;
       Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
       // Record the mixed content status of the docshell in Telemetry
       enum {
         NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
         MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
         MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
@@ -7895,19 +7899,20 @@ nsDocument::GetViewportInfo(const Screen
   float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
   fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
   nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
   float widgetScale = widget ? widget->GetDefaultScale().scale : 1.0f;
   CSSToLayoutDeviceScale layoutDeviceScale(widgetScale * fullZoom);
 
   CSSToScreenScale defaultScale = layoutDeviceScale
                                 * LayoutDeviceToScreenScale(1.0);
-  // Get requested Desktopmode
+
+  // Special behaviour for desktop mode, provided we are not on an about: page
   nsPIDOMWindow* win = GetWindow();
-  if (win && win->IsDesktopModeViewport())
+  if (win && win->IsDesktopModeViewport() && !IsAboutPage())
   {
     float viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
     float scaleToFit = aDisplaySize.width / viewportWidth;
     float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
     ScreenSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
     return nsViewportInfo(RoundedToInt(viewportSize),
                           CSSToScreenScale(scaleToFit),
                           /*allowZoom*/false,
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1765,16 +1765,19 @@ private:
                                        bool aUpdateCSSLoader);
 
   // Revoke any pending notifications due to requestAnimationFrame calls
   void RevokeAnimationFrameNotifications();
   // Reschedule any notifications we need to handle
   // requestAnimationFrame, if it's OK to do so.
   void MaybeRescheduleAnimationFrameNotifications();
 
+  // Returns true if the scheme for the url for this document is "about"
+  bool IsAboutPage();
+
   // These are not implemented and not supported.
   nsDocument(const nsDocument& aOther);
   nsDocument& operator=(const nsDocument& aOther);
 
   // The layout history state that should be used by nodes in this
   // document.  We only actually store a pointer to it when:
   // 1)  We have no script global object.
   // 2)  We haven't had Destroy() called on us yet.
--- a/dom/base/test/test_bug416317-2.html
+++ b/dom/base/test/test_bug416317-2.html
@@ -18,15 +18,15 @@ https://bugzilla.mozilla.org/show_bug.cg
 </p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 416317 **/
-SimpleTest.requestLongerTimeout(2);
+SimpleTest.requestLongerTimeout(3);
 // Subframe handles the test
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "pk11pub.h"
 #include "cryptohi.h"
+#include "nsNSSComponent.h"
 #include "ScopedNSSTypes.h"
 #include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 
 // Templates taken from security/nss/lib/cryptohi/seckey.c
 // These would ideally be exported by NSS and until that
@@ -1244,16 +1245,21 @@ CryptoKey::WriteStructuredClone(JSStruct
 bool
 CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return false;
   }
 
+  // Ensure that NSS is initialized.
+  if (!EnsureNSSInitializedChromeOrContent()) {
+    return false;
+  }
+
   uint32_t version;
   CryptoBuffer sym, priv, pub;
 
   bool read = JS_ReadUint32Pair(aReader, &mAttributes, &version) &&
               (version == CRYPTOKEY_SC_VERSION) &&
               ReadBuffer(aReader, sym) &&
               ReadBuffer(aReader, priv) &&
               ReadBuffer(aReader, pub) &&
--- a/dom/crypto/moz.build
+++ b/dom/crypto/moz.build
@@ -20,11 +20,12 @@ UNIFIED_SOURCES += [
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/security/manager/ssl',
+    '/security/pkix/include',
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/file_indexedDB.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 1188750 - WebCrypto must ensure NSS is initialized before deserializing</title>
+</head>
+<body>
+  <script type="application/javascript;version=1.8">
+    let db;
+
+    function err(resolve) {
+      return e => resolve(e.target.error.message);
+    }
+
+    function openDatabase() {
+      return new Promise((resolve, reject) => {
+        let request = indexedDB.open("keystore", 1);
+
+        request.onerror = err(reject);
+        request.onsuccess = function (event) {
+          db = event.target.result;
+          resolve();
+        };
+
+        request.onupgradeneeded = function(event) {
+          db = event.target.result;
+          let objectStore = db.createObjectStore("keys", {autoIncrement: true});
+          objectStore.transaction.oncomplete = resolve;
+        };
+      });
+    }
+
+    function storeKey(key) {
+      return new Promise((resolve, reject) => {
+        let transaction = db.transaction("keys", "readwrite");
+        transaction.objectStore("keys").put(key, key.type);
+
+        transaction.onabort = err(reject);
+        transaction.onerror = err(reject);
+
+        transaction.oncomplete = function () {
+          resolve(key);
+        };
+      });
+    };
+
+    function retrieveKey() {
+      return new Promise((resolve, reject) => {
+        let transaction = db.transaction("keys", "readonly");
+        let cursor = transaction.objectStore("keys").openCursor();
+
+        cursor.onerror = err(reject);
+        cursor.onabort = err(reject);
+
+        cursor.onsuccess = function (event) {
+          try {
+            let result = event.target.result;
+            resolve(result && result.value);
+          } catch (e) {
+            reject(e.message);
+          }
+        };
+      });
+    }
+
+    function generateKey() {
+      let algorithm = {
+        name: "RSASSA-PKCS1-v1_5",
+        hash: "SHA-256",
+        modulusLength: 1024,
+        publicExponent: new Uint8Array([0x01, 0x00, 0x01])
+      };
+
+      return crypto.subtle.generateKey(algorithm, true, ["sign", "verify"]);
+    }
+
+    openDatabase()
+      .then(retrieveKey).then(generateKey).then(storeKey)
+      .then(() => alert("ok")).catch(alert);
+  </script>
+</body>
+</html>
--- a/dom/crypto/test/mochitest.ini
+++ b/dom/crypto/test/mochitest.ini
@@ -1,17 +1,20 @@
 [DEFAULT]
 # Bug 1010743 - Re-enable WebCrypto tests on b2g
 skip-if = (buildapp == 'b2g')
 support-files =
+  file_indexedDB.html
   test-array.js
   test-vectors.js
   test_WebCrypto.css
   util.js
 
+[test_indexedDB.html]
+skip-if = toolkit == 'android' # bug 1200570
 [test_WebCrypto.html]
 [test_WebCrypto_DH.html]
 [test_WebCrypto_ECDH.html]
 [test_WebCrypto_ECDSA.html]
 [test_WebCrypto_JWK.html]
 [test_WebCrypto_Normalize.html]
 [test_WebCrypto_PBKDF2.html]
 [test_WebCrypto_Reject_Generating_Keys_Without_Usages.html]
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_indexedDB.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 1188750 - WebCrypto must ensure NSS is initialized before deserializing</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"?>
+</head>
+<body>
+  <script type="application/javascript;version=1.8">
+    /*
+     * Bug 1188750 - The WebCrypto API must ensure that NSS was initialized
+     * for the current process before trying to deserialize objects like
+     * CryptoKeys from e.g. IndexedDB.
+     */
+    "use strict";
+
+    const TEST_URI = "http://www.example.com/tests/" +
+                     "dom/crypto/test/file_indexedDB.html";
+
+    SimpleTest.waitForExplicitFinish();
+
+    function createMozBrowserFrame(cb) {
+      let frame = document.createElement("iframe");
+      SpecialPowers.wrap(frame).mozbrowser = true;
+      frame.src = TEST_URI;
+
+      frame.addEventListener("mozbrowsershowmodalprompt", function onPrompt(e) {
+        frame.removeEventListener("mozbrowsershowmodalprompt", onPrompt);
+        cb(frame, e.detail.message);
+      });
+
+      document.body.appendChild(frame);
+    }
+
+    function runTest() {
+      // Load the test app once, to generate and store keys.
+      createMozBrowserFrame((frame, result) => {
+        is(result, "ok", "stored keys successfully");
+        frame.remove();
+
+        // Load the test app again to retrieve stored keys.
+        createMozBrowserFrame((frame, result) => {
+          is(result, "ok", "retrieved keys successfully");
+          frame.remove();
+          SimpleTest.finish();
+        });
+      });
+    }
+
+    addEventListener("load", function () {
+      SpecialPowers.addPermission("browser", true, document);
+      SpecialPowers.pushPrefEnv({set: [
+        ["dom.ipc.browser_frames.oop_by_default", true],
+        ["dom.mozBrowserFramesEnabled", true]
+      ]}, runTest);
+    });
+  </script>
+</body>
+</html>
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -201,49 +201,125 @@ InternalRequest::MapContentPolicyTypeToR
     break;
   default:
     MOZ_ASSERT(false, "Unhandled nsContentPolicyType value");
     break;
   }
   return context;
 }
 
+// static
 bool
-InternalRequest::IsNavigationRequest() const
+InternalRequest::IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType)
 {
   // https://fetch.spec.whatwg.org/#navigation-request-context
   //
   // A navigation request context is one of "form", "frame", "hyperlink",
   // "iframe", "internal" (as long as context frame type is not "none"),
   // "location", "metarefresh", and "prerender".
   //
-  // TODO: include equivalent check for "form" context
-  // TODO: include equivalent check for "prerender" context
-  return mContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
-         mContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
-         mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
-         mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
-         mContentPolicyType == nsIContentPolicy::TYPE_REFRESH;
+  // Note, all of these request types are effectively initiated by nsDocShell.
+  //
+  // The TYPE_REFRESH is used in some code paths for metarefresh, but will not
+  // be seen during the actual load.  Instead the new load gets a normal
+  // nsDocShell policy type.  We include it here in case this utility method
+  // is called before the load starts.
+  return aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+         aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
+         aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
+         aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
+         aContentPolicyType == nsIContentPolicy::TYPE_REFRESH;
 }
 
+// static
 bool
-InternalRequest::IsWorkerRequest() const
+InternalRequest::IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType)
 {
   // https://fetch.spec.whatwg.org/#worker-request-context
   //
   // A worker request context is one of "serviceworker", "sharedworker", and
   // "worker".
   //
   // Note, service workers are not included here because currently there is
   // no way to generate a Request with a "serviceworker" RequestContext.
   // ServiceWorker scripts cannot be intercepted.
-  return mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
-         mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
+  return aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
+         aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
+}
+
+bool
+InternalRequest::IsNavigationRequest() const
+{
+  return IsNavigationContentPolicy(mContentPolicyType);
+}
+
+bool
+InternalRequest::IsWorkerRequest() const
+{
+  return IsWorkerContentPolicy(mContentPolicyType);
 }
 
 bool
 InternalRequest::IsClientRequest() const
 {
   return IsNavigationRequest() || IsWorkerRequest();
 }
 
+// static
+RequestMode
+InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel)
+{
+  MOZ_ASSERT(aChannel);
+
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aChannel->GetLoadInfo(getter_AddRefs(loadInfo))));
+
+  // RequestMode deviates from our internal security mode for navigations.
+  // While navigations normally allow cross origin we must set a same-origin
+  // RequestMode to get the correct service worker interception restrictions
+  // in place.
+  // TODO: remove the worker override once securityMode is fully implemented (bug 1189945)
+  nsContentPolicyType contentPolicy = loadInfo->InternalContentPolicyType();
+  if (IsNavigationContentPolicy(contentPolicy) ||
+      IsWorkerContentPolicy(contentPolicy)) {
+    return RequestMode::Same_origin;
+  }
+
+  uint32_t securityMode;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSecurityMode(&securityMode)));
+
+  switch(securityMode) {
+    case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS:
+    case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED:
+      return RequestMode::Same_origin;
+    case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS:
+    case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL:
+      return RequestMode::No_cors;
+    case nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS:
+      // TODO: Check additional flag force-preflight after bug 1199693 (bug 1189945)
+      return RequestMode::Cors;
+    default:
+      // TODO: assert never reached after CorsMode flag removed (bug 1189945)
+      MOZ_ASSERT(securityMode == nsILoadInfo::SEC_NORMAL);
+      break;
+  }
+
+  // TODO: remove following code once securityMode is fully implemented (bug 1189945)
+
+  // We only support app:// protocol interception in non-release builds.
+#ifndef RELEASE_BUILD
+  nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aChannel);
+  if (jarChannel) {
+    return RequestMode::No_cors;
+  }
+#endif
+
+  nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
+
+  uint32_t corsMode;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(httpChannel->GetCorsMode(&corsMode)));
+
+  // This cast is valid due to static asserts in ServiceWorkerManager.cpp.
+  return static_cast<RequestMode>(corsMode);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -372,26 +372,34 @@ public:
   IsNavigationRequest() const;
 
   bool
   IsWorkerRequest() const;
 
   bool
   IsClientRequest() const;
 
+  static RequestMode
+  MapChannelToRequestMode(nsIChannel* aChannel);
 
 private:
   // Does not copy mBodyStream.  Use fallible Clone() for complete copy.
   explicit InternalRequest(const InternalRequest& aOther);
 
   ~InternalRequest();
 
   static RequestContext
   MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType);
 
+  static bool
+  IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType);
+
+  static bool
+  IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType);
+
   nsCString mMethod;
   // mURL always stores the url with the ref stripped
   nsCString mURL;
   nsRefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBodyStream;
 
   nsContentPolicyType mContentPolicyType;
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -125,17 +125,20 @@ public:
   AutoNotifyAudioChannelAgent(mozilla::dom::HTMLMediaElement* aElement,
                               bool aNotify
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mElement(aElement)
     , mShouldNotify(aNotify)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (mShouldNotify) {
-      mElement->NotifyAudioChannelAgent(false);
+      // The audio channel agent may not exist now.
+      if (mElement->MaybeCreateAudioChannelAgent()) {
+        mElement->NotifyAudioChannelAgent(false);
+      }
     }
   }
   ~AutoNotifyAudioChannelAgent()
   {
     if (mShouldNotify) {
       // The audio channel agent is destroyed at this point.
       if (mElement->MaybeCreateAudioChannelAgent()) {
         mElement->NotifyAudioChannelAgent(true);
--- a/dom/html/test/forms/test_input_sanitization.html
+++ b/dom/html/test/forms/test_input_sanitization.html
@@ -13,16 +13,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <pre id="test">
 <div id='content'>
   <form>
   </form>
 </div>
 <script type="application/javascript">
 
+SimpleTest.requestLongerTimeout(2);
+
 /**
  * This files tests the 'value sanitization algorithm' for the various input
  * types. Note that an input's value is affected by more than just its type's
  * value sanitization algorithm; e.g. some type=range has actions that the user
  * agent must perform to change the element's value to avoid underflow/overflow
  * and step mismatch (when possible). We specifically avoid triggering these
  * other actions here so that this test only tests the value sanitization
  * algorithm for the various input types.
--- a/dom/media/AudioChannelFormat.cpp
+++ b/dom/media/AudioChannelFormat.cpp
@@ -10,84 +10,9 @@
 namespace mozilla {
 
 uint32_t
 GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2)
 {
   return std::max(aChannels1, aChannels2);
 }
 
-/**
- * UpMixMatrix represents a conversion matrix by exploiting the fact that
- * each output channel comes from at most one input channel.
- */
-struct UpMixMatrix {
-  uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
-};
-
-static const UpMixMatrix
-gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
-{
-  // Upmixes from mono
-  { { 0, 0 } },
-  { { 0, IGNORE, IGNORE } },
-  { { 0, 0, IGNORE, IGNORE } },
-  { { 0, IGNORE, IGNORE, IGNORE, IGNORE } },
-  { { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } },
-  // Upmixes from stereo
-  { { 0, 1, IGNORE } },
-  { { 0, 1, IGNORE, IGNORE } },
-  { { 0, 1, IGNORE, IGNORE, IGNORE } },
-  { { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } },
-  // Upmixes from 3-channel
-  { { 0, 1, 2, IGNORE } },
-  { { 0, 1, 2, IGNORE, IGNORE } },
-  { { 0, 1, 2, IGNORE, IGNORE, IGNORE } },
-  // Upmixes from quad
-  { { 0, 1, 2, 3, IGNORE } },
-  { { 0, 1, IGNORE, IGNORE, 2, 3 } },
-  // Upmixes from 5-channel
-  { { 0, 1, 2, 3, 4, IGNORE } }
-};
-
-void
-AudioChannelsUpMix(nsTArray<const void*>* aChannelArray,
-                   uint32_t aOutputChannelCount,
-                   const void* aZeroChannel)
-{
-  uint32_t inputChannelCount = aChannelArray->Length();
-  uint32_t outputChannelCount =
-    GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount);
-  NS_ASSERTION(outputChannelCount > inputChannelCount,
-               "No up-mix needed");
-  MOZ_ASSERT(inputChannelCount > 0, "Bad number of channels");
-  MOZ_ASSERT(outputChannelCount > 0, "Bad number of channels");
-
-  aChannelArray->SetLength(outputChannelCount);
-
-  if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS &&
-      outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) {
-    const UpMixMatrix& m = gUpMixMatrices[
-      gMixingMatrixIndexByChannels[inputChannelCount - 1] +
-      outputChannelCount - inputChannelCount - 1];
-
-    const void* outputChannels[CUSTOM_CHANNEL_LAYOUTS];
-
-    for (uint32_t i = 0; i < outputChannelCount; ++i) {
-      uint8_t channelIndex = m.mInputDestination[i];
-      if (channelIndex == IGNORE) {
-        outputChannels[i] = aZeroChannel;
-      } else {
-        outputChannels[i] = aChannelArray->ElementAt(channelIndex);
-      }
-    }
-    for (uint32_t i = 0; i < outputChannelCount; ++i) {
-      aChannelArray->ElementAt(i) = outputChannels[i];
-    }
-    return;
-  }
-
-  for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
-    aChannelArray->ElementAt(i) = aZeroChannel;
-  }
-}
-
 } // namespace mozilla
--- a/dom/media/AudioChannelFormat.h
+++ b/dom/media/AudioChannelFormat.h
@@ -54,34 +54,16 @@ const int gMixingMatrixIndexByChannels[C
 /**
  * Return a channel count whose channel layout includes all the channels from
  * aChannels1 and aChannels2.
  */
 uint32_t
 GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2);
 
 /**
- * Given an array of input channel data, and an output channel count,
- * replaces the array with an array of upmixed channels.
- * This shuffles the array and may set some channel buffers to aZeroChannel.
- * Don't call this with input count >= output count.
- * This may return *more* channels than requested. In that case, downmixing
- * is required to to get to aOutputChannelCount. (This is how we handle
- * odd cases like 3 -> 4 upmixing.)
- * If aChannelArray.Length() was the input to one of a series of
- * GetAudioChannelsSuperset calls resulting in aOutputChannelCount,
- * no downmixing will be required.
- */
-void
-AudioChannelsUpMix(nsTArray<const void*>* aChannelArray,
-                   uint32_t aOutputChannelCount,
-                   const void* aZeroChannel);
-
-
-/**
  * DownMixMatrix represents a conversion matrix efficiently by exploiting the
  * fact that each input channel contributes to at most one output channel,
  * except possibly for the C input channel in layouts that have one. Also,
  * every input channel is multiplied by the same coefficient for every output
  * channel it contributes to.
  */
 struct DownMixMatrix {
   // Every input channel c is copied to output channel mInputDestination[c]
@@ -119,59 +101,146 @@ gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*
 };
 
 /**
  * Given an array of input channels, downmix to aOutputChannelCount, and copy
  * the results to the channel buffers in aOutputChannels.  Don't call this with
  * input count <= output count.
  */
 template<typename T>
-void AudioChannelsDownMix(const nsTArray<const void*>& aChannelArray,
-                     T** aOutputChannels,
-                     uint32_t aOutputChannelCount,
-                     uint32_t aDuration)
+void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
+                          T** aOutputChannels,
+                          uint32_t aOutputChannelCount,
+                          uint32_t aDuration)
 {
   uint32_t inputChannelCount = aChannelArray.Length();
-  const void* const* inputChannels = aChannelArray.Elements();
+  const T* const* inputChannels = aChannelArray.Elements();
   NS_ASSERTION(inputChannelCount > aOutputChannelCount, "Nothing to do");
 
   if (inputChannelCount > 6) {
     // Just drop the unknown channels.
     for (uint32_t o = 0; o < aOutputChannelCount; ++o) {
-      memcpy(aOutputChannels[o], inputChannels[o], aDuration*sizeof(T));
+      PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
     }
     return;
   }
 
   // Ignore unknown channels, they're just dropped.
   inputChannelCount = std::min<uint32_t>(6, inputChannelCount);
 
   const DownMixMatrix& m = gDownMixMatrices[
     gMixingMatrixIndexByChannels[aOutputChannelCount - 1] +
     inputChannelCount - aOutputChannelCount - 1];
 
   // This is slow, but general. We can define custom code for special
   // cases later.
   for (uint32_t s = 0; s < aDuration; ++s) {
     // Reserve an extra junk channel at the end for the cases where we
     // want an input channel to contribute to nothing
-    T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1];
-    memset(outputChannels, 0, sizeof(T)*(CUSTOM_CHANNEL_LAYOUTS));
+    T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1] = {0};
     for (uint32_t c = 0; c < inputChannelCount; ++c) {
       outputChannels[m.mInputDestination[c]] +=
         m.mInputCoefficient[c]*(static_cast<const T*>(inputChannels[c]))[s];
     }
     // Utilize the fact that in every layout, C is the third channel.
     if (m.mCExtraDestination != IGNORE) {
       outputChannels[m.mCExtraDestination] +=
         m.mInputCoefficient[SURROUND_C]*(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
     }
 
     for (uint32_t c = 0; c < aOutputChannelCount; ++c) {
       aOutputChannels[c][s] = outputChannels[c];
     }
   }
 }
 
+/**
+ * UpMixMatrix represents a conversion matrix by exploiting the fact that
+ * each output channel comes from at most one input channel.
+ */
+struct UpMixMatrix {
+  uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
+};
+
+static const UpMixMatrix
+gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
+{
+  // Upmixes from mono
+  { { 0, 0 } },
+  { { 0, IGNORE, IGNORE } },
+  { { 0, 0, IGNORE, IGNORE } },
+  { { 0, IGNORE, IGNORE, IGNORE, IGNORE } },
+  { { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } },
+  // Upmixes from stereo
+  { { 0, 1, IGNORE } },
+  { { 0, 1, IGNORE, IGNORE } },
+  { { 0, 1, IGNORE, IGNORE, IGNORE } },
+  { { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } },
+  // Upmixes from 3-channel
+  { { 0, 1, 2, IGNORE } },
+  { { 0, 1, 2, IGNORE, IGNORE } },
+  { { 0, 1, 2, IGNORE, IGNORE, IGNORE } },
+  // Upmixes from quad
+  { { 0, 1, 2, 3, IGNORE } },
+  { { 0, 1, IGNORE, IGNORE, 2, 3 } },
+  // Upmixes from 5-channel
+  { { 0, 1, 2, 3, 4, IGNORE } }
+};
+
+
+/**
+ * Given an array of input channel data, and an output channel count,
+ * replaces the array with an array of upmixed channels.
+ * This shuffles the array and may set some channel buffers to aZeroChannel.
+ * Don't call this with input count >= output count.
+ * This may return *more* channels than requested. In that case, downmixing
+ * is required to to get to aOutputChannelCount. (This is how we handle
+ * odd cases like 3 -> 4 upmixing.)
+ * If aChannelArray.Length() was the input to one of a series of
+ * GetAudioChannelsSuperset calls resulting in aOutputChannelCount,
+ * no downmixing will be required.
+ */
+template<typename T>
+void
+AudioChannelsUpMix(nsTArray<const T*>* aChannelArray,
+                   uint32_t aOutputChannelCount,
+                   const T* aZeroChannel)
+{
+  uint32_t inputChannelCount = aChannelArray->Length();
+  uint32_t outputChannelCount =
+    GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount);
+  NS_ASSERTION(outputChannelCount > inputChannelCount,
+               "No up-mix needed");
+  MOZ_ASSERT(inputChannelCount > 0, "Bad number of channels");
+  MOZ_ASSERT(outputChannelCount > 0, "Bad number of channels");
+
+  aChannelArray->SetLength(outputChannelCount);
+
+  if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS &&
+      outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) {
+    const UpMixMatrix& m = gUpMixMatrices[
+      gMixingMatrixIndexByChannels[inputChannelCount - 1] +
+      outputChannelCount - inputChannelCount - 1];
+
+    const T* outputChannels[CUSTOM_CHANNEL_LAYOUTS];
+
+    for (uint32_t i = 0; i < outputChannelCount; ++i) {
+      uint8_t channelIndex = m.mInputDestination[i];
+      if (channelIndex == IGNORE) {
+        outputChannels[i] = aZeroChannel;
+      } else {
+        outputChannels[i] = aChannelArray->ElementAt(channelIndex);
+      }
+    }
+    for (uint32_t i = 0; i < outputChannelCount; ++i) {
+      aChannelArray->ElementAt(i) = outputChannels[i];
+    }
+    return;
+  }
+
+  for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
+    aChannelArray->ElementAt(i) = aZeroChannel;
+  }
+}
 
 } // namespace mozilla
 
 #endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */
new file mode 100644
--- /dev/null
+++ b/dom/media/AudioPacketizer.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef AudioPacketizer_h_
+#define AudioPacketizer_h_
+
+#include <mozilla/PodOperations.h>
+#include <mozilla/Assertions.h>
+#include <nsAutoPtr.h>
+#include <AudioSampleFormat.h>
+
+// Enable this to warn when `Output` has been called but not enough data was
+// buffered.
+// #define LOG_PACKETIZER_UNDERRUN
+
+namespace mozilla {
+/**
+ * This class takes arbitrary input data, and returns packets of a specific
+ * size. In the process, it can convert audio samples from 16bit integers to
+ * float (or vice-versa).
+ *
+ * Input and output, as well as length units in the public interface are
+ * interleaved frames.
+ *
+ * Allocations of output buffer can be performed by this class.  Buffers can
+ * simply be delete-d.  This is because packets are intended to be sent off to
+ * non-gecko code using normal pointers/length pairs
+ *
+ * Alternatively, consumers can pass in a buffer in which the output is copied.
+ * The buffer needs to be large enough to store a packet worth of audio.
+ *
+ * The implementation uses a circular buffer using absolute virtual indices.
+ */
+template <typename InputType, typename OutputType>
+class AudioPacketizer
+{
+public:
+  AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels)
+    : mPacketSize(aPacketSize)
+    , mChannels(aChannels)
+    , mReadIndex(0)
+    , mWriteIndex(0)
+    // Start off with a single packet
+    , mStorage(new InputType[aPacketSize * aChannels])
+    , mLength(aPacketSize * aChannels)
+  {
+     MOZ_ASSERT(aPacketSize > 0 && aChannels > 0,
+       "The packet size and the number of channel should be strictly positive");
+  }
+
+  void Input(const InputType* aFrames, uint32_t aFrameCount)
+  {
+    uint32_t inputSamples = aFrameCount * mChannels;
+    // Need to grow the storage. This should rarely happen, if at all, once the
+    // array has the right size.
+    if (inputSamples > EmptySlots()) {
+      // Calls to Input and Output are roughtly interleaved
+      // (Input,Output,Input,Output, etc.), or balanced
+      // (Input,Input,Input,Output,Output,Output), so we update the buffer to
+      // the exact right size in order to not waste space.
+      uint32_t newLength = AvailableSamples() + inputSamples;
+      uint32_t toCopy = AvailableSamples();
+      nsAutoPtr<InputType> oldStorage = mStorage;
+      mStorage = new InputType[newLength];
+      // Copy the old data at the beginning of the new storage.
+      if (WriteIndex() >= ReadIndex()) {
+        PodCopy(mStorage.get(),
+                oldStorage.get() + ReadIndex(),
+                AvailableSamples());
+      } else {
+        uint32_t firstPartLength = mLength - ReadIndex();
+        uint32_t secondPartLength = AvailableSamples() - firstPartLength;
+        PodCopy(mStorage.get(),
+                oldStorage.get() + ReadIndex(),
+                firstPartLength);
+        PodCopy(mStorage.get() + firstPartLength,
+                oldStorage.get(),
+                secondPartLength);
+      }
+      mWriteIndex = toCopy;
+      mReadIndex = 0;
+      mLength = newLength;
+    }
+
+    if (WriteIndex() + inputSamples <= mLength) {
+      PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels);
+    } else {
+      uint32_t firstPartLength = mLength - WriteIndex();
+      uint32_t secondPartLength = inputSamples - firstPartLength;
+      PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength);
+      PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength);
+    }
+
+    mWriteIndex += inputSamples;
+  }
+
+  OutputType* Output()
+  {
+    uint32_t samplesNeeded = mPacketSize * mChannels;
+    OutputType* out = new OutputType[samplesNeeded];
+
+    Output(out);
+
+    return out;
+  }
+
+  void Output(OutputType* aOutputBuffer)
+  {
+    uint32_t samplesNeeded = mPacketSize * mChannels;
+
+    // Under-run. Pad the end of the buffer with silence.
+    if (AvailableSamples() < samplesNeeded) {
+#ifdef LOG_PACKETIZER_UNDERRUN
+      char buf[256];
+      snprintf(buf, 256,
+               "AudioPacketizer %p underrun: available: %u, needed: %u\n",
+               this, AvailableSamples(), samplesNeeded);
+      NS_WARNING(buf);
+#endif
+      uint32_t zeros = samplesNeeded - AvailableSamples();
+      PodZero(aOutputBuffer + AvailableSamples(), zeros);
+      samplesNeeded -= zeros;
+    }
+    if (ReadIndex() + samplesNeeded <= mLength) {
+      ConvertAudioSamples<InputType,OutputType>(mStorage.get() + ReadIndex(),
+                                                aOutputBuffer,
+                                                samplesNeeded);
+    } else {
+      uint32_t firstPartLength = mLength - ReadIndex();
+      uint32_t secondPartLength = samplesNeeded - firstPartLength;
+      ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(),
+                                                 aOutputBuffer,
+                                                 firstPartLength);
+      ConvertAudioSamples<InputType, OutputType>(mStorage.get(),
+                                                 aOutputBuffer + firstPartLength,
+                                                 secondPartLength);
+    }
+    mReadIndex += samplesNeeded;
+  }
+
+  uint32_t PacketsAvailable() const {
+    return AvailableSamples() / mChannels / mPacketSize;
+  }
+
+  bool Empty() const {
+   return mWriteIndex == mReadIndex;
+  }
+
+  bool Full() const {
+    return mWriteIndex - mReadIndex == mLength;
+  }
+
+  uint32_t PacketSize() const {
+    return mPacketSize;
+  }
+
+  uint32_t Channels() const {
+    return mChannels;
+  }
+
+private:
+  uint32_t ReadIndex() const {
+    return mReadIndex % mLength;
+  }
+
+  uint32_t WriteIndex() const {
+    return mWriteIndex % mLength;
+  }
+
+  uint32_t AvailableSamples() const {
+    return mWriteIndex - mReadIndex;
+  }
+
+  uint32_t EmptySlots() const {
+    return mLength - AvailableSamples();
+  }
+
+  // Size of one packet of audio, in frames
+  uint32_t mPacketSize;
+  // Number of channels of the stream flowing through this packetizer
+  uint32_t mChannels;
+  // Two virtual index into the buffer: the read position and the write
+  // position.
+  uint64_t mReadIndex;
+  uint64_t mWriteIndex;
+  // Storage for the samples
+  nsAutoPtr<InputType> mStorage;
+  // Length of the buffer, in samples
+  uint32_t mLength;
+};
+
+} // mozilla
+
+#endif // AudioPacketizer_h_
--- a/dom/media/AudioSampleFormat.h
+++ b/dom/media/AudioSampleFormat.h
@@ -91,16 +91,61 @@ FloatToAudioSample<float>(float aValue)
 template <> inline int16_t
 FloatToAudioSample<int16_t>(float aValue)
 {
   float v = aValue*32768.0f;
   float clamped = std::max(-32768.0f, std::min(32767.0f, v));
   return int16_t(clamped);
 }
 
+template <typename T> T IntegerToAudioSample(int16_t aValue);
+
+template <> inline float
+IntegerToAudioSample<float>(int16_t aValue)
+{
+  return aValue / 32768.0f;
+}
+template <> inline int16_t
+IntegerToAudioSample<int16_t>(int16_t aValue)
+{
+  return aValue;
+}
+
+template<typename SrcT, typename DstT>
+inline void
+ConvertAudioSample(SrcT aIn, DstT& aOut);
+
+template<>
+inline void
+ConvertAudioSample(int16_t aIn, int16_t & aOut)
+{
+  aOut = aIn;
+}
+
+template<>
+inline void
+ConvertAudioSample(int16_t aIn, float& aOut)
+{
+  aOut = AudioSampleToFloat(aIn);
+}
+
+template<>
+inline void
+ConvertAudioSample(float aIn, float& aOut)
+{
+  aOut = aIn;
+}
+
+template<>
+inline void
+ConvertAudioSample(float aIn, int16_t& aOut)
+{
+  aOut = FloatToAudioSample<int16_t>(aIn);
+}
+
 // Sample buffer conversion
 
 template <typename From, typename To> inline void
 ConvertAudioSamples(const From* aFrom, To* aTo, int aCount)
 {
   for (int i = 0; i < aCount; ++i) {
     aTo[i] = FloatToAudioSample<To>(AudioSampleToFloat(aFrom[i]));
   }
--- a/dom/media/AudioSegment.cpp
+++ b/dom/media/AudioSegment.cpp
@@ -1,124 +1,44 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioSegment.h"
 
-#include "AudioStream.h"
 #include "AudioMixer.h"
 #include "AudioChannelFormat.h"
 #include "Latency.h"
 #include <speex/speex_resampler.h>
 
 namespace mozilla {
 
-template <class SrcT, class DestT>
-static void
-InterleaveAndConvertBuffer(const SrcT** aSourceChannels,
-                           int32_t aLength, float aVolume,
-                           int32_t aChannels,
-                           DestT* aOutput)
+const uint8_t SilentChannel::gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*SilentChannel::AUDIO_PROCESSING_FRAMES] = {0};
+
+template<>
+const float* SilentChannel::ZeroChannel<float>()
 {
-  DestT* output = aOutput;
-  for (int32_t i = 0; i < aLength; ++i) {
-    for (int32_t channel = 0; channel < aChannels; ++channel) {
-      float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
-      *output = FloatToAudioSample<DestT>(v);
-      ++output;
-    }
-  }
+  return reinterpret_cast<const float*>(SilentChannel::gZeroChannel);
 }
 
-void
-InterleaveAndConvertBuffer(const void** aSourceChannels,
-                           AudioSampleFormat aSourceFormat,
-                           int32_t aLength, float aVolume,
-                           int32_t aChannels,
-                           AudioDataValue* aOutput)
+template<>
+const int16_t* SilentChannel::ZeroChannel<int16_t>()
 {
-  switch (aSourceFormat) {
-  case AUDIO_FORMAT_FLOAT32:
-    InterleaveAndConvertBuffer(reinterpret_cast<const float**>(aSourceChannels),
-                               aLength,
-                               aVolume,
-                               aChannels,
-                               aOutput);
-    break;
-  case AUDIO_FORMAT_S16:
-    InterleaveAndConvertBuffer(reinterpret_cast<const int16_t**>(aSourceChannels),
-                               aLength,
-                               aVolume,
-                               aChannels,
-                               aOutput);
-    break;
-   case AUDIO_FORMAT_SILENCE:
-    // nothing to do here.
-    break;
-  }
+  return reinterpret_cast<const int16_t*>(SilentChannel::gZeroChannel);
 }
 
 void
 AudioSegment::ApplyVolume(float aVolume)
 {
   for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
     ci->mVolume *= aVolume;
   }
 }
 
-static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
-static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0};
-
-void
-DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
-                     AudioSampleFormat aSourceFormat, int32_t aDuration,
-                     float aVolume, uint32_t aOutputChannels,
-                     AudioDataValue* aOutput)
-{
-  nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channelData;
-  nsAutoTArray<float,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> downmixConversionBuffer;
-  nsAutoTArray<float,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> downmixOutputBuffer;
-
-  channelData.SetLength(aChannelData.Length());
-  if (aSourceFormat != AUDIO_FORMAT_FLOAT32) {
-    NS_ASSERTION(aSourceFormat == AUDIO_FORMAT_S16, "unknown format");
-    downmixConversionBuffer.SetLength(aDuration*aChannelData.Length());
-    for (uint32_t i = 0; i < aChannelData.Length(); ++i) {
-      float* conversionBuf = downmixConversionBuffer.Elements() + (i*aDuration);
-      const int16_t* sourceBuf = static_cast<const int16_t*>(aChannelData[i]);
-      for (uint32_t j = 0; j < (uint32_t)aDuration; ++j) {
-        conversionBuf[j] = AudioSampleToFloat(sourceBuf[j]);
-      }
-      channelData[i] = conversionBuf;
-    }
-  } else {
-    for (uint32_t i = 0; i < aChannelData.Length(); ++i) {
-      channelData[i] = aChannelData[i];
-    }
-  }
-
-  downmixOutputBuffer.SetLength(aDuration*aOutputChannels);
-  nsAutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannelBuffers;
-  nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> outputChannelData;
-  outputChannelBuffers.SetLength(aOutputChannels);
-  outputChannelData.SetLength(aOutputChannels);
-  for (uint32_t i = 0; i < (uint32_t)aOutputChannels; ++i) {
-    outputChannelData[i] = outputChannelBuffers[i] =
-        downmixOutputBuffer.Elements() + aDuration*i;
-  }
-  if (channelData.Length() > aOutputChannels) {
-    AudioChannelsDownMix(channelData, outputChannelBuffers.Elements(),
-                         aOutputChannels, aDuration);
-  }
-  InterleaveAndConvertBuffer(outputChannelData.Elements(), AUDIO_FORMAT_FLOAT32,
-                             aDuration, aVolume, aOutputChannels, aOutput);
-}
-
 void AudioSegment::ResampleChunks(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate)
 {
   if (mChunks.IsEmpty()) {
     return;
   }
 
   MOZ_ASSERT(aResampler || IsNull(), "We can only be here without a resampler if this segment is null.");
 
@@ -160,19 +80,19 @@ PointerForOffsetInChannel(AudioDataValue
              "Offset request out of bounds.");
   return aData + beginningOfChannel + aOffsetSamples;
 }
 
 void
 AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
                   uint32_t aSampleRate)
 {
-  nsAutoTArray<AudioDataValue, AUDIO_PROCESSING_FRAMES* GUESS_AUDIO_CHANNELS>
+  nsAutoTArray<AudioDataValue, SilentChannel::AUDIO_PROCESSING_FRAMES* GUESS_AUDIO_CHANNELS>
   buf;
-  nsAutoTArray<const void*, GUESS_AUDIO_CHANNELS> channelData;
+  nsAutoTArray<const AudioDataValue*, GUESS_AUDIO_CHANNELS> channelData;
   uint32_t offsetSamples = 0;
   uint32_t duration = GetDuration();
 
   if (duration <= 0) {
     MOZ_ASSERT(duration == 0);
     return;
   }
 
@@ -192,21 +112,21 @@ AudioSegment::Mix(AudioMixer& aMixer, ui
                                     aOutputChannels, channel, offsetSamples);
         PodZero(ptr, frames);
       }
     } else {
       // Othewise, we need to upmix or downmix appropriately, depending on the
       // desired input and output channels.
       channelData.SetLength(c.mChannelData.Length());
       for (uint32_t i = 0; i < channelData.Length(); ++i) {
-        channelData[i] = c.mChannelData[i];
+        channelData[i] = static_cast<const AudioDataValue*>(c.mChannelData[i]);
       }
       if (channelData.Length() < aOutputChannels) {
         // Up-mix.
-        AudioChannelsUpMix(&channelData, aOutputChannels, gZeroChannel);
+        AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<AudioDataValue>());
         for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
           AudioDataValue* ptr =
             PointerForOffsetInChannel(buf.Elements(), outBufferLength,
                                       aOutputChannels, channel, offsetSamples);
           PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
                   frames);
         }
         MOZ_ASSERT(channelData.Length() == aOutputChannels);
@@ -241,65 +161,48 @@ AudioSegment::Mix(AudioMixer& aMixer, ui
                "We forgot to write some samples?");
     aMixer.Mix(buf.Elements(), aOutputChannels, offsetSamples, aSampleRate);
   }
 }
 
 void
 AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels, uint32_t aSampleRate)
 {
-  nsAutoTArray<AudioDataValue,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
-  nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channelData;
-  // Offset in the buffer that will end up sent to the AudioStream, in samples.
+  nsAutoTArray<AudioDataValue,SilentChannel::AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
+  // Offset in the buffer that will be written to the mixer, in samples.
   uint32_t offset = 0;
 
   if (GetDuration() <= 0) {
     MOZ_ASSERT(GetDuration() == 0);
     return;
   }
 
   uint32_t outBufferLength = GetDuration() * aOutputChannels;
   buf.SetLength(outBufferLength);
 
 
   for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
     AudioChunk& c = *ci;
-    uint32_t frames = c.mDuration;
 
-    // If we have written data in the past, or we have real (non-silent) data
-    // to write, we can proceed. Otherwise, it means we just started the
-    // AudioStream, and we don't have real data to write to it (just silence).
-    // To avoid overbuffering in the AudioStream, we simply drop the silence,
-    // here. The stream will underrun and output silence anyways.
-    if (c.mBuffer && c.mBufferFormat != AUDIO_FORMAT_SILENCE) {
-      channelData.SetLength(c.mChannelData.Length());
-      for (uint32_t i = 0; i < channelData.Length(); ++i) {
-        channelData[i] = c.mChannelData[i];
-      }
-      if (channelData.Length() < aOutputChannels) {
-        // Up-mix. Note that this might actually make channelData have more
-        // than aOutputChannels temporarily.
-        AudioChannelsUpMix(&channelData, aOutputChannels, gZeroChannel);
-      }
-      if (channelData.Length() > aOutputChannels) {
-        // Down-mix.
-        DownmixAndInterleave(channelData, c.mBufferFormat, frames,
-                             c.mVolume, aOutputChannels, buf.Elements() + offset);
-      } else {
-        InterleaveAndConvertBuffer(channelData.Elements(), c.mBufferFormat,
-                                   frames, c.mVolume,
-                                   aOutputChannels,
-                                   buf.Elements() + offset);
-      }
-    } else {
-      // Assumes that a bit pattern of zeroes == 0.0f
-      memset(buf.Elements() + offset, 0, aOutputChannels * frames * sizeof(AudioDataValue));
+    switch (c.mBufferFormat) {
+      case AUDIO_FORMAT_S16:
+        WriteChunk<int16_t>(c, aOutputChannels, buf.Elements() + offset);
+        break;
+      case AUDIO_FORMAT_FLOAT32:
+        WriteChunk<float>(c, aOutputChannels, buf.Elements() + offset);
+        break;
+      case AUDIO_FORMAT_SILENCE:
+        // The mixer is expecting interleaved data, so this is ok.
+        PodZero(buf.Elements() + offset, c.mDuration * aOutputChannels);
+        break;
+      default:
+        MOZ_ASSERT(false, "Not handled");
     }
 
-    offset += frames * aOutputChannels;
+    offset += c.mDuration * aOutputChannels;
 
 #if !defined(MOZILLA_XPCOMRT_API)
     if (!c.mTimeStamp.IsNull()) {
       TimeStamp now = TimeStamp::Now();
       // would be more efficient to c.mTimeStamp to ms on create time then pass here
       LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
               (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
     }
--- a/dom/media/AudioSegment.h
+++ b/dom/media/AudioSegment.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_AUDIOSEGMENT_H_
 #define MOZILLA_AUDIOSEGMENT_H_
 
 #include "MediaSegment.h"
 #include "AudioSampleFormat.h"
+#include "AudioChannelFormat.h"
 #include "SharedBuffer.h"
 #include "WebAudioUtils.h"
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/TimeStamp.h"
 #endif
 #include <float.h>
 
 namespace mozilla {
@@ -51,31 +52,92 @@ class AudioMixer;
  */
 const int GUESS_AUDIO_CHANNELS = 2;
 
 // We ensure that the graph advances in steps that are multiples of the Web
 // Audio block size
 const uint32_t WEBAUDIO_BLOCK_SIZE_BITS = 7;
 const uint32_t WEBAUDIO_BLOCK_SIZE = 1 << WEBAUDIO_BLOCK_SIZE_BITS;
 
-void InterleaveAndConvertBuffer(const void** aSourceChannels,
-                                AudioSampleFormat aSourceFormat,
-                                int32_t aLength, float aVolume,
-                                int32_t aChannels,
-                                AudioDataValue* aOutput);
+template <typename SrcT, typename DestT>
+static void
+InterleaveAndConvertBuffer(const SrcT* const* aSourceChannels,
+                           uint32_t aLength, float aVolume,
+                           uint32_t aChannels,
+                           DestT* aOutput)
+{
+  DestT* output = aOutput;
+  for (size_t i = 0; i < aLength; ++i) {
+    for (size_t channel = 0; channel < aChannels; ++channel) {
+      float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
+      *output = FloatToAudioSample<DestT>(v);
+      ++output;
+    }
+  }
+}
+
+template <typename SrcT, typename DestT>
+static void
+DeinterleaveAndConvertBuffer(const SrcT* aSourceBuffer,
+                             uint32_t aFrames, uint32_t aChannels,
+                             DestT** aOutput)
+{
+  for (size_t i = 0; i < aChannels; i++) {
+    size_t interleavedIndex = i;
+    for (size_t j = 0; j < aFrames; j++) {
+      ConvertAudioSample(aSourceBuffer[interleavedIndex],
+                         aOutput[i][j]);
+      interleavedIndex += aChannels;
+    }
+  }
+}
+
+class SilentChannel
+{
+public:
+  static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
+  static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES];
+  // We take advantage of the fact that zero in float and zero in int have the
+  // same all-zeros bit layout.
+  template<typename T>
+  static const T* ZeroChannel();
+};
+
 
 /**
  * Given an array of input channels (aChannelData), downmix to aOutputChannels,
  * interleave the channel data. A total of aOutputChannels*aDuration
  * interleaved samples will be copied to a channel buffer in aOutput.
  */
-void DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
-                          AudioSampleFormat aSourceFormat, int32_t aDuration,
-                          float aVolume, uint32_t aOutputChannels,
-                          AudioDataValue* aOutput);
+template <typename SrcT, typename DestT>
+void
+DownmixAndInterleave(const nsTArray<const SrcT*>& aChannelData,
+                     int32_t aDuration, float aVolume, uint32_t aOutputChannels,
+                     DestT* aOutput)
+{
+
+  if (aChannelData.Length() == aOutputChannels) {
+    InterleaveAndConvertBuffer(aChannelData.Elements(),
+                               aDuration, aVolume, aOutputChannels, aOutput);
+  } else {
+    nsAutoTArray<SrcT*,GUESS_AUDIO_CHANNELS> outputChannelData;
+    nsAutoTArray<SrcT, SilentChannel::AUDIO_PROCESSING_FRAMES * GUESS_AUDIO_CHANNELS> outputBuffers;
+    outputChannelData.SetLength(aOutputChannels);
+    outputBuffers.SetLength(aDuration * aOutputChannels);
+    for (uint32_t i = 0; i < aOutputChannels; i++) {
+      outputChannelData[i] = outputBuffers.Elements() + aDuration * i;
+    }
+    AudioChannelsDownMix(aChannelData,
+                         outputChannelData.Elements(),
+                         aOutputChannels,
+                         aDuration);
+    InterleaveAndConvertBuffer(outputChannelData.Elements(),
+                               aDuration, aVolume, aOutputChannels, aOutput);
+  }
+}
 
 /**
  * An AudioChunk represents a multi-channel buffer of audio samples.
  * It references an underlying ThreadSharedObject which manages the lifetime
  * of the buffer. An AudioChunk maintains its own duration and channel data
  * pointers so it can represent a subinterval of a buffer without copying.
  * An AudioChunk can store its individual channels anywhere; it maintains
  * separate pointers to each channel's buffer.
@@ -185,16 +247,23 @@ struct AudioChunk {
       amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
     }
 
     // Memory in the array is owned by mBuffer.
     amount += mChannelData.ShallowSizeOfExcludingThis(aMallocSizeOf);
     return amount;
   }
 
+  template<typename T>
+  const nsTArray<const T*>& ChannelData()
+  {
+    MOZ_ASSERT(AudioSampleTypeToFormat<T>::Format == mBufferFormat);
+    return *reinterpret_cast<nsTArray<const T*>*>(&mChannelData);
+  }
+
   StreamTime mDuration; // in frames within the buffer
   nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes
   nsTArray<const void*> mChannelData; // one pointer per channel; empty if and only if mBuffer is null
   float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull)
   SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
 #ifdef MOZILLA_INTERNAL_API
   mozilla::TimeStamp mTimeStamp;           // time at which this has been fetched from the MediaEngine
 #endif
@@ -351,11 +420,39 @@ public:
   static Type StaticType() { return AUDIO; }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 };
 
+template<typename SrcT>
+void WriteChunk(AudioChunk& aChunk,
+                uint32_t aOutputChannels,
+                AudioDataValue* aOutputBuffer)
+{
+  nsAutoTArray<const SrcT*,GUESS_AUDIO_CHANNELS> channelData;
+
+  channelData = aChunk.ChannelData<SrcT>();
+
+  if (channelData.Length() < aOutputChannels) {
+    // Up-mix. Note that this might actually make channelData have more
+    // than aOutputChannels temporarily.
+    AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<SrcT>());
+  }
+  if (channelData.Length() > aOutputChannels) {
+    // Down-mix.
+    DownmixAndInterleave(channelData, aChunk.mDuration,
+        aChunk.mVolume, aOutputChannels, aOutputBuffer);
+  } else {
+    InterleaveAndConvertBuffer(channelData.Elements(),
+        aChunk.mDuration, aChunk.mVolume,
+        aOutputChannels,
+        aOutputBuffer);
+  }
+}
+
+
+
 } // namespace mozilla
 
 #endif /* MOZILLA_AUDIOSEGMENT_H_ */
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -354,16 +354,17 @@ OutputStreamManager::Disconnect()
 
 DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
                              MediaQueue<MediaData>& aAudioQueue,
                              MediaQueue<MediaData>& aVideoQueue)
   : mOwnerThread(aOwnerThread)
   , mShuttingDown(false)
   , mPlaying(false)
   , mVolume(1.0)
+  , mSameOrigin(false)
   , mAudioQueue(aAudioQueue)
   , mVideoQueue(aVideoQueue)
 {
 }
 
 DecodedStream::~DecodedStream()
 {
   MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended.");
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -2478,34 +2478,29 @@ SourceMediaStream::ResampleAudioToGraphS
 {
   if (aSegment->GetType() != MediaSegment::AUDIO ||
       aTrackData->mInputRate == GraphImpl()->GraphRate()) {
     return;
   }
   AudioSegment* segment = static_cast<AudioSegment*>(aSegment);
   int channels = segment->ChannelCount();
 
-  // If this segment is just silence, we delay instanciating the resampler.
-  if (channels) {
-    if (aTrackData->mResampler) {
-      MOZ_ASSERT(aTrackData->mResamplerChannelCount == segment->ChannelCount());
-    } else {
-      SpeexResamplerState* state = speex_resampler_init(channels,
-                                                        aTrackData->mInputRate,
-                                                        GraphImpl()->GraphRate(),
-                                                        SPEEX_RESAMPLER_QUALITY_MIN,
-                                                        nullptr);
-      if (!state) {
-        return;
-      }
-      aTrackData->mResampler.own(state);
-#ifdef DEBUG
-      aTrackData->mResamplerChannelCount = channels;
-#endif
+  // If this segment is just silence, we delay instanciating the resampler. We
+  // also need to recreate the resampler if the channel count changes.
+  if (channels && aTrackData->mResamplerChannelCount != channels) {
+    SpeexResamplerState* state = speex_resampler_init(channels,
+        aTrackData->mInputRate,
+        GraphImpl()->GraphRate(),
+        SPEEX_RESAMPLER_QUALITY_MIN,
+        nullptr);
+    if (!state) {
+      return;
     }
+    aTrackData->mResampler.own(state);
+    aTrackData->mResamplerChannelCount = channels;
   }
   segment->ResampleChunks(aTrackData->mResampler, aTrackData->mInputRate, GraphImpl()->GraphRate());
 }
 
 bool
 SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment)
 {
   MutexAutoLock lock(mMutex);
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -892,19 +892,17 @@ protected:
    */
   struct TrackData {
     TrackID mID;
     // Sample rate of the input data.
     TrackRate mInputRate;
     // Resampler if the rate of the input track does not match the
     // MediaStreamGraph's.
     nsAutoRef<SpeexResamplerState> mResampler;
-#ifdef DEBUG
     int mResamplerChannelCount;
-#endif
     StreamTime mStart;
     // End-time of data already flushed to the track (excluding mData)
     StreamTime mEndOfFlushedData;
     // Each time the track updates are flushed to the media graph thread,
     // the segment buffer is emptied.
     nsAutoPtr<MediaSegment> mData;
     // Each time the track updates are flushed to the media graph thread,
     // this is cleared.
new file mode 100644
--- /dev/null
+++ b/dom/media/compiledtest/TestAudioPacketizer.cpp
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h>
+#include <assert.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include "../AudioPacketizer.h"
+
+using namespace mozilla;
+
+template<typename T>
+class AutoBuffer
+{
+public:
+  explicit AutoBuffer(size_t aLength)
+  {
+    mStorage = new T[aLength];
+  }
+  ~AutoBuffer() {
+    delete [] mStorage;
+  }
+  T* Get() {
+    return mStorage;
+  }
+private:
+  T* mStorage;
+};
+
+int16_t Sequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0)
+{
+  uint32_t i;
+  for (i = 0; i < aSize; i++) {
+    aBuffer[i] = aStart + i;
+  }
+  return aStart + i;
+}
+
+void IsSequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0)
+{
+  for (uint32_t i = 0; i < aSize; i++) {
+    if (aBuffer[i] != static_cast<int64_t>(aStart + i)) {
+      fprintf(stderr, "Buffer is not a sequence at offset %u\n", i);
+      assert(false);
+    }
+  }
+  assert("Buffer is a sequence.");
+}
+
+void Zero(int16_t* aBuffer, uint32_t aSize)
+{
+  for (uint32_t i = 0; i < aSize; i++) {
+    if (aBuffer[i] != 0) {
+      fprintf(stderr, "Buffer is not null at offset %u\n", i);
+      assert(false);
+    }
+  }
+}
+
+double sine(uint32_t aPhase) {
+ return sin(aPhase * 2 * M_PI * 440 / 44100);
+}
+
+int main() {
+  for (int16_t channels = 1; channels < 2; channels++) {
+    // Test that the packetizer returns zero on underrun
+    {
+      AudioPacketizer<int16_t, int16_t> ap(441, channels);
+      for (int16_t i = 0; i < 10; i++) {
+        int16_t* out = ap.Output();
+        Zero(out, 441);
+        delete out;
+      }
+    }
+    // Simple test, with input/output buffer size aligned on the packet size,
+    // alternating Input and Output calls.
+    {
+      AudioPacketizer<int16_t, int16_t> ap(441, channels);
+      int16_t seqEnd = 0;
+      for (int16_t i = 0; i < 10; i++) {
+        AutoBuffer<int16_t> b(441 * channels);
+        int16_t prevEnd = seqEnd;
+        seqEnd = Sequence(b.Get(), channels * 441, prevEnd);
+        ap.Input(b.Get(), 441);
+        int16_t* out = ap.Output();
+        IsSequence(out, 441 * channels, prevEnd);
+        delete out;
+      }
+    }
+    // Simple test, with input/output buffer size aligned on the packet size,
+    // alternating two Input and Output calls.
+    {
+      AudioPacketizer<int16_t, int16_t> ap(441, channels);
+      int16_t seqEnd = 0;
+      for (int16_t i = 0; i < 10; i++) {
+        AutoBuffer<int16_t> b(441 * channels);
+        AutoBuffer<int16_t> b1(441 * channels);
+        int16_t prevEnd0 = seqEnd;
+        seqEnd = Sequence(b.Get(), 441 * channels, prevEnd0);
+        int16_t prevEnd1 = seqEnd;
+        seqEnd = Sequence(b1.Get(), 441 * channels, seqEnd);
+        ap.Input(b.Get(), 441);
+        ap.Input(b1.Get(), 441);
+        int16_t* out = ap.Output();
+        int16_t* out2 = ap.Output();
+        IsSequence(out, 441 * channels, prevEnd0);
+        IsSequence(out2, 441 * channels, prevEnd1);
+        delete out;
+        delete out2;
+      }
+    }
+    // Input/output buffer size not aligned on the packet size,
+    // alternating two Input and Output calls.
+    {
+      AudioPacketizer<int16_t, int16_t> ap(441, channels);
+      int16_t prevEnd = 0;
+      int16_t prevSeq = 0;
+      for (int16_t i = 0; i < 10; i++) {
+        AutoBuffer<int16_t> b(480 * channels);
+        AutoBuffer<int16_t> b1(480 * channels);
+        prevSeq = Sequence(b.Get(), 480 * channels, prevSeq);
+        prevSeq = Sequence(b1.Get(), 480 * channels, prevSeq);
+        ap.Input(b.Get(), 480);
+        ap.Input(b1.Get(), 480);
+        int16_t* out = ap.Output();
+        int16_t* out2 = ap.Output();
+        IsSequence(out, 441 * channels, prevEnd);
+        prevEnd += 441 * channels;
+        IsSequence(out2, 441 * channels, prevEnd);
+        prevEnd += 441 * channels;
+        delete out;
+        delete out2;
+      }
+      printf("Available: %d\n", ap.PacketsAvailable());
+    }
+
+    // "Real-life" test case: streaming a sine wave through a packetizer, and
+    // checking that we have the right output.
+    // 128 is, for example, the size of a Web Audio API block, and 441 is the
+    // size of a webrtc.org packet when the sample rate is 44100 (10ms)
+    {
+      AudioPacketizer<int16_t, int16_t> ap(441, channels);
+      AutoBuffer<int16_t> b(128 * channels);
+      uint32_t phase = 0;
+      uint32_t outPhase = 0;
+      for (int16_t i = 0; i < 1000; i++) {
+        for (int32_t j = 0; j < 128; j++) {
+          for (int32_t c = 0; c < channels; c++) {
+            // int16_t sinewave at 440Hz/44100Hz sample rate
+            b.Get()[j * channels + c] = (2 << 14) * sine(phase);
+          }
+          phase++;
+        }
+        ap.Input(b.Get(), 128);
+        while (ap.PacketsAvailable()) {
+          int16_t* packet = ap.Output();
+          for (uint32_t k = 0; k < ap.PacketSize(); k++) {
+            for (int32_t c = 0; c < channels; c++) {
+              assert(packet[k * channels + c] ==
+                     static_cast<int16_t>(((2 << 14) * sine(outPhase))));
+            }
+            outPhase++;
+          }
+          delete [] packet;
+        }
+      }
+    }
+  }
+
+  printf("OK\n");
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/compiledtest/TestAudioSegment.cpp
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AudioSegment.h"
+#include <assert.h>
+#include <iostream>
+
+using namespace mozilla;
+
+namespace mozilla {
+uint32_t
+GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2)
+{
+  return std::max(aChannels1, aChannels2);
+}
+}
+
+/* Helper function to give us the maximum and minimum value that don't clip,
+ * for a given sample format (integer or floating-point). */
+template<typename T>
+T GetLowValue();
+
+template<typename T>
+T GetHighValue();
+
+template<typename T>
+T GetSilentValue();
+
+template<>
+float GetLowValue<float>() {
+  return -1.0;
+}
+
+template<>
+int16_t GetLowValue<short>() {
+  return -INT16_MAX;
+}
+
+template<>
+float GetHighValue<float>() {
+  return 1.0;
+}
+
+template<>
+int16_t GetHighValue<short>() {
+  return INT16_MAX;
+}
+
+template<>
+float GetSilentValue() {
+  return 0.0;
+}
+
+template<>
+int16_t GetSilentValue() {
+  return 0;
+}
+
+
+// Get an array of planar audio buffers that has the inverse of the index of the
+// channel (1-indexed) as samples.
+template<typename T>
+const T* const* GetPlanarChannelArray(size_t aChannels, size_t aSize)
+{
+  T** channels = new T*[aChannels];
+  for (size_t c = 0; c < aChannels; c++) {
+    channels[c] = new T[aSize];
+    for (size_t i = 0; i < aSize; i++) {
+      channels[c][i] = FloatToAudioSample<T>(1. / (c + 1));
+    }
+  }
+  return channels;
+}
+
+template<typename T>
+void DeletePlanarChannelsArray(const T* const* aArrays, size_t aChannels)
+{
+  for (size_t channel = 0; channel < aChannels; channel++) {
+    delete [] aArrays[channel];
+  }
+  delete [] aArrays;
+}
+
+template<typename T>
+T** GetPlanarArray(size_t aChannels, size_t aSize)
+{
+  T** channels = new T*[aChannels];
+  for (size_t c = 0; c < aChannels; c++) {
+    channels[c] = new T[aSize];
+    for (size_t i = 0; i < aSize; i++) {
+      channels[c][i] = 0.0f;
+    }
+  }
+  return channels;
+}
+
+template<typename T>
+void DeletePlanarArray(T** aArrays, size_t aChannels)
+{
+  for (size_t channel = 0; channel < aChannels; channel++) {
+    delete [] aArrays[channel];
+  }
+  delete [] aArrays;
+}
+
+// Get an array of audio samples that have the inverse of the index of the
+// channel (1-indexed) as samples.
+template<typename T>
+const T* GetInterleavedChannelArray(size_t aChannels, size_t aSize)
+{
+  size_t sampleCount = aChannels * aSize;
+  T* samples = new T[sampleCount];
+  for (size_t i = 0; i < sampleCount; i++) {
+    uint32_t channel = (i % aChannels) + 1;
+    samples[i] = FloatToAudioSample<T>(1. / channel);
+  }
+  return samples;
+}
+
+template<typename T>
+void DeleteInterleavedChannelArray(const T* aArray)
+{
+  delete [] aArray;
+}
+
+bool FuzzyEqual(float aLhs, float aRhs) {
+  return std::abs(aLhs - aRhs) < 0.01;
+}
+
+template<typename SrcT, typename DstT>
+void TestInterleaveAndConvert()
+{
+  size_t arraySize = 1024;
+  size_t maxChannels = 8; // 7.1
+  for (uint32_t channels = 1; channels < maxChannels; channels++) {
+    const SrcT* const* src = GetPlanarChannelArray<SrcT>(channels, arraySize);
+    DstT* dst = new DstT[channels * arraySize];
+
+    InterleaveAndConvertBuffer(src, arraySize, 1.0, channels, dst);
+
+    uint32_t channelIndex = 0;
+    for (size_t i = 0; i < arraySize * channels; i++) {
+      assert(FuzzyEqual(dst[i],
+                        FloatToAudioSample<DstT>(1. / (channelIndex + 1))));
+      channelIndex++;
+      channelIndex %= channels;
+    }
+
+    DeletePlanarChannelsArray(src, channels);
+    delete [] dst;
+  }
+}
+
+template<typename SrcT, typename DstT>
+void TestDeinterleaveAndConvert()
+{
+  size_t arraySize = 1024;
+  size_t maxChannels = 8; // 7.1
+  for (uint32_t channels = 1; channels < maxChannels; channels++) {
+    const SrcT* src = GetInterleavedChannelArray<SrcT>(channels, arraySize);
+    DstT** dst = GetPlanarArray<DstT>(channels, arraySize);
+
+    DeinterleaveAndConvertBuffer(src, arraySize, channels, dst);
+
+    for (size_t channel = 0; channel < channels; channel++) {
+      for (size_t i = 0; i < arraySize; i++) {
+        assert(FuzzyEqual(dst[channel][i],
+                          FloatToAudioSample<DstT>(1. / (channel + 1))));
+      }
+    }
+
+    DeleteInterleavedChannelArray(src);
+    DeletePlanarArray(dst, channels);
+  }
+}
+
+uint8_t gSilence[4096] = {0};
+
+template<typename T>
+T* SilentChannel()
+{
+  return reinterpret_cast<T*>(gSilence);
+}
+
+template<typename T>
+void TestUpmixStereo()
+{
+  size_t arraySize = 1024;
+  nsTArray<T*> channels;
+  nsTArray<const T*> channelsptr;
+
+  channels.SetLength(1);
+  channelsptr.SetLength(1);
+
+  channels[0] = new T[arraySize];
+
+  for (size_t i = 0; i < arraySize; i++) {
+    channels[0][i] = GetHighValue<T>();
+  }
+  channelsptr[0] = channels[0];
+
+  AudioChannelsUpMix(&channelsptr, 2, ::SilentChannel<T>());
+
+  for (size_t channel = 0; channel < 2; channel++) {
+    for (size_t i = 0; i < arraySize; i++) {
+      if (channelsptr[channel][i] != GetHighValue<T>()) {
+        assert(false);
+      }
+    }
+  }
+  assert(true);
+  delete channels[0];
+}
+
+template<typename T>
+void TestDownmixStereo()
+{
+  const size_t arraySize = 1024;
+  nsTArray<const T*> inputptr;
+  nsTArray<T*> input;
+  T** output;
+
+  output = new T*[1];
+  output[0] = new T[arraySize];
+
+  input.SetLength(2);
+  inputptr.SetLength(2);
+
+  for (size_t channel = 0; channel < input.Length(); channel++) {
+    input[channel] = new T[arraySize];
+    for (size_t i = 0; i < arraySize; i++) {
+      input[channel][i] = channel == 0 ? GetLowValue<T>() : GetHighValue<T>();
+    }
+    inputptr[channel] = input[channel];
+  }
+
+  AudioChannelsDownMix(inputptr, output, 1, arraySize);
+
+  for (size_t i = 0; i < arraySize; i++) {
+    if (output[0][i] != GetSilentValue<T>()) {
+      assert(false);
+    }
+  }
+  assert(true);
+
+  delete output[0];
+  delete output;
+}
+
+int main(int argc, char* argv[]) {
+  TestInterleaveAndConvert<float, float>();
+  TestInterleaveAndConvert<float, int16_t>();
+  TestInterleaveAndConvert<int16_t, float>();
+  TestInterleaveAndConvert<int16_t, int16_t>();
+  TestDeinterleaveAndConvert<float, float>();
+  TestDeinterleaveAndConvert<float, int16_t>();
+  TestDeinterleaveAndConvert<int16_t, float>();
+  TestDeinterleaveAndConvert<int16_t, int16_t>();
+  TestUpmixStereo<float>();
+  TestUpmixStereo<int16_t>();
+  TestDownmixStereo<float>();
+  TestDownmixStereo<int16_t>();
+
+  return 0;
+}
+
--- a/dom/media/compiledtest/moz.build
+++ b/dom/media/compiledtest/moz.build
@@ -1,17 +1,19 @@
 # -*- 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/.
 
 GeckoCppUnitTests([
     'TestAudioBuffers',
-    'TestAudioMixer'
+    'TestAudioMixer',
+    'TestAudioPacketizer',
+    'TestAudioSegment'
 ])
 
 LOCAL_INCLUDES += [
     '..',
 ]
 
 USE_LIBS += [
     'lgpllibs',
--- a/dom/media/encoder/TrackEncoder.cpp
+++ b/dom/media/encoder/TrackEncoder.cpp
@@ -118,39 +118,46 @@ AudioTrackEncoder::AppendAudioSegment(co
 
   if (mRawSegment.GetDuration() >= GetPacketDuration()) {
     mReentrantMonitor.NotifyAll();
   }
 
   return NS_OK;
 }
 
-static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
-static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0};
-
 /*static*/
 void
 AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
                                        int32_t aDuration,
                                        uint32_t aOutputChannels,
                                        AudioDataValue* aOutput)
 {
-  if (aChunk.mChannelData.Length() < aOutputChannels) {
-    // Up-mix. This might make the mChannelData have more than aChannels.
-    AudioChannelsUpMix(&aChunk.mChannelData, aOutputChannels, gZeroChannel);
-  }
-
-  if (aChunk.mChannelData.Length() > aOutputChannels) {
-    DownmixAndInterleave(aChunk.mChannelData, aChunk.mBufferFormat, aDuration,
-                         aChunk.mVolume, aOutputChannels, aOutput);
-  } else {
-    InterleaveAndConvertBuffer(aChunk.mChannelData.Elements(),
-                               aChunk.mBufferFormat, aDuration, aChunk.mVolume,
-                               aOutputChannels, aOutput);
-  }
+  switch(aChunk.mBufferFormat) {
+    case AUDIO_FORMAT_S16: {
+      nsAutoTArray<const int16_t*, 2> array;
+      array.SetLength(aOutputChannels);
+      for (uint32_t i = 0; i < array.Length(); i++) {
+        array[i] = static_cast<const int16_t*>(aChunk.mChannelData[i]);
+      }
+      InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
+      break;
+    }
+    case AUDIO_FORMAT_FLOAT32: {
+      nsAutoTArray<const float*, 2> array;
+      array.SetLength(aOutputChannels);
+      for (uint32_t i = 0; i < array.Length(); i++) {
+        array[i] = static_cast<const float*>(aChunk.mChannelData[i]);
+      }
+      InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
+      break;
+   }
+   case AUDIO_FORMAT_SILENCE: {
+      MOZ_ASSERT(false, "To implement.");
+    }
+  };
 }
 
 /*static*/
 void
 AudioTrackEncoder::DeInterleaveTrackData(AudioDataValue* aInput,
                                          int32_t aDuration,
                                          int32_t aChannels,
                                          AudioDataValue* aOutput)
--- a/dom/media/encoder/TrackEncoder.h
+++ b/dom/media/encoder/TrackEncoder.h
@@ -143,16 +143,38 @@ public:
     , mSamplingRate(0)
   {}
 
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         StreamTime aTrackOffset,
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) override;
 
+  template<typename T>
+  static
+  void InterleaveTrackData(nsTArray<const T*>& aInput,
+                           int32_t aDuration,
+                           uint32_t aOutputChannels,
+                           AudioDataValue* aOutput,
+                           float aVolume)
+  {
+    if (aInput.Length() < aOutputChannels) {
+      // Up-mix. This might make the mChannelData have more than aChannels.
+      AudioChannelsUpMix(&aInput, aOutputChannels, SilentChannel::ZeroChannel<T>());
+    }
+
+    if (aInput.Length() > aOutputChannels) {
+      DownmixAndInterleave(aInput, aDuration,
+                           aVolume, aOutputChannels, aOutput);
+    } else {
+      InterleaveAndConvertBuffer(aInput.Elements(), aDuration, aVolume,
+                                 aOutputChannels, aOutput);
+    }
+  }
+
   /**
    * Interleaves the track data and stores the result into aOutput. Might need
    * to up-mix or down-mix the channel data if the channels number of this chunk
    * is different from aOutputChannels. The channel data from aChunk might be
    * modified by up-mixing.
    */
   static void InterleaveTrackData(AudioChunk& aChunk, int32_t aDuration,
                                   uint32_t aOutputChannels,
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -96,16 +96,17 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'AbstractMediaDecoder.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
     'AudioMixer.h',
+    'AudioPacketizer.h',
     'AudioSampleFormat.h',
     'AudioSegment.h',
     'AudioStream.h',
     'BufferMediaResource.h',
     'CubebUtils.h',
     'DecodedStream.h',
     'DecoderTraits.h',
     'DOMMediaStream.h',
--- a/dom/media/webaudio/AudioNodeExternalInputStream.cpp
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.cpp
@@ -37,55 +37,43 @@ AudioNodeExternalInputStream::Create(Med
   return stream.forget();
 }
 
 /**
  * Copies the data in aInput to aOffsetInBlock within aBlock.
  * aBlock must have been allocated with AllocateInputBlock and have a channel
  * count that's a superset of the channels in aInput.
  */
+template <typename T>
 static void
-CopyChunkToBlock(const AudioChunk& aInput, AudioChunk *aBlock,
+CopyChunkToBlock(AudioChunk& aInput, AudioChunk *aBlock,
                  uint32_t aOffsetInBlock)
 {
   uint32_t blockChannels = aBlock->ChannelCount();
-  nsAutoTArray<const void*,2> channels;
+  nsAutoTArray<const T*,2> channels;
   if (aInput.IsNull()) {
     channels.SetLength(blockChannels);
     PodZero(channels.Elements(), blockChannels);
   } else {
-    channels.SetLength(aInput.ChannelCount());
-    PodCopy(channels.Elements(), aInput.mChannelData.Elements(), channels.Length());
+    const nsTArray<const T*>& inputChannels = aInput.ChannelData<T>();
+    channels.SetLength(inputChannels.Length());
+    PodCopy(channels.Elements(), inputChannels.Elements(), channels.Length());
     if (channels.Length() != blockChannels) {
       // We only need to upmix here because aBlock's channel count has been
       // chosen to be a superset of the channel count of every chunk.
-      AudioChannelsUpMix(&channels, blockChannels, nullptr);
+      AudioChannelsUpMix(&channels, blockChannels, static_cast<T*>(nullptr));
     }
   }
 
-  uint32_t duration = aInput.GetDuration();
   for (uint32_t c = 0; c < blockChannels; ++c) {
     float* outputData = aBlock->ChannelFloatsForWrite(c) + aOffsetInBlock;
     if (channels[c]) {
-      switch (aInput.mBufferFormat) {
-      case AUDIO_FORMAT_FLOAT32:
-        ConvertAudioSamplesWithScale(
-            static_cast<const float*>(channels[c]), outputData, duration,
-            aInput.mVolume);
-        break;
-      case AUDIO_FORMAT_S16:
-        ConvertAudioSamplesWithScale(
-            static_cast<const int16_t*>(channels[c]), outputData, duration,
-            aInput.mVolume);
-        break;
-      default:
-        NS_ERROR("Unhandled format");
-      }
+      ConvertAudioSamplesWithScale(channels[c], outputData, aInput.GetDuration(), aInput.mVolume);
     } else {
-      PodZero(outputData, duration);
+      PodZero(outputData, aInput.GetDuration());
     }
   }
 }
 
 /**
  * Converts the data in aSegment to a single chunk aBlock. aSegment must have
  * duration WEBAUDIO_BLOCK_SIZE. aFallbackChannelCount is a superset of the
  * channels in every chunk of aSegment. aBlock must be float format or null.
@@ -106,17 +94,28 @@ static void ConvertSegmentToAudioBlock(A
       return;
     }
   }
 
   AllocateAudioBlock(aFallbackChannelCount, aBlock);
 
   uint32_t duration = 0;
   for (AudioSegment::ChunkIterator ci(*aSegment); !ci.IsEnded(); ci.Next()) {
-    CopyChunkToBlock(*ci, aBlock, duration);
+    switch (ci->mBufferFormat) {
+      case AUDIO_FORMAT_S16: {
+        CopyChunkToBlock<int16_t>(*ci, aBlock, duration);
+        break;
+      }
+      case AUDIO_FORMAT_FLOAT32: {
+        CopyChunkToBlock<float>(*ci, aBlock, duration);
+        break;
+      }
+      case AUDIO_FORMAT_SILENCE:
+        break;
+    }
     duration += ci->GetDuration();
   }
 }
 
 void
 AudioNodeExternalInputStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
                                            uint32_t aFlags)
 {
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -425,17 +425,17 @@ AudioNodeStream::ObtainInputBlock(AudioC
   }
 }
 
 void
 AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
                                       AudioChunk* aBlock,
                                       nsTArray<float>* aDownmixBuffer)
 {
-  nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channels;
+  nsAutoTArray<const float*,GUESS_AUDIO_CHANNELS> channels;
   UpMixDownMixChunk(&aChunk, aBlock->mChannelData.Length(), channels, *aDownmixBuffer);
 
   for (uint32_t c = 0; c < channels.Length(); ++c) {
     const float* inputData = static_cast<const float*>(channels[c]);
     float* outputData = aBlock->ChannelFloatsForWrite(c);
     if (inputData) {
       if (aInputIndex == 0) {
         AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);
@@ -448,25 +448,27 @@ AudioNodeStream::AccumulateInputChunk(ui
       }
     }
   }
 }
 
 void
 AudioNodeStream::UpMixDownMixChunk(const AudioChunk* aChunk,
                                    uint32_t aOutputChannelCount,
-                                   nsTArray<const void*>& aOutputChannels,
+                                   nsTArray<const float*>& aOutputChannels,
                                    nsTArray<float>& aDownmixBuffer)
 {
   static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {0.f};
 
-  aOutputChannels.AppendElements(aChunk->mChannelData);
+  for (uint32_t i = 0; i < aChunk->mChannelData.Length(); i++) {
+    aOutputChannels.AppendElement(static_cast<const float*>(aChunk->mChannelData[i]));
+  }
   if (aOutputChannels.Length() < aOutputChannelCount) {
     if (mChannelInterpretation == ChannelInterpretation::Speakers) {
-      AudioChannelsUpMix(&aOutputChannels, aOutputChannelCount, nullptr);
+      AudioChannelsUpMix(&aOutputChannels, aOutputChannelCount, SilentChannel::ZeroChannel<float>());
       NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
                    "We called GetAudioChannelsSuperset to avoid this");
     } else {
       // Fill up the remaining aOutputChannels by zeros
       for (uint32_t j = aOutputChannels.Length(); j < aOutputChannelCount; ++j) {
         aOutputChannels.AppendElement(silenceChannel);
       }
     }
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -178,17 +178,17 @@ public:
 
 protected:
   void AdvanceOutputSegment();
   void FinishOutput();
   void AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
                             AudioChunk* aBlock,
                             nsTArray<float>* aDownmixBuffer);
   void UpMixDownMixChunk(const AudioChunk* aChunk, uint32_t aOutputChannelCount,
-                         nsTArray<const void*>& aOutputChannels,
+                         nsTArray<const float*>& aOutputChannels,
                          nsTArray<float>& aDownmixBuffer);
 
   uint32_t ComputedNumberOfChannels(uint32_t aInputChannelCount);
   void ObtainInputBlock(AudioChunk& aTmpChunk, uint32_t aPortIndex);
 
   // The engine that will generate output for this node.
   nsAutoPtr<AudioNodeEngine> mEngine;
   // The mixed input blocks are kept from iteration to iteration to avoid
--- a/dom/media/webaudio/DelayBuffer.cpp
+++ b/dom/media/webaudio/DelayBuffer.cpp
@@ -151,17 +151,17 @@ DelayBuffer::ReadChannels(const double a
       if (!mChunks[readChunk].IsNull()) {
         int readOffset = OffsetForPosition(positions[tick]);
         UpdateUpmixChannels(readChunk, totalChannelCount,
                             aChannelInterpretation);
         double multiplier = interpolationFactor * mChunks[readChunk].mVolume;
         for (uint32_t channel = aFirstChannel;
              channel < readChannelsEnd; ++channel) {
           aOutputChunk->ChannelFloatsForWrite(channel)[i] += multiplier *
-            static_cast<const float*>(mUpmixChannels[channel])[readOffset];
+            mUpmixChannels[channel][readOffset];
         }
       }
 
       interpolationFactor = 1.0 - interpolationFactor;
     }
   }
 }
 
@@ -233,32 +233,31 @@ void
 DelayBuffer::UpdateUpmixChannels(int aNewReadChunk, uint32_t aChannelCount,
                                  ChannelInterpretation aChannelInterpretation)
 {
   if (aNewReadChunk == mLastReadChunk) {
     MOZ_ASSERT(mUpmixChannels.Length() == aChannelCount);
     return;
   }
 
-  static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {};
-
   NS_WARN_IF_FALSE(mHaveWrittenBlock || aNewReadChunk != mCurrentChunk,
                    "Smoothing is making feedback delay too small.");
 
   mLastReadChunk = aNewReadChunk;
-  mUpmixChannels = mChunks[aNewReadChunk].mChannelData;
+  mUpmixChannels = mChunks[aNewReadChunk].ChannelData<float>();
   MOZ_ASSERT(mUpmixChannels.Length() <= aChannelCount);
   if (mUpmixChannels.Length() < aChannelCount) {
     if (aChannelInterpretation == ChannelInterpretation::Speakers) {
-      AudioChannelsUpMix(&mUpmixChannels, aChannelCount, silenceChannel);
+      AudioChannelsUpMix(&mUpmixChannels,
+                         aChannelCount, SilentChannel::ZeroChannel<float>());
       MOZ_ASSERT(mUpmixChannels.Length() == aChannelCount,
                  "We called GetAudioChannelsSuperset to avoid this");
     } else {
       // Fill up the remaining channels with zeros
       for (uint32_t channel = mUpmixChannels.Length();
            channel < aChannelCount; ++channel) {
-        mUpmixChannels.AppendElement(silenceChannel);
+        mUpmixChannels.AppendElement(SilentChannel::ZeroChannel<float>());
       }
     }
   }
 }
 
 } // namespace mozilla
--- a/dom/media/webaudio/DelayBuffer.h
+++ b/dom/media/webaudio/DelayBuffer.h
@@ -89,17 +89,17 @@ private:
   int OffsetForPosition(int aPosition);
   int ChunkForDelay(int aDelay);
   void UpdateUpmixChannels(int aNewReadChunk, uint32_t channelCount,
                            ChannelInterpretation aChannelInterpretation);
 
   // Circular buffer for capturing delayed samples.
   FallibleTArray<AudioChunk> mChunks;
   // Cache upmixed channel arrays.
-  nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> mUpmixChannels;
+  nsAutoTArray<const float*,GUESS_AUDIO_CHANNELS> mUpmixChannels;
   double mSmoothingRate;
   // Current delay, in fractional ticks
   double mCurrentDelay;
   // Maximum delay, in ticks
   int mMaxDelayTicks;
   // The current position in the circular buffer.  The next write will be to
   // this chunk, and the next read may begin before this chunk.
   int mCurrentChunk;
--- a/dom/media/webspeech/synth/ipc/PSpeechSynthesisRequest.ipdl
+++ b/dom/media/webspeech/synth/ipc/PSpeechSynthesisRequest.ipdl
@@ -18,17 +18,17 @@ async protocol PSpeechSynthesisRequest
   Pause();
 
   Resume();
 
   Cancel();
 
   ForceEnd();
 
-  SetAudioOutputVolume(uint32_t aVolume);
+  SetAudioOutputVolume(float aVolume);
 
  child:
 
   __delete__(bool aIsError, float aElapsedTime, uint32_t aCharIndex);
 
   OnStart(nsString aUri);
 
   OnPause(float aElapsedTime, uint32_t aCharIndex);
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
@@ -187,17 +187,17 @@ SpeechTaskChild::Cancel()
 void
 SpeechTaskChild::ForceEnd()
 {
   MOZ_ASSERT(mActor);
   mActor->SendForceEnd();
 }
 
 void
-SpeechTaskChild::SetAudioOutputVolume(uint32_t aVolume)
+SpeechTaskChild::SetAudioOutputVolume(float aVolume)
 {
   if (mActor) {
     mActor->SendSetAudioOutputVolume(aVolume);
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
@@ -87,17 +87,17 @@ public:
   virtual void Pause() override;
 
   virtual void Resume() override;
 
   virtual void Cancel() override;
 
   virtual void ForceEnd() override;
 
-  virtual void SetAudioOutputVolume(uint32_t aVolume) override;
+  virtual void SetAudioOutputVolume(float aVolume) override;
 
 private:
   SpeechSynthesisRequestChild* mActor;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.cpp
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.cpp
@@ -123,17 +123,17 @@ bool
 SpeechSynthesisRequestParent::RecvForceEnd()
 {
   MOZ_ASSERT(mTask);
   mTask->ForceEnd();
   return true;
 }
 
 bool
-SpeechSynthesisRequestParent::RecvSetAudioOutputVolume(const uint32_t& aVolume)
+SpeechSynthesisRequestParent::RecvSetAudioOutputVolume(const float& aVolume)
 {
   MOZ_ASSERT(mTask);
   mTask->SetAudioOutputVolume(aVolume);
   return true;
 }
 
 // SpeechTaskParent
 
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.h
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.h
@@ -65,17 +65,17 @@ protected:
   virtual bool RecvPause() override;
 
   virtual bool RecvResume() override;
 
   virtual bool RecvCancel() override;
 
   virtual bool RecvForceEnd() override;
 
-  virtual bool RecvSetAudioOutputVolume(const uint32_t& aVolume) override;
+  virtual bool RecvSetAudioOutputVolume(const float& aVolume) override;
 };
 
 class SpeechTaskParent : public nsSpeechTask
 {
   friend class SpeechSynthesisRequestParent;
 public:
   SpeechTaskParent(float aVolume, const nsAString& aUtterance)
     : nsSpeechTask(aVolume, aUtterance) {}
--- a/dom/media/webspeech/synth/nsISpeechService.idl
+++ b/dom/media/webspeech/synth/nsISpeechService.idl
@@ -7,33 +7,39 @@
 
 typedef unsigned short SpeechServiceType;
 
 /**
  * A callback is implemented by the service. For direct audio services, it is
  * required to implement these, although it could be helpful to use the
  * cancel method for shutting down the speech resources.
  */
-[scriptable, uuid(408251b0-1d7b-4876-888f-718859ce8c9d)]
+[scriptable, uuid(c576de0c-8a3d-4570-be7e-9876d3e5bed2)]
 interface nsISpeechTaskCallback : nsISupports
 {
   /**
    * The user or application has paused the speech.
    */
   void onPause();
 
   /**
    * The user or application has resumed the speech.
    */
   void onResume();
 
   /**
    * The user or application has canceled the speech.
    */
   void onCancel();
+
+  /**
+   * The user or application has changed the volume of this speech.
+   * This is only used on indirect audio service type.
+   */
+  void onVolumeChanged(in float aVolume);
 };
 
 
 /**
  * A task is associated with a single utterance. It is provided by the browser
  * to the service in the speak() method.
  */
 [scriptable, builtinclass, uuid(ad59949c-2437-4b35-8eeb-d760caab75c5)]
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -662,43 +662,50 @@ nsSpeechTask::CreateAudioChannelAgent()
   if (mAudioChannelAgent) {
     mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(mUtterance->GetOwner(),
                                            static_cast<int32_t>(AudioChannelService::GetDefaultAudioChannel()),
                                            this);
+  float volume = 0.0f;
+  bool muted = true;
+  mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY, &volume, &muted);
+  WindowVolumeChanged(volume, muted);
 }
 
 void
 nsSpeechTask::DestroyAudioChannelAgent()
 {
   if (mAudioChannelAgent) {
     mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
     mAudioChannelAgent = nullptr;
   }
 }
 
 NS_IMETHODIMP
 nsSpeechTask::WindowVolumeChanged(float aVolume, bool aMuted)
 {
-  SetAudioOutputVolume(mVolume * aVolume * aMuted);
+  SetAudioOutputVolume(aMuted ? 0.0 : mVolume * aVolume);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSpeechTask::WindowAudioCaptureChanged()
 {
   // This is not supported yet.
   return NS_OK;
 }
 
 void
-nsSpeechTask::SetAudioOutputVolume(uint32_t aVolume)
+nsSpeechTask::SetAudioOutputVolume(float aVolume)
 {
   if (mStream) {
     mStream->SetAudioOutputVolume(this, aVolume);
   }
+  if (mIndirectAudio) {
+    mCallback->OnVolumeChanged(aVolume);
+  }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/nsSpeechTask.h
+++ b/dom/media/webspeech/synth/nsSpeechTask.h
@@ -47,17 +47,17 @@ public:
   uint32_t GetCurrentCharOffset();
 
   void SetSpeechSynthesis(SpeechSynthesis* aSpeechSynthesis);
 
   void Init(ProcessedMediaStream* aStream);
 
   void SetChosenVoiceURI(const nsAString& aUri);
 
-  virtual void SetAudioOutputVolume(uint32_t aVolume);
+  virtual void SetAudioOutputVolume(float aVolume);
 
   bool IsPreCanceled()
   {
     return mPreCanceled;
   };
 
   bool IsPrePaused()
   {
--- a/dom/media/webspeech/synth/pico/nsPicoService.cpp
+++ b/dom/media/webspeech/synth/pico/nsPicoService.cpp
@@ -404,16 +404,22 @@ PicoCallbackRunnable::OnResume()
 
 NS_IMETHODIMP
 PicoCallbackRunnable::OnCancel()
 {
   mService->mCurrentTask = nullptr;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PicoCallbackRunnable::OnVolumeChanged(float aVolume)
+{
+  return NS_OK;
+}
+
 NS_INTERFACE_MAP_BEGIN(nsPicoService)
   NS_INTERFACE_MAP_ENTRY(nsISpeechService)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsPicoService)
 NS_IMPL_RELEASE(nsPicoService)
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
@@ -86,16 +86,21 @@ public:
   {
     if (mTask) {
       mTask->DispatchEnd(1.5, 1);
     }
 
     return NS_OK;
   }
 
+  NS_IMETHOD OnVolumeChanged(float aVolume) override
+  {
+    return NS_OK;
+  }
+
 private:
   virtual ~FakeSynthCallback() { }
 
   nsCOMPtr<nsISpeechTask> mTask;
 };
 
 NS_IMPL_CYCLE_COLLECTION(FakeSynthCallback, mTask);
 
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -98,16 +98,23 @@ SapiCallback::OnCancel()
   mSpeakTextLen = 0;
   // Purge all the previous utterances and speak an empty string
   if (FAILED(mSapiClient->Speak(nullptr, SPF_PURGEBEFORESPEAK, nullptr))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SapiCallback::OnVolumeChanged(float aVolume)
+{
+  mSapiClient->SetVolume(static_cast<USHORT>(aVolume * 100));
+  return NS_OK;
+}
+
 void
 SapiCallback::OnSpeechEvent(const SPEVENT& speechEvent)
 {
   switch (speechEvent.eEventId) {
   case SPEI_START_INPUT_STREAM:
     mTask->DispatchStart();
     break;
   case SPEI_END_INPUT_STREAM:
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -244,28 +244,41 @@ nsNPAPIPlugin::~nsNPAPIPlugin()
 void
 nsNPAPIPlugin::PluginCrashed(const nsAString& pluginDumpID,
                              const nsAString& browserDumpID)
 {
   nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
   host->PluginCrashed(this, pluginDumpID, browserDumpID);
 }
 
+bool
+nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag)
+{
+#ifdef MOZ_WIDGET_ANDROID
+  return false;
+#else
+  return true;
+#endif
+}
+
 inline PluginLibrary*
 GetNewPluginLibrary(nsPluginTag *aPluginTag)
 {
   if (!aPluginTag) {
     return nullptr;
   }
 
   if (XRE_IsContentProcess()) {
     return PluginModuleContentParent::LoadModule(aPluginTag->mId);
   }
 
-  return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId, aPluginTag);
+  if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
+    return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId, aPluginTag);
+  }
+  return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary);
 }
 
 // Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance).
 nsresult
 nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
 {
   *aResult = nullptr;
 
--- a/dom/plugins/base/nsNPAPIPlugin.h
+++ b/dom/plugins/base/nsNPAPIPlugin.h
@@ -48,16 +48,18 @@ public:
 
   // The IPC mechanism notifies the nsNPAPIPlugin if the plugin
   // crashes and is no longer usable. pluginDumpID/browserDumpID are
   // the IDs of respective minidumps that were written, or empty if no
   // minidump was written.
   void PluginCrashed(const nsAString& pluginDumpID,
                      const nsAString& browserDumpID);
 
+  static bool RunPluginOOP(const nsPluginTag *aPluginTag);
+
   nsresult Shutdown();
 
   static nsresult RetainStream(NPStream *pstream, nsISupports **aRetainedPeer);
 
 protected:
   virtual ~nsNPAPIPlugin();
 
   NPPluginFuncs mPluginFuncs;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1325,21 +1325,45 @@ nsPluginHost::FindNativePluginForExtensi
     return nullptr;
   }
 
   // Re-fetch the matching type because of how FindPreferredPlugin works...
   preferredPlugin->HasExtension(aExtension, aMimeType);
   return preferredPlugin;
 }
 
+static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
+                                  nsNPAPIPlugin **aOutNPAPIPlugin)
+{
+  // If this is an in-process plugin we'll need to load it here if we haven't already.
+  if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
+    if (aPluginTag->mFullPath.IsEmpty())
+      return NS_ERROR_FAILURE;
+    nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
+    file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath));
+    nsPluginFile pluginFile(file);
+    PRLibrary* pluginLibrary = nullptr;
+
+    if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary)
+      return NS_ERROR_FAILURE;
+
+    aPluginTag->mLibrary = pluginLibrary;
+  }
+
+  nsresult rv;
+  rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
+
+  return rv;
+}
+
 nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
 {
   nsRefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
   if (!plugin) {
-    nsresult rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, getter_AddRefs(plugin));
+    nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
     if (NS_FAILED(rv)) {
       return rv;
     }
     aPluginTag->mPlugin = plugin;
   }
   return NS_OK;
 }
 
@@ -2227,16 +2251,24 @@ nsresult nsPluginHost::ScanPluginsDirect
     }
 
     // do it if we still want it
     if (!seenBefore) {
       // We have a valid new plugin so report that plugins have changed.
       *aPluginsChanged = true;
     }
 
+    // Avoid adding different versions of the same plugin if they are running
+    // in-process, otherwise we risk undefined behaviour.
+    if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
+      if (HaveSamePlugin(pluginTag)) {
+        continue;
+      }
+    }
+
     // Don't add the same plugin again if it hasn't changed
     if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
       if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
         continue;
       }
     }
 
     // If we're not creating a plugin list, simply looking for changes,
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -145,17 +145,17 @@ public:
   }
 };
 
 class RespondWithHandler final : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
   nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
   const RequestMode mRequestMode;
-  const bool mIsClientRequest;
+  const DebugOnly<bool> mIsClientRequest;
   const bool mIsNavigationRequest;
 public:
   NS_DECL_ISUPPORTS
 
   RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                      nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
                      RequestMode aRequestMode, bool aIsClientRequest,
                      bool aIsNavigationRequest)
@@ -267,39 +267,31 @@ RespondWithHandler::ResolvedCallback(JSC
     autoCancel.SetCancelStatus(NS_ERROR_OPAQUE_INTERCEPTION_DISABLED);
     return;
   }
 
   // Section "HTTP Fetch", step 2.2:
   //  If one of the following conditions is true, return a network error:
   //    * response's type is "error".
   //    * request's mode is not "no-cors" and response's type is "opaque".
-  //    * request is a client request and response's type is neither "basic"
-  //      nor "default".
   //    * request is not a navigation request and response's type is
   //      "opaqueredirect".
 
   if (response->Type() == ResponseType::Error) {
     autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_ERROR_RESPONSE);
     return;
   }
 
+  MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin);
+
   if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) {
     autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE);
     return;
   }
 
-  // TODO: remove this case as its no longer in the spec (bug 1184967)
-  if (mIsClientRequest && response->Type() != ResponseType::Basic &&
-      response->Type() != ResponseType::Default &&
-      response->Type() != ResponseType::Opaqueredirect) {
-    autoCancel.SetCancelStatus(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION);
-    return;
-  }
-
   if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) {
     autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION);
     return;
   }
 
   if (NS_WARN_IF(response->BodyUsed())) {
     autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_USED_RESPONSE);
     return;
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3704,32 +3704,17 @@ public:
       mIsHttpChannel = true;
 
       rv = httpChannel->GetRequestMethod(mMethod);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
       NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
 
-      uint32_t corsMode;
-      internalChannel->GetCorsMode(&corsMode);
-      switch (corsMode) {
-        case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN:
-          mRequestMode = RequestMode::Same_origin;
-          break;
-        case nsIHttpChannelInternal::CORS_MODE_NO_CORS:
-          mRequestMode = RequestMode::No_cors;
-          break;
-        case nsIHttpChannelInternal::CORS_MODE_CORS:
-        case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT:
-          mRequestMode = RequestMode::Cors;
-          break;
-        default:
-          MOZ_CRASH("Unexpected CORS mode");
-      }
+      mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
 
       // This is safe due to static_asserts at top of file.
       uint32_t redirectMode;
       internalChannel->GetRedirectMode(&redirectMode);
       mRequestRedirect = static_cast<RequestRedirect>(redirectMode);
 
       if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
         mRequestCredentials = RequestCredentials::Omit;
@@ -3752,16 +3737,18 @@ public:
       }
     } else {
       nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
       // If it is not an HTTP channel it must be a JAR one.
       NS_ENSURE_TRUE(jarChannel, NS_ERROR_NOT_AVAILABLE);
 
       mMethod = "GET";
 
+      mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
+
       if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
         mRequestCredentials = RequestCredentials::Omit;
       }
     }
 
     return NS_OK;
   }
 
--- a/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
@@ -1,16 +1,15 @@
 var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/https/";
 
 self.addEventListener("install", function(event) {
   event.waitUntil(
     self.caches.open("origin-cache")
       .then(c => {
-        return c.add(new Request(prefix + 'index-https.sjs',
-                                 { redirect: 'manual' }));
+        return c.add(prefix + 'index-https.sjs');
       })
   );
 });
 
 self.addEventListener("fetch", function(event) {
   if (event.request.url.indexOf("index-cached-https.sjs") >= 0) {
     event.respondWith(
       self.caches.open("origin-cache")
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^
@@ -0,0 +1,1 @@
+Access-Control-Allow-Origin: https://example.com
--- a/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
+++ b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
@@ -1,20 +1,18 @@
 var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/";
 
 self.addEventListener("install", function(event) {
   event.waitUntil(
     self.caches.open("origin-cache")
       .then(c => {
         return Promise.all(
           [
-            c.add(new Request(prefix + 'index.sjs',
-                              { redirect: 'manual' } )),
-            c.add(new Request(prefix + 'index-to-https.sjs',
-                              { redirect: 'manual' } ))
+            c.add(prefix + 'index.sjs'),
+            c.add(prefix + 'index-to-https.sjs')
           ]
         );
       })
   );
 });
 
 self.addEventListener("fetch", function(event) {
   if (event.request.url.indexOf("index-cached.sjs") >= 0) {
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^
@@ -0,0 +1,1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -48,21 +48,23 @@ support-files =
   fetch/https/https_test.js
   fetch/https/clonedresponse/index.html
   fetch/https/clonedresponse/register.html
   fetch/https/clonedresponse/unregister.html
   fetch/https/clonedresponse/https_test.js
   fetch/origin/index.sjs
   fetch/origin/index-to-https.sjs
   fetch/origin/realindex.html
+  fetch/origin/realindex.html^headers^
   fetch/origin/register.html
   fetch/origin/unregister.html
   fetch/origin/origin_test.js
   fetch/origin/https/index-https.sjs
   fetch/origin/https/realindex.html
+  fetch/origin/https/realindex.html^headers^
   fetch/origin/https/register.html
   fetch/origin/https/unregister.html
   fetch/origin/https/origin_test.js
   fetch/requesturl/index.html
   fetch/requesturl/redirect.sjs
   fetch/requesturl/redirector.html
   fetch/requesturl/register.html
   fetch/requesturl/requesturl_test.js
--- a/gfx/layers/IMFYCbCrImage.cpp
+++ b/gfx/layers/IMFYCbCrImage.cpp
@@ -265,38 +265,21 @@ IMFYCbCrImage::GetTextureClient(Composit
     ctx->UpdateSubresource(textureY, 0, nullptr, mData.mYChannel,
                            mData.mYStride, mData.mYStride * mData.mYSize.height);
     ctx->UpdateSubresource(textureCb, 0, nullptr, mData.mCbChannel,
                            mData.mCbCrStride, mData.mCbCrStride * mData.mCbCrSize.height);
     ctx->UpdateSubresource(textureCr, 0, nullptr, mData.mCrChannel,
                            mData.mCbCrStride, mData.mCbCrStride * mData.mCbCrSize.height);
   }
 
-  RefPtr<IDXGIResource> resource;
-
-  HANDLE shareHandleY;
-  textureY->QueryInterface((IDXGIResource**)byRef(resource));
-  hr = resource->GetSharedHandle(&shareHandleY);
-
-  HANDLE shareHandleCb;
-  textureCb->QueryInterface((IDXGIResource**)byRef(resource));
-  hr = resource->GetSharedHandle(&shareHandleCb);
-
-  HANDLE shareHandleCr;
-  textureCr->QueryInterface((IDXGIResource**)byRef(resource));
-  hr = resource->GetSharedHandle(&shareHandleCr);
-
   mTextureClient = DXGIYCbCrTextureClient::Create(aClient->GetForwarder(),
                                                   TextureFlags::DEFAULT,
                                                   textureY,
                                                   textureCb,
                                                   textureCr,
-                                                  shareHandleY,
-                                                  shareHandleCb,
-                                                  shareHandleCr,
                                                   GetSize(),
                                                   mData.mYSize,
                                                   mData.mCbCrSize);
 
   return mTextureClient;
 }
 
 } // namespace layers
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -165,16 +165,17 @@ public:
   Mutex mLock;
   ImageContainer* mImageContainer;
 };
 
 ImageContainer::ImageContainer(Mode flag)
 : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
+  mAttachCount(0),
   mDroppedImageCount(0),
   mImageFactory(new ImageFactory()),
   mRecycleBin(new BufferRecycleBin()),
   mImageClient(nullptr),
   mCurrentProducerID(-1),
   mIPDLChild(nullptr)
 {
   if (ImageBridgeChild::IsCreated()) {
@@ -230,16 +231,38 @@ ImageContainer::CreateImage(ImageFormat 
     if (img) {
       return img.forget();
     }
   }
   return mImageFactory->CreateImage(aFormat, mScaleHint, mRecycleBin);
 }
 
 void
+ImageContainer::NotifyAttached()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  if (!mAttachCount++) {
+    if (IsAsync()) {
+      ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
+    }
+  }
+}
+
+void
+ImageContainer::NotifyDetached()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  if (!--mAttachCount) {
+    if (IsAsync()) {
+      ImageBridgeChild::FlushAllImagesAsync(mImageClient);
+    }
+  }
+}
+
+void
 ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   mGenerationCounter = ++sGenerationCounter;
 
   if (!aImages.IsEmpty()) {
     NS_ASSERTION(mCurrentImages.IsEmpty() ||
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -384,16 +384,28 @@ public:
    * Returns 0 if this ImageContainer does not use ImageBridge. Note that
    * 0 is always an invalid ID for asynchronous image containers.
    *
    * Can be called from any thread.
    */
   uint64_t GetAsyncContainerID() const;
 
   /**
+   * We track when ImageContainers are attached to layers so that we can
+   * avoid sending images through ImageBridge if they won't be displayed.
+   */
+  void NotifyAttached();
+  void NotifyDetached();
+
+  bool IsAttached() {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    return !!mAttachCount;
+  }
+
+  /**
    * Returns if the container currently has an image.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    */
   bool HasCurrentImage();
 
   struct OwningImage {
     OwningImage() : mFrameID(0), mProducerID(0), mComposited(false) {}
@@ -514,16 +526,18 @@ private:
   // Updates every time mActiveImage changes
   uint32_t mGenerationCounter;
 
   // Number of contained images that have been painted at least once.  It's up
   // to the ImageContainer implementation to ensure accesses to this are
   // threadsafe.
   uint32_t mPaintCount;
 
+  int32_t mAttachCount;
+
   // See GetPaintDelay. Accessed only with mReentrantMonitor held.
   TimeDuration mPaintDelay;
 
   // See GetDroppedImageCount. Accessed only with mReentrantMonitor held.
   uint32_t mDroppedImageCount;
 
   // This is the image factory used by this container, layer managers using
   // this container can set an alternative image factory that will be used to
--- a/gfx/layers/ImageLayers.cpp
+++ b/gfx/layers/ImageLayers.cpp
@@ -14,21 +14,35 @@ namespace mozilla {
 namespace layers {
 
 ImageLayer::ImageLayer(LayerManager* aManager, void* aImplData)
 : Layer(aManager, aImplData), mFilter(GraphicsFilter::FILTER_GOOD)
 , mScaleMode(ScaleMode::SCALE_NONE), mDisallowBigImage(false)
 {}
 
 ImageLayer::~ImageLayer()
-{}
+{
+  if (mContainer) {
+    mContainer->NotifyDetached();
+  }
+}
 
 void ImageLayer::SetContainer(ImageContainer* aContainer) 
 {
+  if (aContainer == mContainer) {
+    return;
+  }
+
+  if (mContainer) {
+    mContainer->NotifyDetached();
+  }
   mContainer = aContainer;
+  if (mContainer) {
+    mContainer->NotifyAttached();
+  }
 }
 
 void ImageLayer::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
 {
   gfx::Matrix4x4 local = GetLocalTransform();
 
   // Snap image edges to pixel boundaries
   gfxRect sourceRect(0, 0, 0, 0);
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -662,45 +662,105 @@ DXGIYCbCrTextureClient::~DXGIYCbCrTextur
   }
   MOZ_COUNT_DTOR(DXGIYCbCrTextureClient);
 }
 
 // static
 already_AddRefed<DXGIYCbCrTextureClient>
 DXGIYCbCrTextureClient::Create(ISurfaceAllocator* aAllocator,
                                TextureFlags aFlags,
-                               IUnknown* aTextureY,
-                               IUnknown* aTextureCb,
-                               IUnknown* aTextureCr,
+                               IDirect3DTexture9* aTextureY,
+                               IDirect3DTexture9* aTextureCb,
+                               IDirect3DTexture9* aTextureCr,
                                HANDLE aHandleY,
                                HANDLE aHandleCb,
                                HANDLE aHandleCr,
                                const gfx::IntSize& aSize,
                                const gfx::IntSize& aSizeY,
                                const gfx::IntSize& aSizeCbCr)
 {
   if (!aHandleY || !aHandleCb || !aHandleCr ||
       !aTextureY || !aTextureCb || !aTextureCr) {
     return nullptr;
   }
 
+  aTextureY->SetPrivateData(sD3D11TextureUsage,
+    new TextureMemoryMeasurer(aSizeY.width * aSizeY.height), sizeof(IUnknown*), D3DSPD_IUNKNOWN);
+  aTextureCb->SetPrivateData(sD3D11TextureUsage,
+    new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height), sizeof(IUnknown*), D3DSPD_IUNKNOWN);
+  aTextureCr->SetPrivateData(sD3D11TextureUsage,
+    new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height), sizeof(IUnknown*), D3DSPD_IUNKNOWN);
+
   RefPtr<DXGIYCbCrTextureClient> texture =
     new DXGIYCbCrTextureClient(aAllocator, aFlags);
   texture->mHandles[0] = aHandleY;
   texture->mHandles[1] = aHandleCb;
   texture->mHandles[2] = aHandleCr;
   texture->mHoldRefs[0] = aTextureY;
   texture->mHoldRefs[1] = aTextureCb;
   texture->mHoldRefs[2] = aTextureCr;
   texture->mSize = aSize;
   texture->mSizeY = aSizeY;
   texture->mSizeCbCr = aSizeCbCr;
   return texture.forget();
 }
 
+already_AddRefed<DXGIYCbCrTextureClient>
+DXGIYCbCrTextureClient::Create(ISurfaceAllocator* aAllocator,
+                               TextureFlags aFlags,
+                               ID3D11Texture2D* aTextureY,
+                               ID3D11Texture2D* aTextureCb,
+                               ID3D11Texture2D* aTextureCr,
+                               const gfx::IntSize& aSize,
+                               const gfx::IntSize& aSizeY,
+                               const gfx::IntSize& aSizeCbCr)
+{
+  if (!aTextureY || !aTextureCb || !aTextureCr) {
+    return nullptr;
+  }
+
+  aTextureY->SetPrivateDataInterface(sD3D11TextureUsage,
+    new TextureMemoryMeasurer(aSize.width * aSize.height));
+  aTextureCb->SetPrivateDataInterface(sD3D11TextureUsage,
+    new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+  aTextureCr->SetPrivateDataInterface(sD3D11TextureUsage,
+    new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+
+  RefPtr<DXGIYCbCrTextureClient> texture =
+    new DXGIYCbCrTextureClient(aAllocator, aFlags);
+
+  RefPtr<IDXGIResource> resource;
+
+  aTextureY->QueryInterface((IDXGIResource**)byRef(resource));
+  HRESULT hr = resource->GetSharedHandle(&texture->mHandles[0]);
+  if (FAILED(hr)) {
+    return nullptr;
+  }
+
+  aTextureCb->QueryInterface((IDXGIResource**)byRef(resource));
+  hr = resource->GetSharedHandle(&texture->mHandles[1]);
+  if (FAILED(hr)) {
+    return nullptr;
+  }
+
+  aTextureCr->QueryInterface((IDXGIResource**)byRef(resource));
+  hr = resource->GetSharedHandle(&texture->mHandles[2]);
+  if (FAILED(hr)) {
+    return nullptr;
+  }
+
+  texture->mHoldRefs[0] = aTextureY;
+  texture->mHoldRefs[1] = aTextureCb;
+  texture->mHoldRefs[2] = aTextureCr;
+  texture->mSize = aSize;
+  texture->mSizeY = aSizeY;
+  texture->mSizeCbCr = aSizeCbCr;
+  return texture.forget();
+}
+
 bool
 DXGIYCbCrTextureClient::Lock(OpenMode)
 {
   MOZ_ASSERT(!mIsLocked);
   if (!IsValid()) {
     return false;
   }
   mIsLocked = true;
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -7,16 +7,17 @@
 #define MOZILLA_GFX_TEXTURED3D11_H
 
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/TextureHost.h"
 #include "gfxWindowsPlatform.h"
 #include "mozilla/GfxMessageUtils.h"
 #include <d3d11.h>
+#include "d3d9.h"
 #include <vector>
 
 namespace mozilla {
 namespace layers {
 
 class CompositorD3D11;
 
 /**
@@ -104,26 +105,37 @@ public:
                          TextureFlags aFlags);
 
   virtual ~DXGIYCbCrTextureClient();
 
   // Creates a TextureClient and init width.
   static already_AddRefed<DXGIYCbCrTextureClient>
   Create(ISurfaceAllocator* aAllocator,
          TextureFlags aFlags,
-         IUnknown* aTextureY,
-         IUnknown* aTextureCb,
-         IUnknown* aTextureCr,
+         IDirect3DTexture9* aTextureY,
+         IDirect3DTexture9* aTextureCb,
+         IDirect3DTexture9* aTextureCr,
          HANDLE aHandleY,
          HANDLE aHandleCb,
          HANDLE aHandleCr,
          const gfx::IntSize& aSize,
          const gfx::IntSize& aSizeY,
          const gfx::IntSize& aSizeCbCr);
 
+  // Creates a TextureClient and init width.
+  static already_AddRefed<DXGIYCbCrTextureClient>
+  Create(ISurfaceAllocator* aAllocator,
+         TextureFlags aFlags,
+         ID3D11Texture2D* aTextureY,
+         ID3D11Texture2D* aTextureCb,
+         ID3D11Texture2D* aTextureCr,
+         const gfx::IntSize& aSize,
+         const gfx::IntSize& aSizeY,
+         const gfx::IntSize& aSizeCbCr);
+
   // TextureClient
 
   virtual bool IsAllocated() const override{ return !!mHoldRefs[0]; }
 
   virtual bool Lock(OpenMode aOpenMode) override;
 
   virtual void Unlock() override;
 
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -402,16 +402,19 @@ void ImageBridgeChild::DispatchReleaseTe
     FROM_HERE,
     NewRunnableFunction(&ReleaseTextureClientNow, aClient));
 }
 
 static void UpdateImageClientNow(ImageClient* aClient, ImageContainer* aContainer)
 {
   MOZ_ASSERT(aClient);
   MOZ_ASSERT(aContainer);
+  if (!aContainer->IsAttached()) {
+    return;
+  }
   sImageBridgeChildSingleton->BeginTransaction();
   aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 //static
 void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
                                                  ImageContainer* aContainer)
@@ -470,16 +473,43 @@ void ImageBridgeChild::FlushAllImages(Im
 
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction(&FlushAllImagesSync, aClient, aContainer, waiter));
 
   waiter->WaitComplete();
 }
 
+static void FlushAllImagesAsyncInternal(ImageClient* aClient)
+{
+  MOZ_ASSERT(aClient);
+  sImageBridgeChildSingleton->BeginTransaction();
+  aClient->FlushAllImages(nullptr);
+  sImageBridgeChildSingleton->EndTransaction();
+}
+
+//static
+void ImageBridgeChild::FlushAllImagesAsync(ImageClient* aClient)
+{
+  if (!IsCreated()) {
+    return;
+  }
+  MOZ_ASSERT(aClient);
+  MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
+  MOZ_ASSERT(!InImageBridgeChildThread());
+  if (InImageBridgeChildThread()) {
+    NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
+    return;
+  }
+
+  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
+    FROM_HERE,
+    NewRunnableFunction(&FlushAllImagesAsyncInternal, aClient));
+}
+
 void
 ImageBridgeChild::BeginTransaction()
 {
   MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
   mTxn->Begin();
 }
 
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -216,16 +216,18 @@ public:
   static void DispatchReleaseTextureClient(TextureClient* aClient);
   static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer);
 
   /**
    * Flush all Images sent to CompositableHost.
    */
   static void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
 
+  static void FlushAllImagesAsync(ImageClient* aClient);
+
   // CompositableForwarder
 
   virtual void Connect(CompositableClient* aCompositable,
                        ImageContainer* aImageContainer) override;
 
   virtual bool IsImageBridgeChild() const override { return true; }
 
   /**
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -314,17 +314,17 @@ AsmJSModule::finish(ExclusiveContext* cx
     // AsmJSModule.
     MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
     masm.executableCopy(code_);
 
     // c.f. JitCode::copyFrom
     MOZ_ASSERT(masm.jumpRelocationTableBytes() == 0);
     MOZ_ASSERT(masm.dataRelocationTableBytes() == 0);
     MOZ_ASSERT(masm.preBarrierTableBytes() == 0);
-    MOZ_ASSERT(!masm.hasEnteredExitFrame());
+    MOZ_ASSERT(!masm.hasSelfReference());
 
     // Copy over metadata, making sure to update all offsets on ARM.
 
     staticLinkData_.interruptExitOffset = masm.actualOffset(interruptLabel.offset());
     staticLinkData_.outOfBoundsExitOffset = masm.actualOffset(outOfBoundsLabel.offset());
 
     // Heap-access metadata used for link-time patching and fault-handling.
     heapAccesses_ = masm.extractAsmJSHeapAccesses();
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1362,17 +1362,17 @@ class MOZ_STACK_CLASS ModuleCompiler
         static HashNumber hash(const ExitDescriptor& d) {
             HashNumber hn = HashGeneric(d.name_, d.sig_->retType().which());
             const VarTypeVector& args = d.sig_->args();
             for (unsigned i = 0; i < args.length(); i++)
                 hn = AddToHash(hn, args[i].which());
             return hn;
         }
         static bool match(const ExitDescriptor& lhs, const ExitDescriptor& rhs) {
-            return lhs.name_ == rhs.name_ && lhs.sig_ == rhs.sig_;
+            return lhs.name_ == rhs.name_ && *lhs.sig_ == *rhs.sig_;
         }
     };
 
     typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
 
     struct MathBuiltin
     {
         enum Kind { Function, Constant };
@@ -11448,17 +11448,17 @@ GenerateFFIIonExit(ModuleCompiler& m, co
         //   act.prevProfilingActivation_ = cx->runtime()->profilingActivation;
         masm.loadPtr(Address(reg0, offsetOfProfilingActivation), reg2);
         masm.storePtr(reg2, Address(reg1, Activation::offsetOfPrevProfiling()));
         //   cx->runtime()->profilingActivation_ = act;
         masm.storePtr(reg1, Address(reg0, offsetOfProfilingActivation));
     }
 
     AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
-    masm.callJitFromAsmJS(callee);
+    masm.callJitNoProfiler(callee);
     AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
 
     {
         // Disable Activation.
         //
         // This sequence needs three registers, and must preserve the JSReturnReg_Data and
         // JSReturnReg_Type, so there are five live registers.
         MOZ_ASSERT(JSReturnReg_Data == AsmJSIonExitRegReturnData);
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -816,33 +816,32 @@ Parser<FullParseHandler>::cloneLeftHandS
 
     if (opn->isArity(PN_LIST)) {
         MOZ_ASSERT(opn->isKind(PNK_ARRAY) || opn->isKind(PNK_OBJECT));
         pn->makeEmpty();
         for (ParseNode* opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
             ParseNode* pn2;
             if (opn->isKind(PNK_OBJECT)) {
                 if (opn2->isKind(PNK_MUTATEPROTO)) {
-                    ParseNode* target = cloneLeftHandSide(opn2->pn_kid);
+                    ParseNode* target = opn2->pn_kid->isKind(PNK_ASSIGN)
+                                        ? cloneDestructuringDefault(opn2->pn_kid)
+                                        : cloneLeftHandSide(opn2->pn_kid);
                     if (!target)
                         return nullptr;
                     pn2 = handler.new_<UnaryNode>(PNK_MUTATEPROTO, JSOP_NOP, opn2->pn_pos, target);
                 } else {
                     MOZ_ASSERT(opn2->isArity(PN_BINARY));
                     MOZ_ASSERT(opn2->isKind(PNK_COLON) || opn2->isKind(PNK_SHORTHAND));
 
                     ParseNode* tag = cloneParseTree(opn2->pn_left);
                     if (!tag)
                         return nullptr;
-                    ParseNode* target;
-                    if (opn2->pn_right->isKind(PNK_ASSIGN)) {
-                        target = cloneDestructuringDefault(opn2->pn_right);
-                    } else {
-                        target = cloneLeftHandSide(opn2->pn_right);
-                    }
+                    ParseNode* target = opn2->pn_right->isKind(PNK_ASSIGN)
+                                        ? cloneDestructuringDefault(opn2->pn_right)
+                                        : cloneLeftHandSide(opn2->pn_right);
                     if (!target)
                         return nullptr;
 
                     pn2 = handler.new_<BinaryNode>(opn2->getKind(), JSOP_INITPROP, opn2->pn_pos, tag, target);
                 }
             } else if (opn2->isArity(PN_NULLARY)) {
                 MOZ_ASSERT(opn2->isKind(PNK_ELISION));
                 pn2 = cloneParseTree(opn2);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1392,46 +1392,48 @@ Parser<ParseHandler>::newFunction(Handle
     MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
 
     RootedFunction fun(context);
 
     gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
     JSFunction::Flags flags;
     switch (kind) {
       case Expression:
-        flags = JSFunction::INTERPRETED_LAMBDA;
+        flags = (generatorKind == NotGenerator
+                 ? JSFunction::INTERPRETED_LAMBDA
+                 : JSFunction::INTERPRETED_LAMBDA_GENERATOR);
         break;
       case Arrow:
         flags = JSFunction::INTERPRETED_LAMBDA_ARROW;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Method:
         MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
-        if (generatorKind == NotGenerator)
-            flags = JSFunction::INTERPRETED_METHOD;
-        else
-            flags = JSFunction::INTERPRETED_METHOD_GENERATOR;
+        flags = (generatorKind == NotGenerator
+                 ? JSFunction::INTERPRETED_METHOD
+                 : JSFunction::INTERPRETED_METHOD_GENERATOR);
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case ClassConstructor:
       case DerivedClassConstructor:
         flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Getter:
         flags = JSFunction::INTERPRETED_GETTER;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Setter:
         flags = JSFunction::INTERPRETED_SETTER;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       default:
-        flags = JSFunction::INTERPRETED_NORMAL;
-        break;
+        flags = (generatorKind == NotGenerator
+                 ? JSFunction::INTERPRETED_NORMAL
+                 : JSFunction::INTERPRETED_GENERATOR);
     }
 
     fun = NewFunctionWithProto(context, nullptr, 0, flags, nullptr, atom, proto,
                                allocKind, TenuredObject);
     if (!fun)
         return nullptr;
     if (options().selfHostingMode)
         fun->setIsSelfHostedBuiltin();
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3781,24 +3781,31 @@ BaselineCompiler::emit_JSOP_RESUME()
 
     // Load the return value.
     ValueOperand retVal = regs.takeAnyValue();
     masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), retVal);
 
     // Push a fake return address on the stack. We will resume here when the
     // generator returns.
     Label genStart, returnTarget;
+#ifdef JS_USE_LINK_REGISTER
+    masm.call(&genStart);
+#else
     masm.callAndPushReturnAddress(&genStart);
+#endif
 
     // Add an IC entry so the return offset -> pc mapping works.
     if (!appendICEntry(ICEntry::Kind_Op, masm.currentOffset()))
         return false;
 
     masm.jump(&returnTarget);
     masm.bind(&genStart);
+#ifdef JS_USE_LINK_REGISTER
+    masm.pushReturnAddress();
+#endif
 
     // If profiler instrumentation is on, update lastProfilingFrame on
     // current JitActivation
     {
         Register scratchReg = scratch2;
         Label skip;
         AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
         masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -10360,17 +10360,17 @@ ICCall_Native::Compiler::generateStubCod
 
     // Construct a native exit frame.
     masm.push(argcReg);
 
     Register scratch = regs.takeAny();
     EmitBaselineCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(ICTailCallReg);
-    masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
+    masm.enterFakeExitFrame(NativeExitFrameLayoutToken);
 
     // Execute call.
     masm.setupUnalignedABICall(scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(argcReg);
     masm.passABIArg(vpReg);
 
@@ -10458,17 +10458,17 @@ ICCall_ClassHook::Compiler::generateStub
     masm.moveStackPtrTo(vpReg);
 
     // Construct a native exit frame.
     masm.push(argcReg);
 
     EmitBaselineCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(ICTailCallReg);
-    masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
+    masm.enterFakeExitFrame(NativeExitFrameLayoutToken);
 
     // Execute call.
     masm.setupUnalignedABICall(scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(argcReg);
     masm.passABIArg(vpReg);
     masm.callWithABI(Address(ICStubReg, ICCall_ClassHook::offsetOfNative()));
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2867,19 +2867,18 @@ CodeGenerator::visitCallNative(LCallNati
     // Preload arguments into registers.
     masm.loadJSContext(argContextReg);
     masm.move32(Imm32(call->numActualArgs()), argUintNReg);
     masm.moveStackPtrTo(argVpReg);
 
     masm.Push(argUintNReg);
 
     // Construct native exit frame.
-    uint32_t safepointOffset;
-    masm.buildFakeExitFrame(tempReg, &safepointOffset);
-    masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
+    uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
+    masm.enterFakeExitFrame(NativeExitFrameLayoutToken);
 
     markSafepointAt(safepointOffset, call);
 
     // Construct and execute call.
     masm.setupUnalignedABICall(tempReg);
     masm.passABIArg(argContextReg);
     masm.passABIArg(argUintNReg);
     masm.passABIArg(argVpReg);
@@ -2986,19 +2985,18 @@ CodeGenerator::visitCallDOMNative(LCallD
 
     // Push |this| object for passing HandleObject. We push after argc to
     // maintain the same sp-relative location of the object pointer with other
     // DOMExitFrames.
     masm.Push(argObj);
     masm.moveStackPtrTo(argObj);
 
     // Construct native exit frame.
-    uint32_t safepointOffset;
-    masm.buildFakeExitFrame(argJSContext, &safepointOffset);
-    masm.enterFakeExitFrame(IonDOMMethodExitFrameLayout::Token());
+    uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
+    masm.enterFakeExitFrame(IonDOMMethodExitFrameLayoutToken);
 
     markSafepointAt(safepointOffset, call);
 
     // Construct and execute call.
     masm.setupUnalignedABICall(argJSContext);
 
     masm.loadJSContext(argJSContext);
 
@@ -6308,17 +6306,17 @@ JitRuntime::generateLazyLinkStub(JSConte
 
     // The caller did not push an exit frame on the stack, it pushed a
     // JitFrameLayout.  We modify the descriptor to be a valid exit frame and
     // restore it once the lazy link is complete.
     Address descriptor(masm.getStackPointer(), CommonFrameLayout::offsetOfDescriptor());
     size_t convertToExitFrame = JitFrameLayout::Size() - ExitFrameLayout::Size();
     masm.addPtr(Imm32(convertToExitFrame << FRAMESIZE_SHIFT), descriptor);
 
-    masm.enterFakeExitFrame(LazyLinkExitFrameLayout::Token());
+    masm.enterFakeExitFrame(LazyLinkExitFrameLayoutToken);
     masm.PushStubCode();
 
     masm.setupUnalignedABICall(temp0);
     masm.loadJSContext(temp0);
     masm.passABIArg(temp0);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation));
 
     masm.leaveExitFrame(/* stub code */ sizeof(JitCode*));
@@ -9730,19 +9728,18 @@ CodeGenerator::visitGetDOMProperty(LGetD
 
     masm.Push(ObjectReg);
 
     LoadDOMPrivate(masm, ObjectReg, PrivateReg);
 
     // Rooting will happen at GC time.
     masm.moveStackPtrTo(ObjectReg);
 
-    uint32_t safepointOffset;
-    masm.buildFakeExitFrame(JSContextReg, &safepointOffset);
-    masm.enterFakeExitFrame(IonDOMExitFrameLayout::GetterToken());
+    uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
+    masm.enterFakeExitFrame(IonDOMExitFrameLayoutGetterToken);
 
     markSafepointAt(safepointOffset, ins);
 
     masm.setupUnalignedABICall(JSContextReg);
 
     masm.loadJSContext(JSContextReg);
 
     masm.passABIArg(JSContextReg);
@@ -9820,19 +9817,18 @@ CodeGenerator::visitSetDOMProperty(LSetD
 
     masm.Push(ObjectReg);
 
     LoadDOMPrivate(masm, ObjectReg, PrivateReg);
 
     // Rooting will happen at GC time.
     masm.moveStackPtrTo(ObjectReg);
 
-    uint32_t safepointOffset;
-    masm.buildFakeExitFrame(JSContextReg, &safepointOffset);
-    masm.enterFakeExitFrame(IonDOMExitFrameLayout::SetterToken());
+    uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
+    masm.enterFakeExitFrame(IonDOMExitFrameLayoutSetterToken);
 
     markSafepointAt(safepointOffset, ins);
 
     masm.setupUnalignedABICall(JSContextReg);
 
     masm.loadJSContext(JSContextReg);
 
     masm.passABIArg(JSContextReg);
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -940,17 +940,17 @@ EmitGetterCall(JSContext* cx, MacroAssem
         masm.moveStackPtrTo(argVpReg);
 
         // Push marking data for later use.
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
+        masm.enterFakeExitFrame(IonOOLNativeExitFrameLayoutToken);
 
         // Construct and execute call.
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
 
@@ -998,17 +998,17 @@ EmitGetterCall(JSContext* cx, MacroAssem
             masm.Push(scratchReg);
         }
         masm.moveStackPtrTo(argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayout::Token());
+        masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayoutToken);
 
         // Make the call.
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target));
@@ -1581,17 +1581,17 @@ EmitCallProxyGet(JSContext* cx, MacroAss
     masm.Push(object);
     masm.Push(object);
     masm.moveStackPtrTo(argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
-    masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
+    masm.enterFakeExitFrame(IonOOLProxyExitFrameLayoutToken);
 
     // Make the call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argVpReg);
@@ -2292,17 +2292,17 @@ EmitCallProxySet(JSContext* cx, MacroAss
     masm.Push(object);
     masm.Push(object);
     masm.moveStackPtrTo(argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
-    masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
+    masm.enterFakeExitFrame(IonOOLProxyExitFrameLayoutToken);
 
     // Make the call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argValueReg);
     masm.passABIArg(argStrictReg);
@@ -2501,17 +2501,17 @@ GenerateCallSetter(JSContext* cx, IonScr
         masm.move32(Imm32(1), argUintNReg);
 
         // Push data for GC marking
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
+        masm.enterFakeExitFrame(IonOOLNativeExitFrameLayoutToken);
 
         // Make the call
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
 
@@ -2565,17 +2565,17 @@ GenerateCallSetter(JSContext* cx, IonScr
 
         masm.Push(object);
         masm.moveStackPtrTo(argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(IonOOLSetterOpExitFrameLayout::Token());
+        masm.enterFakeExitFrame(IonOOLSetterOpExitFrameLayoutToken);
 
         // Make the call.
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
         masm.passABIArg(argValueReg);
         masm.passABIArg(argResultReg);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4353,36 +4353,49 @@ MaybeUnwrapElements(const MDefinition* e
 
     // MTypedArrayElements and MTypedObjectElements aren't handled.
     if (!elementsOrObj->isElements())
         return nullptr;
 
     return elementsOrObj->toElements();
 }
 
+static inline const MDefinition*
+GetElementsObject(const MDefinition* elementsOrObj)
+{
+    if (elementsOrObj->type() == MIRType_Object)
+        return elementsOrObj;
+
+    const MDefinition* elements = MaybeUnwrapElements(elementsOrObj);
+    if (elements)
+        return elements->toElements()->input();
+
+    return nullptr;
+}
+
 // Gets the MDefinition of the target Object for the given store operation.
 static inline const MDefinition*
 GetStoreObject(const MDefinition* store)
 {
     switch (store->op()) {
-      case MDefinition::Op_StoreElement: {
-        const MDefinition* elementsOrObj = store->toStoreElement()->elements();
-        if (elementsOrObj->type() == MIRType_Object)
-            return elementsOrObj;
-
-        const MDefinition* elements = MaybeUnwrapElements(elementsOrObj);
-        if (elements)
-            return elements->toElements()->input();
-
-        return nullptr;
-      }
+      case MDefinition::Op_StoreElement:
+        return GetElementsObject(store->toStoreElement()->elements());
 
       case MDefinition::Op_StoreElementHole:
         return store->toStoreElementHole()->object();
 
+      case MDefinition::Op_StoreUnboxedObjectOrNull:
+        return GetElementsObject(store->toStoreUnboxedObjectOrNull()->elements());
+
+      case MDefinition::Op_StoreUnboxedString:
+        return GetElementsObject(store->toStoreUnboxedString()->elements());
+
+      case MDefinition::Op_StoreUnboxedScalar:
+        return GetElementsObject(store->toStoreUnboxedScalar()->elements());
+
       default:
         return nullptr;
     }
 }
 
 // Implements mightAlias() logic common to all load operations.
 static bool
 GenericLoadMightAlias(const MDefinition* elementsOrObj, const MDefinition* store)
@@ -4434,16 +4447,40 @@ MLoadElement::mightAlias(const MDefiniti
 
 bool
 MInitializedLength::mightAlias(const MDefinition* store) const
 {
     return GenericLoadMightAlias(elements(), store);
 }
 
 bool
+MLoadUnboxedObjectOrNull::mightAlias(const MDefinition* store) const
+{
+    return GenericLoadMightAlias(elements(), store);
+}
+
+bool
+MLoadUnboxedString::mightAlias(const MDefinition* store) const
+{
+    return GenericLoadMightAlias(elements(), store);
+}
+
+bool
+MLoadUnboxedScalar::mightAlias(const MDefinition* store) const
+{
+    return GenericLoadMightAlias(elements(), store);
+}
+
+bool
+MUnboxedArrayInitializedLength::mightAlias(const MDefinition* store) const
+{
+    return GenericLoadMightAlias(object(), store);
+}
+
+bool
 MGuardReceiverPolymorphic::congruentTo(const MDefinition* ins) const
 {
     if (!ins->isGuardReceiverPolymorphic())
         return false;
 
     const MGuardReceiverPolymorphic* other = ins->toGuardReceiverPolymorphic();
 
     if (numReceivers() != other->numReceivers())
@@ -4983,25 +5020,56 @@ PropertyReadNeedsTypeBarrier(CompilerCon
             return BarrierKind::TypeSet;
         }
     }
 
     property.freeze(constraints);
     return BarrierKind::NoBarrier;
 }
 
+static bool
+ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second)
+{
+    if (first->isSingleton() ||
+        second->isSingleton() ||
+        first->clasp() != second->clasp() ||
+        first->unknownProperties() ||
+        second->unknownProperties())
+    {
+        return false;
+    }
+
+    if (first->clasp() == &ArrayObject::class_) {
+        HeapTypeSetKey firstElements = first->property(JSID_VOID);
+        HeapTypeSetKey secondElements = second->property(JSID_VOID);
+
+        return firstElements.maybeTypes() && secondElements.maybeTypes() &&
+               firstElements.maybeTypes()->equals(secondElements.maybeTypes());
+    }
+
+    if (first->clasp() == &UnboxedArrayObject::class_) {
+        return first->group()->unboxedLayout().elementType() ==
+               second->group()->unboxedLayout().elementType();
+    }
+
+    return false;
+}
+
 BarrierKind
 jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
                                   CompilerConstraintList* constraints,
                                   TypeSet::ObjectKey* key, PropertyName* name,
                                   TemporaryTypeSet* observed, bool updateObserved)
 {
+    if (!updateObserved)
+        return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
+
     // If this access has never executed, try to add types to the observed set
     // according to any property which exists on the object or its prototype.
-    if (updateObserved && observed->empty() && name) {
+    if (observed->empty() && name) {
         JSObject* obj;
         if (key->isSingleton())
             obj = key->singleton();
         else
             obj = key->proto().isLazy() ? nullptr : key->proto().toObjectOrNull();
 
         while (obj) {
             if (!obj->getClass()->isNative())
@@ -5024,16 +5092,40 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
                     }
                 }
             }
 
             obj = obj->getProto();
         }
     }
 
+    // If any objects which could be observed are similar to ones that have
+    // already been observed, add them to the observed type set.
+    if (!key->unknownProperties()) {
+        HeapTypeSetKey property = key->property(name ? NameToId(name) : JSID_VOID);
+
+        if (property.maybeTypes() && !property.maybeTypes()->unknownObject()) {
+            for (size_t i = 0; i < property.maybeTypes()->getObjectCount(); i++) {
+                TypeSet::ObjectKey* key = property.maybeTypes()->getObject(i);
+                if (!key || observed->unknownObject())
+                    continue;
+
+                for (size_t j = 0; j < observed->getObjectCount(); j++) {
+                    TypeSet::ObjectKey* observedKey = observed->getObject(j);
+                    if (observedKey && ObjectSubsumes(observedKey, key)) {
+                        // Note: the return value here is ignored.
+                        observed->addType(TypeSet::ObjectType(key),
+                                          GetJitContext()->temp->lifoAlloc());
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
     return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
 }
 
 BarrierKind
 jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
                                   CompilerConstraintList* constraints,
                                   MDefinition* obj, PropertyName* name,
                                   TemporaryTypeSet* observed)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8003,16 +8003,17 @@ class MUnboxedArrayInitializedLength
         return getOperand(0);
     }
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
+    bool mightAlias(const MDefinition* store) const override;
 
     ALLOW_CLONE(MUnboxedArrayInitializedLength)
 };
 
 // Increment the initialized length of an unboxed array object.
 class MIncrementUnboxedArrayInitializedLength
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
@@ -8756,16 +8757,17 @@ class MLoadUnboxedObjectOrNull
             return false;
         if (offsetAdjustment() != other->offsetAdjustment())
             return false;
         return congruentIfOperandsEqual(other);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::UnboxedElement);
     }
+    bool mightAlias(const MDefinition* store) const override;
 
     ALLOW_CLONE(MLoadUnboxedObjectOrNull)
 };
 
 class MLoadUnboxedString
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
@@ -8805,16 +8807,17 @@ class MLoadUnboxedString
         const MLoadUnboxedString* other = ins->toLoadUnboxedString();
         if (offsetAdjustment() != other->offsetAdjustment())
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::UnboxedElement);
     }
+    bool mightAlias(const MDefinition* store) const override;
 
     ALLOW_CLONE(MLoadUnboxedString)
 };
 
 class MStoreElementCommon
 {
     MIRType elementType_;
     bool needsBarrier_;
@@ -9457,16 +9460,17 @@ class MLoadUnboxedScalar
     }
     AliasSet getAliasSet() const override {
         // When a barrier is needed make the instruction effectful by
         // giving it a "store" effect.
         if (requiresBarrier_)
             return AliasSet::Store(AliasSet::UnboxedElement);
         return AliasSet::Load(AliasSet::UnboxedElement);
     }
+    bool mightAlias(const MDefinition* store) const override;
 
     bool congruentTo(const MDefinition* ins) const override {
         if (requiresBarrier_)
             return false;
         if (!ins->isLoadUnboxedScalar())
             return false;
         const MLoadUnboxedScalar* other = ins->toLoadUnboxedScalar();
         if (storageType_ != other->storageType_)
@@ -12895,17 +12899,17 @@ class MRecompileCheck : public MNullaryI
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 };
 
 // All barriered operations - MMemoryBarrier, MCompareExchangeTypedArrayElement,
 // MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as well as
-// MLoadUnboxedScalar and MStoreUnboxedSclaar when they are marked as requiring
+// MLoadUnboxedScalar and MStoreUnboxedScalar when they are marked as requiring
 // a memory barrer - have the following attributes:
 //
 // - Not movable
 // - Not removable
 // - Not congruent with any other instruction
 // - Effectful (they alias every TypedArray store)
 //
 // The intended effect of those constraints is to prevent all loads
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -89,19 +89,18 @@ void
 MacroAssembler::passABIArg(FloatRegister reg, MoveOp::Type type)
 {
     passABIArg(MoveOperand(reg), type);
 }
 
 template <typename T> void
 MacroAssembler::callWithABI(const T& fun, MoveOp::Type result)
 {
-    profilerPreCall();
+    AutoProfilerCallInstrumentation profiler(*this);
     callWithABINoProfiler(fun, result);
-    profilerPostReturn();
 }
 
 void
 MacroAssembler::appendSignatureType(MoveOp::Type type)
 {
 #ifdef JS_SIMULATOR
     signature_ <<= ArgType_Shift;
     switch (type) {
@@ -147,51 +146,116 @@ MacroAssembler::signature() const
 
     return ABIFunctionType(signature_);
 #else
     // No simulator enabled.
     MOZ_CRASH("Only available for making calls within a simulator.");
 #endif
 }
 
-//}}} check_macroassembler_style
 // ===============================================================
+// Jit Frames.
+
+uint32_t
+MacroAssembler::callJitNoProfiler(Register callee)
+{
+#ifdef JS_USE_LINK_REGISTER
+    // The return address is pushed by the callee.
+    call(callee);
+#else
+    callAndPushReturnAddress(callee);
+#endif
+    return currentOffset();
+}
+
+uint32_t
+MacroAssembler::callJit(Register callee)
+{
+    AutoProfilerCallInstrumentation profiler(*this);
+    uint32_t ret = callJitNoProfiler(callee);
+    return ret;
+}
+
+uint32_t
+MacroAssembler::callJit(JitCode* callee)
+{
+    AutoProfilerCallInstrumentation profiler(*this);
+    call(callee);
+    return currentOffset();
+}
+
+void
+MacroAssembler::makeFrameDescriptor(Register frameSizeReg, FrameType type)
+{
+    // See JitFrames.h for a description of the frame descriptor format.
+
+    lshiftPtr(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
+    // The saved-frame bit is zero for new frames. See js::SavedStacks.
+    orPtr(Imm32(type), frameSizeReg);
+}
+
+void
+MacroAssembler::pushStaticFrameDescriptor(FrameType type)
+{
+    uint32_t descriptor = MakeFrameDescriptor(framePushed(), type);
+    Push(Imm32(descriptor));
+}
+
+uint32_t
+MacroAssembler::buildFakeExitFrame(Register scratch)
+{
+    mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
+
+    pushStaticFrameDescriptor(JitFrame_IonJS);
+    uint32_t retAddr = pushFakeReturnAddress(scratch);
+
+    MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
+    return retAddr;
+}
+
+// ===============================================================
+// Exit frame footer.
 
 void
 MacroAssembler::PushStubCode()
 {
-    exitCodePatch_ = PushWithPatch(ImmWord(-1));
+    // Make sure that we do not erase an existing self-reference.
+    MOZ_ASSERT(!hasSelfReference());
+    selfReferencePatch_ = PushWithPatch(ImmWord(-1));
 }
 
 void
 MacroAssembler::enterExitFrame(const VMFunction* f)
 {
     linkExitFrame();
-    // Push the ioncode. (Bailout or VM wrapper)
+    // Push the JitCode pointer. (Keep the code alive, when on the stack)
     PushStubCode();
     // Push VMFunction pointer, to mark arguments.
     Push(ImmPtr(f));
 }
 
 void
-MacroAssembler::enterFakeExitFrame(JitCode* codeVal)
+MacroAssembler::enterFakeExitFrame(enum ExitFrameTokenValues token)
 {
     linkExitFrame();
-    Push(ImmPtr(codeVal));
+    Push(Imm32(token));
     Push(ImmPtr(nullptr));
 }
 
 void
 MacroAssembler::leaveExitFrame(size_t extraFrame)
 {
     freeStack(ExitFooterFrame::Size() + extraFrame);
 }
 
 bool
-MacroAssembler::hasEnteredExitFrame() const
+MacroAssembler::hasSelfReference() const
 {
-    return exitCodePatch_.offset() != 0;
+    return selfReferencePatch_.offset() != 0;
 }
 
+//}}} check_macroassembler_style
+// ===============================================================
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MacroAssembler_inl_h */
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1583,25 +1583,16 @@ MacroAssembler::loadStringChar(Register 
     jump(&done);
 
     bind(&isLatin1);
     load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
 
     bind(&done);
 }
 
-// Save an exit frame (which must be aligned to the stack pointer) to
-// PerThreadData::jitTop of the main thread.
-void
-MacroAssembler::linkExitFrame()
-{
-    AbsoluteAddress jitTop(GetJitContext()->runtime->addressOfJitTop());
-    storeStackPtr(jitTop);
-}
-
 static void
 BailoutReportOverRecursed(JSContext* cx)
 {
     ReportOverRecursed(cx);
 }
 
 void
 MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
@@ -1662,17 +1653,17 @@ MacroAssembler::generateBailoutTail(Regi
 
         // Enter exit frame for the FinishBailoutToBaseline call.
         loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)), temp);
         load32(Address(temp, BaselineFrame::reverseOffsetOfFrameSize()), temp);
         makeFrameDescriptor(temp, JitFrame_BaselineJS);
         push(temp);
         push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
         // No GC things to mark on the stack, push a bare token.
-        enterFakeExitFrame(ExitFrameLayout::BareToken());
+        enterFakeExitFrame(ExitFrameLayoutBareToken);
 
         // If monitorStub is non-null, handle resumeAddr appropriately.
         Label noMonitor;
         Label done;
         branchPtr(Assembler::Equal,
                   Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)),
                   ImmPtr(nullptr),
                   &noMonitor);
@@ -2360,34 +2351,18 @@ MacroAssembler::finish()
 
     MacroAssemblerSpecific::finish();
 }
 
 void
 MacroAssembler::link(JitCode* code)
 {
     MOZ_ASSERT(!oom());
-    // If this code can transition to C++ code and witness a GC, then we need to store
-    // the JitCode onto the stack in order to GC it correctly.  exitCodePatch should
-    // be unset if the code never needed to push its JitCode*.
-    if (hasEnteredExitFrame()) {
-        exitCodePatch_.fixup(this);
-        PatchDataWithValueCheck(CodeLocationLabel(code, exitCodePatch_),
-                                ImmPtr(code),
-                                ImmPtr((void*)-1));
-    }
-
-    // Fix up the code pointers to be written for locations where profilerCallSite
-    // emitted moves of RIP to a register.
-    for (size_t i = 0; i < profilerCallSites_.length(); i++) {
-        CodeOffsetLabel offset = profilerCallSites_[i];
-        offset.fixup(this);
-        CodeLocationLabel location(code, offset);
-        PatchDataWithValueCheck(location, ImmPtr(location.raw()), ImmPtr((void*)-1));
-    }
+    linkSelfReference(code);
+    linkProfilerCallSites(code);
 }
 
 void
 MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch, Label* label)
 {
     // 16-bit loads are slow and unaligned 32-bit loads may be too so
     // perform an aligned 32-bit load and adjust the bitmask accordingly.
     MOZ_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0);
@@ -2431,39 +2406,51 @@ MacroAssembler::branchEqualTypeIfNeeded(
             branchTestObject(Equal, tag, label);
             break;
           default:
             MOZ_CRASH("Unsupported type");
         }
     }
 }
 
-void
-MacroAssembler::profilerPreCallImpl()
+MacroAssembler::AutoProfilerCallInstrumentation::AutoProfilerCallInstrumentation(
+    MacroAssembler& masm
+    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    if (!masm.emitProfilingInstrumentation_)
+        return;
+
     Register reg = CallTempReg0;
     Register reg2 = CallTempReg1;
-    push(reg);
-    push(reg2);
-    profilerPreCallImpl(reg, reg2);
-    pop(reg2);
-    pop(reg);
+    masm.push(reg);
+    masm.push(reg2);
+
+    JitContext* icx = GetJitContext();
+    AbsoluteAddress profilingActivation(icx->runtime->addressOfProfilingActivation());
+
+    CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), reg);
+    masm.loadPtr(profilingActivation, reg2);
+    masm.storePtr(reg, Address(reg2, JitActivation::offsetOfLastProfilingCallSite()));
+
+    masm.appendProfilerCallSite(label);
+
+    masm.pop(reg2);
+    masm.pop(reg);
 }
 
 void
-MacroAssembler::profilerPreCallImpl(Register reg, Register reg2)
+MacroAssembler::linkProfilerCallSites(JitCode* code)
 {
-    JitContext* icx = GetJitContext();
-    AbsoluteAddress profilingActivation(icx->runtime->addressOfProfilingActivation());
-
-    CodeOffsetLabel label = movWithPatch(ImmWord(uintptr_t(-1)), reg);
-    loadPtr(profilingActivation, reg2);
-    storePtr(reg, Address(reg2, JitActivation::offsetOfLastProfilingCallSite()));
-
-    appendProfilerCallSite(label);
+    for (size_t i = 0; i < profilerCallSites_.length(); i++) {
+        CodeOffsetLabel offset = profilerCallSites_[i];
+        offset.fixup(this);
+        CodeLocationLabel location(code, offset);
+        PatchDataWithValueCheck(location, ImmPtr(location.raw()), ImmPtr((void*)-1));
+    }
 }
 
 void
 MacroAssembler::alignJitStackBasedOnNArgs(Register nargs)
 {
     if (JitStackValueAlignment == 1)
         return;
 
@@ -2541,37 +2528,37 @@ MacroAssembler::alignJitStackBasedOnNArg
         andToStackPtr(Imm32(~(JitStackAlignment - 1)));
     }
 }
 
 // ===============================================================
 
 MacroAssembler::MacroAssembler(JSContext* cx, IonScript* ion,
                                JSScript* script, jsbytecode* pc)
-  : emitProfilingInstrumentation_(false),
-    framePushed_(0)
+  : framePushed_(0),
 #ifdef DEBUG
-  , inCall_(false)
+    inCall_(false),
 #endif
+    emitProfilingInstrumentation_(false)
 {
     constructRoot(cx);
     jitContext_.emplace(cx, (js::jit::TempAllocator*)nullptr);
     alloc_.emplace(cx);
     moveResolver_.setAllocator(*jitContext_->temp);
 #if defined(JS_CODEGEN_ARM)
     initWithAllocator();
     m_buffer.id = GetJitContext()->getNextAssemblerId();
 #elif defined(JS_CODEGEN_ARM64)
     initWithAllocator();
     armbuffer_.id = GetJitContext()->getNextAssemblerId();
 #endif
     if (ion) {
         setFramePushed(ion->frameSize());
         if (pc && cx->runtime()->spsProfiler.enabled())
-            emitProfilingInstrumentation_ = true;
+            enableProfilingInstrumentation();
     }
 }
 
 void
 MacroAssembler::resetForNewCodeGenerator(TempAllocator& alloc)
 {
     setFramePushed(0);
     moveResolver_.clearTempObjectPool();
@@ -2869,16 +2856,40 @@ void
 MacroAssembler::callWithABINoProfiler(AsmJSImmPtr imm, MoveOp::Type result)
 {
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust, /* callFromAsmJS = */ true);
     call(imm);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Exit frame footer.
+
+void
+MacroAssembler::linkExitFrame()
+{
+    AbsoluteAddress jitTop(GetJitContext()->runtime->addressOfJitTop());
+    storeStackPtr(jitTop);
+}
+
+void
+MacroAssembler::linkSelfReference(JitCode* code)
+{
+    // If this code can transition to C++ code and witness a GC, then we need to store
+    // the JitCode onto the stack in order to GC it correctly.  exitCodePatch should
+    // be unset if the code never needed to push its JitCode*.
+    if (hasSelfReference()) {
+        selfReferencePatch_.fixup(this);
+        PatchDataWithValueCheck(CodeLocationLabel(code, selfReferencePatch_),
+                                ImmPtr(code),
+                                ImmPtr((void*)-1));
+    }
+}
+
 //}}} check_macroassembler_style
 
 namespace js {
 namespace jit {
 
 #ifdef DEBUG
 template <class RegisterType>
 AutoGenericRegisterScope<RegisterType>::AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg)
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -167,16 +167,19 @@
 #define IMM32_16ADJ(X) X << 16
 #else
 #define IMM32_16ADJ(X) X
 #endif
 
 namespace js {
 namespace jit {
 
+// Defined in JitFrames.h
+enum ExitFrameTokenValues;
+
 // The public entrypoint for emitting assembly. Note that a MacroAssembler can
 // use cx->lifoAlloc, so take care not to interleave masm use with other
 // lifoAlloc use if one will be destroyed before the other.
 class MacroAssembler : public MacroAssemblerSpecific
 {
     MacroAssembler* thisFromCtor() {
         return this;
     }
@@ -315,31 +318,26 @@ class MacroAssembler : public MacroAssem
         }
     };
 
     mozilla::Maybe<AutoRooter> autoRooter_;
     mozilla::Maybe<JitContext> jitContext_;
     mozilla::Maybe<AutoJitContextAlloc> alloc_;
 
   private:
-    // This field is used to manage profiling instrumentation output. If
-    // provided and enabled, then instrumentation will be emitted around call
-    // sites.
-    bool emitProfilingInstrumentation_;
-
     // Labels for handling exceptions and failures.
     NonAssertingLabel failureLabel_;
 
   public:
     MacroAssembler()
-      : emitProfilingInstrumentation_(false),
-        framePushed_(0)
+      : framePushed_(0),
 #ifdef DEBUG
-      , inCall_(false)
+        inCall_(false),
 #endif
+        emitProfilingInstrumentation_(false)
     {
         JitContext* jcx = GetJitContext();
         JSContext* cx = jcx->cx;
         if (cx)
             constructRoot(cx);
 
         if (!jcx->temp) {
             MOZ_ASSERT(cx);
@@ -360,35 +358,31 @@ class MacroAssembler : public MacroAssem
     // This constructor should only be used when there is no JitContext active
     // (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp).
     explicit MacroAssembler(JSContext* cx, IonScript* ion = nullptr,
                             JSScript* script = nullptr, jsbytecode* pc = nullptr);
 
     // asm.js compilation handles its own JitContext-pushing
     struct AsmJSToken {};
     explicit MacroAssembler(AsmJSToken)
-      : emitProfilingInstrumentation_(false),
-        framePushed_(0)
+      : framePushed_(0),
 #ifdef DEBUG
-      , inCall_(false)
+        inCall_(false),
 #endif
+        emitProfilingInstrumentation_(false)
     {
 #if defined(JS_CODEGEN_ARM)
         initWithAllocator();
         m_buffer.id = 0;
 #elif defined(JS_CODEGEN_ARM64)
         initWithAllocator();
         armbuffer_.id = 0;
 #endif
     }
 
-    void enableProfilingInstrumentation() {
-        emitProfilingInstrumentation_ = true;
-    }
-
     void resetForNewCodeGenerator(TempAllocator& alloc);
 
     void constructRoot(JSContext* cx) {
         autoRooter_.emplace(cx, this);
     }
 
     MoveResolver& moveResolver() {
         return moveResolver_;
@@ -485,16 +479,24 @@ class MacroAssembler : public MacroAssem
     void call(ImmPtr imm) PER_SHARED_ARCH;
     void call(AsmJSImmPtr imm) PER_SHARED_ARCH;
     // Call a target JitCode, which must be traceable, and may be movable.
     void call(JitCode* c) PER_SHARED_ARCH;
 
     inline void call(const CallSiteDesc& desc, const Register reg);
     inline void call(const CallSiteDesc& desc, Label* label);
 
+    // Push the return address and make a call. On platforms where this function
+    // is not defined, push the link register (pushReturnAddress) at the entry
+    // point of the callee.
+    void callAndPushReturnAddress(Register reg) DEFINED_ON(mips32, x86_shared);
+    void callAndPushReturnAddress(Label* label) DEFINED_ON(mips32, x86_shared);
+
+    void pushReturnAddress() DEFINED_ON(arm, arm64);
+
   public:
     // ===============================================================
     // ABI function calls.
 
     // Setup a call to C/C++ code, given the assumption that the framePushed
     // accruately define the state of the stack, and that the top of the stack
     // was properly aligned. Note that this only supports cdecl.
     void setupAlignedABICall(); // CRASH_ON(arm64)
@@ -558,16 +560,106 @@ class MacroAssembler : public MacroAssem
 
 #ifdef JS_SIMULATOR
     // The signature is used to accumulate all types of arguments which are used
     // by the caller. This is used by the simulators to decode the arguments
     // properly, and cast the function pointer to the right type.
     uint32_t signature_;
 #endif
 
+  public:
+    // ===============================================================
+    // Jit Frames.
+    //
+    // These functions are used to build the content of the Jit frames.  See
+    // CommonFrameLayout class, and all its derivatives. The content should be
+    // pushed in the opposite order as the fields of the structures, such that
+    // the structures can be used to interpret the content of the stack.
+
+    // Call the Jit function, and push the return address (or let the callee
+    // push the return address).
+    //
+    // These functions return the offset of the return address, in order to use
+    // the return address to index the safepoints, which are used to list all
+    // live registers.
+    inline uint32_t callJitNoProfiler(Register callee);
+    inline uint32_t callJit(Register callee);
+    inline uint32_t callJit(JitCode* code);
+
+    // The frame descriptor is the second field of all Jit frames, pushed before
+    // calling the Jit function.  It is a composite value defined in JitFrames.h
+    inline void makeFrameDescriptor(Register frameSizeReg, FrameType type);
+
+    // Push the frame descriptor, based on the statically known framePushed.
+    inline void pushStaticFrameDescriptor(FrameType type);
+
+    // This function emulates a call by pushing an exit frame on the stack,
+    // except that the fake-function is inlined within the body of the caller.
+    //
+    // This function assumes that the current frame is an IonJS frame.
+    //
+    // This function returns the offset of the /fake/ return address, in order to use
+    // the return address to index the safepoints, which are used to list all
+    // live registers.
+    //
+    // This function should be balanced with a call to adjustStack, to pop the
+    // exit frame and emulate the return statement of the inlined function.
+    inline uint32_t buildFakeExitFrame(Register scratch);
+
+  private:
+    // This function is used by buildFakeExitFrame to push a fake return address
+    // on the stack. This fake return address should never be used for resuming
+    // any execution, and can even be an invalid pointer into the instruction
+    // stream, as long as it does not alias any other.
+    uint32_t pushFakeReturnAddress(Register scratch) PER_SHARED_ARCH;
+
+  public:
+    // ===============================================================
+    // Exit frame footer.
+    //
+    // When calling outside the Jit we push an exit frame. To mark the stack
+    // correctly, we have to push additional information, called the Exit frame
+    // footer, which is used to identify how the stack is marked.
+    //
+    // See JitFrames.h, and MarkJitExitFrame in JitFrames.cpp.
+
+    // If the current piece of code might be garbage collected, then the exit
+    // frame footer must contain a pointer to the current JitCode, such that the
+    // garbage collector can keep the code alive as long this code is on the
+    // stack. This function pushes a placeholder which is replaced when the code
+    // is linked.
+    inline void PushStubCode();
+
+    // Return true if the code contains a self-reference which needs to be
+    // patched when the code is linked.
+    inline bool hasSelfReference() const;
+
+    // Push stub code and the VMFunction pointer.
+    inline void enterExitFrame(const VMFunction* f = nullptr);
+
+    // Push an exit frame token to identify which fake exit frame this footer
+    // corresponds to.
+    inline void enterFakeExitFrame(enum ExitFrameTokenValues token);
+
+    // Pop ExitFrame footer in addition to the extra frame.
+    inline void leaveExitFrame(size_t extraFrame = 0);
+
+  private:
+    // Save the top of the stack into PerThreadData::jitTop of the main thread,
+    // which should be the location of the latest exit frame.
+    void linkExitFrame();
+
+    // Patch the value of PushStubCode with the pointer to the finalized code.
+    void linkSelfReference(JitCode* code);
+
+    // If the JitCode that created this assembler needs to transition into the VM,
+    // we want to store the JitCode on the stack in order to mark it during a GC.
+    // This is a reference to a patch location where the JitCode* will be written.
+    CodeOffsetLabel selfReferencePatch_;
+
     //}}} check_macroassembler_style
   public:
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
 
@@ -1053,86 +1145,20 @@ class MacroAssembler : public MacroAssem
     void newGCString(Register result, Register temp, Label* fail);
     void newGCFatInlineString(Register result, Register temp, Label* fail);
 
     // Compares two strings for equality based on the JSOP.
     // This checks for identical pointers, atoms and length and fails for everything else.
     void compareStrings(JSOp op, Register left, Register right, Register result,
                         Label* fail);
 
-    // If the JitCode that created this assembler needs to transition into the VM,
-    // we want to store the JitCode on the stack in order to mark it during a GC.
-    // This is a reference to a patch location where the JitCode* will be written.
-  private:
-    CodeOffsetLabel exitCodePatch_;
-
-  private:
-    void linkExitFrame();
-
   public:
-    inline void PushStubCode();
-
-    // Push stub code, and the VMFunction pointer.
-    inline void enterExitFrame(const VMFunction* f = nullptr);
-
-    // The JitCode * argument here is one of the tokens defined in the various
-    // exit frame layout classes, e.g. NativeExitFrameLayout::Token().
-    inline void enterFakeExitFrame(JitCode* codeVal);
-
-    // Pop ExitFrame footer in addition to the extra frame.
-    inline void leaveExitFrame(size_t extraFrame = 0);
-
-    inline bool hasEnteredExitFrame() const;
-
     // Generates code used to complete a bailout.
     void generateBailoutTail(Register scratch, Register bailoutInfo);
 
-    // These functions exist as small wrappers around sites where execution can
-    // leave the currently running stream of instructions. They exist so that
-    // instrumentation may be put in place around them if necessary and the
-    // instrumentation is enabled. For the functions that return a uint32_t,
-    // they are returning the offset of the assembler just after the call has
-    // been made so that a safepoint can be made at that location.
-
-    // see above comment for what is returned
-    uint32_t callJit(Register callee) {
-        profilerPreCall();
-        MacroAssemblerSpecific::callJit(callee);
-        uint32_t ret = currentOffset();
-        profilerPostReturn();
-        return ret;
-    }
-
-    // see above comment for what is returned
-    uint32_t callWithExitFrame(Label* target) {
-        profilerPreCall();
-        MacroAssemblerSpecific::callWithExitFrame(target);
-        uint32_t ret = currentOffset();
-        profilerPostReturn();
-        return ret;
-    }
-
-    // see above comment for what is returned
-    uint32_t callWithExitFrame(JitCode* target) {
-        profilerPreCall();
-        MacroAssemblerSpecific::callWithExitFrame(target);
-        uint32_t ret = currentOffset();
-        profilerPostReturn();
-        return ret;
-    }
-
-    // see above comment for what is returned
-    uint32_t callWithExitFrame(JitCode* target, Register dynStack) {
-        profilerPreCall();
-        MacroAssemblerSpecific::callWithExitFrame(target, dynStack);
-        uint32_t ret = currentOffset();
-        profilerPostReturn();
-        return ret;
-    }
-
     void branchTestObjectTruthy(bool truthy, Register objReg, Register scratch,
                                 Label* slowCheck, Label* checked)
     {
         // The branches to out-of-line code here implement a conservative version
         // of the JSObject::isWrapper test performed in EmulatesUndefined.  If none
         // of the branches are taken, we can check class flags directly.
         loadObjClass(objReg, scratch);
         Address flags(scratch, Class::offsetOfFlags());
@@ -1213,32 +1239,51 @@ class MacroAssembler : public MacroAssem
         branchPtr(cond, getStackPointer(), rhs, label);
     }
     template <typename T>
     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.
-    void profilerPreCall() {
-        if (!emitProfilingInstrumentation_)
-            return;
-        profilerPreCallImpl();
+  public:
+    void enableProfilingInstrumentation() {
+        emitProfilingInstrumentation_ = true;
     }
 
-    void profilerPostReturn() {
-        if (!emitProfilingInstrumentation_)
-            return;
-        profilerPostReturnImpl();
+  private:
+    // This class is used to surround call sites throughout the assembler. This
+    // is used by callWithABI, and callJit functions, except if suffixed by
+    // NoProfiler.
+    class AutoProfilerCallInstrumentation {
+        MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+      public:
+        explicit AutoProfilerCallInstrumentation(MacroAssembler& masm
+                                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+        ~AutoProfilerCallInstrumentation() {}
+    };
+    friend class AutoProfilerCallInstrumentation;
+
+    void appendProfilerCallSite(CodeOffsetLabel label) {
+        propagateOOM(profilerCallSites_.append(label));
     }
 
+    // Fix up the code pointers to be written for locations where profilerCallSite
+    // emitted moves of RIP to a register.
+    void linkProfilerCallSites(JitCode* code);
+
+    // This field is used to manage profiling instrumentation output. If
+    // provided and enabled, then instrumentation will be emitted around call
+    // sites.
+    bool emitProfilingInstrumentation_;
+
+    // Record locations of the call sites.
+    Vector<CodeOffsetLabel, 0, SystemAllocPolicy> profilerCallSites_;
+
   public:
     void loadBaselineOrIonRaw(Register script, Register dest, Label* failure);
     void loadBaselineOrIonNoArgCheck(Register callee, Register dest, Label* failure);
 
     void loadBaselineFramePtr(Register framePtr, Register dest);
 
     void pushBaselineFramePtr(Register framePtr, Register scratch) {
         loadBaselineFramePtr(framePtr, scratch);
@@ -1540,20 +1585,16 @@ class MacroAssembler : public MacroAssem
         // Check that all remaining bits are zero.
         branchTestStackPtr(Assembler::Zero, Imm32((alignment - 1) ^ offset), &ok);
 
         bind(&bad);
         breakpoint();
         bind(&ok);
 #endif
     }
-
-    void profilerPreCallImpl();
-    void profilerPreCallImpl(Register reg, Register reg2);
-    void profilerPostReturnImpl() {}
 };
 
 static inline Assembler::DoubleCondition
 JSOpToDoubleCondition(JSOp op)
 {
     switch (op) {
       case JSOP_EQ:
       case JSOP_STRICTEQ:
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1860,107 +1860,29 @@ BufferOffset
 MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
                            int32_t offset, Condition cc)
 {
     ScratchRegisterScope scratch(asMasm());
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
     return ma_vstr(src, Address(scratch, offset), cc);
 }
 
-void
-MacroAssemblerARMCompat::buildFakeExitFrame(Register scratch, uint32_t* offset)
-{
-    DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-
-    asMasm().Push(Imm32(descriptor)); // descriptor_
-
-    enterNoPool(2);
-    DebugOnly<uint32_t> offsetBeforePush = currentOffset();
-    asMasm().Push(pc); // actually pushes $pc + 8.
-
-    // Consume an additional 4 bytes. The start of the next instruction will
-    // then be 8 bytes after the instruction for Push(pc); this offset can
-    // therefore be fed to the safepoint.
-    ma_nop();
-    uint32_t pseudoReturnOffset = currentOffset();
-    leaveNoPool();
-
-    MOZ_ASSERT(asMasm().framePushed() == initialDepth + ExitFrameLayout::Size());
-    MOZ_ASSERT(pseudoReturnOffset - offsetBeforePush == 8);
-
-    *offset = pseudoReturnOffset;
-}
-
 bool
 MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
 {
     DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
     uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
 
     asMasm().Push(Imm32(descriptor)); // descriptor_
     asMasm().Push(ImmPtr(fakeReturnAddr));
 
     return true;
 }
 
 void
-MacroAssemblerARMCompat::callWithExitFrame(Label* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor)); // descriptor
-
-    ma_callJitHalfPush(target);
-}
-
-void
-MacroAssemblerARMCompat::callWithExitFrame(JitCode* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor)); // descriptor
-
-    addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
-    RelocStyle rs;
-    if (HasMOVWT())
-        rs = L_MOVWT;
-    else
-        rs = L_LDR;
-
-    ScratchRegisterScope scratch(asMasm());
-    ma_movPatchable(ImmPtr(target->raw()), scratch, Always, rs);
-    ma_callJitHalfPush(scratch);
-}
-
-void
-MacroAssemblerARMCompat::callWithExitFrame(JitCode* target, Register dynStack)
-{
-    ma_add(Imm32(asMasm().framePushed()), dynStack);
-    makeFrameDescriptor(dynStack, JitFrame_IonJS);
-    asMasm().Push(dynStack); // descriptor
-
-    addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
-    RelocStyle rs;
-    if (HasMOVWT())
-        rs = L_MOVWT;
-    else
-        rs = L_LDR;
-
-    ScratchRegisterScope scratch(asMasm());
-    ma_movPatchable(ImmPtr(target->raw()), scratch, Always, rs);
-    ma_callJitHalfPush(scratch);
-}
-
-void
-MacroAssemblerARMCompat::callJit(Register callee)
-{
-    MOZ_ASSERT((asMasm().framePushed() & 7) == 4);
-    ma_callJitHalfPush(callee);
-}
-
-void
 MacroAssembler::alignFrameForICArguments(AfterICSaveLive& aic)
 {
     // Exists for MIPS compatibility.
 }
 
 void
 MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive& aic)
 {
@@ -3803,46 +3725,16 @@ MacroAssemblerARMCompat::storeTypeTag(Im
     ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
 
     ScratchRegisterScope scratch(asMasm());
     ma_mov(tag, scratch);
     ma_str(scratch, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
     ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
 }
 
-// ARM says that all reads of pc will return 8 higher than the address of the
-// currently executing instruction. This means we are correctly storing the
-// address of the instruction after the call in the register.
-//
-// Also ION is breaking the ARM EABI here (sort of). The ARM EABI says that a
-// function call should move the pc into the link register, then branch to the
-// function, and *sp is data that is owned by the caller, not the callee. The
-// ION ABI says *sp should be the address that we will return to when leaving
-// this function.
-void
-MacroAssemblerARM::ma_callJitHalfPush(const Register r)
-{
-    // The stack is unaligned by 4 bytes. We push the pc to the stack to align
-    // the stack before the call, when we return the pc is poped and the stack
-    // is restored to its unaligned state.
-    as_blx(r);
-}
-
-void
-MacroAssemblerARM::ma_callJitHalfPush(Label* label)
-{
-    // The stack is unaligned by 4 bytes. The callee will push the lr to the stack to align
-    // the stack after the call, when we return the pc is poped and the stack
-    // is restored to its unaligned state.
-
-    // leave the stack as-is so the callee-side can push when necessary.
-
-    as_bl(label, Always);
-}
-
 void
 MacroAssemblerARM::ma_call(ImmPtr dest)
 {
     RelocStyle rs;
     if (HasMOVWT())
         rs = L_MOVWT;
     else
         rs = L_LDR;
@@ -4959,24 +4851,16 @@ MacroAssemblerARMCompat::profilerEnterFr
 }
 
 void
 MacroAssemblerARMCompat::profilerExitFrame()
 {
     branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
 }
 
-void
-MacroAssemblerARMCompat::callAndPushReturnAddress(Label* label)
-{
-    AutoForbidPools afp(this, 2);
-    ma_push(pc);
-    asMasm().call(label);
-}
-
 MacroAssembler&
 MacroAssemblerARM::asMasm()
 {
     return *static_cast<MacroAssembler*>(this);
 }
 
 const MacroAssembler&
 MacroAssemblerARM::asMasm() const
@@ -5181,19 +5065,24 @@ MacroAssembler::call(JitCode* c)
     RelocStyle rs;
     if (HasMOVWT())
         rs = L_MOVWT;
     else
         rs = L_LDR;
 
     ScratchRegisterScope scratch(*this);
     ma_movPatchable(ImmPtr(c->raw()), scratch, Always, rs);
-    ma_callJitHalfPush(scratch);
-}
-
+    callJitNoProfiler(scratch);
+}
+
+void
+MacroAssembler::pushReturnAddress()
+{
+    push(lr);
+}
 
 // ===============================================================
 // ABI function calls.
 
 void
 MacroAssembler::setupUnalignedABICall(Register scratch)
 {
     setupABICall();
@@ -5302,9 +5191,33 @@ MacroAssembler::callWithABINoProfiler(co
     // IntArg registers clobbered before the call.
     ma_ldr(fun, r12);
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(r12);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Jit Frames.
+
+uint32_t
+MacroAssembler::pushFakeReturnAddress(Register scratch)
+{
+    // On ARM any references to the pc, adds an additional 8 to it, which
+    // correspond to 2 instructions of 4 bytes.  Thus we use an additional nop
+    // to pad until we reach the pushed pc.
+    //
+    // Note: In practice this should not be necessary, as this fake return
+    // address is never used for resuming any execution. Thus theoriticaly we
+    // could just do a Push(pc), and ignore the nop as well as the pool.
+    enterNoPool(2);
+    DebugOnly<uint32_t> offsetBeforePush = currentOffset();
+    Push(pc); // actually pushes $pc + 8.
+    ma_nop();
+    uint32_t pseudoReturnOffset = currentOffset();
+    leaveNoPool();
+
+    MOZ_ASSERT(pseudoReturnOffset - offsetBeforePush == 8);
+    return pseudoReturnOffset;
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -424,23 +424,16 @@ class MacroAssemblerARM : public Assembl
     BufferOffset ma_vldr(VFPRegister src, Register base, Register index, int32_t shift = defaultShift, Condition cc = Always);
 
     BufferOffset ma_vstr(VFPRegister src, VFPAddr addr, Condition cc = Always);
     BufferOffset ma_vstr(VFPRegister src, const Address& addr, Condition cc = Always);
 
     BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
                          int32_t offset, Condition cc = Always);
 
-    // Calls an ion function, assuming that the stack is currently not 8 byte
-    // aligned.
-    void ma_callJitHalfPush(const Register reg);
-    // Calls an ion function, assuming that the stack is currently not 8 byte
-    // aligned.
-    void ma_callJitHalfPush(Label* label);
-
     void ma_call(ImmPtr dest);
 
     // Float registers can only be loaded/stored in continuous runs when using
     // vstm/vldm. This function breaks set into continuous runs and loads/stores
     // them at [rm]. rm will be modified and left in a state logically suitable
     // for the next load/store. Returns the offset from [dm] for the logical
     // next load/store.
     int32_t transferMultipleByRuns(FloatRegisterSet set, LoadStore ls,
@@ -527,18 +520,16 @@ class MacroAssemblerARMCompat : public M
     }
     void mov(Register src, Address dest) {
         MOZ_CRASH("NYI-IC");
     }
     void mov(Address src, Register dest) {
         MOZ_CRASH("NYI-IC");
     }
 
-    void callAndPushReturnAddress(Label* label);
-
     void branch(JitCode* c) {
         BufferOffset bo = m_buffer.nextOffset();
         addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
         RelocStyle rs;
         if (HasMOVWT())
             rs = L_MOVWT;
         else
             rs = L_LDR;
@@ -1183,40 +1174,22 @@ class MacroAssemblerARMCompat : public M
 
     void storePayload(const Value& val, const Address& dest);
     void storePayload(Register src, const Address& dest);
     void storePayload(const Value& val, const BaseIndex& dest);
     void storePayload(Register src, const BaseIndex& dest);
     void storeTypeTag(ImmTag tag, const Address& dest);
     void storeTypeTag(ImmTag tag, const BaseIndex& dest);
 
-    void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
-        ma_lsl(Imm32(FRAMESIZE_SHIFT), frameSizeReg, frameSizeReg);
-        ma_orr(Imm32(type), frameSizeReg);
-    }
-
     void handleFailureWithHandlerTail(void* handler);
 
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
   public:
-    // Builds an exit frame on the stack, with a return address to an internal
-    // non-function. Returns offset to be passed to markSafepointAt().
-    void buildFakeExitFrame(Register scratch, uint32_t* offset);
-
-    void callWithExitFrame(Label* target);
-    void callWithExitFrame(JitCode* target);
-    void callWithExitFrame(JitCode* target, Register dynStack);
-
-    // Makes a call using the only two methods that it is sane for
-    // independent code to make a call.
-    void callJit(Register callee);
-    void callJitFromAsmJS(Register callee) { as_blx(callee); }
-
     void add32(Register src, Register dest);
     void add32(Imm32 imm, Register dest);
     void add32(Imm32 imm, const Address& dest);
     void sub32(Imm32 imm, Register dest);
     void sub32(Register src, Register dest);
     template <typename T>
     void branchAdd32(Condition cond, T src, Register dest, Label* label) {
         add32(src, dest);
@@ -1813,20 +1786,16 @@ class MacroAssemblerARMCompat : public M
     void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
 
     void loadAsmJSActivation(Register dest) {
         loadPtr(Address(GlobalReg, AsmJSActivationGlobalDataOffset - AsmJSGlobalRegBias), dest);
     }
     void loadAsmJSHeapRegisterFromGlobalData() {
         loadPtr(Address(GlobalReg, AsmJSHeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg);
     }
-    void pushReturnAddress() {
-        push(lr);
-    }
-
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
 };
 
 typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
 
 } // namespace jit
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -282,17 +282,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.ma_sub(sp, scratch, sp);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
         masm.push(scratch);
         masm.push(Imm32(0)); // Fake return address.
         // No GC things to mark on the stack, push a bare token.
-        masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
+        masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
 
         masm.push(framePtr); // BaselineFrame
         masm.push(r0); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(r11); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
@@ -343,17 +343,17 @@ JitRuntime::generateEnterJIT(JSContext* 
     // transfer did before making the call.
     masm.addPtr(Imm32(sizeof(uintptr_t)), sp);
 
     // The callee will push the return address on the stack, thus we check that
     // the stack would be aligned once the call is complete.
     masm.assertStackAlignment(JitStackAlignment, sizeof(uintptr_t));
 
     // Call the function.
-    masm.ma_callJitHalfPush(r0);
+    masm.callJitNoProfiler(r0);
 
     if (type == EnterJitBaseline) {
         // Baseline OSR will return here.
         masm.bind(&returnLabel);
     }
 
     // The top of the stack now points to the address of the field following the
     // return address because the return address is popped for the return, so we
@@ -535,19 +535,17 @@ JitRuntime::generateArgumentsRectifier(J
     masm.ma_push(r1); // callee token
     masm.ma_push(r6); // frame descriptor.
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(CalleeTokenMask), r1);
     masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3);
     masm.loadBaselineOrIonRaw(r3, r3, nullptr);
-    masm.ma_callJitHalfPush(r3);
-
-    uint32_t returnOffset = masm.currentOffset();
+    uint32_t returnOffset = masm.callJitNoProfiler(r3);
 
     // arg1
     //  ...
     // argN
     // num actual args
     // callee token
     // sizeDescriptor     <- sp now
     // return address
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -32,62 +32,16 @@ MacroAssembler::clampDoubleToUint8(Float
         Csel(dest, dest, scratch32, LessThan);
     }
 
     Cmp(dest, Operand(0));
     Csel(dest, dest, wzr, GreaterThan);
 }
 
 void
-MacroAssemblerCompat::buildFakeExitFrame(Register scratch, uint32_t* offset)
-{
-    mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
-    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-
-    asMasm().Push(Imm32(descriptor)); // descriptor_
-
-    enterNoPool(3);
-    Label fakeCallsite;
-    Adr(ARMRegister(scratch, 64), &fakeCallsite);
-    asMasm().Push(scratch);
-    bind(&fakeCallsite);
-    uint32_t pseudoReturnOffset = currentOffset();
-    leaveNoPool();
-
-    MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
-
-    *offset = pseudoReturnOffset;
-}
-
-void
-MacroAssemblerCompat::callWithExitFrame(Label* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor)); // descriptor
-    asMasm().call(target);
-}
-
-void
-MacroAssemblerCompat::callWithExitFrame(JitCode* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor));
-    asMasm().call(target);
-}
-
-void
-MacroAssemblerCompat::callWithExitFrame(JitCode* target, Register dynStack)
-{
-    add32(Imm32(framePushed()), dynStack);
-    makeFrameDescriptor(dynStack, JitFrame_IonJS);
-    Push(dynStack); // descriptor
-    asMasm().call(target);
-}
-
-void
 MacroAssembler::alignFrameForICArguments(MacroAssembler::AfterICSaveLive& aic)
 {
     // Exists for MIPS compatibility.
 }
 
 void
 MacroAssembler::restoreFrameAlignmentForICArguments(MacroAssembler::AfterICSaveLive& aic)
 {
@@ -283,35 +237,16 @@ MacroAssemblerCompat::branchValueIsNurse
 
     movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), temp);
     addPtr(value.valueReg(), temp);
     branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
               temp, ImmWord(nursery.nurserySize()), label);
 }
 
 void
-MacroAssemblerCompat::callAndPushReturnAddress(Label* label)
-{
-    // FIXME: Jandem said he would refactor the code to avoid making
-    // this instruction required, but probably forgot about it.
-    // Instead of implementing this function, we should make it unnecessary.
-    Label ret;
-    {
-        vixl::UseScratchRegisterScope temps(this);
-        const ARMRegister scratch64 = temps.AcquireX();
-
-        Adr(scratch64, &ret);
-        asMasm().Push(scratch64.asUnsized());
-    }
-
-    Bl(label);
-    bind(&ret);
-}
-
-void
 MacroAssemblerCompat::breakpoint()
 {
     static int code = 0xA77;
     Brk((code++) & 0xffff);
 }
 
 //{{{ check_macroassembler_style
 // ===============================================================
@@ -510,16 +445,22 @@ MacroAssembler::call(JitCode* c)
     vixl::UseScratchRegisterScope temps(this);
     const ARMRegister scratch64 = temps.AcquireX();
     syncStackPtr();
     BufferOffset off = immPool64(scratch64, uint64_t(c->raw()));
     addPendingJump(off, ImmPtr(c->raw()), Relocation::JITCODE);
     blr(scratch64);
 }
 
+void
+MacroAssembler::pushReturnAddress()
+{
+    push(lr);
+}
+
 // ===============================================================
 // ABI function calls.
 
 void
 MacroAssembler::setupUnalignedABICall(Register scratch)
 {
     setupABICall();
     dynamicAlignment_ = true;
@@ -620,12 +561,30 @@ MacroAssembler::callWithABINoProfiler(co
     loadPtr(fun, scratch);
 
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(scratch);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Jit Frames.
+
+uint32_t
+MacroAssembler::pushFakeReturnAddress(Register scratch)
+{
+    enterNoPool(3);
+    Label fakeCallsite;
+
+    Adr(ARMRegister(scratch, 64), &fakeCallsite);
+    Push(scratch);
+    bind(&fakeCallsite);
+    uint32_t pseudoReturnOffset = currentOffset();
+
+    leaveNoPool();
+    return = pseudoReturnOffset;
+}
+
 //}}} check_macroassembler_style
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -202,19 +202,16 @@ class MacroAssemblerCompat : public vixl
     void pop(Register r0, Register r1, Register r2, Register r3) {
         vixl::MacroAssembler::Pop(ARMRegister(r0, 64), ARMRegister(r1, 64),
                                   ARMRegister(r2, 64), ARMRegister(r3, 64));
     }
     void pop(ARMFPRegister r0, ARMFPRegister r1, ARMFPRegister r2, ARMFPRegister r3) {
         vixl::MacroAssembler::Pop(r0, r1, r2, r3);
     }
 
-    void pushReturnAddress() {
-        push(lr);
-    }
     void pop(const ValueOperand& v) {
         pop(v.valueReg());
     }
     void pop(const FloatRegister& f) {
         vixl::MacroAssembler::Pop(ARMRegister(f.code(), 64));
     }
 
     void implicitPop(uint32_t args) {
@@ -2635,22 +2632,16 @@ class MacroAssemblerCompat : public vixl
 
   public:
     CodeOffsetLabel labelForPatch() {
         return CodeOffsetLabel(nextOffset().getOffset());
     }
 
     void handleFailureWithHandlerTail(void* handler);
 
-    // FIXME: This is the same on all platforms. Can be common code?
-    void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
-        lshiftPtr(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
-        orPtr(Imm32(type), frameSizeReg);
-    }
-
     // FIXME: See CodeGeneratorX64 calls to noteAsmJSGlobalAccess.
     void patchAsmJSGlobalAccess(CodeOffsetLabel patchAt, uint8_t* code,
                                 uint8_t* globalData, unsigned globalDataOffset)
     {
         MOZ_CRASH("patchAsmJSGlobalAccess");
     }
 
     void memIntToValue(const Address& src, const Address& dest) {
@@ -2660,44 +2651,24 @@ class MacroAssemblerCompat : public vixl
         MOZ_ASSERT(scratch != dest.base);
         load32(src, scratch);
         storeValue(JSVAL_TYPE_INT32, scratch, dest);
     }
 
     void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
     void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
 
-    // Builds an exit frame on the stack, with a return address to an internal
-    // non-function. Returns offset to be passed to markSafepointAt().
-    void buildFakeExitFrame(Register scratch, uint32_t* offset);
-
-    void callWithExitFrame(Label* target);
-    void callWithExitFrame(JitCode* target);
-    void callWithExitFrame(JitCode* target, Register dynStack);
-
-    void callJit(Register callee) {
-        // AArch64 cannot read from the PC, so pushing must be handled callee-side.
-        syncStackPtr();
-        Blr(ARMRegister(callee, 64));
-    }
-
     void appendCallSite(const CallSiteDesc& desc) {
         MOZ_CRASH("appendCallSite");
     }
 
     void callExit(AsmJSImmPtr imm, uint32_t stackArgBytes) {
         MOZ_CRASH("callExit");
     }
 
-    void callJitFromAsmJS(Register reg) {
-        Blr(ARMRegister(reg, 64));
-    }
-
-    void callAndPushReturnAddress(Label* label);
-
     void profilerEnterFrame(Register framePtr, Register scratch) {
         AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
         loadPtr(activation, scratch);
         storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
         storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
     }
     void profilerExitFrame() {
         branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -184,17 +184,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.Lsl(w19, ARMRegister(reg_osrNStack, 32), 3); // w19 = num_stack_values * sizeof(Value).
         masm.subFromStackPtr(r19);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), r19);
         masm.makeFrameDescriptor(r19, JitFrame_BaselineJS);
         masm.asVIXL().Push(x19, xzr); // Push xzr for a fake return address.
         // No GC things to mark: push a bare token.
-        masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
+        masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
 
         masm.push(BaselineFrameReg, reg_code);
 
         // Initialize the frame, including filling in the slots.
         masm.setupUnalignedABICall(r19);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame.
         masm.passABIArg(reg_osrFrame); // InterpreterFrame.
         masm.passABIArg(reg_osrNStack);
@@ -220,17 +220,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.B(&osrReturnPoint);
 
         masm.bind(&notOsr);
         masm.movePtr(reg_scope, R1_);
     }
 
     // Call function.
     // Since AArch64 doesn't have the pc register available, the callee must push lr.
-    masm.call(reg_code);
+    masm.callJitNoProfiler(reg_code);
 
     // Baseline OSR will return here.
     if (type == EnterJitBaseline)
         masm.bind(&osrReturnPoint);
 
     // Return back to SP.
     masm.Pop(r19);
     masm.Add(masm.GetStackPointer64(), masm.GetStackPointer64(),
@@ -400,18 +400,17 @@ JitRuntime::generateArgumentsRectifier(J
 
     masm.push(r0,  // Number of actual arguments.
               r1,  // Callee token.
               r6); // Frame descriptor.
 
     // Load the address of the code that is getting called.
     masm.Ldr(x3, MemOperand(x5, JSFunction::offsetOfNativeOrScript()));
     masm.loadBaselineOrIonRaw(r3, r3, nullptr);
-    masm.call(r3);
-    uint32_t returnOffset = masm.currentOffset();
+    uint32_t returnOffset = masm.callJitNoProfiler(r3);
 
     // Clean up!
     // Get the size of the stack frame, and clean up the later fixed frame.
     masm.Ldr(x4, MemOperand(masm.GetStackPointer64(), 24, vixl::PostIndex));
 
     // Now that the size of the stack frame sans the fixed frame has been loaded,
     // add that onto the stack pointer.
     masm.Add(masm.GetStackPointer64(), masm.GetStackPointer64(),
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -1493,86 +1493,28 @@ void
 MacroAssemblerMIPS::ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label* label,
                             DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc)
 {
     FloatTestKind testKind;
     compareFloatingPoint(DoubleFloat, lhs, rhs, c, &testKind, fcc);
     branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
 }
 
-void
-MacroAssemblerMIPSCompat::buildFakeExitFrame(Register scratch, uint32_t* offset)
-{
-    mozilla::DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
-
-    CodeLabel cl;
-    ma_li(scratch, cl.dest());
-
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor));
-    asMasm().Push(scratch);
-
-    bind(cl.src());
-    *offset = currentOffset();
-
-    MOZ_ASSERT(asMasm().framePushed() == initialDepth + ExitFrameLayout::Size());
-    addCodeLabel(cl);
-}
-
 bool
 MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
 {
     uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
 
     asMasm().Push(Imm32(descriptor)); // descriptor_
     asMasm().Push(ImmPtr(fakeReturnAddr));
 
     return true;
 }
 
 void
-MacroAssemblerMIPSCompat::callWithExitFrame(Label* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor)); // descriptor
-
-    ma_callJitHalfPush(target);
-}
-
-void
-MacroAssemblerMIPSCompat::callWithExitFrame(JitCode* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor)); // descriptor
-
-    addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
-    ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
-    ma_callJitHalfPush(ScratchRegister);
-}
-
-void
-MacroAssemblerMIPSCompat::callWithExitFrame(JitCode* target, Register dynStack)
-{
-    ma_addu(dynStack, dynStack, Imm32(asMasm().framePushed()));
-    makeFrameDescriptor(dynStack, JitFrame_IonJS);
-    asMasm().Push(dynStack); // descriptor
-
-    addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
-    ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
-    ma_callJitHalfPush(ScratchRegister);
-}
-
-void
-MacroAssemblerMIPSCompat::callJit(Register callee)
-{
-    MOZ_ASSERT((asMasm().framePushed() & 7) == 4);
-    ma_callJitHalfPush(callee);
-}
-
-void
 MacroAssemblerMIPSCompat::add32(Register src, Register dest)
 {
     as_addu(dest, dest, src);
 }
 
 void
 MacroAssemblerMIPSCompat::add32(Imm32 imm, Register dest)
 {
@@ -3058,38 +3000,16 @@ MacroAssemblerMIPSCompat::storeTypeTag(I
 {
     MOZ_ASSERT(dest.offset == 0);
 
     computeScaledAddress(dest, SecondScratchReg);
     ma_li(ScratchRegister, tag);
     as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET);
 }
 
-// This macrosintruction calls the ion code and pushes the return address to
-// the stack in the case when stack is not alligned.
-void
-MacroAssemblerMIPS::ma_callJitHalfPush(const Register r)
-{
-    // This is a MIPS hack to push return address during jalr delay slot.
-    as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
-    as_jalr(r);
-    as_sw(ra, StackPointer, 0);
-}
-
-// This macrosintruction calls the ion code and pushes the return address to
-// the stack in the case when stack is not alligned.
-void
-MacroAssemblerMIPS::ma_callJitHalfPush(Label* label)
-{
-    // This is a MIPS hack to push return address during jalr delay slot.
-    as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
-    ma_bal(label, DontFillDelaySlot);
-    as_sw(ra, StackPointer, 0);
-}
-
 void
 MacroAssemblerMIPS::ma_call(ImmPtr dest)
 {
     ma_liPatchable(CallReg, dest);
     as_jalr(CallReg);
     as_nop();
 }
 
@@ -3513,17 +3433,35 @@ MacroAssembler::call(ImmPtr target)
 }
 
 void
 MacroAssembler::call(JitCode* c)
 {
     BufferOffset bo = m_buffer.nextOffset();
     addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
     ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
-    ma_callJitHalfPush(ScratchRegister);
+    callJitNoProfiler(ScratchRegister);
+}
+
+void
+MacroAssembler::callAndPushReturnAddress(Register callee)
+{
+    // Push return address during jalr delay slot.
+    as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
+    as_jalr(callee);
+    as_sw(ra, StackPointer, 0);
+}
+
+void
+MacroAssembler::callAndPushReturnAddress(Label* label)
+{
+    // Push return address during jalr delay slot.
+    as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
+    as_jalr(label);
+    as_sw(ra, StackPointer, 0);
 }
 
 // ===============================================================
 // ABI function calls.
 
 void
 MacroAssembler::setupUnalignedABICall(Register scratch)
 {
@@ -3617,9 +3555,26 @@ MacroAssembler::callWithABINoProfiler(co
     // Load the callee in t9, as above.
     ma_lw(t9, Address(fun.base, fun.offset));
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(t9);
     callWithABIPost(stackAdjust, result);
 }
 
+// ===============================================================
+// Jit Frames.
+
+uint32_t
+MacroAssembler::pushFakeReturnAddress(Register scratch)
+{
+    CodeLabel cl;
+
+    ma_li(scratch, cl.dest());
+    Push(scratch);
+    bind(cl.src());
+    uint32_t retAddr = currentOffset();
+
+    addCodeLabel(cl);
+    return retAddr;
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -310,20 +310,16 @@ class MacroAssemblerMIPS : public Assemb
     void branchWithCode(InstImm code, Label* label, JumpKind jumpKind);
     Condition ma_cmp(Register rd, Register lhs, Register rhs, Condition c);
 
     void compareFloatingPoint(FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
                               DoubleCondition c, FloatTestKind* testKind,
                               FPConditionBit fcc = FCC0);
 
   public:
-    // calls an ion function, assuming that the stack is currently not 8 byte aligned
-    void ma_callJitHalfPush(const Register reg);
-    void ma_callJitHalfPush(Label* label);
-
     void ma_call(ImmPtr dest);
 
     void ma_jump(ImmPtr dest);
 
     void ma_cmp_set(Register dst, Register lhs, Register rhs, Condition c);
     void ma_cmp_set(Register dst, Register lhs, Imm32 imm, Condition c);
     void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c) {
         ma_cmp_set(dst, lhs, Imm32(uint32_t(imm.value)), c);
@@ -370,20 +366,16 @@ class MacroAssemblerMIPSCompat : public 
     }
     void mov(Register src, Address dest) {
         MOZ_CRASH("NYI-IC");
     }
     void mov(Address src, Register dest) {
         MOZ_CRASH("NYI-IC");
     }
 
-    void callAndPushReturnAddress(Label* label) {
-        ma_callJitHalfPush(label);
-    }
-
     void branch(JitCode* c) {
         BufferOffset bo = m_buffer.nextOffset();
         addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
         ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
         as_jr(ScratchRegister);
         as_nop();
     }
     void branch(const Register reg) {
@@ -885,21 +877,16 @@ public:
 
     void storePayload(const Value& val, Address dest);
     void storePayload(Register src, Address dest);
     void storePayload(const Value& val, const BaseIndex& dest);
     void storePayload(Register src, const BaseIndex& dest);
     void storeTypeTag(ImmTag tag, Address dest);
     void storeTypeTag(ImmTag tag, const BaseIndex& dest);
 
-    void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
-        ma_sll(frameSizeReg, frameSizeReg, Imm32(FRAMESIZE_SHIFT));
-        ma_or(frameSizeReg, frameSizeReg, Imm32(type));
-    }
-
     void handleFailureWithHandlerTail(void* handler);
 
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
   public:
     // The following functions are exposed for use in platform-shared code.
 
@@ -1112,29 +1099,16 @@ public:
     void atomicXor16(const T& value, const S& mem) {
         MOZ_CRASH("NYI");
     }
     template <typename T, typename S>
     void atomicXor32(const T& value, const S& mem) {
         MOZ_CRASH("NYI");
     }
 
-    // Builds an exit frame on the stack, with a return address to an internal
-    // non-function. Returns offset to be passed to markSafepointAt().
-    void buildFakeExitFrame(Register scratch, uint32_t* offset);
-
-    void callWithExitFrame(Label* target);
-    void callWithExitFrame(JitCode* target);
-    void callWithExitFrame(JitCode* target, Register dynStack);
-
-    // Makes a call using the only two methods that it is sane for indep code
-    // to make a call.
-    void callJit(Register callee);
-    void callJitFromAsmJS(Register callee) { callJit(callee); }
-
     void add32(Register src, Register dest);
     void add32(Imm32 imm, Register dest);
     void add32(Imm32 imm, const Address& dest);
     void sub32(Imm32 imm, Register dest);
     void sub32(Register src, Register dest);
 
     void incrementInt32Value(const Address& addr) {
         add32(Imm32(1), ToPayload(addr));
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -236,17 +236,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
 
         // Push frame descriptor and fake return address.
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); // Frame descriptor
         masm.storePtr(zero, Address(StackPointer, 0)); // fake return address
 
         // No GC things to mark, push a bare token.
-        masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
+        masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
 
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
         masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
@@ -298,17 +298,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.loadPtr(slotScopeChain, R1.scratchReg());
     }
 
     // The call will push the return address on the stack, thus we check that
     // the stack would be aligned once the call is complete.
     masm.assertStackAlignment(JitStackAlignment, sizeof(uintptr_t));
 
     // Call the function with pushing return address to stack.
-    masm.ma_callJitHalfPush(reg_code);
+    masm.callJitNoProfiler(reg_code);
 
     if (type == EnterJitBaseline) {
         // Baseline OSR will return here.
         masm.bind(returnLabel.src());
         masm.addCodeLabel(returnLabel);
     }
 
     // Pop arguments off the stack.
@@ -492,19 +492,17 @@ JitRuntime::generateArgumentsRectifier(J
     // Push frame descriptor.
     masm.storePtr(t0, Address(StackPointer, 0));
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(CalleeTokenMask), calleeTokenReg);
     masm.loadPtr(Address(calleeTokenReg, JSFunction::offsetOfNativeOrScript()), t1);
     masm.loadBaselineOrIonRaw(t1, t1, nullptr);
-    masm.ma_callJitHalfPush(t1);
-
-    uint32_t returnOffset = masm.currentOffset();
+    uint32_t returnOffset = masm.callJitNoProfiler(t1);
 
     // arg1
     //  ...
     // argN
     // num actual args
     // callee token
     // sizeDescriptor     <- sp now
     // return address
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -188,27 +188,16 @@ class MacroAssemblerNone : public Assemb
     void haltingAlign(size_t) { MOZ_CRASH(); }
     void nopAlign(size_t) { MOZ_CRASH(); }
     void checkStackAlignment() { MOZ_CRASH(); }
     uint32_t currentOffset() { MOZ_CRASH(); }
     uint32_t actualOffset(uint32_t) { MOZ_CRASH(); }
     uint32_t labelOffsetToPatchOffset(uint32_t) { MOZ_CRASH(); }
     CodeOffsetLabel labelForPatch() { MOZ_CRASH(); }
 
-    template <typename T> void call(T) { MOZ_CRASH(); }
-    template <typename T, typename S> void call(T, S) { MOZ_CRASH(); }
-    void callAndPushReturnAddress(Label* label) { MOZ_CRASH(); }
-
-    void callWithExitFrame(Label*) { MOZ_CRASH(); }
-    void callWithExitFrame(JitCode*) { MOZ_CRASH(); }
-    void callWithExitFrame(JitCode*, Register) { MOZ_CRASH(); }
-
-    void callJit(Register callee) { MOZ_CRASH(); }
-    void callJitFromAsmJS(Register callee) { MOZ_CRASH(); }
-
     void nop() { MOZ_CRASH(); }
     void breakpoint() { MOZ_CRASH(); }
     void abiret() { MOZ_CRASH(); }
     void ret() { MOZ_CRASH(); }
 
     CodeOffsetLabel toggledJump(Label*) { MOZ_CRASH(); }
     CodeOffsetLabel toggledCall(JitCode*, bool) { MOZ_CRASH(); }
     static size_t ToggledCallSize(uint8_t*) { MOZ_CRASH(); }
@@ -444,17 +433,16 @@ class MacroAssemblerNone : public Assemb
     template <typename T, typename S> void and32(T, S) { MOZ_CRASH(); }
     template <typename T> void not32(T) { MOZ_CRASH(); }
     void convertUInt32ToDouble(Register, FloatRegister) { MOZ_CRASH(); }
     void convertUInt32ToFloat32(Register, FloatRegister) { MOZ_CRASH(); }
     void inc64(AbsoluteAddress) { MOZ_CRASH(); }
     void incrementInt32Value(Address) { MOZ_CRASH(); }
     void ensureDouble(ValueOperand, FloatRegister, Label*) { MOZ_CRASH(); }
     void handleFailureWithHandlerTail(void*) { MOZ_CRASH(); }
-    void makeFrameDescriptor(Register, FrameType) { MOZ_CRASH(); }
 
     void branchPtrInNurseryRange(Condition, Register, Register, Label*) { MOZ_CRASH(); }
     void branchValueIsNurseryObject(Condition, ValueOperand, Register, Label*) { MOZ_CRASH(); }
 
     void buildFakeExitFrame(Register, uint32_t*) { MOZ_CRASH(); }
     bool buildOOLFakeExitFrame(void*) { MOZ_CRASH(); }
     void loadAsmJSActivation(Register) { MOZ_CRASH(); }
     void loadAsmJSHeapRegisterFromGlobalData() { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -909,17 +909,16 @@ struct AsmJSAbsoluteLink
 class AssemblerShared
 {
     Vector<CallSite, 0, SystemAllocPolicy> callsites_;
     Vector<AsmJSHeapAccess, 0, SystemAllocPolicy> asmJSHeapAccesses_;
     Vector<AsmJSGlobalAccess, 0, SystemAllocPolicy> asmJSGlobalAccesses_;
     Vector<AsmJSAbsoluteLink, 0, SystemAllocPolicy> asmJSAbsoluteLinks_;
 
   protected:
-    Vector<CodeOffsetLabel, 0, SystemAllocPolicy> profilerCallSites_;
     bool enoughMemory_;
     bool embedsNurseryPointers_;
 
   public:
     AssemblerShared()
      : enoughMemory_(true),
        embedsNurseryPointers_(false)
     {}
@@ -931,20 +930,16 @@ class AssemblerShared
     void setOOM() {
         enoughMemory_ = false;
     }
 
     bool oom() const {
         return !enoughMemory_;
     }
 
-    void appendProfilerCallSite(CodeOffsetLabel label) {
-        enoughMemory_ &= profilerCallSites_.append(label);
-    }
-
     bool embedsNurseryPointers() const {
         return embedsNurseryPointers_;
     }
 
     void append(const CallSiteDesc& desc, size_t currentOffset, size_t framePushed) {
         // framePushed does not include sizeof(AsmJSFrame), so add it in here (see
         // CallSite::stackDepth).
         CallSite callsite(desc, currentOffset, framePushed + sizeof(AsmJSFrame));
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1345,26 +1345,32 @@ CodeGeneratorShared::callVM(const VMFunc
         return;
     }
 
 #ifdef CHECK_OSIPOINT_REGISTERS
     if (shouldVerifyOsiPointRegs(ins->safepoint()))
         StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
 #endif
 
+    // Push an exit frame descriptor. If |dynStack| is a valid pointer to a
+    // register, then its value is added to the value of the |framePushed()| to
+    // fill the frame descriptor.
+    if (dynStack) {
+        masm.addPtr(Imm32(masm.framePushed()), *dynStack);
+        masm.makeFrameDescriptor(*dynStack, JitFrame_IonJS);
+        masm.Push(*dynStack); // descriptor
+    } else {
+        masm.pushStaticFrameDescriptor(JitFrame_IonJS);
+    }
+
     // Call the wrapper function.  The wrapper is in charge to unwind the stack
     // when returning from the call.  Failures are handled with exceptions based
     // on the return value of the C functions.  To guard the outcome of the
     // returned value, use another LIR instruction.
-    uint32_t callOffset;
-    if (dynStack)
-        callOffset = masm.callWithExitFrame(wrapper, *dynStack);
-    else
-        callOffset = masm.callWithExitFrame(wrapper);
-
+    uint32_t callOffset = masm.callJit(wrapper);
     markSafepointAt(callOffset, ins);
 
     // Remove rest of the frame left on the stack. We remove the return address
     // which is implicitly poped when returning.
     int framePop = sizeof(ExitFrameLayout) - sizeof(void*);
 
     // Pop arguments from framePushed.
     masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop);
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -303,25 +303,16 @@ template void
 MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
                                      MIRType slotType);
 
 template void
 MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
                                      MIRType slotType);
 
 void
-MacroAssemblerX64::callWithExitFrame(JitCode* target, Register dynStack)
-{
-    addPtr(Imm32(asMasm().framePushed()), dynStack);
-    makeFrameDescriptor(dynStack, JitFrame_IonJS);
-    asMasm().Push(dynStack);
-    asMasm().call(target);
-}
-
-void
 MacroAssemblerX64::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label)
 {
     ScratchRegisterScope scratch(asMasm());
 
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     MOZ_ASSERT(ptr != temp);
     MOZ_ASSERT(ptr != scratch);
 
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -70,17 +70,16 @@ class MacroAssemblerX64 : public MacroAs
         explicit SimdData(const SimdConstant& v) : value(v) {}
         SimdConstant::Type type() { return value.type(); }
     };
     Vector<SimdData, 0, SystemAllocPolicy> simds_;
     typedef HashMap<SimdConstant, size_t, SimdConstant, SystemAllocPolicy> SimdMap;
     SimdMap simdMap_;
 
   public:
-    using MacroAssemblerX86Shared::callWithExitFrame;
     using MacroAssemblerX86Shared::branch32;
     using MacroAssemblerX86Shared::branchTest32;
     using MacroAssemblerX86Shared::load32;
     using MacroAssemblerX86Shared::store32;
 
     MacroAssemblerX64()
     {
     }
@@ -1420,23 +1419,16 @@ class MacroAssemblerX64 : public MacroAs
         unboxDouble(source, dest);
 
         bind(&done);
     }
 
   public:
     void handleFailureWithHandlerTail(void* handler);
 
-    void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
-        shlq(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
-        orq(Imm32(type), frameSizeReg);
-    }
-
-    void callWithExitFrame(JitCode* target, Register dynStack);
-
     // See CodeGeneratorX64 calls to noteAsmJSGlobalAccess.
     void patchAsmJSGlobalAccess(CodeOffsetLabel patchAt, uint8_t* code, uint8_t* globalData,
                                 unsigned globalDataOffset)
     {
         uint8_t* nextInsn = code + patchAt.offset();
         MOZ_ASSERT(nextInsn <= globalData);
         uint8_t* target = globalData + globalDataOffset;
         ((int32_t*)nextInsn)[-1] = target - nextInsn;
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -221,17 +221,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.subPtr(valuesSize, rsp);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), valuesSize);
         masm.makeFrameDescriptor(valuesSize, JitFrame_BaselineJS);
         masm.push(valuesSize);
         masm.push(Imm32(0)); // Fake return address.
         // No GC things to mark, push a bare token.
-        masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
+        masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
 
         regs.add(valuesSize);
 
         masm.push(framePtr);
         masm.push(reg_code);
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(framePtr); // BaselineFrame
@@ -277,17 +277,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.movq(scopeChain, R1.scratchReg());
     }
 
     // The call will push the return address on the stack, thus we check that
     // the stack would be aligned once the call is complete.
     masm.assertStackAlignment(JitStackAlignment, sizeof(uintptr_t));
 
     // Call function.
-    masm.call(reg_code);
+    masm.callJitNoProfiler(reg_code);
 
     if (type == EnterJitBaseline) {
         // Baseline OSR will return here.
         masm.bind(returnLabel.src());
         masm.addCodeLabel(returnLabel);
     }
 
     // Pop arguments and padding from stack.
@@ -520,18 +520,17 @@ JitRuntime::generateArgumentsRectifier(J
     masm.push(rax); // callee token
     masm.push(r9); // descriptor
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
     masm.andq(Imm32(uint32_t(CalleeTokenMask)), rax);
     masm.loadPtr(Address(rax, JSFunction::offsetOfNativeOrScript()), rax);
     masm.loadBaselineOrIonRaw(rax, rax, nullptr);
-    masm.call(rax);
-    uint32_t returnOffset = masm.currentOffset();
+    uint32_t returnOffset = masm.callJitNoProfiler(rax);
 
     // Remove the rectifier frame.
     masm.pop(r9);             // r9 <- descriptor with FrameType.
     masm.shrq(Imm32(FRAMESIZE_SHIFT), r9);
     masm.pop(r11);            // Discard calleeToken.
     masm.pop(r11);            // Discard numActualArgs.
     masm.addq(r9, rsp);       // Discard pushed arguments.
 
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -58,53 +58,16 @@ MacroAssembler::clampDoubleToUint8(Float
     bind(&outOfRange);
     {
         move32(Imm32(255), output);
     }
 
     bind(&done);
 }
 
-// Builds an exit frame on the stack, with a return address to an internal
-// non-function. Returns offset to be passed to markSafepointAt().
-void
-MacroAssemblerX86Shared::buildFakeExitFrame(Register scratch, uint32_t* offset)
-{
-    mozilla::DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
-
-    CodeLabel cl;
-    mov(cl.dest(), scratch);
-
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor));
-    asMasm().Push(scratch);
-
-    bind(cl.src());
-    *offset = currentOffset();
-
-    MOZ_ASSERT(asMasm().framePushed() == initialDepth + ExitFrameLayout::Size());
-    addCodeLabel(cl);
-}
-
-void
-MacroAssemblerX86Shared::callWithExitFrame(Label* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor));
-    call(target);
-}
-
-void
-MacroAssemblerX86Shared::callWithExitFrame(JitCode* target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
-    asMasm().Push(Imm32(descriptor));
-    call(target);
-}
-
 void
 MacroAssembler::alignFrameForICArguments(AfterICSaveLive& aic)
 {
     // Exists for MIPS compatibility.
 }
 
 void
 MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive& aic)
@@ -163,34 +126,16 @@ MacroAssemblerX86Shared::branchNegativeZ
                                                    Register scratch,
                                                    Label* label)
 {
     vmovd(reg, scratch);
     cmp32(scratch, Imm32(1));
     j(Overflow, label);
 }
 
-void
-MacroAssemblerX86Shared::callJit(Register callee)
-{
-    call(callee);
-}
-
-void
-MacroAssemblerX86Shared::callJitFromAsmJS(Register callee)
-{
-    call(callee);
-}
-
-void
-MacroAssemblerX86Shared::callAndPushReturnAddress(Label* label)
-{
-    call(label);
-}
-
 MacroAssembler&
 MacroAssemblerX86Shared::asMasm()
 {
     return *static_cast<MacroAssembler*>(this);
 }
 
 const MacroAssembler&
 MacroAssemblerX86Shared::asMasm() const
@@ -415,9 +360,38 @@ MacroAssembler::call(ImmPtr target)
 }
 
 void
 MacroAssembler::call(JitCode* target)
 {
     Assembler::call(target);
 }
 
+void
+MacroAssembler::callAndPushReturnAddress(Register reg)
+{
+    call(reg);
+}
+
+void
+MacroAssembler::callAndPushReturnAddress(Label* label)
+{
+    call(label);
+}
+
+// ===============================================================
+// Jit Frames.
+
+uint32_t
+MacroAssembler::pushFakeReturnAddress(Register scratch)
+{
+    CodeLabel cl;
+
+    mov(cl.dest(), scratch);
+    Push(scratch);
+    bind(cl.src());
+    uint32_t retAddr = currentOffset();
+
+    addCodeLabel(cl);
+    return retAddr;
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
@@ -1468,26 +1468,16 @@ class MacroAssemblerX86Shared : public A
         return offset;
     }
 
     template <typename T>
     void computeEffectiveAddress(const T& address, Register dest) {
         lea(Operand(address), dest);
     }
 
-    // Builds an exit frame on the stack, with a return address to an internal
-    // non-function. Returns offset to be passed to markSafepointAt().
-    void buildFakeExitFrame(Register scratch, uint32_t* offset);
-    void callWithExitFrame(Label* target);
-    void callWithExitFrame(JitCode* target);
-
-    void callJit(Register callee);
-    void callJitFromAsmJS(Register callee);
-    void callAndPushReturnAddress(Label* label);
-
     void checkStackAlignment() {
         // Exists for ARM compatibility.
     }
 
     CodeOffsetLabel labelForPatch() {
         return CodeOffsetLabel(size());
     }
 
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -341,25 +341,16 @@ template void
 MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
                                      MIRType slotType);
 
 template void
 MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
                                      MIRType slotType);
 
 void
-MacroAssemblerX86::callWithExitFrame(JitCode* target, Register dynStack)
-{
-    addPtr(ImmWord(asMasm().framePushed()), dynStack);
-    makeFrameDescriptor(dynStack, JitFrame_IonJS);
-    asMasm().Push(dynStack);
-    asMasm().call(target);
-}
-
-void
 MacroAssemblerX86::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
                                            Label* label)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     MOZ_ASSERT(ptr != temp);
     MOZ_ASSERT(temp != InvalidReg);  // A temp register is required for x86.
 
     const Nursery& nursery = GetJitContext()->runtime->gcNursery();
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -78,17 +78,16 @@ class MacroAssemblerX86 : public MacroAs
     }
     Operand tagOf(const BaseIndex& address) {
         return Operand(address.base, address.index, address.scale, address.offset + 4);
     }
 
     void setupABICall(uint32_t args);
 
   public:
-    using MacroAssemblerX86Shared::callWithExitFrame;
     using MacroAssemblerX86Shared::branch32;
     using MacroAssemblerX86Shared::branchTest32;
     using MacroAssemblerX86Shared::load32;
     using MacroAssemblerX86Shared::store32;
     using MacroAssemblerX86Shared::call;
 
     MacroAssemblerX86()
     {
@@ -1133,23 +1132,16 @@ class MacroAssemblerX86 : public MacroAs
 
         bind(&done);
     }
 
   public:
     // Used from within an Exit frame to handle a pending exception.
     void handleFailureWithHandlerTail(void* handler);
 
-    void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
-        shll(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
-        orl(Imm32(type), frameSizeReg);
-    }
-
-    void callWithExitFrame(JitCode* target, Register dynStack);
-
     void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
     void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
 
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
 };
 
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -214,17 +214,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.subPtr(scratch, esp);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
         masm.push(scratch); // Fake return address.
         masm.push(Imm32(0));
         // No GC things to mark on the stack, push a bare token.
-        masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
+        masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
 
         masm.push(framePtr);
         masm.push(jitcode);
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(framePtr); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
@@ -512,18 +512,17 @@ JitRuntime::generateArgumentsRectifier(J
     masm.push(eax); // callee token
     masm.push(ebx); // descriptor
 
     // Call the target function.
     // Note that this assumes the function is JITted.
     masm.andl(Imm32(CalleeTokenMask), eax);
     masm.loadPtr(Address(eax, JSFunction::offsetOfNativeOrScript()), eax);
     masm.loadBaselineOrIonRaw(eax, eax, nullptr);
-    masm.call(eax);
-    uint32_t returnOffset = masm.currentOffset();
+    uint32_t returnOffset = masm.callJitNoProfiler(eax);
 
     // Remove the rectifier frame.
     masm.pop(ebx);            // ebx <- descriptor with FrameType.
     masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor.
     masm.pop(edi);            // Discard calleeToken.
     masm.pop(edi);            // Discard number of actual arguments.
 
     // Discard pushed arguments, but not the pushed frame pointer.
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -428,25 +428,26 @@ fun_resolve(JSContext* cx, HandleObject 
          * Built-in functions do not have a .prototype property per ECMA-262,
          * or (Object.prototype, Function.prototype, etc.) have that property
          * created eagerly.
          *
          * ES5 15.3.4.5: bound functions don't have a prototype property. The
          * isBuiltin() test covers this case because bound functions are native
          * (and thus built-in) functions by definition/construction.
          *
-         * In ES6 9.2.8 MakeConstructor the .prototype property is only assigned
-         * to constructors.
+         * ES6 9.2.8 MakeConstructor defines the .prototype property on constructors.
+         * Generators are not constructors, but they have a .prototype property anyway,
+         * according to errata to ES6. See bug 1191486.
          *
          * Thus all of the following don't get a .prototype property:
          * - Methods (that are not class-constructors or generators)
          * - Arrow functions
          * - Function.prototype
          */
-        if (fun->isBuiltin() || !fun->isConstructor())
+        if (fun->isBuiltin() || (!fun->isConstructor() && !fun->isGenerator()))
             return true;
 
         if (!ResolveInterpretedFunctionPrototype(cx, fun, id))
             return false;
 
         *resolvedp = true;
         return true;
     }
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -76,23 +76,25 @@ class JSFunction : public js::NativeObje
         SETTER_KIND = Setter << FUNCTION_KIND_SHIFT,
 
         /* Derived Flags values for convenience: */
         NATIVE_FUN = 0,
         NATIVE_CTOR = NATIVE_FUN | CONSTRUCTOR,
         ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR,
         ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA,
         INTERPRETED_METHOD = INTERPRETED | METHOD_KIND,
-        INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND | CONSTRUCTOR,
+        INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND,
         INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR,
         INTERPRETED_GETTER = INTERPRETED | GETTER_KIND,
         INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
         INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR,
         INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND,
+        INTERPRETED_LAMBDA_GENERATOR = INTERPRETED | LAMBDA,
         INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR,
+        INTERPRETED_GENERATOR = INTERPRETED,
         NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
 
         STABLE_ACROSS_CLONES = IS_FUN_PROTO | CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM |
                                LAMBDA | SELF_HOSTED |  HAS_REST | FUNCTION_KIND_MASK
     };
 
     static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS,
                   "jsfriendapi.h's JSFunction::INTERPRETED-alike is wrong");
--- a/js/src/tests/ecma_6/Class/methDefnGen.js
+++ b/js/src/tests/ecma_6/Class/methDefnGen.js
@@ -70,18 +70,13 @@ assertEq(next.value.world, 3);
 assertEq(b.g.hasOwnProperty("prototype"), true);
 
 // Strict mode
 a = {*b(c){"use strict";yield c;}};
 assertEq(a.b(1).next().value, 1);
 a = {*["b"](c){"use strict";return c;}};
 assertEq(a.b(1).next().value, 1);
 
-// Constructing
+// Generators should not have [[Construct]]
 a = {*g() { yield 1; }}
-it = new a.g;
-next = it.next();
-assertEq(next.done, false);
-assertEq(next.value, 1);
-next = it.next();
-assertEq(next.done, true);
+assertThrowsInstanceOf(() => { new a.g }, TypeError);
 
 reportCompare(0, 0, "ok");
--- a/js/src/tests/ecma_6/Class/newTargetGenerators.js
+++ b/js/src/tests/ecma_6/Class/newTargetGenerators.js
@@ -5,20 +5,10 @@ function *generatorNewTarget(expected) {
     yield (() => new.target);
 }
 
 const ITERATIONS = 25;
 
 for (let i = 0; i < ITERATIONS; i++)
     assertEq(generatorNewTarget(undefined).next().value(), undefined);
 
-for (let i = 0; i < ITERATIONS; i++)
-    assertEq(new generatorNewTarget(generatorNewTarget).next().value(),
-             generatorNewTarget);
-
-// also check to make sure it's useful in yield inside generators.
-// Plus, this code is so ugly, how could it not be a test? ;)
-// Thanks to anba for supplying this ludicrous expression.
-assertDeepEq([...new function*(i) { yield i; if(i > 0) yield* new new.target(i-1) }(10)],
-             [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Generators/iteration.js
+++ b/js/src/tests/ecma_6/Generators/iteration.js
@@ -55,19 +55,19 @@ function TestGenerator(g, expected_value
     testSend(g);
     testThrow(g);
 
     testNext(function*() { return yield* g(); });
     testSend(function*() { return yield* g(); });
     testThrow(function*() { return yield* g(); });
 
     if (g instanceof GeneratorFunction) {
-        testNext(function() { return new g(); });
-        testSend(function() { return new g(); });
-        testThrow(function() { return new g(); });
+        testNext(function() { return g(); });
+        testSend(function() { return g(); });
+        testThrow(function() { return g(); });
     }
 }
 
 TestGenerator(function* g1() { },
               [undefined],
               "foo",
               [undefined]);
 
--- a/js/src/tests/ecma_6/Generators/objects.js
+++ b/js/src/tests/ecma_6/Generators/objects.js
@@ -10,24 +10,16 @@ function TestGeneratorObject() {
   function* g() { yield 1; }
 
   var iter = g();
   assertEq(Object.getPrototypeOf(iter), g.prototype);
   assertTrue(iter instanceof g);
   assertEq(String(iter), "[object Generator]");
   assertDeepEq(Object.getOwnPropertyNames(iter), []);
   assertNotEq(g(), iter);
-
-  // g() is the same as new g().
-  iter = new g();
-  assertEq(Object.getPrototypeOf(iter), g.prototype);
-  assertTrue(iter instanceof g);
-  assertEq(String(iter), "[object Generator]");
-  assertDeepEq(Object.getOwnPropertyNames(iter), []);
-  assertNotEq(new g(), iter);
 }
 TestGeneratorObject();
 
 
 // Test the methods of generator objects.
 function TestGeneratorObjectMethods() {
   function* g() { yield 1; }
   var iter = g();
--- a/js/src/tests/ecma_6/TypedArray/of.js
+++ b/js/src/tests/ecma_6/TypedArray/of.js
@@ -63,20 +63,22 @@ for (var constructor of constructors) {
     // Throw if `this` is a method definition or a getter/setter function.
     assertThrowsInstanceOf(() => {
         constructor.of.call({method() {}}.method);
     }, TypeError);
     assertThrowsInstanceOf(() => {
         constructor.of.call(Object.getOwnPropertyDescriptor({get getter() {}}, "getter").get);
     }, TypeError);
 
-    // Generators are also legal constructors.
-    assertEq(constructor.of.call(function*(len) {
+    // Generators are not legal constructors.
+    assertThrowsInstanceOf(() => {
+      constructor.of.call(function*(len) {
         return len;
-    }, "a", "b", "c").next().value, 3);
+      }, "a")
+    }, TypeError);
 
     // An exception might be thrown in a strict assignment to the new object's indexed properties.
     assertThrowsInstanceOf(() => {
         constructor.of.call(function() {
             return {get 0() {}};
         }, "a");
     }, TypeError);
 
--- a/layout/base/ZoomConstraintsClient.cpp
+++ b/layout/base/ZoomConstraintsClient.cpp
@@ -20,16 +20,17 @@
 #include "UnitTransforms.h"
 
 #define ZCC_LOG(...)
 // #define ZCC_LOG(...) printf_stderr("ZCC: " __VA_ARGS__)
 
 NS_IMPL_ISUPPORTS(ZoomConstraintsClient, nsIDOMEventListener, nsIObserver)
 
 static const nsLiteralString DOM_META_ADDED = NS_LITERAL_STRING("DOMMetaAdded");
+static const nsLiteralString DOM_META_CHANGED = NS_LITERAL_STRING("DOMMetaChanged");
 static const nsLiteralCString BEFORE_FIRST_PAINT = NS_LITERAL_CSTRING("before-first-paint");
 
 using namespace mozilla;
 using namespace mozilla::layers;
 
 ZoomConstraintsClient::ZoomConstraintsClient() :
   mDocument(nullptr),
   mPresShell(nullptr)
@@ -61,16 +62,17 @@ ZoomConstraintsClient::Destroy()
   if (!(mPresShell && mDocument)) {
     return;
   }
 
   ZCC_LOG("Destroying %p\n", this);
 
   if (mEventTarget) {
     mEventTarget->RemoveEventListener(DOM_META_ADDED, this, false);
+    mEventTarget->RemoveEventListener(DOM_META_CHANGED, this, false);
     mEventTarget = nullptr;
   }
 
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   if (observerService) {
     observerService->RemoveObserver(this, BEFORE_FIRST_PAINT.Data());
   }
 
@@ -97,16 +99,17 @@ ZoomConstraintsClient::Init(nsIPresShell
   mPresShell = aPresShell;
   mDocument = aDocument;
 
   if (nsCOMPtr<nsPIDOMWindow> window = mDocument->GetWindow()) {
     mEventTarget = window->GetChromeEventHandler();
   }
   if (mEventTarget) {
     mEventTarget->AddEventListener(DOM_META_ADDED, this, false);
+    mEventTarget->AddEventListener(DOM_META_CHANGED, this, false);
   }
 
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   if (observerService) {
     observerService->AddObserver(this, BEFORE_FIRST_PAINT.Data(), false);
   }
 }
 
@@ -114,17 +117,21 @@ NS_IMETHODIMP
 ZoomConstraintsClient::HandleEvent(nsIDOMEvent* event)
 {
   nsAutoString type;
   event->GetType(type);
 
   if (type.Equals(DOM_META_ADDED)) {
     ZCC_LOG("Got a dom-meta-added event in %p\n", this);
     RefreshZoomConstraints();
+  } else if (type.Equals(DOM_META_CHANGED)) {
+    ZCC_LOG("Got a dom-meta-changed event in %p\n", this);
+    RefreshZoomConstraints();
   }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ZoomConstraintsClient::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
 {
   if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) {
     ZCC_LOG("Got a before-first-paint event in %p\n", this);
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1115,16 +1115,18 @@ nsStyleClipPath::ReleaseRef()
 {
   if (mType == NS_STYLE_CLIP_PATH_SHAPE) {
     NS_ASSERTION(mBasicShape, "expected pointer");
     mBasicShape->Release();
   } else if (mType == NS_STYLE_CLIP_PATH_URL) {
     NS_ASSERTION(mURL, "expected pointer");
     mURL->Release();
   }
+  // mBasicShap, mURL, etc. are all pointers in a union of pointers. Nulling
+  // one of them nulls all of them:
   mURL = nullptr;
 }
 
 void
 nsStyleClipPath::SetURL(nsIURI* aURL)
 {
   NS_ASSERTION(aURL, "expected pointer");
   ReleaseRef();
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -943,106 +943,90 @@ NewData(MediaStreamGraph* graph, TrackID
     // Ignore
   }
 }
 
 void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk(
     AudioSessionConduit *conduit,
     TrackRate rate,
     AudioChunk& chunk) {
-  // TODO(ekr@rtfm.com): Do more than one channel
-  nsAutoArrayPtr<int16_t> samples(new int16_t[chunk.mDuration]);
+
+  // Convert to interleaved, 16-bits integer audio, with a maximum of two
+  // channels (since the WebRTC.org code below makes the assumption that the
+  // input audio is either mono or stereo).
+  uint32_t outputChannels = chunk.ChannelCount() == 1 ? 1 : 2;
+  const int16_t* samples = nullptr;
+  nsAutoArrayPtr<int16_t> convertedSamples;
 
-  if (enabled_ && chunk.mBuffer) {
+  // If this track is not enabled, simply ignore the data in the chunk.
+  if (!enabled_) {
+    chunk.mBufferFormat = AUDIO_FORMAT_SILENCE;
+  }
+
+  // We take advantage of the fact that the common case (microphone directly to
+  // PeerConnection, that is, a normal call), the samples are already 16-bits
+  // mono, so the representation in interleaved and planar is the same, and we
+  // can just use that.
+  if (outputChannels == 1 && chunk.mBufferFormat == AUDIO_FORMAT_S16) {
+    samples = chunk.ChannelData<int16_t>().Elements()[0];
+  } else {
+    convertedSamples = new int16_t[chunk.mDuration * outputChannels];
+
     switch (chunk.mBufferFormat) {
-      case AUDIO_FORMAT_FLOAT32:
-        {
-          const float* buf = static_cast<const float *>(chunk.mChannelData[0]);
-          ConvertAudioSamplesWithScale(buf, static_cast<int16_t*>(samples),
-                                       chunk.mDuration, chunk.mVolume);
-        }
-        break;
-      case AUDIO_FORMAT_S16:
-        {
-          const short* buf = static_cast<const short *>(chunk.mChannelData[0]);
-          ConvertAudioSamplesWithScale(buf, samples, chunk.mDuration, chunk.mVolume);
-        }
-        break;
-      case AUDIO_FORMAT_SILENCE:
-        memset(samples, 0, chunk.mDuration * sizeof(samples[0]));
-        break;
-      default:
-        MOZ_ASSERT_UNREACHABLE("Unexpected AudioSampleFormat");
-        return;
+        case AUDIO_FORMAT_FLOAT32:
+          DownmixAndInterleave(chunk.ChannelData<float>(),
+                               chunk.mDuration, chunk.mVolume, outputChannels,
+                               convertedSamples.get());
+          break;
+        case AUDIO_FORMAT_S16:
+          DownmixAndInterleave(chunk.ChannelData<int16_t>(),
+                               chunk.mDuration, chunk.mVolume, outputChannels,
+                               convertedSamples.get());
+          break;
+        case AUDIO_FORMAT_SILENCE:
+          PodZero(convertedSamples.get(), chunk.mDuration * outputChannels);
+          break;
     }
-  } else {
-    // This means silence.
-    memset(samples, 0, chunk.mDuration * sizeof(samples[0]));
+    samples = convertedSamples.get();
   }
 
   MOZ_ASSERT(!(rate%100)); // rate should be a multiple of 100
 
-  // Check if the rate has changed since the last time we came through
-  // I realize it may be overkill to check if the rate has changed, but
-  // I believe it is possible (e.g. if we change sources) and it costs us
-  // very little to handle this case
+  // Check if the rate or the number of channels has changed since the last time
+  // we came through. I realize it may be overkill to check if the rate has
+  // changed, but I believe it is possible (e.g. if we change sources) and it
+  // costs us very little to handle this case.
+
+  uint32_t audio_10ms = rate / 100;
 
-  if (samplenum_10ms_ !=  rate/100) {
-    // Determine number of samples in 10 ms from the rate:
-    samplenum_10ms_ = rate/100;
-    // If we switch sample rates (e.g. if we switch codecs),
-    // we throw away what was in the sample_10ms_buffer at the old rate
-    samples_10ms_buffer_ = new int16_t[samplenum_10ms_];
-    buffer_current_ = 0;
-  }
+  if (!packetizer_ ||
+      packetizer_->PacketSize() != audio_10ms ||
+      packetizer_->Channels() != outputChannels) {
+    // It's ok to drop the audio still in the packetizer here.
+    packetizer_ = new AudioPacketizer<int16_t, int16_t>(audio_10ms, outputChannels);
+   }
 
-  // Vars to handle the non-sunny-day case (where the audio chunks
-  // we got are not multiples of 10ms OR there were samples left over
-  // from the last run)
-  int64_t chunk_remaining;
-  int64_t tocpy;
-  int16_t *samples_tmp = samples.get();
-
-  chunk_remaining = chunk.mDuration;
-
-  MOZ_ASSERT(chunk_remaining >= 0);
+  packetizer_->Input(samples, chunk.mDuration);
 
-  if (buffer_current_) {
-    tocpy = std::min(chunk_remaining, samplenum_10ms_ - buffer_current_);
-    memcpy(&samples_10ms_buffer_[buffer_current_], samples_tmp, tocpy * sizeof(int16_t));
-    buffer_current_ += tocpy;
-    samples_tmp += tocpy;
-    chunk_remaining -= tocpy;
-
-    if (buffer_current_ == samplenum_10ms_) {
-      // Send out the audio buffer we just finished filling
-      conduit->SendAudioFrame(samples_10ms_buffer_, samplenum_10ms_, rate, 0);
-      buffer_current_ = 0;
-    } else {
-      // We still don't have enough data to send a buffer
-      return;
-    }
-  }
+  while (packetizer_->PacketsAvailable()) {
+    uint32_t samplesPerPacket = packetizer_->PacketSize() *
+                                packetizer_->Channels();
 
-  // Now send (more) frames if there is more than 10ms of input left
-  tocpy = (chunk_remaining / samplenum_10ms_) * samplenum_10ms_;
-  if (tocpy > 0) {
-    conduit->SendAudioFrame(samples_tmp, tocpy, rate, 0);
-    samples_tmp += tocpy;
-    chunk_remaining -= tocpy;
+    // We know that webrtc.org's code going to copy the samples down the line,
+    // so we can just use a stack buffer here instead of malloc-ing.
+    // Max size given stereo is 480*2*2 = 1920 (10ms of 16-bits stereo audio at
+    // 48KHz)
+    const size_t AUDIO_SAMPLE_BUFFER_MAX = 1920;
+    int16_t packet[AUDIO_SAMPLE_BUFFER_MAX];
+
+    packetizer_->Output(packet);
+    conduit->SendAudioFrame(packet,
+                            samplesPerPacket,
+                            rate, 0);
   }
-  // Copy what remains for the next run
-
-  MOZ_ASSERT(chunk_remaining < samplenum_10ms_);
-
-  if (chunk_remaining) {
-    memcpy(samples_10ms_buffer_, samples_tmp, chunk_remaining * sizeof(int16_t));
-    buffer_current_ = chunk_remaining;
-  }
-
 }
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
 void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk(
     VideoSessionConduit* conduit,
     VideoChunk& chunk) {
   Image *img = chunk.mFrame.GetImage();
 
@@ -1360,56 +1344,78 @@ NotifyPull(MediaStreamGraph* graph, Stre
   if (!source_) {
     MOZ_MTLOG(ML_ERROR, "NotifyPull() called from a non-SourceMediaStream");
     return;
   }
 
   // This comparison is done in total time to avoid accumulated roundoff errors.
   while (source_->TicksToTimeRoundDown(track_rate_, played_ticks_) <
          desired_time) {
-    // TODO(ekr@rtfm.com): Is there a way to avoid mallocating here?  Or reduce the size?
-    // Max size given mono is 480*2*1 = 960 (48KHz)
-#define AUDIO_SAMPLE_BUFFER_MAX 1000
-    MOZ_ASSERT((track_rate_/100)*sizeof(uint16_t) <= AUDIO_SAMPLE_BUFFER_MAX);
+    // Max size given stereo is 480*2*2 = 1920 (48KHz)
+    const size_t AUDIO_SAMPLE_BUFFER_MAX = 1920;
+    MOZ_ASSERT((track_rate_/100)*sizeof(uint16_t) * 2 <= AUDIO_SAMPLE_BUFFER_MAX);
 
-    nsRefPtr<SharedBuffer> samples = SharedBuffer::Create(AUDIO_SAMPLE_BUFFER_MAX);
-    int16_t *samples_data = static_cast<int16_t *>(samples->Data());
+    int16_t scratch_buffer[AUDIO_SAMPLE_BUFFER_MAX];
+
     int samples_length;
 
-    // This fetches 10ms of data
+    // This fetches 10ms of data, either mono or stereo
     MediaConduitErrorCode err =
         static_cast<AudioSessionConduit*>(conduit_.get())->GetAudioFrame(
-            samples_data,
+            scratch_buffer,
             track_rate_,
             0,  // TODO(ekr@rtfm.com): better estimate of "capture" (really playout) delay
             samples_length);
 
     if (err != kMediaConduitNoError) {
       // Insert silence on conduit/GIPS failure (extremely unlikely)
       MOZ_MTLOG(ML_ERROR, "Audio conduit failed (" << err
                 << ") to return data @ " << played_ticks_
                 << " (desired " << desired_time << " -> "
                 << source_->StreamTimeToSeconds(desired_time) << ")");
       samples_length = track_rate_/100; // if this is not enough we'll loop and provide more
-      memset(samples_data, '\0', samples_length * sizeof(uint16_t));
+      PodArrayZero(scratch_buffer);
     }
 
     MOZ_ASSERT(samples_length * sizeof(uint16_t) < AUDIO_SAMPLE_BUFFER_MAX);
 
     MOZ_MTLOG(ML_DEBUG, "Audio conduit returned buffer of length "
               << samples_length);
 
+    nsRefPtr<SharedBuffer> samples = SharedBuffer::Create(samples_length * sizeof(uint16_t));
+    int16_t *samples_data = static_cast<int16_t *>(samples->Data());
     AudioSegment segment;
-    nsAutoTArray<const int16_t*,1> channels;
-    channels.AppendElement(samples_data);
-    segment.AppendFrames(samples.forget(), channels, samples_length);
+    // We derive the number of channels of the stream from the number of samples
+    // the AudioConduit gives us, considering it gives us packets of 10ms and we
+    // know the rate.
+    uint32_t channelCount = samples_length / (track_rate_ / 100);
+    nsAutoTArray<int16_t*,2> channels;
+    nsAutoTArray<const int16_t*,2> outputChannels;
+    size_t frames = samples_length / channelCount;
+
+    channels.SetLength(channelCount);
+
+    size_t offset = 0;
+    for (size_t i = 0; i < channelCount; i++) {
+      channels[i] = samples_data + offset;
+      offset += frames;
+    }
+
+    DeinterleaveAndConvertBuffer(scratch_buffer,
+                                 frames,
+                                 channelCount,
+                                 channels.Elements());
+
+    outputChannels.AppendElements(channels);
+
+    segment.AppendFrames(samples.forget(), outputChannels, frames);
 
     // Handle track not actually added yet or removed/finished
     if (source_->AppendToTrack(track_id_, &segment)) {
-      played_ticks_ += track_rate_/100; // 10ms in TrackTicks
+      played_ticks_ += frames;
     } else {
       MOZ_MTLOG(ML_ERROR, "AppendToTrack failed");
       // we can't un-read the data, but that's ok since we don't want to
       // buffer - but don't i-loop!
       return;
     }
   }
 }
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -21,16 +21,17 @@
 #include "MediaPipelineFilter.h"
 #include "AudioSegment.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/Atomics.h"
 #include "SrtpFlow.h"
 #include "databuffer.h"
 #include "runnable_utils.h"
 #include "transportflow.h"
+#include "AudioPacketizer.h"
 
 #if defined(MOZILLA_INTERNAL_API)
 #include "VideoSegment.h"
 #endif
 
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 
 namespace mozilla {
@@ -441,19 +442,17 @@ public:
     explicit PipelineListener(const RefPtr<MediaSessionConduit>& conduit)
       : conduit_(conduit),
         track_id_(TRACK_INVALID),
         mMutex("MediaPipelineTransmit::PipelineListener"),
         track_id_external_(TRACK_INVALID),
         active_(false),
         enabled_(false),
         direct_connect_(false),
-        samples_10ms_buffer_(nullptr),
-        buffer_current_(0),
-        samplenum_10ms_(0)
+        packetizer_(nullptr)
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
         , last_img_(-1)
 #endif // MOZILLA_INTERNAL_API
     {
     }
 
     ~PipelineListener()
     {
@@ -521,26 +520,17 @@ public:
     // active is true if there is a transport to send on
     mozilla::Atomic<bool> active_;
     // enabled is true if the media access control permits sending
     // actual content; when false you get black/silence
     mozilla::Atomic<bool> enabled_;
 
     bool direct_connect_;
 
-
-    // These vars handle breaking audio samples into exact 10ms chunks:
-    // The buffer of 10ms audio samples that we will send once full
-    // (can be carried over from one call to another).
-    nsAutoArrayPtr<int16_t> samples_10ms_buffer_;
-    // The location of the pointer within that buffer (in units of samples).
-    int64_t buffer_current_;
-    // The number of samples in a 10ms audio chunk.
-    int64_t samplenum_10ms_;
-
+    nsAutoPtr<AudioPacketizer<int16_t, int16_t>> packetizer_;
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
     int32_t last_img_; // serial number of last Image
 #endif // MOZILLA_INTERNAL_API
   };
 
  private:
   RefPtr<PipelineListener> listener_;
   DOMMediaStream *domstream_;
--- a/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc
+++ b/media/webrtc/trunk/webrtc/voice_engine/output_mixer.cc
@@ -292,16 +292,21 @@ OutputMixer::GetOutputVolumePan(float& l
     left = _panLeft;
     right = _panRight;
     WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
                  "GetOutputVolumePan() => left=%2.1f, right=%2.1f",
                  left, right);
     return 0;
 }
 
+int OutputMixer::GetOutputChannelCount()
+{
+  return _audioFrame.num_channels_;
+}
+
 int OutputMixer::StartRecordingPlayout(const char* fileName,
                                        const CodecInst* codecInst)
 {
     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
                  "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName);
 
     if (_outputFileRecording)
     {
--- a/media/webrtc/trunk/webrtc/voice_engine/output_mixer.h
+++ b/media/webrtc/trunk/webrtc/voice_engine/output_mixer.h
@@ -112,16 +112,18 @@ public:
     void RecordNotification(int32_t id, uint32_t durationMs);
 
     void PlayFileEnded(int32_t id);
     void RecordFileEnded(int32_t id);
 
     // so ExternalPlayoutData() can insert far-end audio from the audio drivers
     void APMAnalyzeReverseStream(AudioFrame &audioFrame);
 
+    int GetOutputChannelCount();
+
 private:
     OutputMixer(uint32_t instanceId);
     int InsertInbandDtmfTone();
 
     // uses
     Statistics* _engineStatisticsPtr;
     AudioProcessing* _audioProcessingModulePtr;
 
--- a/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc
+++ b/media/webrtc/trunk/webrtc/voice_engine/voe_external_media_impl.cc
@@ -209,17 +209,20 @@ int VoEExternalMediaImpl::ExternalRecord
     if (current_delay_ms < 0)
     {
         shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
             "SetExternalRecordingStatus() invalid delay)");
         return -1;
     }
 
     uint16_t blockSize = samplingFreqHz / 100;
-    uint32_t nBlocks = lengthSamples / blockSize;
+    // We know the number of samples for 10ms of audio, so we can derive the
+    // number of channels here:
+    uint32_t channels = lengthSamples * 100 / samplingFreqHz;
+    uint32_t nBlocks = lengthSamples / blockSize / channels;
     int16_t totalDelayMS = 0;
     uint16_t playoutDelayMS = 0;
 
     for (uint32_t i = 0; i < nBlocks; i++)
     {
         if (!shared_->ext_playout())
         {
             // Use real playout delay if external playout is not enabled.
@@ -237,17 +240,17 @@ int VoEExternalMediaImpl::ExternalRecord
             // Compensate for block sizes larger than 10ms
             totalDelayMS -= (int16_t)(i*10);
             if (totalDelayMS < 0)
                 totalDelayMS = 0;
         }
         shared_->transmit_mixer()->PrepareDemux(
             (const int8_t*)(&speechData10ms[i*blockSize]),
             blockSize,
-            1,
+            channels,
             samplingFreqHz,
             totalDelayMS,
             0,
             0,
             false); // Typing detection not supported
 
         shared_->transmit_mixer()->DemuxAndMix();
         shared_->transmit_mixer()->EncodeAndSend();
@@ -375,26 +378,33 @@ int VoEExternalMediaImpl::ExternalPlayou
     {
         shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
             "ExternalPlayoutGetData() invalid delay)");
         return -1;
     }
 
     AudioFrame audioFrame;
 
+    uint32_t channels = shared_->output_mixer()->GetOutputChannelCount();
+    // If we have not received any data yet, consider it's mono since it's the
+    // most common case.
+    if (channels == 0) {
+      channels = 1;
+    }
+
     // Retrieve mixed output at the specified rate
     shared_->output_mixer()->MixActiveChannels();
     shared_->output_mixer()->DoOperationsOnCombinedSignal(true);
-    shared_->output_mixer()->GetMixedAudio(samplingFreqHz, 1, &audioFrame);
+    shared_->output_mixer()->GetMixedAudio(samplingFreqHz, channels, &audioFrame);
 
     // Deliver audio (PCM) samples to the external sink
     memcpy(speechData10ms,
            audioFrame.data_,
-           sizeof(int16_t)*(audioFrame.samples_per_channel_));
-    lengthSamples = audioFrame.samples_per_channel_;
+           sizeof(int16_t)*audioFrame.samples_per_channel_*channels);
+    lengthSamples = audioFrame.samples_per_channel_ * channels;
 
     // Store current playout delay (to be used by ExternalRecordingInsertData).
     playout_delay_ms_ = current_delay_ms;
 
     return 0;
 #else
     shared_->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
        "ExternalPlayoutGetData() external playout is not supported");
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/cellscanner/CellScannerImplementation.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/scanners/cellscanner/CellScannerImplementation.java
@@ -80,17 +80,17 @@ public class CellScannerImplementation i
     @Override
     public synchronized void start() {
         if (mIsStarted || !isSupportedOnThisDevice()) {
             return;
         }
         mIsStarted = true;
 
         if (mTelephonyManager == null) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            if (Build.VERSION.SDK_INT >= 18 /*Build.VERSION_CODES.JELLY_BEAN_MR2 */) {
                 mGetAllInfoCellScanner = new GetAllCellInfoScannerMr2();
             } else {
                 mGetAllInfoCellScanner = new GetAllCellInfoScannerDummy();
             }
 
             mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         }
 
@@ -165,17 +165,18 @@ public class CellScannerImplementation i
             return info;
         } catch (IllegalArgumentException iae) {
             Log.e(LOG_TAG, "Skip invalid or incomplete CellLocation: " + currentCell, iae);
         }
         return null;
     }
 
     private List<CellInfo> getNeighboringCells() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+        // For max fennec compatibility, avoid VERSION_CODES
+        if (Build.VERSION.SDK_INT >= 22 /* Build.VERSION_CODES.LOLLIPOP_MR1 */) {
             return Collections.emptyList();
         }
 
         @SuppressWarnings("deprecation")
         Collection<NeighboringCellInfo> cells = mTelephonyManager.getNeighboringCellInfo();
         if (cells == null || cells.isEmpty()) {
             return Collections.emptyList();
         }
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -1147,19 +1147,22 @@ Http2Session::SetInputFrameDataStream(ui
 }
 
 nsresult
 Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength)
 {
   if (mInputFrameFlags & kFlag_PADDED) {
     paddingLength = *reinterpret_cast<uint8_t *>(mInputFrameBuffer + kFrameHeaderBytes);
     paddingControlBytes = 1;
+  } else {
+    paddingLength = 0;
+    paddingControlBytes = 0;
   }
 
-  if (paddingLength > mInputFrameDataSize) {
+  if (static_cast<uint32_t>(paddingLength + paddingControlBytes) > mInputFrameDataSize) {
     // This is fatal to the session
     LOG3(("Http2Session::ParsePadding %p stream 0x%x PROTOCOL_ERROR "
           "paddingLength %d > frame size %d\n",
           this, mInputFrameID, paddingLength, mInputFrameDataSize));
     RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
   }
 
   return NS_OK;
@@ -1207,16 +1210,21 @@ Http2Session::RecvHeaders(Http2Session *
         "paddingLength=%d padded=%d\n",
         self, self->mInputFrameID, priorityLen, self->mInputFrameDataStream,
         self->mInputFrameFlags & kFlag_END_STREAM,
         self->mInputFrameFlags & kFlag_END_HEADERS,
         self->mInputFrameFlags & kFlag_PRIORITY,
         paddingLength,
         self->mInputFrameFlags & kFlag_PADDED));
 
+  if ((paddingControlBytes + priorityLen + paddingLength) > self->mInputFrameDataSize) {
+    // This is fatal to the session
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
   if (!self->mInputFrameDataStream) {
     // Cannot find stream. We can continue the session, but we need to
     // uncompress the header block to maintain the correct compression context
 
     LOG3(("Http2Session::RecvHeaders %p lookup mInputFrameID stream "
           "0x%X failed. NextStreamID = 0x%X\n",
           self, self->mInputFrameID, self->mNextStreamID));
 
--- a/testing/marionette/client/marionette/runtests.py
+++ b/testing/marionette/client/marionette/runtests.py
@@ -35,18 +35,18 @@ def startTestRunner(runner_class, args):
     args = vars(args)
     tests = args.pop('tests')
     runner = runner_class(**args)
     runner.run_tests(tests)
     return runner
 
 def cli(runner_class=MarionetteTestRunner, parser_class=MarionetteArguments):
     parser = parser_class(
-        usage='%prog [options] test_file_or_dir <test_file_or_dir> ...',
-        version="%prog {version} (using marionette-driver: {driver_version}"
+        usage='%(prog)s [options] test_file_or_dir <test_file_or_dir> ...',
+        version="%(prog)s {version} (using marionette-driver: {driver_version}"
                 ", marionette-transport: {transport_version})".format(
                     version=__version__,
                     driver_version=driver_version,
                     transport_version=transport_version)
     )
     mozlog.commandline.add_logging_group(parser)
     args = parser.parse_args()
     parser.verify_usage(args)
--- a/testing/mozharness/configs/talos/linux_config.py
+++ b/testing/mozharness/configs/talos/linux_config.py
@@ -1,27 +1,35 @@
 import os
+import platform
 
 PYTHON = '/tools/buildbot/bin/python'
 VENV_PATH = '%s/build/venv' % os.getcwd()
+if platform.architecture()[0] == '64bit':
+    TOOLTOOL_MANIFEST_PATH = "config/tooltool-manifests/linux64/releng.manifest"
+    MINIDUMP_STACKWALK_PATH = "linux64-minidump_stackwalk"
+else:
+    TOOLTOOL_MANIFEST_PATH = "config/tooltool-manifests/linux32/releng.manifest"
+    MINIDUMP_STACKWALK_PATH = "linux32-minidump_stackwalk"
 
 config = {
     "log_name": "talos",
     "buildbot_json_path": "buildprops.json",
     "installer_path": "installer.exe",
     "virtualenv_path": VENV_PATH,
     "find_links": [
         "http://pypi.pvt.build.mozilla.org/pub",
         "http://pypi.pub.build.mozilla.org/pub",
     ],
     "pip_index": False,
     "use_talos_json": True,
     "exes": {
         'python': PYTHON,
         'virtualenv': [PYTHON, '/tools/misc-python/virtualenv.py'],
+        'tooltool.py': "/tools/tooltool.py",
     },
     "title": os.uname()[1].lower().split('.')[0],
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
         "clone-talos",
         "create-virtualenv",
@@ -30,9 +38,13 @@ config = {
     ],
     "python_webserver": False,
     "webroot": '%s/../talos-data' % os.getcwd(),
     "populate_webroot": True,
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
+    "download_minidump_stackwalk": True,
+    "minidump_stackwalk_path": MINIDUMP_STACKWALK_PATH,
+    "minidump_tooltool_manifest_path": TOOLTOOL_MANIFEST_PATH,
+    "tooltool_cache": "/builds/tooltool_cache",
 }
--- a/testing/mozharness/configs/talos/mac_config.py
+++ b/testing/mozharness/configs/talos/mac_config.py
@@ -22,16 +22,17 @@ config = {
         "http://pypi.pvt.build.mozilla.org/pub",
         "http://pypi.pub.build.mozilla.org/pub",
     ],
     "pip_index": False,
     "use_talos_json": True,
     "exes": {
         'python': PYTHON,
         'virtualenv': [PYTHON, '/tools/misc-python/virtualenv.py'],
+        'tooltool.py': "/tools/tooltool.py",
     },
     "title": os.uname()[1].lower().split('.')[0],
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
         "clone-talos",
         "create-virtualenv",
@@ -47,9 +48,13 @@ config = {
     ],
     "postflight_run_cmd_suites": [
         SCREEN_RESOLUTION_CHECK,
     ],
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
+    "download_minidump_stackwalk": True,
+    "minidump_stackwalk_path": "macosx64-minidump_stackwalk",
+    "minidump_tooltool_manifest_path": "config/tooltool-manifests/macosx64/releng.manifest",
+    "tooltool_cache": "/builds/tooltool_cache",
 }
--- a/testing/mozharness/configs/talos/windows_config.py
+++ b/testing/mozharness/configs/talos/windows_config.py
@@ -21,16 +21,17 @@ config = {
     "exes": {
         'python': PYTHON,
         'virtualenv': [PYTHON, 'c:/mozilla-build/buildbotve/virtualenv.py'],
         'easy_install': ['%s/scripts/python' % VENV_PATH,
                          '%s/scripts/easy_install-2.7-script.py' % VENV_PATH],
         'mozinstall': ['%s/scripts/python' % VENV_PATH,
                        '%s/scripts/mozinstall-script.py' % VENV_PATH],
         'hg': 'c:/mozilla-build/hg/hg',
+        'tooltool.py': [PYTHON, 'C:/mozilla-build/tooltool.py'],
     },
     "title": socket.gethostname().split('.')[0],
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
         "clone-talos",
         "create-virtualenv",
@@ -42,9 +43,12 @@ config = {
     "populate_webroot": True,
     # Srsly gly? Ys
     "webroot_extract_cmd": r'''c:/mozilla-build/msys/bin/bash -c "PATH=/c/mozilla-build/msys/bin:$PATH tar zx --strip-components=1 -f '%(tarball)s' --wildcards '**/talos/'"''',
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
     "metro_harness_path_frmt": "%(metro_base_path)s/metro/metrotestharness.exe",
+    "download_minidump_stackwalk": True,
+    "minidump_stackwalk_path": "win32-minidump_stackwalk.exe",
+    "minidump_tooltool_manifest_path": "config/tooltool-manifests/win32/releng.manifest",
 }
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -162,16 +162,17 @@ class Talos(TestingMixin, MercurialScrip
                                               'install',
                                               'run-tests',
                                               ])
         kwargs.setdefault('config', {})
         super(Talos, self).__init__(**kwargs)
 
         self.workdir = self.query_abs_dirs()['abs_work_dir']  # convenience
 
+        self.run_local = self.config.get('run_local')
         self.installer_url = self.config.get("installer_url")
         self.talos_json_url = self.config.get("talos_json_url")
         self.talos_json = self.config.get("talos_json")
         self.talos_json_config = self.config.get("talos_json_config")
         self.talos_path = os.path.join(self.workdir, 'talos_repo')
         self.has_cloned_talos = False
         self.tests = None
         self.pagesets_url = None
@@ -562,16 +563,18 @@ class Talos(TestingMixin, MercurialScrip
 
         # XXX temporary python version check
         python = self.query_python_path()
         self.run_command([python, "--version"])
         parser = TalosOutputParser(config=self.config, log_obj=self.log_obj,
                                    error_list=TalosErrorList)
         env = {}
         env['MOZ_UPLOAD_DIR'] = self.query_abs_dirs()['abs_blob_upload_dir']
+        if not self.run_local:
+            env['MINIDUMP_STACKWALK'] = self.query_minidump_stackwalk()
         env['MINIDUMP_SAVE_PATH'] = self.query_abs_dirs()['abs_blob_upload_dir']
         if not os.path.isdir(env['MOZ_UPLOAD_DIR']):
             self.mkdir_p(env['MOZ_UPLOAD_DIR'])
         env = self.query_env(partial_env=env, log_level=INFO)
         # adjust PYTHONPATH to be able to use talos as a python package
         if 'PYTHONPATH' in env:
             env['PYTHONPATH'] = self.talos_path + os.pathsep + env['PYTHONPATH']
         else:
--- a/testing/mozharness/scripts/web_platform_tests.py
+++ b/testing/mozharness/scripts/web_platform_tests.py
@@ -21,16 +21,21 @@ from mozharness.base.log import INFO
 
 class WebPlatformTest(TestingMixin, MercurialScript, BlobUploadMixin):
     config_options = [
         [['--test-type'], {
             "action": "extend",
             "dest": "test_type",
             "help": "Specify the test types to run."}
          ],
+        [['--e10s'], {
+            "action": "store_true",
+            "dest": "e10s",
+            "help": "Run with e10s enabled"}
+         ],
         [["--total-chunks"], {
             "action": "store",
             "dest": "total_chunks",
             "help": "Number of total chunks"}
          ],
         [["--this-chunk"], {
             "action": "store",
             "dest": "this_chunk",
@@ -123,16 +128,19 @@ class WebPlatformTest(TestingMixin, Merc
                                                    "wpt_raw.log"),
                      "--binary=%s" % self.binary_path,
                      "--symbols-path=%s" % self.query_symbols_url(),
                      "--stackwalk-binary=%s" % self.query_minidump_stackwalk()]
 
         for test_type in c.get("test_type", []):
             base_cmd.append("--test-type=%s" % test_type)
 
+        if c.get("e10s"):
+            base_cmd.append("--e10s")
+
         for opt in ["total_chunks", "this_chunk"]:
             val = c.get(opt)
             if val:
                 base_cmd.append("--%s=%s" % (opt.replace("_", "-"), val))
 
         options = list(c.get("options", []))
 
         str_format_values = {
--- a/testing/talos/mach_commands.py
+++ b/testing/talos/mach_commands.py
@@ -55,16 +55,17 @@ class TalosRunner(MozbuildObject):
         self.binary_path = self.get_binary_path()
         self.virtualenv_script = os.path.join(self.topsrcdir, 'python',
                                               'virtualenv', 'virtualenv.py')
         self.virtualenv_path = os.path.join(self.mozharness_dir, 'venv')
         self.python_interp = sys.executable
 
     def make_config(self):
         self.config = {
+            'run_local': True,
             'talos_json': self.talos_json,
             'binary_path': self.binary_path,
             'log_name': 'talos',
             'virtualenv_path': self.virtualenv_path,
             'pypi_url': 'http://pypi.python.org/simple',
             'use_talos_json': True,
             'base_work_dir': self.mozharness_dir,
             'exes': {
--- a/testing/web-platform/harness/wptrunner/browsers/firefox.py
+++ b/testing/web-platform/harness/wptrunner/browsers/firefox.py
@@ -22,17 +22,18 @@ here = os.path.join(os.path.split(__file
 
 __wptrunner__ = {"product": "firefox",
                  "check_args": "check_args",
                  "browser": "FirefoxBrowser",
                  "executor": {"testharness": "MarionetteTestharnessExecutor",
                               "reftest": "MarionetteRefTestExecutor"},
                  "browser_kwargs": "browser_kwargs",
                  "executor_kwargs": "executor_kwargs",
-                 "env_options": "env_options"}
+                 "env_options": "env_options",
+                 "run_info_extras": "run_info_extras"}
 
 
 def check_args(**kwargs):
     require_arg(kwargs, "binary")
     if kwargs["ssl_type"] != "none":
         require_arg(kwargs, "certutil_binary")
 
 
@@ -59,16 +60,18 @@ def executor_kwargs(test_type, server_co
 
 def env_options():
     return {"host": "127.0.0.1",
             "external_host": "web-platform.test",
             "bind_hostname": "false",
             "certificate_domain": "web-platform.test",
             "supports_debugger": True}
 
+def run_info_extras(**kwargs):
+    return {"e10s": kwargs["gecko_e10s"]}
 
 class FirefoxBrowser(Browser):
     used_ports = set()
 
     def __init__(self, logger, binary, prefs_root, debug_info=None,
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                  ca_certificate_path=None, e10s=False):
         Browser.__init__(self, logger)
--- a/testing/web-platform/harness/wptrunner/manifestupdate.py
+++ b/testing/web-platform/harness/wptrunner/manifestupdate.py
@@ -324,17 +324,17 @@ def group_conditionals(values):
     # for all the values
     if len(values) > 1:
         for key, statuses in by_property.copy().iteritems():
             if len(statuses) == len(values):
                 del by_property[key]
 
     properties = set(item[0] for item in by_property.iterkeys())
 
-    prop_order = ["debug", "os", "version", "processor", "bits"]
+    prop_order = ["debug", "e10s", "os", "version", "processor", "bits"]
     include_props = []
 
     for prop in prop_order:
         if prop in properties:
             include_props.append(prop)
 
     conditions = {}
 
@@ -351,17 +351,17 @@ def group_conditionals(values):
 
 def make_expr(prop_set, status):
     """Create an AST that returns the value ``status`` given all the
     properties in prop_set match."""
     root = ConditionalNode()
 
     assert len(prop_set) > 0
 
-    no_value_props = set(["debug"])
+    no_value_props = set(["debug", "e10s"])
 
     expressions = []
     for prop, value in prop_set:
         number_types = (int, float, long)
         value_cls = (NumberNode
                      if type(value) in number_types
                      else StringNode)
         if prop not in no_value_props:
--- a/testing/web-platform/harness/wptrunner/products.py
+++ b/testing/web-platform/harness/wptrunner/products.py
@@ -38,18 +38,20 @@ def load_product(config, product):
     module = product_module(config, product)
     data = module.__wptrunner__
 
     check_args = getattr(module, data["check_args"])
     browser_cls = getattr(module, data["browser"])
     browser_kwargs = getattr(module, data["browser_kwargs"])
     executor_kwargs = getattr(module, data["executor_kwargs"])
     env_options = getattr(module, data["env_options"])()
+    run_info_extras = (getattr(module, data["run_info_extras"])
+                       if "run_info_extras" in data else lambda **kwargs:{})
 
     executor_classes = {}
     for test_type, cls_name in data["executor"].iteritems():
         cls = getattr(module, cls_name)
         executor_classes[test_type] = cls
 
     return (check_args,
             browser_cls, browser_kwargs,
             executor_classes, executor_kwargs,
-            env_options)
+            env_options, run_info_extras)
--- a/testing/web-platform/harness/wptrunner/wptrunner.py
+++ b/testing/web-platform/harness/wptrunner/wptrunner.py
@@ -35,18 +35,22 @@ The upstream repository has the facility
 format. This manifest is used directly to determine which tests exist. Local
 metadata files are used to store the expected test results.
 """
 
 def setup_logging(*args, **kwargs):
     global logger
     logger = wptlogging.setup(*args, **kwargs)
 
-def get_loader(test_paths, product, ssl_env, debug=None, **kwargs):
-    run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=debug)
+def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, **kwargs):
+    if run_info_extras is None:
+        run_info_extras = {}
+
+    run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=debug,
+                                    extras=run_info_extras)
 
     test_manifests = testloader.ManifestLoader(test_paths, force_manifest_update=kwargs["manifest_update"]).load()
 
     manifest_filters = []
     meta_filters = []
 
     if kwargs["include"] or kwargs["exclude"] or kwargs["include_manifest"]:
         manifest_filters.append(testloader.TestFilter(include=kwargs["include"],
@@ -106,27 +110,31 @@ def get_pause_after_test(test_loader, **
 
 def run_tests(config, test_paths, product, **kwargs):
     with wptlogging.CaptureIO(logger, not kwargs["no_capture_stdio"]):
         env.do_delayed_imports(logger, test_paths)
 
         (check_args,
          browser_cls, get_browser_kwargs,
          executor_classes, get_executor_kwargs,
-         env_options) = products.load_product(config, product)
+         env_options, run_info_extras) = products.load_product(config, product)
 
         ssl_env = env.ssl_env(logger, **kwargs)
 
         check_args(**kwargs)
 
         if "test_loader" in kwargs:
-            run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=None)
+            run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=None,
+                                            extras=run_info_extras(**kwargs))
             test_loader = kwargs["test_loader"]
         else:
-            run_info, test_loader = get_loader(test_paths, product, ssl_env,
+            run_info, test_loader = get_loader(test_paths,
+                                               product,
+                                               ssl_env,
+                                               run_info_extras=run_info_extras(**kwargs),
                                                **kwargs)
 
         if kwargs["run_by_dir"] is False:
             test_source_cls = testloader.SingleTestSource
             test_source_kwargs = {}
         else:
             # A value of None indicates infinite depth
             test_source_cls = testloader.PathGroupedSource
--- a/testing/web-platform/harness/wptrunner/wpttest.py
+++ b/testing/web-platform/harness/wptrunner/wpttest.py
@@ -52,25 +52,27 @@ class TestharnessSubtestResult(SubtestRe
 def get_run_info(metadata_root, product, **kwargs):
     if product == "b2g":
         return B2GRunInfo(metadata_root, product, **kwargs)
     else:
         return RunInfo(metadata_root, product, **kwargs)
 
 
 class RunInfo(dict):
-    def __init__(self, metadata_root, product, debug):
+    def __init__(self, metadata_root, product, debug, extras=None):
         self._update_mozinfo(metadata_root)
         self.update(mozinfo.info)
         self["product"] = product
         if debug is not None:
             self["debug"] = debug
         elif "debug" not in self:
             # Default to release
             self["debug"] = False
+        if extras is not None:
+            self.update(extras)
 
     def _update_mozinfo(self, metadata_root):
         """Add extra build information from a mozinfo.json file in a parent
         directory"""
         path = metadata_root
         dirs = set()
         while path != os.path.expanduser('~'):
             if path in dirs:
deleted file mode 100644
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-frame-resource.https.html.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[fetch-frame-resource.https.html]
-  type: testharness
-  [CORS type response could be loaded in the iframe.]
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1184967
-    expected: FAIL
-
-  [CORS type response could be loaded in the new window.]
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1184967
-    expected: FAIL
-
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html
@@ -101,17 +101,17 @@ async_test(function(t) {
       .then(function(reg) {
           return wait_for_state(t, reg.installing, 'activated');
         })
       .then(function() {
           var frame = document.createElement('iframe');
           frame.src =
             scope + '?mode=cors&url=' +
             encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path +
-                               '?ACAOrigin=' + host_info['HTTP_ORIGIN']);
+                               '?ACAOrigin=' + host_info['HTTPS_ORIGIN']);
           document.body.appendChild(frame);
           return getLoadedFrameAsObject(frame);
         })
       .then(function(result) {
           assert_equals(
             result.jsonpResult,
             'success',
             'CORS type response could be loaded in the iframe.');
@@ -171,17 +171,17 @@ async_test(function(t) {
     service_worker_unregister_and_register(t, worker, scope)
       .then(function(reg) {
           return wait_for_state(t, reg.installing, 'activated');
         })
       .then(function() {
           var win = window.open(
             scope + '?mode=cors&url=' +
             encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path +
-                               '?ACAOrigin=' + host_info['HTTP_ORIGIN']));
+                               '?ACAOrigin=' + host_info['HTTPS_ORIGIN']));
           return getLoadedWindowAsObject(win);
         })
       .then(function(result) {
           assert_equals(
             result.jsonpResult,
             'success',
             'CORS type response could be loaded in the new window.');
           return service_worker_unregister_and_done(t, scope);
--- a/toolkit/components/crashes/CrashManager.jsm
+++ b/toolkit/components/crashes/CrashManager.jsm
@@ -11,16 +11,17 @@ Cu.import("resource://gre/modules/Log.js
 Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
 Cu.import("resource://gre/modules/Timer.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://services-common/utils.js", this);
 Cu.import("resource://gre/modules/TelemetryController.jsm");
+Cu.import("resource://gre/modules/KeyValueParser.jsm");
 
 this.EXPORTED_SYMBOLS = [
   "CrashManager",
 ];
 
 /**
  * How long to wait after application startup before crash event files are
  * automatically aggregated.
@@ -519,21 +520,17 @@ this.CrashManager.prototype = Object.fre
           if (lines.length > 1) {
             this._log.warn("Multiple lines unexpected in payload for " +
                            entry.path);
             return this.EVENT_FILE_ERROR_MALFORMED;
           }
           // fall-through
         case "crash.main.2":
           let crashID = lines[0];
-          let metadata = {};
-          for (let i = 1; i < lines.length; i++) {
-            let [key, val] = lines[i].split("=");
-            metadata[key] = val;
-          }
+          let metadata = parseKeyValuePairsFromLines(lines.slice(1));
           store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH,
                          crashID, date, metadata);
 
           // If we have a saved environment, use it. Otherwise report
           // the current environment.
           let crashEnvironment = null;
           let reportMeta = Cu.cloneInto(metadata, myScope);
           if ('TelemetryEnvironment' in reportMeta) {
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
@@ -209,16 +209,20 @@ add_task(function* test_schedule_mainten
   Assert.equal(crashes[0].id, "id1");
 });
 
 add_task(function* test_main_crash_event_file() {
   let ac = new TelemetryArchiveTesting.Checker();
   yield ac.promiseInit();
   let theEnvironment = TelemetryEnvironment.currentEnvironment;
 
+  // To test proper escaping, add data to the environment with an embedded
+  // double-quote
+  theEnvironment.testValue = "MyValue\"";
+
   let m = yield getManager();
   yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1\nk1=v1\nk2=v2\nTelemetryEnvironment=" + JSON.stringify(theEnvironment));
   let count = yield m.aggregateEventsFiles();
   Assert.equal(count, 1);
 
   let crashes = yield m.getCrashes();
   Assert.equal(crashes.length, 1);
   Assert.equal(crashes[0].id, "id1");
--- a/toolkit/components/passwordmgr/test/unit/xpcshell.ini
+++ b/toolkit/components/passwordmgr/test/unit/xpcshell.ini
@@ -12,16 +12,18 @@ skip-if = os == "android"
 
 # Test SQLite database backup and migration, applicable to Android only.
 [test_storage_mozStorage.js]
 skip-if = true || os != "android" # Bug 1171687: Needs fixing on Android
 
 # The following tests apply to any storage back-end.
 [test_context_menu.js]
 skip-if = os == "android" # The context menu isn't used on Android.
+# LoginManagerContextMenu is only included for MOZ_BUILD_APP == 'browser'.
+run-if = buildapp == "browser"
 [test_disabled_hosts.js]
 [test_getFormFields.js]
 [test_getPasswordFields.js]
 [test_legacy_empty_formSubmitURL.js]
 [test_legacy_validation.js]
 [test_logins_change.js]
 [test_logins_decrypt_failure.js]
 skip-if = os == "android" # Bug 1171687: Needs fixing on Android
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -466,17 +466,17 @@ function checkActiveAddon(data){
     appDisabled: "boolean",
     version: "string",
     scope: "number",
     type: "string",
     foreignInstall: "boolean",
     hasBinaryComponents: "boolean",
     installDay: "number",
     updateDay: "number",
-    signedState: "number",
+    signedState: mozinfo.addon_signing ? "number" : "undefined",
   };
 
   for (let f in EXPECTED_ADDON_FIELDS_TYPES) {
     Assert.ok(f in data, f + " must be available.");
     Assert.equal(typeof data[f], EXPECTED_ADDON_FIELDS_TYPES[f],
                  f + " must have the correct type.");
   }
 
@@ -891,17 +891,17 @@ add_task(function* test_addonsAndPlugins
     appDisabled: false,
     version: "1.0",
     scope: 1,
     type: "extension",
     foreignInstall: false,
     hasBinaryComponents: false,
     installDay: ADDON_INSTALL_DATE,
     updateDay: ADDON_INSTALL_DATE,
-    signedState: AddonManager.SIGNEDSTATE_MISSING,
+    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
   };
 
   const EXPECTED_PLUGIN_DATA = {
     name: FLASH_PLUGIN_NAME,
     version: FLASH_PLUGIN_VERSION,
     description: FLASH_PLUGIN_DESC,
     blocklisted: false,
     disabled: false,
@@ -955,17 +955,17 @@ add_task(function* test_signedAddon() {
     appDisabled: false,
     version: "1.0",
     scope: 1,
     type: "extension",
     foreignInstall: false,
     hasBinaryComponents: false,
     installDay: ADDON_INSTALL_DATE,
     updateDay: ADDON_INSTALL_DATE,
-    signedState: AddonManager.SIGNEDSTATE_SIGNED,
+    signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
   };
 
   // Set the clock in the future so our changes don't get throttled.
   gNow = fakeNow(futureDate(gNow, 10 * MILLISECONDS_PER_MINUTE));
   let deferred = PromiseUtils.defer();
   TelemetryEnvironment.registerChangeListener("test_signedAddon", deferred.resolve);
 
   // Install the addon.
--- a/toolkit/crashreporter/KeyValueParser.jsm
+++ b/toolkit/crashreporter/KeyValueParser.jsm
@@ -1,41 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 this.EXPORTED_SYMBOLS = [
+  "parseKeyValuePairsFromLines",
   "parseKeyValuePairs",
   "parseKeyValuePairsFromFile"
 ];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
-this.parseKeyValuePairs = function parseKeyValuePairs(text) {
-  let lines = text.split('\n');
+this.parseKeyValuePairsFromLines = function(lines) {
   let data = {};
-  for (let i = 0; i < lines.length; i++) {
-    if (lines[i] == '')
+  for (let line of lines) {
+    if (line == '')
       continue;
 
     // can't just .split() because the value might contain = characters
-    let eq = lines[i].indexOf('=');
+    let eq = line.indexOf('=');
     if (eq != -1) {
-      let [key, value] = [lines[i].substring(0, eq),
-                          lines[i].substring(eq + 1)];
+      let [key, value] = [line.substring(0, eq),
+                          line.substring(eq + 1)];
       if (key && value)
         data[key] = value.replace(/\\n/g, "\n").replace(/\\\\/g, "\\");
     }
   }
   return data;
 }
 
+this.parseKeyValuePairs = function parseKeyValuePairs(text) {
+  let lines = text.split('\n');
+  return parseKeyValuePairsFromLines(lines);
+};
+
 this.parseKeyValuePairsFromFile = function parseKeyValuePairsFromFile(file) {
   let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
                 createInstance(Ci.nsIFileInputStream);
   fstream.init(file, -1, 0, 0);
   let is = Cc["@mozilla.org/intl/converter-input-stream;1"].
            createInstance(Ci.nsIConverterInputStream);
   is.init(fstream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
   let str = {};
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -2966,16 +2966,18 @@ this.AddonManager = {
   STARTUP_CHANGE_DISABLED: "disabled",
   // Add-ons that were detected as enabled during startup, normally because of
   // an application change making an add-on compatible. Doesn't include
   // add-ons that were pending being enabled the last time the application ran.
   STARTUP_CHANGE_ENABLED: "enabled",
 
   // Constants for Addon.signedState. Any states that should cause an add-on
   // to be unusable in builds that require signing should have negative values.
+  // Add-on signing is not required, e.g. because the pref is disabled.
+  SIGNEDSTATE_NOT_REQUIRED: undefined,
   // Add-on is signed but signature verification has failed.
   SIGNEDSTATE_BROKEN: -2,
   // Add-on may be signed but by an certificate that doesn't chain to our
   // our trusted certificate.
   SIGNEDSTATE_UNKNOWN: -1,
   // Add-on is unsigned.
   SIGNEDSTATE_MISSING: 0,
   // Add-on is preliminarily reviewed.
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1481,17 +1481,17 @@ function getSignedStatus(aRv, aCert, aEx
  * @param  aFile
  *         the xpi file to check
  * @param  aAddon
  *         the add-on object to verify
  * @return a Promise that resolves to an AddonManager.SIGNEDSTATE_* constant.
  */
 function verifyZipSignedState(aFile, aAddon) {
   if (!ADDON_SIGNING || !SIGNED_TYPES.has(aAddon.type))
-    return Promise.resolve(undefined);
+    return Promise.resolve(AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
   let certDB = Cc["@mozilla.org/security/x509certdb;1"]
                .getService(Ci.nsIX509CertDB);
 
   let root = Ci.nsIX509CertDB.AddonsPublicRoot;
   if (!REQUIRE_SIGNING && Preferences.get(PREF_XPI_SIGNATURES_DEV_ROOT, false))
     root = Ci.nsIX509CertDB.AddonsStageRoot;
 
@@ -1511,17 +1511,17 @@ function verifyZipSignedState(aFile, aAd
  * @param  aDir
  *         the directory to check
  * @param  aAddon
  *         the add-on object to verify
  * @return a Promise that resolves to an AddonManager.SIGNEDSTATE_* constant.
  */
 function verifyDirSignedState(aDir, aAddon) {
   if (!ADDON_SIGNING || !SIGNED_TYPES.has(aAddon.type))
-    return Promise.resolve(undefined);
+    return Promise.resolve(AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
   let certDB = Cc["@mozilla.org/security/x509certdb;1"]
                .getService(Ci.nsIX509CertDB);
 
   let root = Ci.nsIX509CertDB.AddonsPublicRoot;
   if (!REQUIRE_SIGNING && Preferences.get(PREF_XPI_SIGNATURES_DEV_ROOT, false))
     root = Ci.nsIX509CertDB.AddonsStageRoot;
 
@@ -5609,17 +5609,17 @@ AddonInstall.prototype = {
           return Promise.reject([AddonManager.ERROR_SIGNEDSTATE_REQUIRED,
                                  "signature is required but missing"])
 
         return Promise.reject([AddonManager.ERROR_CORRUPT_FILE,
                                "signature verification failed"])
       }
     }
     else if (this.addon.signedState == AddonManager.SIGNEDSTATE_UNKNOWN ||
-             this.addon.signedState == undefined) {
+             this.addon.signedState == AddonManager.SIGNEDSTATE_NOT_REQUIRED) {
       // Check object signing certificate, if any
       let x509 = zipreader.getSigningCert(null);
       if (x509) {
         logger.debug("Verifying XPI signature");
         if (verifyZipSigning(zipreader, x509)) {
           this.certificate = x509;
           if (this.certificate.commonName.length > 0) {
             this.certName = this.certificate.commonName;
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
@@ -48,17 +48,17 @@ add_task(function*() {
   let addon = yield promiseAddonByID(ID);
   do_check_neq(addon, null);
   do_check_eq(addon.version, "1.0");
   do_check_eq(addon.name, "Web Extension Name");
   do_check_true(addon.isCompatible);
   do_check_false(addon.appDisabled);
   do_check_true(addon.isActive);
   do_check_eq(addon.type, "extension");
-  do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+  do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
   // Should persist through a restart
   yield promiseShutdownManager();
 
   do_check_eq(GlobalManager.count, 0);
   do_check_false(GlobalManager.extensionMap.has(ID));
 
   startupManager();
@@ -70,17 +70,17 @@ add_task(function*() {
   addon = yield promiseAddonByID(ID);
   do_check_neq(addon, null);
   do_check_eq(addon.version, "1.0");
   do_check_eq(addon.name, "Web Extension Name");
   do_check_true(addon.isCompatible);
   do_check_false(addon.appDisabled);
   do_check_true(addon.isActive);
   do_check_eq(addon.type, "extension");
-  do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+  do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
   let file = getFileForAddon(profileDir, ID);
   do_check_true(file.exists());
 
   addon.userDisabled = true;
 
   do_check_eq(GlobalManager.count, 0);
   do_check_false(GlobalManager.extensionMap.has(ID));
@@ -117,17 +117,17 @@ add_task(function*() {
   let addon = yield promiseAddonByID(ID);
   do_check_neq(addon, null);
   do_check_eq(addon.version, "1.0");
   do_check_eq(addon.name, "Web Extension Name");
   do_check_true(addon.isCompatible);
   do_check_false(addon.appDisabled);
   do_check_true(addon.isActive);
   do_check_eq(addon.type, "extension");
-  do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+  do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
   let file = getFileForAddon(profileDir, ID);
   do_check_true(file.exists());
 
   addon.uninstall();
 
   yield promiseRestartManager();
 });
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -842,22 +842,16 @@ public:
   void SetIMEOpenState(bool aOpen);
   void SetASCIICapableOnly(bool aASCIICapableOnly);
 
   /**
    * True if OSX believes that our view has keyboard focus.
    */
   bool IsFocused();
 
-  /**
-   * True if our view has keyboard focus (and our window is key), or if
-   * it would have keyboard focus if our window were key.
-   */
-  bool IsOrWouldBeFocused();
-
   static CFArrayRef CreateAllIMEModeList();
   static void DebugPrintAllIMEModes();
 
   // Don't use ::TSMGetActiveDocument() API directly, the document may not
   // be what you want.
   static TSMDocumentID GetCurrentTSMDocumentID();
 
 protected:
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -3578,29 +3578,16 @@ IMEInputHandler::IsFocused()
   return [window firstResponder] == mView &&
          [window isKeyWindow] &&
          [[NSApplication sharedApplication] isActive];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
 }
 
 bool
-IMEInputHandler::IsOrWouldBeFocused()
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
-  NS_ENSURE_TRUE(!Destroyed(), false);
-  NSWindow* window = [mView window];
-  NS_ENSURE_TRUE(window, false);
-  return [window firstResponder] == mView && ![window attachedSheet];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
-}
-
-bool
 IMEInputHandler::IsIMEOpened()
 {
   TISInputSourceWrapper tis;
   tis.InitByCurrentInputSource();
   return tis.IsOpenedIMEMode();
 }
 
 void
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1641,20 +1641,22 @@ nsChildView::NotifyIMEInternal(const IME
       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
       mTextInputHandler->CommitIMEComposition();
       return NS_OK;
     case REQUEST_TO_CANCEL_COMPOSITION:
       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
       mTextInputHandler->CancelIMEComposition();
       return NS_OK;
     case NOTIFY_IME_OF_FOCUS:
-      if (mInputContext.IsPasswordEditor()) {
-        TextInputHandler::EnableSecureEventInput();
-      } else {
-        TextInputHandler::EnsureSecureEventInputDisabled();
+      if (mTextInputHandler->IsFocused()) {
+        if (mInputContext.IsPasswordEditor()) {
+          TextInputHandler::EnableSecureEventInput();
+        } else {
+          TextInputHandler::EnsureSecureEventInputDisabled();
+        }
       }
 
       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
       mTextInputHandler->OnFocusChangeInGecko(true);
       return NS_OK;
     case NOTIFY_IME_OF_BLUR:
       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
       mTextInputHandler->OnFocusChangeInGecko(false);
@@ -1710,17 +1712,17 @@ nsChildView::SetPluginFocused(bool& aFoc
 }
 
 NS_IMETHODIMP_(void)
 nsChildView::SetInputContext(const InputContext& aContext,
                              const InputContextAction& aAction)
 {
   NS_ENSURE_TRUE_VOID(mTextInputHandler);
 
-  if (mTextInputHandler->IsOrWouldBeFocused()) {
+  if (mTextInputHandler->IsFocused()) {
     if (aContext.IsPasswordEditor()) {
       TextInputHandler::EnableSecureEventInput();
     } else {
       TextInputHandler::EnsureSecureEventInputDisabled();
     }
   }
 
   mInputContext = aContext;
@@ -3135,17 +3137,17 @@ GLPresenter::EndFrame()
 #pragma mark -
 
 @implementation ChildView
 
 // globalDragPboard is non-null during native drag sessions that did not originate
 // in our native NSView (it is set in |draggingEntered:|). It is unset when the
 // drag session ends for this view, either with the mouse exiting or when a drop
 // occurs in this view.
-NSPasteboard* globalDragPboard = nil;
+NSPasteboardWrapper* globalDragPboard = nil;
 
 // gLastDragView and gLastDragMouseDownEvent are used to communicate information
 // to the drag service during drag invocation (starting a drag in from the view).
 // gLastDragView is only non-null while mouseDragged is on the call stack.
 NSView* gLastDragView = nil;
 NSEvent* gLastDragMouseDownEvent = nil;
 
 + (void)initialize
@@ -5636,16 +5638,22 @@ PanGestureTypeForEvent(NSEvent* aEvent)
 
   nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
   if (listener)
     listener->WindowActivated();
 
   if (isMozWindow)
     [[self window] setSuppressMakeKeyFront:NO];
 
+  if (mGeckoChild->GetInputContext().IsPasswordEditor()) {
+    TextInputHandler::EnableSecureEventInput();
+  } else {
+    TextInputHandler::EnsureSecureEventInputDisabled();
+  }
+
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)viewsWindowDidResignKey
 {
   if (!mGeckoChild)
     return;
 
@@ -5833,17 +5841,18 @@ PanGestureTypeForEvent(NSEvent* aEvent)
 
   // there should never be a globalDragPboard when "draggingEntered:" is
   // called, but just in case we'll take care of it here.
   [globalDragPboard release];
 
   // Set the global drag pasteboard that will be used for this drag session.
   // This will be set back to nil when the drag session ends (mouse exits
   // the view or a drop happens within the view).
-  globalDragPboard = [[sender draggingPasteboard] retain];
+  globalDragPboard =
+    [[NSPasteboardWrapper alloc] initWithPasteboard:[sender draggingPasteboard]];
 
   return [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
 }
 
 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
 {
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -2275,45 +2275,30 @@ void nsCocoaWindow::SetPopupWindowLevel(
   }
 }
 
 nsresult
 nsCocoaWindow::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
   switch (aIMENotification.mMessage) {
     case NOTIFY_IME_OF_FOCUS:
-      if (mInputContext.IsPasswordEditor()) {
-        TextInputHandler::EnableSecureEventInput();
-      } else {
-        TextInputHandler::EnsureSecureEventInputDisabled();
-      }
       return NS_OK;
     case NOTIFY_IME_OF_BLUR:
       return NS_OK;
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 NS_IMETHODIMP_(void)
 nsCocoaWindow::SetInputContext(const InputContext& aContext,
                                const InputContextAction& aAction)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  if (mWindow &&
-      [mWindow firstResponder] == mWindow &&
-      [mWindow isKeyWindow] &&
-      [[NSApplication sharedApplication] isActive]) {
-    if (aContext.IsPasswordEditor()) {
-      TextInputHandler::EnableSecureEventInput();
-    } else {
-      TextInputHandler::EnsureSecureEventInputDisabled();
-    }
-  }
   mInputContext = aContext;
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 NS_IMETHODIMP_(bool)
 nsCocoaWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
                                        const WidgetKeyboardEvent& aEvent,
@@ -2540,16 +2525,26 @@ nsCocoaWindow::ExecuteNativeKeyBinding(N
 
   RollUpPopups();
   ChildViewMouseTracker::ReEvaluateMouseEnterState();
 
   NSWindow* window = [aNotification object];
   if ([window isSheet])
     [WindowDelegate paintMenubarForWindow:window];
 
+  nsChildView* mainChildView =
+    static_cast<nsChildView*>([[(BaseWindow*)window mainChildView] widget]);
+  if (mainChildView) {
+    if (mainChildView->GetInputContext().IsPasswordEditor()) {
+      TextInputHandler::EnableSecureEventInput();
+    } else {
+      TextInputHandler::EnsureSecureEventInputDisabled();
+    }
+  }
+
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)windowDidResignKey:(NSNotification *)aNotification
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   RollUpPopups();
--- a/widget/cocoa/nsDragService.h
+++ b/widget/cocoa/nsDragService.h
@@ -10,16 +10,26 @@
 
 #include <Cocoa/Cocoa.h>
 
 extern NSString* const kWildcardPboardType;
 extern NSString* const kCorePboardType_url;
 extern NSString* const kCorePboardType_urld;
 extern NSString* const kCorePboardType_urln;
 
+@interface NSPasteboardWrapper : NSObject
+{
+  NSPasteboard* mPasteboard;
+  NSArray* mFilenames;
+}
+- (id)initWithPasteboard:(NSPasteboard*)aPasteboard;
+- (id)propertyListForType:(NSString*)aType;
+- (NSPasteboard*)pasteboard;
+@end
+
 class nsDragService : public nsBaseDragService
 {
 public:
   nsDragService();
 
   // nsIDragService
   NS_IMETHOD InvokeDragSession(nsIDOMNode *aDOMNode, nsISupportsArray * anArrayTransferables,
                                nsIScriptableRegion * aRegion, uint32_t aActionType);
--- a/widget/cocoa/nsDragService.mm
+++ b/widget/cocoa/nsDragService.mm
@@ -30,30 +30,70 @@
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 extern PRLogModuleInfo* sCocoaLog;
 
 extern void EnsureLogInitialized();
 
-extern NSPasteboard* globalDragPboard;
+extern NSPasteboardWrapper* globalDragPboard;
 extern NSView* gLastDragView;
 extern NSEvent* gLastDragMouseDownEvent;
 extern bool gUserCancelledDrag;
 
 // This global makes the transferable array available to Cocoa's promised
 // file destination callback.
 nsISupportsArray *gDraggedTransferables = nullptr;
 
 NSString* const kWildcardPboardType = @"MozillaWildcard";
 NSString* const kCorePboardType_url  = @"CorePasteboardFlavorType 0x75726C20"; // 'url '  url
 NSString* const kCorePboardType_urld = @"CorePasteboardFlavorType 0x75726C64"; // 'urld'  desc
 NSString* const kCorePboardType_urln = @"CorePasteboardFlavorType 0x75726C6E"; // 'urln'  title
 
+@implementation NSPasteboardWrapper
+- (id)initWithPasteboard:(NSPasteboard*)aPasteboard
+{
+  if ((self = [super init])) {
+    mPasteboard = [aPasteboard retain];
+    mFilenames = nil;
+  }
+  return self;
+}
+
+- (id)propertyListForType:(NSString *)aType
+{
+  if (![aType isEqualToString:NSFilenamesPboardType]) {
+    return [mPasteboard propertyListForType:aType];
+  }
+
+  if (!mFilenames) {
+    mFilenames = [[mPasteboard propertyListForType:aType] retain];
+  }
+
+  return mFilenames;
+}
+
+- (NSPasteboard*) pasteboard
+{
+  return mPasteboard;
+}
+
+- (void)dealloc
+{
+  [mPasteboard release];
+  mPasteboard = nil;
+
+  [mFilenames release];
+  mFilenames = nil;
+
+  [super dealloc];
+}
+@end
+
 nsDragService::nsDragService()
 {
   mNativeDragView = nil;
   mNativeDragEvent = nil;
 
   EnsureLogInitialized();
 }
 
@@ -396,17 +436,17 @@ nsDragService::GetData(nsITransferable* 
     }
 
     NSString *pboardType = NSStringPboardType;
 
     if (nsClipboard::IsStringType(flavorStr, &pboardType) ||
         flavorStr.EqualsLiteral(kURLMime) ||
         flavorStr.EqualsLiteral(kURLDataMime) ||
         flavorStr.EqualsLiteral(kURLDescriptionMime)) {
-      NSString* pString = [globalDragPboard stringForType:pboardType];
+      NSString* pString = [[globalDragPboard pasteboard] stringForType:pboardType];
       if (!pString)
         continue;
 
       NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
       unsigned int dataLength = [stringData length];
       void* clipboardDataPtr = malloc(dataLength);
       if (!clipboardDataPtr)
         return NS_ERROR_OUT_OF_MEMORY;
@@ -495,27 +535,27 @@ nsDragService::IsDataFlavorSupported(con
         }
       }
     }
   }
 
   NSString *pboardType = nil;
 
   if (dataFlavor.EqualsLiteral(kFileMime)) {
-    NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]];
+    NSString* availableType = [[globalDragPboard pasteboard] availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]];
     if (availableType && [availableType isEqualToString:NSFilenamesPboardType])
       *_retval = true;
   }
   else if (dataFlavor.EqualsLiteral(kURLMime)) {
-    NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObject:kCorePboardType_url]];
+    NSString* availableType = [[globalDragPboard pasteboard] availableTypeFromArray:[NSArray arrayWithObject:kCorePboardType_url]];
     if (availableType && [availableType isEqualToString:kCorePboardType_url])
       *_retval = true;
   }
   else if (nsClipboard::IsStringType(dataFlavor, &pboardType)) {
-    NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObject:pboardType]];
+    NSString* availableType = [[globalDragPboard pasteboard] availableTypeFromArray:[NSArray arrayWithObject:pboardType]];
     if (availableType && [availableType isEqualToString:pboardType])
       *_retval = true;
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
@@ -529,17 +569,17 @@ nsDragService::GetNumDropItems(uint32_t*
 
   // first check to see if we have a number of items cached
   if (mDataItems) {
     mDataItems->Count(aNumItems);
     return NS_OK;
   }
 
   // if there is a clipboard and there is something on it, then there is at least 1 item
-  NSArray* clipboardTypes = [globalDragPboard types];
+  NSArray* clipboardTypes = [[globalDragPboard pasteboard] types];
   if (globalDragPboard && [clipboardTypes count] > 0)
     *aNumItems = 1;
   else 
     return NS_OK;
   
   // if there is a list of files, send the number of files in that list
   NSArray* fileNames = [globalDragPboard propertyListForType:NSFilenamesPboardType];
   if (fileNames)