Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 28 May 2015 10:03:07 -0400
changeset 276722 6bf6fe1c6516366db3acf1b561a7ed451194c2bd
parent 276656 136bcd7ad566b9b97153c2f4840c1fbb78981d1f (current diff)
parent 276721 da2e29afa891646b7a782f5c9868f53fca47c354 (diff)
child 276763 f986e55c4e0b41c6b50bd74d287614b564d7895f
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.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
dom/browser-element/BrowserElementChildPreload.js
netwerk/base/nsISiteSecurityService.idl
security/manager/boot/moz.build
security/manager/boot/public/moz.build
security/manager/boot/public/nsIBufEntropyCollector.idl
security/manager/boot/public/nsICertBlocklist.idl
security/manager/boot/public/nsISSLStatusProvider.idl
security/manager/boot/public/nsISecurityUITelemetry.idl
security/manager/boot/src/CertBlocklist.cpp
security/manager/boot/src/CertBlocklist.h
security/manager/boot/src/DataStorage.cpp
security/manager/boot/src/DataStorage.h
security/manager/boot/src/PublicKeyPinningService.cpp
security/manager/boot/src/PublicKeyPinningService.h
security/manager/boot/src/RootCertificateTelemetryUtils.cpp
security/manager/boot/src/RootCertificateTelemetryUtils.h
security/manager/boot/src/RootHashes.inc
security/manager/boot/src/StaticHPKPins.errors
security/manager/boot/src/StaticHPKPins.h
security/manager/boot/src/moz.build
security/manager/boot/src/nsBOOTModule.cpp
security/manager/boot/src/nsEntropyCollector.cpp
security/manager/boot/src/nsEntropyCollector.h
security/manager/boot/src/nsSTSPreloadList.errors
security/manager/boot/src/nsSTSPreloadList.inc
security/manager/boot/src/nsSecureBrowserUIImpl.cpp
security/manager/boot/src/nsSecureBrowserUIImpl.h
security/manager/boot/src/nsSecurityHeaderParser.cpp
security/manager/boot/src/nsSecurityHeaderParser.h
security/manager/boot/src/nsSiteSecurityService.cpp
security/manager/boot/src/nsSiteSecurityService.h
security/manager/ssl/public/moz.build
security/manager/ssl/public/nsIASN1Object.idl
security/manager/ssl/public/nsIASN1PrintableItem.idl
security/manager/ssl/public/nsIASN1Sequence.idl
security/manager/ssl/public/nsIAssociatedContentSecurity.idl
security/manager/ssl/public/nsIBadCertListener2.idl
security/manager/ssl/public/nsICertOverrideService.idl
security/manager/ssl/public/nsICertPickDialogs.idl
security/manager/ssl/public/nsICertTree.idl
security/manager/ssl/public/nsICertificateDialogs.idl
security/manager/ssl/public/nsIClientAuthDialogs.idl
security/manager/ssl/public/nsIDataSignatureVerifier.idl
security/manager/ssl/public/nsIGenKeypairInfoDlg.idl
security/manager/ssl/public/nsIKeyModule.idl
security/manager/ssl/public/nsIKeygenThread.idl
security/manager/ssl/public/nsINSSVersion.idl
security/manager/ssl/public/nsIPK11Token.idl
security/manager/ssl/public/nsIPK11TokenDB.idl
security/manager/ssl/public/nsIPKCS11.idl
security/manager/ssl/public/nsIPKCS11Module.idl
security/manager/ssl/public/nsIPKCS11ModuleDB.idl
security/manager/ssl/public/nsIPKCS11Slot.idl
security/manager/ssl/public/nsIProtectedAuthThread.idl
security/manager/ssl/public/nsISSLStatus.idl
security/manager/ssl/public/nsITokenDialogs.idl
security/manager/ssl/public/nsITokenPasswordDialogs.idl
security/manager/ssl/public/nsIUserCertPicker.idl
security/manager/ssl/public/nsIX509Cert.idl
security/manager/ssl/public/nsIX509CertDB.idl
security/manager/ssl/public/nsIX509CertList.idl
security/manager/ssl/public/nsIX509CertValidity.idl
security/manager/ssl/src/CryptoTask.cpp
security/manager/ssl/src/CryptoTask.h
security/manager/ssl/src/CryptoUtil.h
security/manager/ssl/src/IntolerantFallbackList.inc
security/manager/ssl/src/NSSErrorsService.cpp
security/manager/ssl/src/NSSErrorsService.h
security/manager/ssl/src/PPSMContentDownloader.ipdl
security/manager/ssl/src/PSMContentListener.cpp
security/manager/ssl/src/PSMContentListener.h
security/manager/ssl/src/PSMRunnable.cpp
security/manager/ssl/src/PSMRunnable.h
security/manager/ssl/src/PublicSSL.h
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/src/SSLServerCertVerification.h
security/manager/ssl/src/ScopedNSSTypes.h
security/manager/ssl/src/SharedCertVerifier.h
security/manager/ssl/src/SharedSSLState.cpp
security/manager/ssl/src/SharedSSLState.h
security/manager/ssl/src/TransportSecurityInfo.cpp
security/manager/ssl/src/TransportSecurityInfo.h
security/manager/ssl/src/md4.c
security/manager/ssl/src/md4.h
security/manager/ssl/src/moz.build
security/manager/ssl/src/nsCertOverrideService.cpp
security/manager/ssl/src/nsCertOverrideService.h
security/manager/ssl/src/nsCertPicker.cpp
security/manager/ssl/src/nsCertPicker.h
security/manager/ssl/src/nsCertTree.cpp
security/manager/ssl/src/nsCertTree.h
security/manager/ssl/src/nsCertVerificationThread.cpp
security/manager/ssl/src/nsCertVerificationThread.h
security/manager/ssl/src/nsClientAuthRemember.cpp
security/manager/ssl/src/nsClientAuthRemember.h
security/manager/ssl/src/nsCrypto.cpp
security/manager/ssl/src/nsCrypto.h
security/manager/ssl/src/nsCryptoHash.cpp
security/manager/ssl/src/nsCryptoHash.h
security/manager/ssl/src/nsDataSignatureVerifier.cpp
security/manager/ssl/src/nsDataSignatureVerifier.h
security/manager/ssl/src/nsKeyModule.cpp
security/manager/ssl/src/nsKeyModule.h
security/manager/ssl/src/nsKeygenHandler.cpp
security/manager/ssl/src/nsKeygenHandler.h
security/manager/ssl/src/nsKeygenHandlerContent.cpp
security/manager/ssl/src/nsKeygenHandlerContent.h
security/manager/ssl/src/nsKeygenThread.cpp
security/manager/ssl/src/nsKeygenThread.h
security/manager/ssl/src/nsNSSASN1Object.cpp
security/manager/ssl/src/nsNSSASN1Object.h
security/manager/ssl/src/nsNSSCallbacks.cpp
security/manager/ssl/src/nsNSSCallbacks.h
security/manager/ssl/src/nsNSSCertHelper.cpp
security/manager/ssl/src/nsNSSCertHelper.h
security/manager/ssl/src/nsNSSCertTrust.cpp
security/manager/ssl/src/nsNSSCertTrust.h
security/manager/ssl/src/nsNSSCertValidity.cpp
security/manager/ssl/src/nsNSSCertValidity.h
security/manager/ssl/src/nsNSSCertificate.cpp
security/manager/ssl/src/nsNSSCertificate.h
security/manager/ssl/src/nsNSSCertificateDB.cpp
security/manager/ssl/src/nsNSSCertificateDB.h
security/manager/ssl/src/nsNSSCertificateFakeTransport.cpp
security/manager/ssl/src/nsNSSCertificateFakeTransport.h
security/manager/ssl/src/nsNSSComponent.cpp
security/manager/ssl/src/nsNSSComponent.h
security/manager/ssl/src/nsNSSErrors.cpp
security/manager/ssl/src/nsNSSHelper.h
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
security/manager/ssl/src/nsNSSModule.cpp
security/manager/ssl/src/nsNSSShutDown.cpp
security/manager/ssl/src/nsNSSShutDown.h
security/manager/ssl/src/nsNSSVersion.cpp
security/manager/ssl/src/nsNSSVersion.h
security/manager/ssl/src/nsNTLMAuthModule.cpp
security/manager/ssl/src/nsNTLMAuthModule.h
security/manager/ssl/src/nsPK11TokenDB.cpp
security/manager/ssl/src/nsPK11TokenDB.h
security/manager/ssl/src/nsPKCS11Slot.cpp
security/manager/ssl/src/nsPKCS11Slot.h
security/manager/ssl/src/nsPKCS12Blob.cpp
security/manager/ssl/src/nsPKCS12Blob.h
security/manager/ssl/src/nsPSMBackgroundThread.cpp
security/manager/ssl/src/nsPSMBackgroundThread.h
security/manager/ssl/src/nsProtectedAuthThread.cpp
security/manager/ssl/src/nsProtectedAuthThread.h
security/manager/ssl/src/nsRandomGenerator.cpp
security/manager/ssl/src/nsRandomGenerator.h
security/manager/ssl/src/nsSDR.cpp
security/manager/ssl/src/nsSDR.h
security/manager/ssl/src/nsSSLSocketProvider.cpp
security/manager/ssl/src/nsSSLSocketProvider.h
security/manager/ssl/src/nsSSLStatus.cpp
security/manager/ssl/src/nsSSLStatus.h
security/manager/ssl/src/nsSmartCardMonitor.cpp
security/manager/ssl/src/nsSmartCardMonitor.h
security/manager/ssl/src/nsTLSSocketProvider.cpp
security/manager/ssl/src/nsTLSSocketProvider.h
security/manager/ssl/src/nsUsageArrayHelper.cpp
security/manager/ssl/src/nsUsageArrayHelper.h
security/manager/ssl/src/nsVerificationJob.h
--- a/accessible/interfaces/msaa/moz.build
+++ b/accessible/interfaces/msaa/moz.build
@@ -1,24 +1,24 @@
 # -*- 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/.
 
 GeckoSharedLibrary('AccessibleMarshal', linkage=None)
 
-GENERATED_SOURCES += [
-    'dlldata.c',
-    'ISimpleDOMDocument_i.c',
-    'ISimpleDOMDocument_p.c',
-    'ISimpleDOMNode_i.c',
-    'ISimpleDOMNode_p.c',
-    'ISimpleDOMText_i.c',
-    'ISimpleDOMText_p.c',
+SOURCES += [
+    '!dlldata.c',
+    '!ISimpleDOMDocument_i.c',
+    '!ISimpleDOMDocument_p.c',
+    '!ISimpleDOMNode_i.c',
+    '!ISimpleDOMNode_p.c',
+    '!ISimpleDOMText_i.c',
+    '!ISimpleDOMText_p.c',
 ]
 
 DEFINES['REGISTER_PROXY_DLL'] = True
 
 DEFFILE = SRCDIR + '/AccessibleMarshal.def'
 
 OS_LIBS += [
     'kernel32',
--- a/accessible/xpcom/moz.build
+++ b/accessible/xpcom/moz.build
@@ -15,18 +15,18 @@ UNIFIED_SOURCES += [
     'xpcAccessibleImage.cpp',
     'xpcAccessibleSelectable.cpp',
     'xpcAccessibleTable.cpp',
     'xpcAccessibleTableCell.cpp',
     'xpcAccessibleTextRange.cpp',
     'xpcAccessibleValue.cpp',
 ]
 
-GENERATED_SOURCES += [
-    'xpcAccEvents.cpp',
+SOURCES += [
+    '!xpcAccEvents.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
 ]
 
 if CONFIG['MOZ_ENABLE_GTK']:
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -815,17 +815,16 @@
 ; svg
 @RESPATH@/res/svg.css
 @RESPATH@/components/dom_svg.xpt
 @RESPATH@/components/dom_smil.xpt
 
 ; [Personal Security Manager]
 ;
 @BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
-@RESPATH@/components/pipboot.xpt
 @RESPATH@/components/pipnss.xpt
 @RESPATH@/components/pippki.xpt
 @BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
 #ifndef MOZ_FOLD_LIBS
 @BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@
 #endif
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1689,17 +1689,17 @@ pref("pdfjs.previousHandler.alwaysAskBef
 
 // Shumway is only bundled in Nightly.
 #ifdef NIGHTLY_BUILD
 // By default, Shumway (SWF player) is only enabled for whitelisted SWFs on Windows + OS X.
 #ifdef UNIX_BUT_NOT_MAC
 pref("shumway.disabled", true);
 #else
 pref("shumway.disabled", false);
-pref("shumway.swf.whitelist", "http://g-ecx.images-amazon.com/*/AiryBasicRenderer*.swf,http://z-ecx.images-amazon.com/*/AiryFlashlsRenderer._TTW_.swf,http://ia.media-imdb.com/*/AiryFlashlsRenderer._TTW_.swf");
+pref("shumway.swf.whitelist", "http://www.areweflashyet.com/*.swf");
 #endif
 #endif
 
 // The maximum amount of decoded image data we'll willingly keep around (we
 // might keep around more than this, but we'll try to get down to this value).
 // (This is intentionally on the high side; see bug 746055.)
 pref("image.mem.max_decoded_image_kb", 256000);
 
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -111,17 +111,17 @@ var FullScreen = {
         // operating system level in the parent process ourselves.
         if (this._isRemoteBrowser(browser)) {
           this._windowUtils.remoteFrameFullscreenChanged(browser);
         }
         this.enterDomFullscreen(browser);
         break;
       }
       case "DOMFullscreen:NewOrigin": {
-        this.showWarning(aMessage.data.origin);
+        this.showWarning(aMessage.data.originNoSuffix);
         break;
       }
       case "DOMFullscreen:Exited": {
         // Like entering DOM fullscreen, we also need to exit fullscreen
         // at the operating system level in the parent process here.
         if (this._isRemoteBrowser(browser)) {
           this._windowUtils.remoteFrameFullscreenReverted();
         }
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -620,17 +620,17 @@ let DOMFullscreenHandler = {
     switch (aEvent.type) {
       case "MozDOMFullscreen:Entered": {
         sendAsyncMessage("DOMFullscreen:Entered");
         break;
       }
       case "MozDOMFullscreen:NewOrigin": {
         this._fullscreenDoc = aEvent.target;
         sendAsyncMessage("DOMFullscreen:NewOrigin", {
-          origin: this._fullscreenDoc.nodePrincipal.origin,
+          originNoSuffix: this._fullscreenDoc.nodePrincipal.originNoSuffix,
         });
         break;
       }
       case "MozDOMFullscreen:Exited": {
         sendAsyncMessage("DOMFullscreen:Exited");
         break;
       }
     }
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -795,17 +795,16 @@
 @BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@
 #endif
 @BINPATH@/@DLL_PREFIX@softokn3@DLL_SUFFIX@
 #endif
 @RESPATH@/chrome/pippki@JAREXT@
 @RESPATH@/chrome/pippki.manifest
-@RESPATH@/components/pipboot.xpt
 @RESPATH@/components/pipnss.xpt
 @RESPATH@/components/pippki.xpt
 
 ; For process sandboxing
 #if defined(MOZ_SANDBOX)
 #if defined(XP_WIN)
 @BINPATH@/@DLL_PREFIX@sandboxbroker@DLL_SUFFIX@
 #if defined(WOW_HELPER)
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -46,16 +46,17 @@ SEARCH_PATHS = [
     'other-licenses/ply',
     'xpcom/idl-parser',
     'testing',
     'testing/taskcluster',
     'testing/xpcshell',
     'testing/web-platform',
     'testing/web-platform/harness',
     'testing/marionette/client',
+    'testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py',
     'testing/marionette/transport',
     'testing/marionette/driver',
     'testing/luciddream',
     'testing/mozbase/mozcrash',
     'testing/mozbase/mozdebug',
     'testing/mozbase/mozdevice',
     'testing/mozbase/mozfile',
     'testing/mozbase/mozhttpd',
--- a/build/unix/elfhack/inject/moz.build
+++ b/build/unix/elfhack/inject/moz.build
@@ -8,15 +8,15 @@ DIST_INSTALL = False
 
 if CONFIG['TARGET_CPU'].endswith('86'):
     cpu = 'x86'
 elif CONFIG['TARGET_CPU'].startswith('arm'):
     cpu = 'arm'
 else:
     cpu = CONFIG['TARGET_CPU']
 
-GENERATED_SOURCES += [
-    "%s.c" % cpu,
+SOURCES += [
+    "!%s.c" % cpu,
 ]
 
 NO_PGO = True
 
 NO_VISIBILITY_FLAGS = True
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -1,10 +1,11 @@
 marionette_transport.pth:testing/marionette/transport
 marionette_driver.pth:testing/marionette/driver
+browsermobproxy.pth:testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py
 marionette.pth:testing/marionette/client
 blessings.pth:python/blessings
 configobj.pth:python/configobj
 jsmin.pth:python/jsmin
 mach.pth:python/mach
 mozbuild.pth:python/mozbuild
 pymake.pth:build/pymake
 optional:setup.py:python/psutil:build_ext:--inplace
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -960,17 +960,17 @@ BrowserElementChild.prototype = {
   },
 
   _mozEnteredDomFullscreen: function(e) {
     sendAsyncMsg("entered-dom-fullscreen");
   },
 
   _mozFullscreenOriginChange: function(e) {
     sendAsyncMsg("fullscreen-origin-change", {
-      origin: e.target.nodePrincipal.origin
+      originNoSuffix: e.target.nodePrincipal.originNoSuffix
     });
   },
 
   _mozExitedDomFullscreen: function(e) {
     sendAsyncMsg("exited-dom-fullscreen");
   },
 
   _getContentDimensions: function() {
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -941,17 +941,17 @@ BrowserElementParent.prototype = {
   },
 
   _enteredDomFullscreen: function() {
     this._windowUtils.remoteFrameFullscreenChanged(this._frameElement);
   },
 
   _fullscreenOriginChange: function(data) {
     Services.obs.notifyObservers(
-      this._frameElement, "fullscreen-origin-change", data.json.origin);
+      this._frameElement, "fullscreen-origin-change", data.json.originNoSuffix);
   },
 
   _exitedDomFullscreen: function(data) {
     this._windowUtils.remoteFrameFullscreenReverted();
   },
 
   _fullscreenChange: function(evt) {
     if (this._isAlive() && evt.target == this._window.document) {
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -1,16 +1,17 @@
 /* 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 protocol PCache;
 include protocol PCachePushStream;
 include protocol PCacheStreamControl;
 include InputStreamParams;
+include ChannelInfo;
 
 using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
 using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
 using RequestMode from "mozilla/dom/cache/IPCUtils.h";
 using RequestCache from "mozilla/dom/cache/IPCUtils.h";
 using RequestContext from "mozilla/dom/cache/IPCUtils.h";
 using ResponseType from "mozilla/dom/cache/IPCUtils.h";
 using mozilla::void_t from "ipc/IPCMessageUtils.h";
@@ -76,17 +77,17 @@ struct CacheResponse
 {
   ResponseType type;
   nsString url;
   uint32_t status;
   nsCString statusText;
   HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
   CacheReadStreamOrVoid body;
-  nsCString securityInfo;
+  IPCChannelInfo channelInfo;
 };
 
 union CacheResponseOrVoid
 {
   void_t;
   CacheResponse;
 };
 
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -170,18 +170,18 @@ static nsresult QueryCache(mozIStorageCo
 static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
                                   const CacheRequest& aRequest,
                                   EntryId entryId, bool* aSuccessOut);
 static nsresult DeleteEntries(mozIStorageConnection* aConn,
                               const nsTArray<EntryId>& aEntryIdList,
                               nsTArray<nsID>& aDeletedBodyIdListOut,
                               nsTArray<IdCount>& aDeletedSecurityIdListOut,
                               uint32_t aPos=0, int32_t aLen=-1);
-static nsresult InsertSecurity(mozIStorageConnection* aConn,
-                               const nsACString& aData, int32_t *aIdOut);
+static nsresult InsertSecurityInfo(mozIStorageConnection* aConn,
+                                   const nsACString& aData, int32_t *aIdOut);
 static nsresult DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId,
                                    int32_t aCount);
 static nsresult DeleteSecurityInfoList(mozIStorageConnection* aConn,
                                        const nsTArray<IdCount>& aDeletedStorageIdList);
 static nsresult InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
                             const CacheRequest& aRequest,
                             const nsID* aRequestBodyId,
                             const CacheResponse& aResponse,
@@ -1189,18 +1189,18 @@ DeleteEntries(mozIStorageConnection* aCo
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 nsresult
-InsertSecurity(mozIStorageConnection* aConn, const nsACString& aData,
-               int32_t *aIdOut)
+InsertSecurityInfo(mozIStorageConnection* aConn, const nsACString& aData,
+                   int32_t *aIdOut)
 {
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aIdOut);
   MOZ_ASSERT(!aData.IsEmpty());
 
   // We want to use an index to find existing security blobs, but indexing
   // the full blob would be quite expensive.  Instead, we index a small
   // hash value.  Calculate this hash as the first 8 bytes of the SHA1 of
@@ -1389,18 +1389,20 @@ InsertEntry(mozIStorageConnection* aConn
             const nsID* aResponseBodyId)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   nsresult rv = NS_OK;
   int32_t securityId = -1;
 
-  if (!aResponse.securityInfo().IsEmpty()) {
-    rv = InsertSecurity(aConn, aResponse.securityInfo(), &securityId);
+  if (!aResponse.channelInfo().securityInfo().IsEmpty()) {
+    rv = InsertSecurityInfo(aConn,
+                            aResponse.channelInfo().securityInfo(),
+                            &securityId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   nsCOMPtr<mozIStorageStatement> state;
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO entries ("
       "request_method, "
       "request_url, "
@@ -1506,17 +1508,17 @@ InsertEntry(mozIStorageConnection* aConn
 
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_headers_guard"),
     static_cast<int32_t>(aResponse.headersGuard()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = BindId(state, NS_LITERAL_CSTRING("response_body_id"), aResponseBodyId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-  if (aResponse.securityInfo().IsEmpty()) {
+  if (aResponse.channelInfo().securityInfo().IsEmpty()) {
     rv = state->BindNullByName(NS_LITERAL_CSTRING("response_security_info_id"));
   } else {
     rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_security_info_id"),
                                 securityId);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
@@ -1652,17 +1654,17 @@ ReadResponse(mozIStorageConnection* aCon
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedResponseOut->mHasBodyId = !nullBody;
 
   if (aSavedResponseOut->mHasBodyId) {
     rv = ExtractId(state, 5, &aSavedResponseOut->mBodyId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
-  rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.securityInfo());
+  rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "name, "
       "value "
     "FROM response_headers "
     "WHERE entry_id=:entry_id;"
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -219,17 +219,17 @@ TypeUtils::ToCacheResponseWithoutBody(Ca
   nsRefPtr<InternalHeaders> headers = aIn.UnfilteredHeaders();
   MOZ_ASSERT(headers);
   if (HasVaryStar(headers)) {
     aRv.ThrowTypeError(MSG_RESPONSE_HAS_VARY_STAR);
     return;
   }
   ToHeadersEntryList(aOut.headers(), headers);
   aOut.headersGuard() = headers->Guard();
-  aOut.securityInfo() = aIn.GetSecurityInfo();
+  aOut.channelInfo() = aIn.GetChannelInfo().AsIPCChannelInfo();
 }
 
 void
 TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn, ErrorResult& aRv)
 {
   if (aIn.BodyUsed()) {
     aRv.ThrowTypeError(MSG_FETCH_BODY_CONSUMED_ERROR);
     return;
@@ -285,17 +285,17 @@ TypeUtils::ToResponse(const CacheRespons
   nsRefPtr<InternalHeaders> internalHeaders =
     ToInternalHeaders(aIn.headers(), aIn.headersGuard());
   ErrorResult result;
   ir->Headers()->SetGuard(aIn.headersGuard(), result);
   MOZ_ASSERT(!result.Failed());
   ir->Headers()->Fill(*internalHeaders, result);
   MOZ_ASSERT(!result.Failed());
 
-  ir->SetSecurityInfo(aIn.securityInfo());
+  ir->InitChannelInfo(aIn.channelInfo());
 
   nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
   ir->SetBody(stream);
 
   switch (aIn.type())
   {
     case ResponseType::Default:
       break;
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -123,16 +123,24 @@ WebGLContext::InitWebGL2()
 
     std::vector<gl::GLFeature> missingList;
 
     for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) {
         if (!gl->IsSupported(kRequiredFeatures[i]))
             missingList.push_back(kRequiredFeatures[i]);
     }
 
+#ifdef XP_MACOSX
+    // On OSX, GL core profile is used. This requires texture swizzle
+    // support to emulate legacy texture formats: ALPHA, LUMINANCE,
+    // and LUMINANCE_ALPHA.
+    if (!gl->IsSupported(gl::GLFeature::texture_swizzle))
+        missingList.push_back(gl::GLFeature::texture_swizzle);
+#endif
+
     if (missingList.size()) {
         nsAutoCString exts;
         for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
             exts.AppendLiteral("\n  ");
             exts.Append(gl::GLContext::GetFeatureName(*itr));
         }
         GenerateWarning("WebGL 2 unavailable. The following required features are"
                         " unavailible: %s", exts.BeginReading());
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -3193,16 +3193,19 @@ GLenum WebGLContext::CheckedTexImage2D(T
                                              &driverType);
 
     if (sizeMayChange) {
         GetAndFlushUnderlyingGLErrors();
     }
 
     gl->fTexImage2D(texImageTarget.get(), level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
 
+    if (effectiveInternalFormat != driverInternalFormat)
+        SetLegacyTextureSwizzle(gl, texImageTarget.get(), internalformat.get());
+
     GLenum error = LOCAL_GL_NO_ERROR;
     if (sizeMayChange) {
         error = GetAndFlushUnderlyingGLErrors();
     }
 
     return error;
 }
 
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -244,17 +244,17 @@ DriverFormatsFromEffectiveInternalFormat
 
     // driverFormat: always just the unsized internalformat that we just got
     GLenum driverFormat = unsizedinternalformat.get();
 
     // driverInternalFormat: almost always the same as driverFormat, but on desktop GL,
     // in some cases we must pass a different value. On ES, they are equal by definition
     // as it is an error to pass internalformat!=format.
     GLenum driverInternalFormat = driverFormat;
-    if (!gl->IsGLES()) {
+    if (gl->IsCompatibilityProfile()) {
         // Cases where desktop OpenGL requires a tweak to 'format'
         if (driverFormat == LOCAL_GL_SRGB)
             driverFormat = LOCAL_GL_RGB;
         else if (driverFormat == LOCAL_GL_SRGB_ALPHA)
             driverFormat = LOCAL_GL_RGBA;
 
         // WebGL2's new formats are not legal values for internalformat,
         // as using unsized internalformat is deprecated.
@@ -279,21 +279,76 @@ DriverFormatsFromEffectiveInternalFormat
             unsizedinternalformat == LOCAL_GL_DEPTH_STENCIL ||
             type == LOCAL_GL_FLOAT ||
             type == LOCAL_GL_HALF_FLOAT)
         {
             driverInternalFormat = effectiveinternalformat.get();
         }
     }
 
+    // OpenGL core profile removed texture formats ALPHA, LUMINANCE and LUMINANCE_ALPHA
+    if (gl->IsCoreProfile()) {
+        switch (driverFormat) {
+        case LOCAL_GL_ALPHA:
+        case LOCAL_GL_LUMINANCE:
+            driverInternalFormat = driverFormat = LOCAL_GL_RED;
+            break;
+
+        case LOCAL_GL_LUMINANCE_ALPHA:
+            driverInternalFormat = driverFormat = LOCAL_GL_RG;
+            break;
+        }
+    }
+
     *out_driverInternalFormat = driverInternalFormat;
     *out_driverFormat = driverFormat;
     *out_driverType = driverType;
 }
 
+// Map R to A
+static const GLenum kLegacyAlphaSwizzle[4] = {
+    LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_RED
+};
+// Map R to RGB
+static const GLenum kLegacyLuminanceSwizzle[4] = {
+    LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_ONE
+};
+// Map R to RGB, G to A
+static const GLenum kLegacyLuminanceAlphaSwizzle[4] = {
+    LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_GREEN
+};
+
+void
+SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat)
+{
+    if (!gl->IsCoreProfile())
+        return;
+
+    /* Only support swizzling on core profiles. */
+    // Bug 1159117: Fix this.
+    // MOZ_RELEASE_ASSERT(gl->IsSupported(gl::GLFeature::texture_swizzle));
+
+    switch (internalformat) {
+    case LOCAL_GL_ALPHA:
+        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
+                            (GLint*) kLegacyAlphaSwizzle);
+        break;
+
+    case LOCAL_GL_LUMINANCE:
+        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
+                            (GLint*) kLegacyLuminanceSwizzle);
+        break;
+
+    case LOCAL_GL_LUMINANCE_ALPHA:
+        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
+                            (GLint*) kLegacyLuminanceAlphaSwizzle);
+        break;
+    }
+}
+
 /**
  * Return the bits per texel for format & type combination.
  * Assumes that format & type are a valid combination as checked with
  * ValidateTexImageFormatAndType().
  */
 size_t
 GetBitsPerTexel(TexInternalFormat effectiveinternalformat)
 {
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -33,16 +33,18 @@ void
 UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat,
                                                         TexInternalFormat* const out_internalformat,
                                                         TexType* const out_type);
 TexType TypeFromInternalFormat(TexInternalFormat internalformat);
 
 TexInternalFormat
 UnsizedInternalFormatFromInternalFormat(TexInternalFormat internalformat);
 
+void SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat);
+
 size_t GetBitsPerTexel(TexInternalFormat effectiveinternalformat);
 
 // For use with the different texture calls, i.e.
 //   TexImage2D, CopyTex[Sub]Image2D, ...
 // that take a "target" parameter. This parameter is not always the same as
 // the texture binding location, like GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP.
 // For example, cube maps would pass GL_TEXTURE_CUBE_MAP_[POS|NEG]_[X|Y|Z]
 // instead of just GL_TEXTURE_CUBE_MAP.
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -33,8 +33,10 @@ skip-if = buildapp == 'mulet' || toolkit
 [webgl-mochitest/test_webgl_request_context.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [webgl-mochitest/test_webgl_request_mismatch.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [webgl-mochitest/test_webgl2_not_exposed.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [webgl-mochitest/test_webgl2_invalidate_framebuffer.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
+[webgl-mochitest/test_webgl2_alpha_luminance.html]
+skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test_webgl2_alpha_luminance.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>WebGL2 test: Alpha and Luminance Textures</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="driver-info.js"></script>
+<script src="webgl-util.js"></script>
+<script id="vs" type="x-shader/x-vertex">
+#version 300 es
+in vec2 aTexCoord;
+out vec2 vTexCoord;
+
+void main() {
+  vec2 pos = vec2(2.0)*aTexCoord - vec2(1.0);
+  gl_Position = vec4(pos, 0.0, 1.0);
+  vTexCoord = aTexCoord;
+}
+</script>
+<script id="fs-alpha" type="x-shader/x-fragment">
+#version 300 es
+precision mediump float;
+in vec2 vTexCoord;
+
+out vec4 oFragColor;
+
+uniform sampler2D uTex;
+
+bool compare(in vec4 test, in float ref) {
+  float lo = (ref - 0.1) / 255.0;
+  float hi = (ref + 0.1) / 255.0;
+
+  return (test.rgb == vec3(0) &&
+          lo < test.a && test.a < hi);
+}
+
+void main() {
+  vec4 tex = texture(uTex, vTexCoord);
+
+  oFragColor = compare(tex, 128) ? vec4(0.0, 1.0, 0.0, 1.0)
+                                 : vec4(1.0, 0.0, 0.0, 1.0);
+}
+</script>
+<script id="fs-lum" type="x-shader/x-fragment">
+#version 300 es
+precision mediump float;
+in vec2 vTexCoord;
+
+out vec4 oFragColor;
+
+uniform sampler2D uTex;
+
+bool compare(vec4 test, float ref) {
+  float lo = (ref - 0.1) / 255.0;
+  float hi = (ref + 0.1) / 255.0;
+
+  return (lo < test.r && test.r < hi &&
+          lo < test.g && test.g < hi &&
+          lo < test.b && test.b < hi &&
+          test.a == 1);
+}
+
+void main() {
+  vec4 tex = texture(uTex, vTexCoord);
+
+  oFragColor = compare(tex, 128) ? vec4(0.0, 1.0, 0.0, 1.0)
+                                 : vec4(1.0, 0.0, 0.0, 1.0);
+}
+</script>
+<script id="fs-lumalpha" type="x-shader/x-fragment">
+#version 300 es
+precision mediump float;
+in vec2 vTexCoord;
+
+out vec4 oFragColor;
+
+uniform sampler2D uTex;
+
+bool compare(in vec4 test, in float ref) {
+  float lo = (ref - 0.1) / 255.0;
+  float hi = (ref + 0.1) / 255.0;
+
+  return (lo < test.r && test.r < hi &&
+          lo < test.g && test.g < hi &&
+          lo < test.b && test.b < hi &&
+          lo < test.a && test.a < hi);
+}
+
+void main() {
+  vec4 tex = texture(uTex, vTexCoord);
+
+  oFragColor = compare(tex, 128) ? vec4(0.0, 1.0, 0.0, 1.0)
+                                 : vec4(1.0, 0.0, 0.0, 1.0);
+}
+</script>
+<body>
+<canvas id="c" width="32" height="32"></canvas>
+<script>
+  WebGLUtil.withWebGL2('c', function(gl) {
+
+    function testPixel(x, y, refData, func, infoString) {
+      var pixel = new Uint8Array(4);
+      gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
+
+      var pixelMatches = (pixel[0] == refData[0] &&
+                          pixel[1] == refData[1] &&
+                          pixel[2] == refData[2] &&
+                          pixel[3] == refData[3]);
+      func(pixelMatches, infoString);
+    }
+
+    function testTexture(details) {
+      var prog = WebGLUtil.createProgramByIds(gl, 'vs', details.frag);
+      if (!prog) {
+        ok(false, 'Program linking should succeed.');
+        return false;
+      }
+
+      prog.aTexCoord = gl.getAttribLocation(prog, "aTexCoord");
+      ok(prog.aTexCoord >= 0, '`aTexCoord` should be valid.');
+
+      var tex = gl.createTexture();
+      gl.bindTexture(gl.TEXTURE_2D, tex);
+      gl.texImage2D(gl.TEXTURE_2D, 0, details.format, 2, 2, 0,
+                    details.format, gl.UNSIGNED_BYTE, details.pixels);
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+      gl.useProgram(prog);
+      gl.vertexAttribPointer(prog.aTexCoord, 2, gl.FLOAT, false, 0, 0);
+      gl.enableVertexAttribArray(prog.aTexCoord);
+
+      gl.clear(gl.COLOR_BUFFER_BIT);
+      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+      testPixel(0, 0, [0, 255, 0, 255], ok, 'Should be green after drawing.');
+      return true;
+    }
+
+    gl.disable(gl.DEPTH_TEST);
+
+    var vertData = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, vertData);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 0, 1, 0, 0, 1, 1, 1 ]), gl.STATIC_DRAW);
+
+    gl.clearColor(0, 0, 1, 1);
+    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
+
+    var details = [
+      { frag: 'fs-alpha', format: gl.ALPHA, pixels: new Uint8Array([ 128, 128, 128, 128 ]) },
+      { frag: 'fs-lum', format: gl.LUMINANCE, pixels: new Uint8Array([ 128, 128, 128, 128 ]) },
+      { frag: 'fs-lumalpha', format: gl.LUMINANCE_ALPHA, pixels: new Uint8Array([ 128, 128, 128, 128, 128, 128, 128, 128 ]) }
+    ];
+
+    for (var i = 0; i < details.length; i++) {
+      if (!testTexture(details[i])) {
+        return;
+      }
+    }
+    ok(true, 'Test complete.');
+  }, function() {
+    SimpleTest.finish();
+  });
+
+  SimpleTest.waitForExplicitFinish();
+</script>
--- a/dom/crypto/moz.build
+++ b/dom/crypto/moz.build
@@ -21,12 +21,12 @@ UNIFIED_SOURCES += [
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
-    '/security/manager/ssl/src',
+    '/security/manager/ssl',
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
copy from dom/fetch/InternalResponse.cpp
copy to dom/fetch/ChannelInfo.cpp
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/ChannelInfo.cpp
@@ -1,98 +1,103 @@
 /* -*- 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 "InternalResponse.h"
-
-#include "mozilla/dom/InternalHeaders.h"
-#include "nsStreamUtils.h"
+#include "mozilla/dom/ChannelInfo.h"
+#include "nsCOMPtr.h"
+#include "nsIChannel.h"
+#include "nsIHttpChannel.h"
 #include "nsSerializationHelper.h"
+#include "mozilla/net/HttpBaseChannel.h"
+#include "mozilla/ipc/ChannelInfo.h"
+#include "nsIJARChannel.h"
+#include "nsJARChannel.h"
 
-namespace mozilla {
-namespace dom {
+using namespace mozilla;
+using namespace mozilla::dom;
 
-InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
-  : mType(ResponseType::Default)
-  , mFinalURL(false)
-  , mStatus(aStatus)
-  , mStatusText(aStatusText)
-  , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
+void
+ChannelInfo::InitFromChannel(nsIChannel* aChannel)
 {
-}
-
-already_AddRefed<InternalResponse>
-InternalResponse::Clone()
-{
-  nsRefPtr<InternalResponse> clone = CreateIncompleteCopy();
+  MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
 
-  clone->mHeaders = new InternalHeaders(*mHeaders);
-  if (mWrappedResponse) {
-    clone->mWrappedResponse = mWrappedResponse->Clone();
-    MOZ_ASSERT(!mBody);
-    return clone.forget();
-  }
-
-  if (!mBody) {
-    return clone.forget();
+  nsCOMPtr<nsISupports> securityInfo;
+  aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
+  if (securityInfo) {
+    SetSecurityInfo(securityInfo);
   }
 
-  nsCOMPtr<nsIInputStream> clonedBody;
-  nsCOMPtr<nsIInputStream> replacementBody;
-
-  nsresult rv = NS_CloneInputStream(mBody, getter_AddRefs(clonedBody),
-                                    getter_AddRefs(replacementBody));
-  if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
-
-  clone->mBody.swap(clonedBody);
-  if (replacementBody) {
-    mBody.swap(replacementBody);
-  }
-
-  return clone.forget();
-}
-
-already_AddRefed<InternalResponse>
-InternalResponse::BasicResponse()
-{
-  MOZ_ASSERT(!mWrappedResponse, "Can't BasicResponse a already wrapped response");
-  nsRefPtr<InternalResponse> basic = CreateIncompleteCopy();
-  basic->mType = ResponseType::Basic;
-  basic->mHeaders = InternalHeaders::BasicHeaders(Headers());
-  basic->mWrappedResponse = this;
-  return basic.forget();
-}
-
-already_AddRefed<InternalResponse>
-InternalResponse::CORSResponse()
-{
-  MOZ_ASSERT(!mWrappedResponse, "Can't CORSResponse a already wrapped response");
-  nsRefPtr<InternalResponse> cors = CreateIncompleteCopy();
-  cors->mType = ResponseType::Cors;
-  cors->mHeaders = InternalHeaders::CORSHeaders(Headers());
-  cors->mWrappedResponse = this;
-  return cors.forget();
+  mInited = true;
 }
 
 void
-InternalResponse::SetSecurityInfo(nsISupports* aSecurityInfo)
+ChannelInfo::InitFromIPCChannelInfo(const ipc::IPCChannelInfo& aChannelInfo)
+{
+  MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
+
+  mSecurityInfo = aChannelInfo.securityInfo();
+
+  mInited = true;
+}
+
+void
+ChannelInfo::SetSecurityInfo(nsISupports* aSecurityInfo)
 {
   MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once");
   nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aSecurityInfo);
   if (!serializable) {
     NS_WARNING("A non-serializable object was passed to InternalResponse::SetSecurityInfo");
     return;
   }
   NS_SerializeToString(serializable, mSecurityInfo);
 }
 
-void
-InternalResponse::SetSecurityInfo(const nsCString& aSecurityInfo)
+nsresult
+ChannelInfo::ResurrectInfoOnChannel(nsIChannel* aChannel)
 {
-  MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once");
-  mSecurityInfo = aSecurityInfo;
+  MOZ_ASSERT(mInited);
+
+  if (!mSecurityInfo.IsEmpty()) {
+    nsCOMPtr<nsISupports> infoObj;
+    nsresult rv = NS_DeserializeObject(mSecurityInfo, getter_AddRefs(infoObj));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    nsCOMPtr<nsIHttpChannel> httpChannel =
+      do_QueryInterface(aChannel);
+    if (httpChannel) {
+      net::HttpBaseChannel* httpBaseChannel =
+        static_cast<net::HttpBaseChannel*>(httpChannel.get());
+      rv = httpBaseChannel->OverrideSecurityInfo(infoObj);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    } else {
+      nsCOMPtr<nsIJARChannel> jarChannel =
+        do_QueryInterface(aChannel);
+      if (NS_WARN_IF(!jarChannel)) {
+        return NS_ERROR_FAILURE;
+      }
+      static_cast<nsJARChannel*>(jarChannel.get())->
+        OverrideSecurityInfo(infoObj);
+    }
+  }
+
+  return NS_OK;
 }
 
-} // namespace dom
-} // namespace mozilla
+ipc::IPCChannelInfo
+ChannelInfo::AsIPCChannelInfo() const
+{
+  // This may be called when mInited is false, for example if we try to store
+  // a synthesized Response object into the Cache.  Uninitialized and empty
+  // ChannelInfo objects are indistinguishable at the IPC level, so this is
+  // fine.
+
+  IPCChannelInfo ipcInfo;
+
+  ipcInfo.securityInfo() = mSecurityInfo;
+
+  return ipcInfo;
+}
copy from dom/fetch/Response.h
copy to dom/fetch/ChannelInfo.h
--- a/dom/fetch/Response.h
+++ b/dom/fetch/ChannelInfo.h
@@ -1,129 +1,87 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_Response_h
-#define mozilla_dom_Response_h
-
-#include "nsWrapperCache.h"
-#include "nsISupportsImpl.h"
+#ifndef mozilla_dom_ChannelInfo_h
+#define mozilla_dom_ChannelInfo_h
 
-#include "mozilla/dom/Fetch.h"
-#include "mozilla/dom/ResponseBinding.h"
+#include "nsString.h"
 
-#include "InternalHeaders.h"
-#include "InternalResponse.h"
+class nsIChannel;
 
 namespace mozilla {
+namespace ipc {
+class IPCChannelInfo;
+} // namespace ipc
+
 namespace dom {
 
-class Headers;
-
-class Response final : public nsISupports
-                     , public FetchBody<Response>
-                     , public nsWrapperCache
+// This class represents the information related to a Response that we
+// retrieve from the corresponding channel that is used to perform the fetch.
+//
+// When adding new members to this object, the following code needs to be
+// updated:
+// * IPCChannelInfo
+// * InitFromChannel and InitFromIPCChannelInfo members
+// * ResurrectInfoOnChannel member
+// * AsIPCChannelInfo member
+// * constructors and assignment operators for this class.
+// * DOM Cache schema code (in dom/cache/DBSchema.cpp) to ensure that the newly
+//   added member is saved into the DB and loaded from it properly.
+//
+// Care must be taken when initializing this object, or when calling
+// ResurrectInfoOnChannel().  This object cannot be initialized twice, and
+// ResurrectInfoOnChannel() cannot be called on it before it has been
+// initialized.  There are assertions ensuring these invariants.
+class ChannelInfo final
 {
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Response)
+public:
+  typedef mozilla::ipc::IPCChannelInfo IPCChannelInfo;
 
-public:
-  Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse);
-
-  Response(const Response& aOther) = delete;
-
-  JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
+  ChannelInfo()
+    : mInited(false)
   {
-    return ResponseBinding::Wrap(aCx, this, aGivenProto);
-  }
-
-  ResponseType
-  Type() const
-  {
-    return mInternalResponse->Type();
-  }
-
-  void
-  GetUrl(DOMString& aUrl) const
-  {
-    nsCString url;
-    mInternalResponse->GetUrl(url);
-    aUrl.AsAString() = NS_ConvertUTF8toUTF16(url);
-  }
-
-  uint16_t
-  Status() const
-  {
-    return mInternalResponse->GetStatus();
   }
 
-  bool
-  Ok() const
+  ChannelInfo(const ChannelInfo& aRHS)
+    : mSecurityInfo(aRHS.mSecurityInfo)
+    , mInited(aRHS.mInited)
   {
-    return mInternalResponse->GetStatus() >= 200 &&
-           mInternalResponse->GetStatus() <= 299;
   }
 
-  void
-  GetStatusText(nsCString& aStatusText) const
+  ChannelInfo&
+  operator=(const ChannelInfo& aRHS)
   {
-    aStatusText = mInternalResponse->GetStatusText();
-  }
-
-  InternalHeaders*
-  GetInternalHeaders() const
-  {
-    return mInternalResponse->Headers();
-  }
-
-  const nsCString&
-  GetSecurityInfo() const
-  {
-    return mInternalResponse->GetSecurityInfo();
+    mSecurityInfo = aRHS.mSecurityInfo;
+    mInited = aRHS.mInited;
+    return *this;
   }
 
-  Headers* Headers_();
-
-  void
-  GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
-
-  static already_AddRefed<Response>
-  Error(const GlobalObject& aGlobal);
+  void InitFromChannel(nsIChannel* aChannel);
+  void InitFromIPCChannelInfo(const IPCChannelInfo& aChannelInfo);
 
-  static already_AddRefed<Response>
-  Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus, ErrorResult& aRv);
+  // This restores every possible information stored from a previous channel
+  // object on a new one.
+  nsresult ResurrectInfoOnChannel(nsIChannel* aChannel);
 
-  static already_AddRefed<Response>
-  Constructor(const GlobalObject& aGlobal,
-              const Optional<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& aBody,
-              const ResponseInit& aInit, ErrorResult& rv);
-
-  nsIGlobalObject* GetParentObject() const
+  bool IsInitialized() const
   {
-    return mOwner;
+    return mInited;
   }
 
-  already_AddRefed<Response>
-  Clone(ErrorResult& aRv) const;
-
-  void
-  SetBody(nsIInputStream* aBody);
-
-  already_AddRefed<InternalResponse>
-  GetInternalResponse() const;
+  IPCChannelInfo AsIPCChannelInfo() const;
 
 private:
-  ~Response();
+  void SetSecurityInfo(nsISupports* aSecurityInfo);
 
-  nsCOMPtr<nsIGlobalObject> mOwner;
-  nsRefPtr<InternalResponse> mInternalResponse;
-  // Lazily created
-  nsRefPtr<Headers> mHeaders;
+private:
+  nsCString mSecurityInfo;
+  bool mInited;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_Response_h
+#endif // mozilla_dom_ChannelInfo_h
new file mode 100644
--- /dev/null
+++ b/dom/fetch/ChannelInfo.ipdlh
@@ -0,0 +1,14 @@
+/* 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/. */
+
+namespace mozilla {
+namespace ipc {
+
+struct IPCChannelInfo
+{
+  nsCString securityInfo;
+};
+
+} // namespace ipc
+} // namespace mozilla
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -701,22 +701,18 @@ FetchDriver::OnStartRequest(nsIRequest* 
                   false /* blocking output, since the pipe is 'in'finite */ );
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
     return rv;
   }
   response->SetBody(pipeInputStream);
 
-  nsCOMPtr<nsISupports> securityInfo;
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
-  rv = channel->GetSecurityInfo(getter_AddRefs(securityInfo));
-  if (securityInfo) {
-    response->SetSecurityInfo(securityInfo);
-  }
+  response->InitChannelInfo(channel);
 
   // Resolves fetch() promise which may trigger code running in a worker.  Make
   // sure the Response is fully initialized before calling this.
   mResponse = BeginAndGetFilteredResponse(response);
 
   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InternalResponse.h"
 
 #include "mozilla/dom/InternalHeaders.h"
 #include "nsStreamUtils.h"
-#include "nsSerializationHelper.h"
 
 namespace mozilla {
 namespace dom {
 
 InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
   : mType(ResponseType::Default)
   , mFinalURL(false)
   , mStatus(aStatus)
@@ -70,29 +69,10 @@ InternalResponse::CORSResponse()
   MOZ_ASSERT(!mWrappedResponse, "Can't CORSResponse a already wrapped response");
   nsRefPtr<InternalResponse> cors = CreateIncompleteCopy();
   cors->mType = ResponseType::Cors;
   cors->mHeaders = InternalHeaders::CORSHeaders(Headers());
   cors->mWrappedResponse = this;
   return cors.forget();
 }
 
-void
-InternalResponse::SetSecurityInfo(nsISupports* aSecurityInfo)
-{
-  MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once");
-  nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aSecurityInfo);
-  if (!serializable) {
-    NS_WARNING("A non-serializable object was passed to InternalResponse::SetSecurityInfo");
-    return;
-  }
-  NS_SerializeToString(serializable, mSecurityInfo);
-}
-
-void
-InternalResponse::SetSecurityInfo(const nsCString& aSecurityInfo)
-{
-  MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once");
-  mSecurityInfo = aSecurityInfo;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_InternalResponse_h
 #define mozilla_dom_InternalResponse_h
 
 #include "nsIInputStream.h"
 #include "nsISupportsImpl.h"
 
 #include "mozilla/dom/ResponseBinding.h"
+#include "mozilla/dom/ChannelInfo.h"
 
 namespace mozilla {
 namespace dom {
 
 class InternalHeaders;
 
 class InternalResponse final
 {
@@ -43,17 +44,17 @@ public:
   OpaqueResponse()
   {
     MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueResponse a already wrapped response");
     nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
     response->mType = ResponseType::Opaque;
     response->mTerminationReason = mTerminationReason;
     response->mURL = mURL;
     response->mFinalURL = mFinalURL;
-    response->mSecurityInfo = mSecurityInfo;
+    response->mChannelInfo = mChannelInfo;
     response->mWrappedResponse = this;
     return response.forget();
   }
 
   already_AddRefed<InternalResponse>
   BasicResponse();
 
   already_AddRefed<InternalResponse>
@@ -151,27 +152,39 @@ public:
     if (mWrappedResponse) {
       return mWrappedResponse->SetBody(aBody);
     }
     // A request's body may not be reset once set.
     MOZ_ASSERT(!mBody);
     mBody = aBody;
   }
 
-  const nsCString&
-  GetSecurityInfo() const
+  void
+  InitChannelInfo(nsIChannel* aChannel)
   {
-    return mSecurityInfo;
+    mChannelInfo.InitFromChannel(aChannel);
   }
 
   void
-  SetSecurityInfo(nsISupports* aSecurityInfo);
+  InitChannelInfo(const mozilla::ipc::IPCChannelInfo& aChannelInfo)
+  {
+    mChannelInfo.InitFromIPCChannelInfo(aChannelInfo);
+  }
 
   void
-  SetSecurityInfo(const nsCString& aSecurityInfo);
+  InitChannelInfo(const ChannelInfo& aChannelInfo)
+  {
+    mChannelInfo = aChannelInfo;
+  }
+
+  const ChannelInfo&
+  GetChannelInfo() const
+  {
+    return mChannelInfo;
+  }
 
 private:
   ~InternalResponse()
   { }
 
   explicit InternalResponse(const InternalResponse& aOther) = delete;
   InternalResponse& operator=(const InternalResponse&) = delete;
 
@@ -180,29 +193,29 @@ private:
   // are left uninitialized. Used for cloning and filtering.
   already_AddRefed<InternalResponse> CreateIncompleteCopy()
   {
     nsRefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
     copy->mType = mType;
     copy->mTerminationReason = mTerminationReason;
     copy->mURL = mURL;
     copy->mFinalURL = mFinalURL;
-    copy->mSecurityInfo = mSecurityInfo;
+    copy->mChannelInfo = mChannelInfo;
     return copy.forget();
   }
 
   ResponseType mType;
   nsCString mTerminationReason;
   nsCString mURL;
   bool mFinalURL;
   const uint16_t mStatus;
   const nsCString mStatusText;
   nsRefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBody;
-  nsCString mSecurityInfo;
+  ChannelInfo mChannelInfo;
 
   // For filtered responses.
   // Cache, and SW interception should always serialize/access the underlying
   // unfiltered headers and when deserializing, create an InternalResponse
   // with the unfiltered headers followed by wrapping it.
   nsRefPtr<InternalResponse> mWrappedResponse;
 };
 
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -73,20 +73,26 @@ public:
   }
 
   InternalHeaders*
   GetInternalHeaders() const
   {
     return mInternalResponse->Headers();
   }
 
-  const nsCString&
-  GetSecurityInfo() const
+  void
+  InitChannelInfo(nsIChannel* aChannel)
   {
-    return mInternalResponse->GetSecurityInfo();
+    mInternalResponse->InitChannelInfo(aChannel);
+  }
+
+  const ChannelInfo&
+  GetChannelInfo() const
+  {
+    return mInternalResponse->GetChannelInfo();
   }
 
   Headers* Headers_();
 
   void
   GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
 
   static already_AddRefed<Response>
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -1,36 +1,50 @@
 # -*- 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/.
 
 EXPORTS.mozilla.dom += [
+    'ChannelInfo.h',
     'Fetch.h',
     'FetchDriver.h',
     'Headers.h',
     'InternalHeaders.h',
     'InternalRequest.h',
     'InternalResponse.h',
     'Request.h',
     'Response.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ChannelInfo.cpp',
     'Fetch.cpp',
     'FetchDriver.cpp',
     'Headers.cpp',
     'InternalHeaders.cpp',
     'InternalRequest.cpp',
     'InternalResponse.cpp',
     'Request.cpp',
     'Response.cpp',
 ]
 
+IPDL_SOURCES += [
+    'ChannelInfo.ipdlh',
+]
+
 LOCAL_INCLUDES += [
     '../workers',
+    # For nsJARChannel.h
+    '/modules/libjar',
+    # For HttpBaseChannel.h dependencies
+    '/netwerk/base',
     # For nsDataHandler.h
     '/netwerk/protocol/data',
+    # For HttpBaseChannel.h
+    '/netwerk/protocol/http',
 ]
 
 FAIL_ON_WARNINGS = True
 FINAL_LIBRARY = 'xul'
+
+include('/ipc/chromium/chromium-config.mozbuild')
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2786,33 +2786,33 @@ nsresult HTMLMediaElement::FinishDecoder
   mDecoder->SetAudioChannel(mAudioChannel);
   mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
   mDecoder->SetPreservesPitch(mPreservesPitch);
   mDecoder->SetPlaybackRate(mPlaybackRate);
   if (mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
     mDecoder->SetMinimizePrerollUntilPlaybackStarts();
   }
 
-  for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
-    OutputMediaStream* ms = &mOutputStreams[i];
-    aDecoder->AddOutputStream(ms->mStream->GetStream()->AsProcessedStream(),
-                              ms->mFinishWhenEnded);
-  }
-
   // Update decoder principal before we start decoding, since it
   // can affect how we feed data to MediaStreams
   NotifyDecoderPrincipalChanged();
 
   nsresult rv = aDecoder->Load(aListener, aCloneDonor);
   if (NS_FAILED(rv)) {
     SetDecoder(nullptr);
     LOG(PR_LOG_DEBUG, ("%p Failed to load for decoder %p", this, aDecoder));
     return rv;
   }
 
+  for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
+    OutputMediaStream* ms = &mOutputStreams[i];
+    aDecoder->AddOutputStream(ms->mStream->GetStream()->AsProcessedStream(),
+                              ms->mFinishWhenEnded);
+  }
+
 #ifdef MOZ_EME
   if (mMediaKeys) {
     mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
   }
 #endif
 
   // Decoder successfully created, the decoder now owns the MediaResource
   // which owns the channel.
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -151,46 +151,25 @@ parent:
                      CpowEntry[] aCpows, Principal aPrincipal)
       returns (OwningSerializedStructuredCloneBuffer[] retval);
 
     prio(high) sync RpcMessage(nsString aMessage, ClonedMessageData aData,
                                CpowEntry[] aCpows, Principal aPrincipal)
       returns (OwningSerializedStructuredCloneBuffer[] retval);
 
     /**
-     * The IME sequence number (seqno) parameter is used to make sure
-     * that a notification is discarded if it arrives at the chrome process
-     * too late. If the notification is late and we accept it, we will have
-     * an out-of-date view of the content process, which means events that we
-     * dispatch based on this out-of-date view will be wrong also.
-     * (see Bug 599550 and Bug 591047 comments 44, 50, and 54)
-     *
-     * Chrome increments seqno and includes it in each IME event sent to
-     * content, and content sends its current seqno back to chrome with each
-     * notification. A notification is up-to-date only if the content
-     * seqno is the same as the current chrome seqno, meaning no additional
-     * event was sent to content before the notification was received
-     *
-     * On blur, chrome returns the current seqno to content, and content
-     * uses it to discard subsequent events until the content seqno and
-     * chrome seqno-on-blur match again. These events, meant for the blurred
-     * textfield, are discarded to prevent events going to the wrong target
-     */
-
-    /**
      * Notifies chrome that there is a focus change involving an editable
      * object (input, textarea, document, contentEditable. etc.)
      *
      *  focus        PR_TRUE if editable object is receiving focus
      *               PR_FALSE if losing focus
      *  preference   Native widget preference for IME updates
-     *  seqno        Current seqno value on the chrome side
      */
     prio(urgent) sync NotifyIMEFocus(bool focus)
-      returns (nsIMEUpdatePreference preference, uint32_t seqno);
+      returns (nsIMEUpdatePreference preference);
 
     /**
      * Notifies chrome that there has been a change in text content
      * One call can encompass both a delete and an insert operation
      * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
      *
      *  offset       Starting offset of the change
      *  end          Ending offset of the range deleted
@@ -216,23 +195,22 @@ parent:
                                                         LayoutDeviceIntRect[] rect,
                                                         uint32_t caretOffset,
                                                         LayoutDeviceIntRect caretRect);
 
     /**
      * Notifies chrome that there has been a change in selection
      * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
      *
-     *  seqno        Current seqno value on the content side
      *  anchor       Offset where the selection started
      *  focus        Offset where the caret is
      *  writingMode  CSS writing-mode in effect at the focus
      *  causedByComposition true if the change is caused by composition
      */
-    prio(urgent) async NotifyIMESelection(uint32_t seqno, uint32_t anchor,
+    prio(urgent) async NotifyIMESelection(uint32_t anchor,
                                           uint32_t focus,
                                           WritingMode writingMode,
                                           bool causedByComposition);
 
     /**
      * Notifies chrome to refresh its text cache 
      *
      *  text         The entire content of the text field
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -138,16 +138,29 @@ private:
   {
   }
 
   // Raw pointer is safe here because this object is held by a Timer, which is
   // held by TabChild, we won't stay alive if TabChild dies.
   TabChild *mTabChild;
 };
 
+static bool
+UsingCompositorLRU()
+{
+  static bool sHavePrefs = false;
+  static uint32_t sCompositorLRUSize = 0;
+  if (!sHavePrefs) {
+    sHavePrefs = true;
+    Preferences::AddUintVarCache(&sCompositorLRUSize,
+                                 "layers.compositor-lru-size", 0);
+  }
+  return sCompositorLRUSize != 0;
+}
+
 NS_IMPL_ISUPPORTS(TabChild::DelayedFireContextMenuEvent,
                   nsITimerCallback)
 
 TabChildBase::TabChildBase()
   : mContentDocumentIsDisplayed(false)
   , mTabChildGlobal(nullptr)
   , mInnerSize(0, 0)
 {
@@ -2982,28 +2995,37 @@ TabChild::NotifyPainted()
         mNotified = true;
     }
 }
 
 void
 TabChild::MakeVisible()
 {
   CompositorChild* compositor = CompositorChild::Get();
-  compositor->SendNotifyVisible(mLayersId);
+  if (UsingCompositorLRU()) {
+    compositor->SendNotifyVisible(mLayersId);
+  }
 
   if (mWidget) {
     mWidget->Show(true);
   }
 }
 
 void
 TabChild::MakeHidden()
 {
   CompositorChild* compositor = CompositorChild::Get();
-  compositor->SendNotifyHidden(mLayersId);
+  if (UsingCompositorLRU()) {
+    compositor->SendNotifyHidden(mLayersId);
+  } else {
+    // Clear cached resources directly. This avoids one extra IPC
+    // round-trip from CompositorChild to CompositorParent when
+    // CompositorLRU is not used.
+    compositor->RecvClearCachedResources(mLayersId);
+  }
 
   if (mWidget) {
     mWidget->Show(false);
   }
 }
 
 void
 TabChild::UpdateHitRegion(const nsRegion& aRegion)
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -259,17 +259,16 @@ TabParent::TabParent(nsIContentParent* a
   , mFrameElement(nullptr)
   , mIMESelectionAnchor(0)
   , mIMESelectionFocus(0)
   , mWritingMode()
   , mIMEComposing(false)
   , mIMECompositionEnding(false)
   , mIMEEventCountAfterEnding(0)
   , mIMECompositionStart(0)
-  , mIMESeqno(0)
   , mIMECompositionRectOffset(0)
   , mRect(0, 0, 0, 0)
   , mDimensions(0, 0)
   , mOrientation(0)
   , mDPI(0)
   , mDefaultScale(0)
   , mUpdatedDimensions(false)
   , mManager(aManager)
@@ -1842,21 +1841,18 @@ TabParent::RecvHideTooltip()
   }
 
   xulBrowserWindow->HideTooltip();
   return true;
 }
 
 bool
 TabParent::RecvNotifyIMEFocus(const bool& aFocus,
-                              nsIMEUpdatePreference* aPreference,
-                              uint32_t* aSeqno)
+                              nsIMEUpdatePreference* aPreference)
 {
-  *aSeqno = mIMESeqno;
-
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     *aPreference = nsIMEUpdatePreference();
     return true;
   }
 
   mIMETabParent = aFocus ? this : nullptr;
   mIMESelectionAnchor = 0;
@@ -1917,49 +1913,46 @@ TabParent::RecvNotifyIMESelectedComposit
   if (!widget) {
     return true;
   }
   widget->NotifyIME(IMENotification(NOTIFY_IME_OF_COMPOSITION_UPDATE));
   return true;
 }
 
 bool
-TabParent::RecvNotifyIMESelection(const uint32_t& aSeqno,
-                                  const uint32_t& aAnchor,
+TabParent::RecvNotifyIMESelection(const uint32_t& aAnchor,
                                   const uint32_t& aFocus,
                                   const mozilla::WritingMode& aWritingMode,
                                   const bool& aCausedByComposition)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return true;
 
-  if (aSeqno == mIMESeqno) {
-    mIMESelectionAnchor = aAnchor;
-    mIMESelectionFocus = aFocus;
-    mWritingMode = aWritingMode;
-    const nsIMEUpdatePreference updatePreference =
-      widget->GetIMEUpdatePreference();
-    if (updatePreference.WantSelectionChange() &&
-        (updatePreference.WantChangesCausedByComposition() ||
-         !aCausedByComposition)) {
-      IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
-      notification.mSelectionChangeData.mOffset =
-        std::min(mIMESelectionAnchor, mIMESelectionFocus);
-      notification.mSelectionChangeData.mLength =
-        mIMESelectionAnchor > mIMESelectionFocus ?
-          mIMESelectionAnchor - mIMESelectionFocus :
-          mIMESelectionFocus - mIMESelectionAnchor;
-      notification.mSelectionChangeData.mReversed =
-        mIMESelectionFocus < mIMESelectionAnchor;
-      notification.mSelectionChangeData.SetWritingMode(mWritingMode);
-      notification.mSelectionChangeData.mCausedByComposition =
-        aCausedByComposition;
-      widget->NotifyIME(notification);
-    }
+  mIMESelectionAnchor = aAnchor;
+  mIMESelectionFocus = aFocus;
+  mWritingMode = aWritingMode;
+  const nsIMEUpdatePreference updatePreference =
+    widget->GetIMEUpdatePreference();
+  if (updatePreference.WantSelectionChange() &&
+      (updatePreference.WantChangesCausedByComposition() ||
+       !aCausedByComposition)) {
+    IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
+    notification.mSelectionChangeData.mOffset =
+      std::min(mIMESelectionAnchor, mIMESelectionFocus);
+    notification.mSelectionChangeData.mLength =
+      mIMESelectionAnchor > mIMESelectionFocus ?
+        mIMESelectionAnchor - mIMESelectionFocus :
+        mIMESelectionFocus - mIMESelectionAnchor;
+    notification.mSelectionChangeData.mReversed =
+      mIMESelectionFocus < mIMESelectionAnchor;
+    notification.mSelectionChangeData.SetWritingMode(mWritingMode);
+    notification.mSelectionChangeData.mCausedByComposition =
+      aCausedByComposition;
+    widget->NotifyIME(notification);
   }
   return true;
 }
 
 bool
 TabParent::RecvNotifyIMETextHint(const nsString& aText)
 {
   // Replace our cache with new text
@@ -2293,17 +2286,16 @@ TabParent::SendCompositionEvent(WidgetCo
   }
 
   mIMEComposing = !event.CausesDOMCompositionEndEvent();
   mIMECompositionStart = std::min(mIMESelectionAnchor, mIMESelectionFocus);
   if (mIMECompositionEnding) {
     mIMEEventCountAfterEnding++;
     return true;
   }
-  event.mSeqno = ++mIMESeqno;
   return PBrowserParent::SendCompositionEvent(event);
 }
 
 /**
  * During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
  * widget usually sends a NS_COMPOSITION_CHANGE event to finalize or
  * clear the composition, respectively
  *
@@ -2323,29 +2315,27 @@ TabParent::SendCompositionChangeEvent(Wi
   // we might not receive selection updates in time
   if (!mIMEComposing) {
     mIMECompositionStart = std::min(mIMESelectionAnchor, mIMESelectionFocus);
   }
   mIMESelectionAnchor = mIMESelectionFocus =
       mIMECompositionStart + event.mData.Length();
   mIMEComposing = !event.CausesDOMCompositionEndEvent();
 
-  event.mSeqno = ++mIMESeqno;
   return PBrowserParent::SendCompositionEvent(event);
 }
 
 bool
 TabParent::SendSelectionEvent(WidgetSelectionEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
   mIMESelectionAnchor = event.mOffset + (event.mReversed ? event.mLength : 0);
   mIMESelectionFocus = event.mOffset + (!event.mReversed ? event.mLength : 0);
-  event.mSeqno = ++mIMESeqno;
   return PBrowserParent::SendSelectionEvent(event);
 }
 
 /*static*/ TabParent*
 TabParent::GetFrom(nsFrameLoader* aFrameLoader)
 {
   if (!aFrameLoader) {
     return nullptr;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -156,29 +156,28 @@ public:
                                 InfallibleTArray<CpowEntry>&& aCpows,
                                 const IPC::Principal& aPrincipal,
                                 nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal) override;
     virtual bool RecvAsyncMessage(const nsString& aMessage,
                                   const ClonedMessageData& aData,
                                   InfallibleTArray<CpowEntry>&& aCpows,
                                   const IPC::Principal& aPrincipal) override;
     virtual bool RecvNotifyIMEFocus(const bool& aFocus,
-                                    nsIMEUpdatePreference* aPreference,
-                                    uint32_t* aSeqno) override;
+                                    nsIMEUpdatePreference* aPreference)
+                                      override;
     virtual bool RecvNotifyIMETextChange(const uint32_t& aStart,
                                          const uint32_t& aEnd,
                                          const uint32_t& aNewEnd,
                                          const bool& aCausedByComposition) override;
     virtual bool RecvNotifyIMESelectedCompositionRect(
                    const uint32_t& aOffset,
                    InfallibleTArray<LayoutDeviceIntRect>&& aRects,
                    const uint32_t& aCaretOffset,
                    const LayoutDeviceIntRect& aCaretRect) override;
-    virtual bool RecvNotifyIMESelection(const uint32_t& aSeqno,
-                                        const uint32_t& aAnchor,
+    virtual bool RecvNotifyIMESelection(const uint32_t& aAnchor,
                                         const uint32_t& aFocus,
                                         const mozilla::WritingMode& aWritingMode,
                                         const bool& aCausedByComposition) override;
     virtual bool RecvNotifyIMETextHint(const nsString& aText) override;
     virtual bool RecvNotifyIMEMouseButtonEvent(const widget::IMENotification& aEventMessage,
                                                bool* aConsumedByIME) override;
     virtual bool RecvNotifyIMEEditorRect(const LayoutDeviceIntRect& aRect) override;
     virtual bool RecvNotifyIMEPositionChange(
@@ -477,17 +476,16 @@ protected:
     mozilla::WritingMode mWritingMode;
     bool mIMEComposing;
     bool mIMECompositionEnding;
     uint32_t mIMEEventCountAfterEnding;
     // Buffer to store composition text during ResetInputState
     // Compositions in almost all cases are small enough for nsAutoString
     nsAutoString mIMECompositionText;
     uint32_t mIMECompositionStart;
-    uint32_t mIMESeqno;
 
     uint32_t mIMECompositionRectOffset;
     InfallibleTArray<LayoutDeviceIntRect> mIMECompositionRects;
     uint32_t mIMECaretOffset;
     LayoutDeviceIntRect mIMECaretRect;
     LayoutDeviceIntRect mIMEEditorRect;
 
     nsIntRect mRect;
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -81,16 +81,18 @@ DecodedStreamData::DecodedStreamData(int
   , mHaveSentFinishVideo(false)
   , mStream(aStream)
   , mHaveBlockedForPlayState(false)
   , mHaveBlockedForStateMachineNotPlaying(false)
   , mEOSVideoCompensation(false)
 {
   mListener = new DecodedStreamGraphListener(mStream);
   mStream->AddListener(mListener);
+  // Block the stream until the initialization is done.
+  mStream->ChangeExplicitBlockerCount(1);
 }
 
 DecodedStreamData::~DecodedStreamData()
 {
   mListener->Forget();
   mStream->Destroy();
 }
 
@@ -174,17 +176,17 @@ OutputStreamData::Init(DecodedStream* aD
 
 DecodedStream::DecodedStream(ReentrantMonitor& aMonitor)
   : mMonitor(aMonitor)
 {
   //
 }
 
 DecodedStreamData*
-DecodedStream::GetData()
+DecodedStream::GetData() const
 {
   GetReentrantMonitor().AssertCurrentThreadIn();
   return mData.get();
 }
 
 void
 DecodedStream::DestroyData()
 {
@@ -216,23 +218,29 @@ DecodedStream::DestroyData()
       os.mStream->ChangeExplicitBlockerCount(1);
     }
   }
 
   mData = nullptr;
 }
 
 void
-DecodedStream::RecreateData(int64_t aInitialTime, SourceMediaStream* aStream)
+DecodedStream::RecreateData(int64_t aInitialTime, MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
-  MOZ_ASSERT(!mData);
+  MOZ_ASSERT((aGraph && !mData && OutputStreams().IsEmpty()) || // first time
+             (!aGraph && mData)); // 2nd time and later
 
-  mData.reset(new DecodedStreamData(aInitialTime, aStream));
+  if (!aGraph) {
+    aGraph = mData->mStream->Graph();
+  }
+  auto source = aGraph->CreateSourceStream(nullptr);
+  DestroyData();
+  mData.reset(new DecodedStreamData(aInitialTime, source));
 
   // Note that the delay between removing ports in DestroyDecodedStream
   // and adding new ones won't cause a glitch since all graph operations
   // between main-thread stable states take effect atomically.
   auto& outputStreams = OutputStreams();
   for (int32_t i = outputStreams.Length() - 1; i >= 0; --i) {
     OutputStreamData& os = outputStreams[i];
     MOZ_ASSERT(!os.mStream->IsDestroyed(), "Should've been removed in DestroyData()");
@@ -243,17 +251,17 @@ DecodedStream::RecreateData(int64_t aIni
 nsTArray<OutputStreamData>&
 DecodedStream::OutputStreams()
 {
   GetReentrantMonitor().AssertCurrentThreadIn();
   return mOutputStreams;
 }
 
 ReentrantMonitor&
-DecodedStream::GetReentrantMonitor()
+DecodedStream::GetReentrantMonitor() const
 {
   return mMonitor;
 }
 
 void
 DecodedStream::Connect(OutputStreamData* aStream)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -16,16 +16,17 @@ namespace mozilla {
 
 class MediaInputPort;
 class SourceMediaStream;
 class ProcessedMediaStream;
 class DecodedStream;
 class DecodedStreamGraphListener;
 class OutputStreamListener;
 class ReentrantMonitor;
+class MediaStreamGraph;
 
 namespace layers {
 class Image;
 }
 
 /*
  * All MediaStream-related data is protected by the decoder's monitor.
  * We have at most one DecodedStreamDaata per MediaDecoder. Its stream
@@ -87,21 +88,21 @@ public:
   // mPort connects DecodedStreamData::mStream to our mStream.
   nsRefPtr<MediaInputPort> mPort;
   nsRefPtr<OutputStreamListener> mListener;
 };
 
 class DecodedStream {
 public:
   explicit DecodedStream(ReentrantMonitor& aMonitor);
-  DecodedStreamData* GetData();
+  DecodedStreamData* GetData() const;
   void DestroyData();
-  void RecreateData(int64_t aInitialTime, SourceMediaStream* aStream);
+  void RecreateData(int64_t aInitialTime, MediaStreamGraph* aGraph);
   nsTArray<OutputStreamData>& OutputStreams();
-  ReentrantMonitor& GetReentrantMonitor();
+  ReentrantMonitor& GetReentrantMonitor() const;
   void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
 
 private:
   void Connect(OutputStreamData* aStream);
 
   UniquePtr<DecodedStreamData> mData;
   // Data about MediaStreams that are being fed by the decoder.
   nsTArray<OutputStreamData> mOutputStreams;
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -284,99 +284,22 @@ void MediaDecoder::Pause()
 }
 
 void MediaDecoder::SetVolume(double aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mVolume = aVolume;
 }
 
-void MediaDecoder::UpdateDecodedStream()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  GetReentrantMonitor().AssertCurrentThreadIn();
-
-  if (GetDecodedStream()) {
-    bool blockForPlayState = mPlayState != PLAY_STATE_PLAYING || mLogicallySeeking;
-    if (GetDecodedStream()->mHaveBlockedForPlayState != blockForPlayState) {
-      GetDecodedStream()->mStream->ChangeExplicitBlockerCount(blockForPlayState ? 1 : -1);
-      GetDecodedStream()->mHaveBlockedForPlayState = blockForPlayState;
-    }
-  }
-}
-
-void MediaDecoder::UpdateStreamBlockingForStateMachinePlaying()
-{
-  GetReentrantMonitor().AssertCurrentThreadIn();
-  if (!GetDecodedStream()) {
-    return;
-  }
-  bool blockForStateMachineNotPlaying =
-    mDecoderStateMachine && !mDecoderStateMachine->IsPlaying();
-  if (blockForStateMachineNotPlaying != GetDecodedStream()->mHaveBlockedForStateMachineNotPlaying) {
-    GetDecodedStream()->mHaveBlockedForStateMachineNotPlaying = blockForStateMachineNotPlaying;
-    int32_t delta = blockForStateMachineNotPlaying ? 1 : -1;
-    if (NS_IsMainThread()) {
-      GetDecodedStream()->mStream->ChangeExplicitBlockerCount(delta);
-    } else {
-      nsCOMPtr<nsIRunnable> runnable =
-          NS_NewRunnableMethodWithArg<int32_t>(GetDecodedStream()->mStream.get(),
-              &MediaStream::ChangeExplicitBlockerCount, delta);
-      NS_DispatchToMainThread(runnable);
-    }
-  }
-}
-
-void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs,
-                                         MediaStreamGraph* aGraph)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  DECODER_LOG("RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
-
-  if (!aGraph) {
-    aGraph = GetDecodedStream()->mStream->Graph();
-  }
-
-  mDecodedStream.DestroyData();
-  mDecodedStream.RecreateData(aStartTimeUSecs, aGraph->CreateSourceStream(nullptr));
-
-  UpdateStreamBlockingForStateMachinePlaying();
-
-  GetDecodedStream()->mHaveBlockedForPlayState = mPlayState != PLAY_STATE_PLAYING;
-  if (GetDecodedStream()->mHaveBlockedForPlayState) {
-    GetDecodedStream()->mStream->ChangeExplicitBlockerCount(1);
-  }
-}
-
 void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
                                    bool aFinishWhenEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  DECODER_LOG("AddOutputStream aStream=%p!", aStream);
-
-  {
-    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-    if (mDecoderStateMachine) {
-      mDecoderStateMachine->DispatchAudioCaptured();
-    }
-    if (!GetDecodedStream()) {
-      RecreateDecodedStream(mLogicalPosition, aStream->Graph());
-    }
-    mDecodedStream.Connect(aStream, aFinishWhenEnded);
-  }
-
-  // This can be called before Load(), in which case our mDecoderStateMachine
-  // won't have been created yet and we can rely on Load() to schedule it
-  // once it is created.
-  if (mDecoderStateMachine) {
-    // Make sure the state machine thread runs so that any buffered data
-    // is fed into our stream.
-    ScheduleStateMachine();
-  }
+  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
+  mDecoderStateMachine->AddOutputStream(aStream, aFinishWhenEnded);
 }
 
 double MediaDecoder::GetDuration()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mInfiniteStream) {
     return std::numeric_limits<double>::infinity();
   }
@@ -415,17 +338,16 @@ MediaDecoder::MediaDecoder() :
   mCurrentPosition(AbstractThread::MainThread(), 0, "MediaDecoder::mCurrentPosition (Mirror)"),
   mVolume(AbstractThread::MainThread(), 0.0, "MediaDecoder::mVolume (Canonical)"),
   mPlaybackRate(AbstractThread::MainThread(), 1.0, "MediaDecoder::mPlaybackRate (Canonical)"),
   mPreservesPitch(AbstractThread::MainThread(), true, "MediaDecoder::mPreservesPitch (Canonical)"),
   mDuration(-1),
   mMediaSeekable(true),
   mSameOriginMedia(false),
   mReentrantMonitor("media.decoder"),
-  mDecodedStream(mReentrantMonitor),
   mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
              "MediaDecoder::mPlayState (Canonical)"),
   mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
              "MediaDecoder::mNextState (Canonical)"),
   mLogicallySeeking(AbstractThread::MainThread(), false,
              "MediaDecoder::mLogicallySeeking (Canonical)"),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
@@ -503,22 +425,16 @@ void MediaDecoder::Shutdown()
   mOwner = nullptr;
 
   MediaShutdownManager::Instance().Unregister(this);
 }
 
 MediaDecoder::~MediaDecoder()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  {
-    // Don't destroy the decoded stream until destructor in order to keep the
-    // invariant that the decoded stream is always available in capture mode.
-    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-    mDecodedStream.DestroyData();
-  }
   MediaMemoryTracker::RemoveMediaDecoder(this);
   UnpinForSeek();
   MOZ_COUNT_DTOR(MediaDecoder);
 }
 
 nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -568,19 +484,16 @@ nsresult MediaDecoder::InitializeStateMa
 
   return ScheduleStateMachine();
 }
 
 void MediaDecoder::SetStateMachineParameters()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mDecoderStateMachine->SetDuration(mDuration);
-  if (GetDecodedStream()) {
-    mDecoderStateMachine->DispatchAudioCaptured();
-  }
   if (mMinimizePreroll) {
     mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
   }
 }
 
 void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
 {
   DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
@@ -641,17 +554,16 @@ nsresult MediaDecoder::Seek(double aTime
   int64_t timeUsecs = 0;
   nsresult rv = SecondsToUsecs(aTime, timeUsecs);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mLogicalPosition = aTime;
   mWasEndedWhenEnteredDormant = false;
 
   mLogicallySeeking = true;
-  UpdateDecodedStream();
   SeekTarget target = SeekTarget(timeUsecs, aSeekType);
   CallSeek(target);
 
   if (mPlayState == PLAY_STATE_ENDED) {
     bool paused = false;
     if (mOwner) {
       paused = mOwner->GetPaused();
     }
@@ -1061,17 +973,16 @@ void MediaDecoder::OnSeekResolved(SeekRe
     // An additional seek was requested while the current seek was
     // in operation.
     UnpinForSeek();
     fireEnded = aVal.mAtEnd;
     if (aVal.mAtEnd) {
       ChangeState(PLAY_STATE_ENDED);
     }
     mLogicallySeeking = false;
-    UpdateDecodedStream();
   }
 
   UpdateLogicalPosition(aVal.mEventVisibility);
 
   if (mOwner) {
     if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
       mOwner->SeekCompleted();
       if (fireEnded) {
@@ -1107,18 +1018,16 @@ void MediaDecoder::ChangeState(PlayState
     GetReentrantMonitor().NotifyAll();
     return;
   }
 
   DECODER_LOG("ChangeState %s => %s",
               gPlayStateStr[mPlayState], gPlayStateStr[aState]);
   mPlayState = aState;
 
-  UpdateDecodedStream();
-
   if (mPlayState == PLAY_STATE_PLAYING) {
     ConstructMediaTracks();
   } else if (IsEnded()) {
     RemoveMediaTracks();
   }
 
   ScheduleStateMachine();
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -390,38 +390,16 @@ public:
 
   // All MediaStream-related data is protected by mReentrantMonitor.
   // We have at most one DecodedStreamData per MediaDecoder. Its stream
   // is used as the input for each ProcessedMediaStream created by calls to
   // captureStream(UntilEnded). Seeking creates a new source stream, as does
   // replaying after the input as ended. In the latter case, the new source is
   // not connected to streams created by captureStreamUntilEnded.
 
-  void UpdateDecodedStream();
-
-  /**
-   * Recreates mDecodedStream. Call this to create mDecodedStream at first,
-   * and when seeking, to ensure a new stream is set up with fresh buffers.
-   * aStartTimeUSecs is relative to the state machine's mStartTime.
-   * Decoder monitor must be held.
-   */
-  void RecreateDecodedStream(int64_t aStartTimeUSecs,
-                             MediaStreamGraph* aGraph = nullptr);
-  /**
-   * Call this when mDecoderStateMachine or mDecoderStateMachine->IsPlaying() changes.
-   * Decoder monitor must be held.
-   */
-  void UpdateStreamBlockingForStateMachinePlaying();
-
-  DecodedStreamData* GetDecodedStream()
-  {
-    GetReentrantMonitor().AssertCurrentThreadIn();
-    return mDecodedStream.GetData();
-  }
-
   // Add an output stream. All decoder output will be sent to the stream.
   // The stream is initially blocked. The decoder is responsible for unblocking
   // it while it is playing back.
   virtual void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
 
   // Return the duration of the video in seconds.
   virtual double GetDuration();
 
@@ -1011,23 +989,16 @@ private:
   // change.  Explicitly private for force access via GetReentrantMonitor.
   ReentrantMonitor mReentrantMonitor;
 
 #ifdef MOZ_EME
   nsRefPtr<CDMProxy> mProxy;
 #endif
 
 protected:
-  // The SourceMediaStream we are using to feed the mOutputStreams. This stream
-  // is never exposed outside the decoder.
-  // Only written on the main thread while holding the monitor. Therefore it
-  // can be read on any thread while holding the monitor, or on the main thread
-  // without holding the monitor.
-  DecodedStream mDecodedStream;
-
   // Set to one of the valid play states.
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
   // Any change to the state on the main thread must call NotifyAll on the
   // monitor so the decode thread can wake up.
   Canonical<PlayState> mPlayState;
 
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -172,17 +172,17 @@ public:
   // A function that is called before ReadMetadata() call.
   virtual void PreReadMetadata() {};
 
   // Read header data for all bitstreams in the file. Fills aInfo with
   // the data required to present the media, and optionally fills *aTags
   // with tag metadata from the file.
   // Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
-                                MetadataTags** aTags) = 0;
+                                MetadataTags** aTags) { MOZ_CRASH(); }
 
   // Fills aInfo with the latest cached data required to present the media,
   // ReadUpdatedMetadata will always be called once ReadMetadata has succeeded.
   virtual void ReadUpdatedMetadata(MediaInfo* aInfo) { };
 
   // Moves the decode head to aTime microseconds. aEndTime denotes the end
   // time of the media in usecs. This is only needed for OggReader, and should
   // probably be removed somehow.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -235,17 +235,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDropVideoUntilNextDiscontinuity(false),
   mDecodeToSeekTarget(false),
   mCurrentTimeBeforeSeek(0),
   mCorruptFrames(30),
   mDisabledHardwareAcceleration(false),
   mDecodingFrozenAtStateDecoding(false),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false),
-  mSentPlaybackEndedEvent(false)
+  mSentPlaybackEndedEvent(false),
+  mDecodedStream(mDecoder->GetReentrantMonitor())
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Dispatch initialization that needs to happen on that task queue.
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::InitializationTask);
   mTaskQueue->Dispatch(r.forget());
 
@@ -303,16 +304,18 @@ MediaDecoderStateMachine::Initialization
   // Initialize watchers.
   mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
+  mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
+  mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
 }
 
 bool MediaDecoderStateMachine::HasFutureAudio()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
   // We've got audio ready to play if:
@@ -417,23 +420,36 @@ static bool ZeroDurationAtLastChunk(Vide
   // Get the last video frame's start time in VideoSegment aInput.
   // If the start time is equal to the duration of aInput, means the last video
   // frame's duration is zero.
   StreamTime lastVideoStratTime;
   aInput.GetLastFrame(&lastVideoStratTime);
   return lastVideoStratTime == aInput.GetDuration();
 }
 
+static void
+UpdateStreamBlocking(MediaStream* aStream, bool aBlocking)
+{
+  int32_t delta = aBlocking ? 1 : -1;
+  if (NS_IsMainThread()) {
+    aStream->ChangeExplicitBlockerCount(delta);
+  } else {
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<int32_t>(
+      aStream, &MediaStream::ChangeExplicitBlockerCount, delta);
+    AbstractThread::MainThread()->Dispatch(r.forget());
+  }
+}
+
 void MediaDecoderStateMachine::SendStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
-  DecodedStreamData* stream = mDecoder->GetDecodedStream();
+  DecodedStreamData* stream = GetDecodedStream();
 
   bool finished =
       (!mInfo.HasAudio() || AudioQueue().IsFinished()) &&
       (!mInfo.HasVideo() || VideoQueue().IsFinished());
   if (mDecoder->IsSameOriginMedia()) {
     SourceMediaStream* mediaStream = stream->mStream;
     StreamTime endPosition = 0;
 
@@ -458,16 +474,22 @@ void MediaDecoderStateMachine::SendStrea
         // TODO: We can't initialize |mNextVideoTime| until |mStartTime|
         // is set. This is a good indication that DecodedStreamData is in
         // deep coupling with the state machine and we should move the class
         // into MediaDecoderStateMachine.
         stream->mNextVideoTime = mStartTime + stream->mInitialTime;
       }
       mediaStream->FinishAddTracks();
       stream->mStreamInitialized = true;
+
+      // Make sure stream blocking is updated before sending stream data so we
+      // don't 'leak' data when the stream is supposed to be blocked.
+      UpdateStreamBlockingForPlayState();
+      UpdateStreamBlockingForStateMachinePlaying();
+      UpdateStreamBlocking(mediaStream, false);
     }
 
     if (mInfo.HasAudio()) {
       MOZ_ASSERT(stream->mNextAudioTime != -1, "Should've been initialized");
       TrackID audioTrackId = mInfo.mAudio.mTrackId;
       nsAutoTArray<nsRefPtr<AudioData>,10> audio;
       // It's OK to hold references to the AudioData because AudioData
       // is ref-counted.
@@ -611,17 +633,17 @@ bool MediaDecoderStateMachine::HaveEnoug
   if (AudioQueue().GetSize() == 0 ||
       GetDecodedAudioDuration() < aAmpleAudioUSecs) {
     return false;
   }
   if (!mAudioCaptured) {
     return true;
   }
 
-  DecodedStreamData* stream = mDecoder->GetDecodedStream();
+  DecodedStreamData* stream = GetDecodedStream();
 
   if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishAudio) {
     MOZ_ASSERT(mInfo.HasAudio());
     TrackID audioTrackId = mInfo.mAudio.mTrackId;
     if (!stream->mStream->HaveEnoughBuffered(audioTrackId)) {
       return false;
     }
     stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
@@ -635,17 +657,17 @@ bool MediaDecoderStateMachine::HaveEnoug
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (static_cast<uint32_t>(VideoQueue().GetSize()) < GetAmpleVideoFrames() * mPlaybackRate) {
     return false;
   }
 
-  DecodedStreamData* stream = mDecoder->GetDecodedStream();
+  DecodedStreamData* stream = GetDecodedStream();
 
   if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishVideo) {
     MOZ_ASSERT(mInfo.HasVideo());
     TrackID videoTrackId = mInfo.mVideo.mTrackId;
     if (!stream->mStream->HaveEnoughBuffered(videoTrackId)) {
       return false;
     }
     stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
@@ -1266,17 +1288,17 @@ void MediaDecoderStateMachine::StopPlayb
   if (IsPlaying()) {
     mPlayDuration = GetClock() - mStartTime;
     SetPlayStartTime(TimeStamp());
   }
   // Notify the audio sink, so that it notices that we've stopped playing,
   // so it can pause audio playback.
   mDecoder->GetReentrantMonitor().NotifyAll();
   NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
-  mDecoder->UpdateStreamBlockingForStateMachinePlaying();
+  UpdateStreamBlockingForStateMachinePlaying();
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::MaybeStartPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
@@ -1304,17 +1326,17 @@ void MediaDecoderStateMachine::MaybeStar
   mDecoder->NotifyPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
   MOZ_ASSERT(IsPlaying());
 
   nsresult rv = StartAudioThread();
   NS_ENSURE_SUCCESS_VOID(rv);
 
   mDecoder->GetReentrantMonitor().NotifyAll();
-  mDecoder->UpdateStreamBlockingForStateMachinePlaying();
+  UpdateStreamBlockingForStateMachinePlaying();
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld) (mStartTime=%lld)", aTime, mStartTime);
   AssertCurrentThreadInMonitor();
@@ -1900,18 +1922,18 @@ MediaDecoderStateMachine::InitiateSeek()
   mCurrentSeek.mTarget.mTime = seekTime;
 
   if (mAudioCaptured) {
     // TODO: We should re-create the decoded stream after seek completed as we do
     // for audio thread since it is until then we know which position we seek to
     // as far as fast-seek is concerned. It also fix the problem where stream
     // clock seems to go backwards during seeking.
     nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethodWithArgs<int64_t, MediaStreamGraph*>(mDecoder,
-                                                               &MediaDecoder::RecreateDecodedStream,
+      NS_NewRunnableMethodWithArgs<int64_t, MediaStreamGraph*>(this,
+                                                               &MediaDecoderStateMachine::RecreateDecodedStream,
                                                                seekTime - mStartTime,
                                                                nullptr);
     AbstractThread::MainThread()->Dispatch(event.forget());
   }
 
   mDropAudioUntilNextDiscontinuity = HasAudio();
   mDropVideoUntilNextDiscontinuity = HasVideo();
 
@@ -2712,17 +2734,17 @@ nsresult MediaDecoderStateMachine::RunSt
     }
 
     case DECODER_STATE_COMPLETED: {
       // Play the remaining media. We want to run AdvanceFrame() at least
       // once to ensure the current playback position is advanced to the
       // end of the media, and so that we update the readyState.
       if (VideoQueue().GetSize() > 0 ||
           (HasAudio() && !mAudioCompleted) ||
-          (mAudioCaptured && !mDecoder->GetDecodedStream()->IsFinished()))
+          (mAudioCaptured && !GetDecodedStream()->IsFinished()))
       {
         AdvanceFrame();
         NS_ASSERTION(mPlayState != MediaDecoder::PLAY_STATE_PLAYING ||
                      mLogicallySeeking ||
                      mPlaybackRate == 0 || IsStateMachineScheduled(),
                      "Must have timer scheduled");
         return NS_OK;
       }
@@ -2893,17 +2915,17 @@ int64_t MediaDecoderStateMachine::GetClo
   // the end of the audio, use the audio clock. However if we've finished
   // audio, or don't have audio, use the system clock. If our output is being
   // fed to a MediaStream, use that stream as the source of the clock.
   int64_t clock_time = -1;
   if (!IsPlaying()) {
     clock_time = mPlayDuration + mStartTime;
   } else {
     if (mAudioCaptured) {
-      clock_time = mStartTime + mDecoder->GetDecodedStream()->GetClock();
+      clock_time = mStartTime + GetDecodedStream()->GetClock();
     } else if (HasAudio() && !mAudioCompleted) {
       clock_time = GetAudioClock();
     } else {
       // Audio is disabled on this system. Sync to the system clock.
       clock_time = GetVideoStreamPosition();
     }
     // Ensure the clock can never go backwards.
     // Note we allow clock going backwards in capture mode - we should fix this in bug 1162381.
@@ -3482,16 +3504,93 @@ uint32_t MediaDecoderStateMachine::GetAm
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
     ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
     : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
 }
 
+DecodedStreamData* MediaDecoderStateMachine::GetDecodedStream() const
+{
+  AssertCurrentThreadInMonitor();
+  return mDecodedStream.GetData();
+}
+
+void MediaDecoderStateMachine::DispatchAudioCaptured()
+{
+  nsRefPtr<MediaDecoderStateMachine> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
+  {
+    MOZ_ASSERT(self->OnTaskQueue());
+    ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
+    if (!self->mAudioCaptured) {
+      self->mAudioCaptured = true;
+      self->ScheduleStateMachine();
+    }
+  });
+  TaskQueue()->Dispatch(r.forget());
+}
+
+void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
+                                               bool aFinishWhenEnded)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  DECODER_LOG("AddOutputStream aStream=%p!", aStream);
+
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  if (!GetDecodedStream()) {
+    RecreateDecodedStream(mCurrentPosition.ReadOnWrongThread(), aStream->Graph());
+  }
+  mDecodedStream.Connect(aStream, aFinishWhenEnded);
+  DispatchAudioCaptured();
+}
+
+void MediaDecoderStateMachine::UpdateStreamBlockingForPlayState()
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+
+   auto stream = GetDecodedStream();
+   if (!stream) {
+     return;
+   }
+
+  bool blocking = mPlayState != MediaDecoder::PLAY_STATE_PLAYING ||
+                  mLogicallySeeking;
+  if (blocking != stream->mHaveBlockedForPlayState) {
+    stream->mHaveBlockedForPlayState = blocking;
+    UpdateStreamBlocking(stream->mStream, blocking);
+  }
+}
+
+void MediaDecoderStateMachine::UpdateStreamBlockingForStateMachinePlaying()
+{
+  AssertCurrentThreadInMonitor();
+
+  auto stream = GetDecodedStream();
+  if (!stream) {
+    return;
+  }
+
+  bool blocking = !IsPlaying();
+  if (blocking != stream->mHaveBlockedForStateMachineNotPlaying) {
+    stream->mHaveBlockedForStateMachineNotPlaying = blocking;
+    UpdateStreamBlocking(stream->mStream, blocking);
+  }
+}
+
+void MediaDecoderStateMachine::RecreateDecodedStream(int64_t aInitialTime,
+                                                     MediaStreamGraph* aGraph)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  DECODER_LOG("RecreateDecodedStream aInitialTime=%lld!", aInitialTime);
+  mDecodedStream.RecreateData(aInitialTime, aGraph);
+}
+
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 #undef DECODER_WARN
 #undef DECODER_WARN_HELPER
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -87,23 +87,23 @@ hardware (via AudioStream).
 #include "MediaDecoder.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaMetadataManager.h"
 #include "mozilla/RollingMean.h"
 #include "MediaTimer.h"
 #include "StateMirroring.h"
+#include "DecodedStream.h"
 
 namespace mozilla {
 
 class AudioSegment;
 class MediaTaskQueue;
 class AudioSink;
-class DecodedStreamData;
 
 /*
   The state machine class. This manages the decoding and seeking in the
   MediaDecoderReader on the decode task queue, and A/V sync on the shared
   state machine thread, and controls the audio "push" thread.
 
   All internal state is synchronised via the decoder monitor. State changes
   are either propagated by NotifyAll on the monitor (typically when state
@@ -140,42 +140,46 @@ public:
     DECODER_STATE_ERROR
   };
 
   State GetState() {
     AssertCurrentThreadInMonitor();
     return mState;
   }
 
-  void DispatchAudioCaptured()
-  {
-    nsRefPtr<MediaDecoderStateMachine> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
-    {
-      MOZ_ASSERT(self->OnTaskQueue());
-      ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
-      if (!self->mAudioCaptured) {
-        self->mAudioCaptured = true;
-        self->ScheduleStateMachine();
-      }
-    });
-    TaskQueue()->Dispatch(r.forget());
-  }
+  DecodedStreamData* GetDecodedStream() const;
+
+  void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
 
   // Check if the decoder needs to become dormant state.
   bool IsDormantNeeded();
   // Set/Unset dormant state.
   void SetDormant(bool aDormant);
 
 private:
   // Initialization that needs to happen on the task queue. This is the first
   // task that gets run on the task queue, and is dispatched from the MDSM
   // constructor immediately after the task queue is created.
   void InitializationTask();
 
+  void DispatchAudioCaptured();
+
+  // Update blocking state of mDecodedStream when mPlayState or
+  // mLogicallySeeking change. Decoder monitor must be held.
+  void UpdateStreamBlockingForPlayState();
+
+  // Call this IsPlaying() changes. Decoder monitor must be held.
+  void UpdateStreamBlockingForStateMachinePlaying();
+
+  // Recreates mDecodedStream. Call this to create mDecodedStream at first,
+  // and when seeking, to ensure a new stream is set up with fresh buffers.
+  // aInitialTime is relative to mStartTime.
+  // Decoder monitor must be held.
+  void RecreateDecodedStream(int64_t aInitialTime, MediaStreamGraph* aGraph);
+
   void Shutdown();
 public:
 
   void DispatchShutdown()
   {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown);
     TaskQueue()->Dispatch(runnable.forget());
@@ -348,16 +352,20 @@ public:
   void SetFragmentEndTime(int64_t aEndTime);
 
   // Drop reference to decoder.  Only called during shutdown dance.
   void BreakCycles() {
     MOZ_ASSERT(NS_IsMainThread());
     if (mReader) {
       mReader->BreakCycles();
     }
+    {
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+      mDecodedStream.DestroyData();
+    }
     mDecoder = nullptr;
   }
 
   // Copy queued audio/video data in the reader to any output MediaStreams that
   // need it.
   void SendStreamData();
   void FinishStreamData();
   bool HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs);
@@ -1279,12 +1287,19 @@ protected:
   // True if we are back from DECODER_STATE_DORMANT state and
   // FirstFrameLoadedEvent was already sent, then we can skip
   // SetStartTime because the mStartTime already set before. Also we don't need
   // to decode any audio/video since the MediaDecoder will trigger a seek
   // operation soon.
   bool mSentFirstFrameLoadedEvent;
 
   bool mSentPlaybackEndedEvent;
+
+  // The SourceMediaStream we are using to feed the mOutputStreams. This stream
+  // is never exposed outside the decoder.
+  // Only written on the main thread while holding the monitor. Therefore it
+  // can be read on any thread while holding the monitor, or on the main thread
+  // without holding the monitor.
+  DecodedStream mDecodedStream;
 };
 
 } // namespace mozilla;
 #endif
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -50,23 +50,16 @@ public:
   }
 
   bool HasAudio() override
   {
     return mInfo.HasAudio();
   }
 
   virtual nsRefPtr<MetadataPromise> AsyncReadMetadata() override;
-  virtual nsresult ReadMetadata(MediaInfo* aInfo,
-                                MetadataTags** aTags) override
-  {
-    // Unused as we provide AsyncReadMetadataAPI.
-    // However we must implement it as it's pure virtual.
-    return NS_OK;
-  }
 
   virtual void ReadUpdatedMetadata(MediaInfo* aInfo) override;
 
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aUnused) override;
 
   virtual bool IsMediaSeekable() override
   {
--- a/dom/media/directshow/moz.build
+++ b/dom/media/directshow/moz.build
@@ -23,22 +23,22 @@ UNIFIED_SOURCES += [
 
 SOURCES += [
     'AudioSinkFilter.cpp',
 ]
 
 # If WebRTC isn't being built, we need to compile the DirectShow base classes so that
 # they're available at link time.
 if not CONFIG['MOZ_WEBRTC']:
-    SOURCES += [ '%s/%s' % (TOPSRCDIR, p) for p in [
-        'media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp',
-        'media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp',
-        'media/webrtc/trunk/webrtc/modules/video_capture/windows/BasePin.cpp',
-        'media/webrtc/trunk/webrtc/modules/video_capture/windows/MediaType.cpp',
-    ]]
+    SOURCES += [
+        '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp',
+        '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp',
+        '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BasePin.cpp',
+        '/media/webrtc/trunk/webrtc/modules/video_capture/windows/MediaType.cpp',
+    ]
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/media/webrtc/trunk/webrtc/modules/video_capture/windows',
 ]
 
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -42,17 +42,18 @@ MediaOmxCommonDecoder::SetPlatformCanOff
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mCanOffloadAudio = aCanOffloadAudio;
 }
 
 bool
 MediaOmxCommonDecoder::CheckDecoderCanOffloadAudio()
 {
   return (mCanOffloadAudio && !mFallbackToStateMachine &&
-          !GetDecodedStream() && mPlaybackRate == 1.0);
+          !(GetStateMachine() && GetStateMachine()->GetDecodedStream()) &&
+          mPlaybackRate == 1.0);
 }
 
 void
 MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
                                         MediaDecoderEventVisibility aEventVisibility)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/media/omx/MediaOmxCommonReader.h
+++ b/dom/media/omx/MediaOmxCommonReader.h
@@ -19,16 +19,18 @@ struct MOZ_EXPORT MediaSource;
 
 namespace mozilla {
 
 class AbstractMediaDecoder;
 
 class MediaOmxCommonReader : public MediaDecoderReader
 {
 public:
+  typedef MediaPromise<bool /* aIgnored */, bool /* aIgnored */, /* IsExclusive = */ true> MediaResourcePromise;
+
   MediaOmxCommonReader(AbstractMediaDecoder* aDecoder);
 
   void SetAudioChannel(dom::AudioChannel aAudioChannel) {
     mAudioChannel = aAudioChannel;
   }
 
   virtual android::sp<android::MediaSource> GetAudioOffloadTrack() = 0;
 
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -129,17 +129,16 @@ MediaOmxReader::MediaOmxReader(AbstractM
   , mHasVideo(false)
   , mHasAudio(false)
   , mVideoSeekTimeUs(-1)
   , mAudioSeekTimeUs(-1)
   , mLastParserDuration(-1)
   , mSkipCount(0)
   , mIsShutdown(false)
   , mMP3FrameParser(-1)
-  , mIsWaitingResources(false)
 {
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 
   mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
 }
 
@@ -182,32 +181,21 @@ MediaOmxReader::Shutdown()
 
   // Wait for the superclass to finish tearing things down before releasing
   // the decoder on the main thread.
   p->Then(AbstractThread::MainThread(), __func__, this, &MediaOmxReader::ReleaseDecoder, &MediaOmxReader::ReleaseDecoder);
 
   return p;
 }
 
-bool MediaOmxReader::IsWaitingMediaResources()
-{
-  return mIsWaitingResources;
-}
-
-void MediaOmxReader::UpdateIsWaitingMediaResources()
-{
-  if (mOmxDecoder.get()) {
-    mIsWaitingResources = mOmxDecoder->IsWaitingMediaResources();
-  } else {
-    mIsWaitingResources = false;
-  }
-}
-
 void MediaOmxReader::ReleaseMediaResources()
 {
+  mMediaResourceRequest.DisconnectIfExists();
+  mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
+
   ResetDecode();
   // Before freeing a video codec, all video buffers needed to be released
   // even from graphics pipeline.
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->ClearCurrentFrame();
   }
   if (mOmxDecoder.get()) {
@@ -232,60 +220,65 @@ nsresult MediaOmxReader::InitOmxDecoder(
     mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder);
     if (!mOmxDecoder->Init(mExtractor)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
-void MediaOmxReader::PreReadMetadata()
-{
-  UpdateIsWaitingMediaResources();
-}
-
-nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
-                                      MetadataTags** aTags)
+nsRefPtr<MediaDecoderReader::MetadataPromise>
+MediaOmxReader::AsyncReadMetadata()
 {
   MOZ_ASSERT(OnTaskQueue());
   EnsureActive();
 
-  *aTags = nullptr;
-
   // Initialize the internal OMX Decoder.
   nsresult rv = InitOmxDecoder();
   if (NS_FAILED(rv)) {
-    return rv;
+    return MediaDecoderReader::MetadataPromise::CreateAndReject(
+             ReadMetadataFailureReason::METADATA_ERROR, __func__);
   }
 
   bool isMP3 = mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3);
   if (isMP3) {
     // When read sdcard's file on b2g platform at constructor,
     // the mDecoder->GetResource()->GetLength() would return -1.
     // Delay set the total duration on this function.
     mMP3FrameParser.SetLength(mDecoder->GetResource()->GetLength());
     ProcessCachedData(0, true);
   }
 
-  if (!mOmxDecoder->AllocateMediaResources()) {
-    return NS_ERROR_FAILURE;
-  }
-  // Bug 1050667, both MediaDecoderStateMachine and MediaOmxReader
-  // relies on IsWaitingMediaResources() function. And the waiting state will be
-  // changed by binder thread, so we store the waiting state in a cache value to
-  // make them in consistent state.
-  UpdateIsWaitingMediaResources();
-  if (IsWaitingMediaResources()) {
-    return NS_OK;
-  }
+  nsRefPtr<MediaDecoderReader::MetadataPromise> p = mMetadataPromise.Ensure(__func__);
+
+  nsRefPtr<MediaOmxReader> self = this;
+  mMediaResourceRequest.Begin(mOmxDecoder->AllocateMediaResources()
+    ->RefableThen(GetTaskQueue(), __func__,
+      [self] (bool) -> void {
+        self->mMediaResourceRequest.Complete();
+        self->HandleResourceAllocated();
+      }, [self] (bool) -> void {
+        self->mMediaResourceRequest.Complete();
+        self->mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
+      }));
+
+  return p;
+}
+
+void MediaOmxReader::HandleResourceAllocated()
+{
+  EnsureActive();
+
   // After resources are available, set the metadata.
   if (!mOmxDecoder->EnsureMetadata()) {
-    return NS_ERROR_FAILURE;
+    mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
+    return;
   }
 
+  bool isMP3 = mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3);
   if (isMP3 && mMP3FrameParser.IsMP3()) {
     // Check if the MP3 frame parser found a duration.
     mLastParserDuration = mMP3FrameParser.GetDuration();
   }
 
   if (mLastParserDuration >= 0) {
     // Prefer the parser duration if we have it.
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@@ -307,17 +300,18 @@ nsresult MediaOmxReader::ReadMetadata(Me
                                     &width, &height);
     nsIntRect pictureRect(0, 0, width, height);
 
     // Validate the container-reported frame and pictureRect sizes. This ensures
     // that our video frame creation code doesn't overflow.
     nsIntSize displaySize(displayWidth, displayHeight);
     nsIntSize frameSize(width, height);
     if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
-      return NS_ERROR_FAILURE;
+      mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
+      return;
     }
 
     // Video track's frame sizes will not overflow. Activate the video track.
     mHasVideo = true;
     mInfo.mVideo.mDisplay = displaySize;
     mPicture = pictureRect;
     mInitialFrame = frameSize;
     VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
@@ -331,23 +325,25 @@ nsresult MediaOmxReader::ReadMetadata(Me
   if (mOmxDecoder->HasAudio()) {
     int32_t numChannels, sampleRate;
     mOmxDecoder->GetAudioParameters(&numChannels, &sampleRate);
     mHasAudio = true;
     mInfo.mAudio.mChannels = numChannels;
     mInfo.mAudio.mRate = sampleRate;
   }
 
- *aInfo = mInfo;
+  nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
+  metadata->mInfo = mInfo;
+  metadata->mTags = nullptr;
 
 #ifdef MOZ_AUDIO_OFFLOAD
   CheckAudioOffload();
 #endif
 
-  return NS_OK;
+  mMetadataPromise.Resolve(metadata, __func__);
 }
 
 bool
 MediaOmxReader::IsMediaSeekable()
 {
   // Check the MediaExtract flag if the source is seekable.
   return (mExtractor->flags() & MediaExtractor::CAN_SEEK);
 }
--- a/dom/media/omx/MediaOmxReader.h
+++ b/dom/media/omx/MediaOmxReader.h
@@ -7,68 +7,66 @@
 #define MediaOmxReader_h_
 
 #include "MediaOmxCommonReader.h"
 #include "MediaResource.h"
 #include "MediaDecoderReader.h"
 #include "nsMimeTypes.h"
 #include "MP3FrameParser.h"
 #include "nsRect.h"
+
 #include <ui/GraphicBuffer.h>
 #include <stagefright/MediaSource.h>
 
 namespace android {
 class OmxDecoder;
 class MOZ_EXPORT MediaExtractor;
 }
 
 namespace mozilla {
 
 class AbstractMediaDecoder;
 
 class MediaOmxReader : public MediaOmxCommonReader
 {
+  typedef MediaOmxCommonReader::MediaResourcePromise MediaResourcePromise;
+
   // This mutex is held when accessing the mIsShutdown variable, which is
   // modified on the decode task queue and read on main and IO threads.
   Mutex mShutdownMutex;
   nsCString mType;
   bool mHasVideo;
   bool mHasAudio;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
   int64_t mVideoSeekTimeUs;
   int64_t mAudioSeekTimeUs;
   int64_t mLastParserDuration;
   int32_t mSkipCount;
   // If mIsShutdown is false, and mShutdownMutex is held, then
   // AbstractMediaDecoder::mDecoder will be non-null.
   bool mIsShutdown;
+  MediaPromiseHolder<MediaDecoderReader::MetadataPromise> mMetadataPromise;
+  MediaPromiseConsumerHolder<MediaResourcePromise> mMediaResourceRequest;
 protected:
   android::sp<android::OmxDecoder> mOmxDecoder;
   android::sp<android::MediaExtractor> mExtractor;
   MP3FrameParser mMP3FrameParser;
 
-  // A cache value updated by UpdateIsWaitingMediaResources(), makes the
-  // "waiting resources state" is synchronous to StateMachine.
-  bool mIsWaitingResources;
-
   // Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
   // on decode thread. It create and initialize the OMX decoder including
   // setting up custom extractor. The extractor provide the essential
   // information used for creating OMX decoder such as video/audio codec.
   virtual nsresult InitOmxDecoder();
 
   // Called inside DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek
   // to activate the decoder automatically.
   virtual void EnsureActive();
 
-  // Check the underlying HW resources are available and store the result in
-  // mIsWaitingResources. The result might be changed by binder thread,
-  // Can only called by ReadMetadata.
-  void UpdateIsWaitingMediaResources();
+  virtual void HandleResourceAllocated();
 
 public:
   MediaOmxReader(AbstractMediaDecoder* aDecoder);
   ~MediaOmxReader();
 
   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
 
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
@@ -82,25 +80,21 @@ public:
     return mHasAudio;
   }
 
   virtual bool HasVideo()
   {
     return mHasVideo;
   }
 
-  // Return mIsWaitingResources.
-  virtual bool IsWaitingMediaResources() override;
-
   virtual bool IsDormantNeeded() { return true;}
   virtual void ReleaseMediaResources();
 
-  virtual void PreReadMetadata() override;
-  virtual nsresult ReadMetadata(MediaInfo* aInfo,
-                                MetadataTags** aTags);
+  virtual nsRefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
+
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) override;
 
   virtual bool IsMediaSeekable() override;
 
   virtual void SetIdle() override;
 
   virtual nsRefPtr<ShutdownPromise> Shutdown() override;
--- a/dom/media/omx/OMXCodecProxy.cpp
+++ b/dom/media/omx/OMXCodecProxy.cpp
@@ -89,29 +89,35 @@ OMXCodecProxy::~OMXCodecProxy()
 }
 
 MediaResourceManagerClient::State OMXCodecProxy::getState()
 {
   Mutex::Autolock autoLock(mLock);
   return mState;
 }
 
-void OMXCodecProxy::setEventListener(const wp<OMXCodecProxy::EventListener>& listener)
+void OMXCodecProxy::setListener(const wp<CodecResourceListener>& listener)
 {
   Mutex::Autolock autoLock(mLock);
-  mEventListener = listener;
+  mListener = listener;
 }
 
-void OMXCodecProxy::notifyStatusChangedLocked()
+void OMXCodecProxy::notifyResourceReserved()
 {
-  if (mEventListener != nullptr) {
-    sp<EventListener> listener = mEventListener.promote();
-    if (listener != nullptr) {
-      listener->statusChanged();
-    }
+  sp<CodecResourceListener> listener = mListener.promote();
+  if (listener != nullptr) {
+    listener->codecReserved();
+  }
+}
+
+void OMXCodecProxy::notifyResourceCanceled()
+{
+  sp<CodecResourceListener> listener = mListener.promote();
+  if (listener != nullptr) {
+    listener->codecCanceled();
   }
 }
 
 void OMXCodecProxy::requestResource()
 {
   Mutex::Autolock autoLock(mLock);
 
   if (mClient.get()) {
@@ -124,49 +130,44 @@ void OMXCodecProxy::requestResource()
   if (!mManagerService.get()) {
     mClient = nullptr;
     return;
   }
 
   mManagerService->requestMediaResource(mClient, IMediaResourceManagerService::HW_VIDEO_DECODER, true /* will wait */);
 }
 
-bool OMXCodecProxy::IsWaitingResources()
-{
-  Mutex::Autolock autoLock(mLock);
-  return mState == MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE;
-}
-
 // called on Binder ipc thread
 void OMXCodecProxy::statusChanged(int event)
 {
   Mutex::Autolock autoLock(mLock);
 
   if (mState != MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE) {
     return;
   }
 
   mState = (MediaResourceManagerClient::State) event;
   if (mState != MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED) {
+    notifyResourceCanceled();
     return;
   }
 
   const char *mime;
   if (!mSrcMeta->findCString(kKeyMIMEType, &mime)) {
     mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
-    notifyStatusChangedLocked();
+    notifyResourceCanceled();
     return;
   }
 
   if (!strncasecmp(mime, "video/", 6)) {
     sp<MediaSource> codec;
     mOMXCodec = OMXCodec::Create(mOMX, mSrcMeta, mIsEncoder, mSource, mComponentName, mFlags, mNativeWindow);
     if (mOMXCodec == nullptr) {
       mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
-      notifyStatusChangedLocked();
+      notifyResourceCanceled();
       return;
     }
     // Check if this video is sized such that we're comfortable
     // possibly using an OMX decoder.
     int32_t maxWidth, maxHeight;
     char propValue[PROPERTY_VALUE_MAX];
     property_get("ro.moz.omx.hw.max_width", propValue, "-1");
     maxWidth = atoi(propValue);
@@ -177,29 +178,29 @@ void OMXCodecProxy::statusChanged(int ev
     if (maxWidth > 0 && maxHeight > 0 &&
         !(mOMXCodec->getFormat()->findInt32(kKeyWidth, &width) &&
           mOMXCodec->getFormat()->findInt32(kKeyHeight, &height) &&
           width * height <= maxWidth * maxHeight)) {
       printf_stderr("Failed to get video size, or it was too large for HW decoder (<w=%d, h=%d> but <maxW=%d, maxH=%d>)",
                     width, height, maxWidth, maxHeight);
       mOMXCodec.clear();
       mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
-      notifyStatusChangedLocked();
+      notifyResourceCanceled();
       return;
     }
 
     if (mOMXCodec->start() != OK) {
       NS_WARNING("Couldn't start OMX video source");
       mOMXCodec.clear();
       mState = MediaResourceManagerClient::CLIENT_STATE_SHUTDOWN;
-      notifyStatusChangedLocked();
+      notifyResourceCanceled();
       return;
     }
   }
-  notifyStatusChangedLocked();
+  notifyResourceReserved();
 }
 
 status_t OMXCodecProxy::start(MetaData *params)
 {
   Mutex::Autolock autoLock(mLock);
 
   if (mState != MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED) {
     return NO_INIT;
--- a/dom/media/omx/OMXCodecProxy.h
+++ b/dom/media/omx/OMXCodecProxy.h
@@ -18,34 +18,44 @@
 namespace android {
 
 struct MetaData;
 
 class OMXCodecProxy : public MediaSource,
                       public MediaResourceManagerClient::EventListener
 {
 public:
-  struct EventListener : public virtual RefBase {
-    virtual void statusChanged() = 0;
+  /* Codec resource notification listener.
+   * All functions are called on the Binder thread.
+   */
+  struct CodecResourceListener : public virtual RefBase {
+    /* The codec resource is reserved and can be granted.
+     * The client can allocate the requested resource.
+     */
+    virtual void codecReserved() = 0;
+    /* The codec resource is not reserved any more.
+     * The client should release the resource as soon as possible if the
+     * resource is still being held.
+     */
+    virtual void codecCanceled() = 0;
   };
 
   static sp<OMXCodecProxy> Create(
           const sp<IOMX> &omx,
           const sp<MetaData> &meta, bool createEncoder,
           const sp<MediaSource> &source,
           const char *matchComponentName = nullptr,
           uint32_t flags = 0,
           const sp<ANativeWindow> &nativeWindow = nullptr);
 
     MediaResourceManagerClient::State getState();
 
-    void setEventListener(const wp<EventListener>& listener);
+    void setListener(const wp<CodecResourceListener>& listener);
 
     void requestResource();
-    bool IsWaitingResources();
 
     // MediaResourceManagerClient::EventListener
     virtual void statusChanged(int event);
 
     // MediaSource
     virtual status_t start(MetaData *params = nullptr);
     virtual status_t stop();
 
@@ -63,16 +73,19 @@ protected:
         bool createEncoder,
         const sp<MediaSource> &source,
         const char *matchComponentName,
         uint32_t flags,
         const sp<ANativeWindow> &nativeWindow);
 
     virtual ~OMXCodecProxy();
 
+    void notifyResourceReserved();
+    void notifyResourceCanceled();
+
     void notifyStatusChangedLocked();
 
 private:
     OMXCodecProxy(const OMXCodecProxy &);
     OMXCodecProxy &operator=(const OMXCodecProxy &);
 
     Mutex mLock;
 
@@ -86,14 +99,15 @@ private:
 
     sp<MediaSource> mSource;
 
     sp<MediaSource> mOMXCodec;
     sp<MediaResourceManagerClient> mClient;
     MediaResourceManagerClient::State mState;
 
     sp<IMediaResourceManagerService> mManagerService;
-    wp<OMXCodecProxy::EventListener> mEventListener;
+    // Codec Resource Notification Listener
+    wp<CodecResourceListener> mListener;
 };
 
 }  // namespace android
 
 #endif  // OMX_CODEC_PROXY_DECODER_H_
--- a/dom/media/omx/OmxDecoder.cpp
+++ b/dom/media/omx/OmxDecoder.cpp
@@ -86,22 +86,23 @@ OmxDecoder::~OmxDecoder()
   ReleaseMediaResources();
 
   // unregister AMessage handler from ALooper.
   mLooper->unregisterHandler(mReflector->id());
   // Stop ALooper thread.
   mLooper->stop();
 }
 
-void OmxDecoder::statusChanged()
+void OmxDecoder::codecReserved()
 {
-  sp<AMessage> notify =
-           new AMessage(kNotifyStatusChanged, mReflector->id());
- // post AMessage to OmxDecoder via ALooper.
- notify->post();
+  mMediaResourcePromise.ResolveIfExists(true, __func__);
+}
+void OmxDecoder::codecCanceled()
+{
+  mMediaResourcePromise.RejectIfExists(true, __func__);
 }
 
 static sp<IOMX> sOMX = nullptr;
 static sp<IOMX> GetOMX()
 {
   if(sOMX.get() == nullptr) {
     sOMX = new OMX;
     }
@@ -208,33 +209,27 @@ bool OmxDecoder::EnsureMetadata() {
       NS_WARNING("Couldn't set audio format");
       return false;
     }
   }
 
   return true;
 }
 
-bool OmxDecoder::IsWaitingMediaResources()
-{
-  if (mVideoSource.get()) {
-    return mVideoSource->IsWaitingResources();
-  }
-  return false;
-}
-
 static bool isInEmulator()
 {
   char propQemu[PROPERTY_VALUE_MAX];
   property_get("ro.kernel.qemu", propQemu, "");
   return !strncmp(propQemu, "1", 1);
 }
 
-bool OmxDecoder::AllocateMediaResources()
+nsRefPtr<mozilla::MediaOmxCommonReader::MediaResourcePromise> OmxDecoder::AllocateMediaResources()
 {
+  nsRefPtr<MediaResourcePromise> p = mMediaResourcePromise.Ensure(__func__);
+
   if ((mVideoTrack != nullptr) && (mVideoSource == nullptr)) {
     // OMXClient::connect() always returns OK and abort's fatally if
     // it can't connect.
     OMXClient client;
     DebugOnly<status_t> err = client.connect();
     NS_ASSERTION(err == OK, "Failed to connect to OMX in mediaserver.");
     sp<IOMX> omx = client.interface();
 
@@ -275,36 +270,38 @@ bool OmxDecoder::AllocateMediaResources(
                                 mVideoTrack->getFormat(),
                                 false, // decoder
                                 mVideoTrack,
                                 nullptr,
                                 flags,
                                 mNativeWindowClient);
     if (mVideoSource == nullptr) {
       NS_WARNING("Couldn't create OMX video source");
-      return false;
+      mMediaResourcePromise.Reject(true, __func__);
+      return p;
     } else {
-      sp<OMXCodecProxy::EventListener> listener = this;
-      mVideoSource->setEventListener(listener);
+      sp<OMXCodecProxy::CodecResourceListener> listener = this;
+      mVideoSource->setListener(listener);
       mVideoSource->requestResource();
     }
   }
 
   if ((mAudioTrack != nullptr) && (mAudioSource == nullptr)) {
     // OMXClient::connect() always returns OK and abort's fatally if
     // it can't connect.
     OMXClient client;
     DebugOnly<status_t> err = client.connect();
     NS_ASSERTION(err == OK, "Failed to connect to OMX in mediaserver.");
     sp<IOMX> omx = client.interface();
 
     const char *audioMime = nullptr;
     sp<MetaData> meta = mAudioTrack->getFormat();
     if (!meta->findCString(kKeyMIMEType, &audioMime)) {
-      return false;
+      mMediaResourcePromise.Reject(true, __func__);
+      return p;
     }
     if (!strcasecmp(audioMime, "audio/raw")) {
       mAudioSource = mAudioTrack;
     } else {
       // try to load hardware codec in mediaserver process.
       int flags = kHardwareCodecsOnly;
       mAudioSource = OMXCodec::Create(omx,
                                      mAudioTrack->getFormat(),
@@ -320,30 +317,38 @@ bool OmxDecoder::AllocateMediaResources(
       mAudioSource = OMXCodec::Create(GetOMX(),
                                      mAudioTrack->getFormat(),
                                      false, // decoder
                                      mAudioTrack,
                                      nullptr,
                                      flags);
       if (mAudioSource == nullptr) {
         NS_WARNING("Couldn't create OMX audio source");
-        return false;
+        mMediaResourcePromise.Reject(true, __func__);
+        return p;
       }
     }
     if (mAudioSource->start() != OK) {
       NS_WARNING("Couldn't start OMX audio source");
       mAudioSource.clear();
-      return false;
+      mMediaResourcePromise.Reject(true, __func__);
+      return p;
     }
   }
-  return true;
+  if (!mVideoSource.get()) {
+    // No resource allocation wait.
+    mMediaResourcePromise.Resolve(true, __func__);
+  }
+  return p;
 }
 
 
 void OmxDecoder::ReleaseMediaResources() {
+  mMediaResourcePromise.RejectIfExists(true, __func__);
+
   ReleaseVideoBuffer();
   ReleaseAudioBuffer();
 
   {
     Mutex::Autolock autoLock(mPendingVideoBuffersLock);
     MOZ_ASSERT(mPendingRecycleTexutreClients.empty());
     // Release all pending recycle TextureClients, if they are not recycled yet.
     // This should not happen. See Bug 1042308.
@@ -820,25 +825,16 @@ void OmxDecoder::onMessageReceived(const
       Mutex::Autolock autoLock(mSeekLock);
       // Free pending video buffers when OmxDecoder is not seeking video.
       // If OmxDecoder is seeking video, the buffers are freed on seek exit.
       if (!mIsVideoSeeking) {
         ReleaseAllPendingVideoBuffersLocked();
       }
       break;
     }
-
-    case kNotifyStatusChanged:
-    {
-      // Our decode may have acquired the hardware resource that it needs
-      // to start. Notify the state machine to resume loading metadata.
-      mDecoder->NotifyWaitingForResourcesStatusChanged();
-      break;
-    }
-
     default:
       TRESPASS();
       break;
   }
 }
 
 void OmxDecoder::PostReleaseVideoBuffer(MediaBuffer *aBuffer, const FenceHandle& aReleaseFenceHandle)
 {
--- a/dom/media/omx/OmxDecoder.h
+++ b/dom/media/omx/OmxDecoder.h
@@ -5,44 +5,45 @@
 #include <utils/RefBase.h>
 #include <stagefright/MediaExtractor.h>
 
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
 #include "mozilla/layers/FenceUtils.h"
 #include "MP3FrameParser.h"
 #include "MPAPI.h"
+#include "MediaOmxCommonReader.h"
 #include "MediaResource.h"
 #include "AbstractMediaDecoder.h"
 #include "OMXCodecProxy.h"
 
 namespace android {
 class OmxDecoder;
 };
 
 namespace android {
 
-class OmxDecoder : public OMXCodecProxy::EventListener {
+class OmxDecoder : public OMXCodecProxy::CodecResourceListener {
   typedef MPAPI::AudioFrame AudioFrame;
   typedef MPAPI::VideoFrame VideoFrame;
   typedef mozilla::MP3FrameParser MP3FrameParser;
   typedef mozilla::MediaResource MediaResource;
   typedef mozilla::AbstractMediaDecoder AbstractMediaDecoder;
   typedef mozilla::layers::FenceHandle FenceHandle;
   typedef mozilla::layers::TextureClient TextureClient;
+  typedef mozilla::MediaOmxCommonReader::MediaResourcePromise MediaResourcePromise;
 
   enum {
     kPreferSoftwareCodecs = 1,
     kSoftwareCodecsOnly = 8,
     kHardwareCodecsOnly = 16,
   };
 
   enum {
     kNotifyPostReleaseVideoBuffer = 'noti',
-    kNotifyStatusChanged = 'stat'
   };
 
   AbstractMediaDecoder *mDecoder;
   nsRefPtr<MediaResource> mResource;
   sp<GonkNativeWindow> mNativeWindow;
   sp<GonkNativeWindowClient> mNativeWindowClient;
   sp<MediaSource> mVideoTrack;
   sp<OMXCodecProxy> mVideoSource;
@@ -115,16 +116,18 @@ class OmxDecoder : public OMXCodecProxy:
   // deliver a message to a wrapped object(OmxDecoder).
   // AHandlerReflector is similar to Handler in android Java.
   // http://developer.android.com/reference/android/os/Handler.html
   sp<AHandlerReflector<OmxDecoder> > mReflector;
 
   // 'true' if a read from the audio stream was done while reading the metadata
   bool mAudioMetadataRead;
 
+  mozilla::MediaPromiseHolder<MediaResourcePromise> mMediaResourcePromise;
+
   void ReleaseVideoBuffer();
   void ReleaseAudioBuffer();
   // Call with mSeekLock held.
   void ReleaseAllPendingVideoBuffersLocked();
 
   void PlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   void CbYCrYFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   void SemiPlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
@@ -136,38 +139,35 @@ class OmxDecoder : public OMXCodecProxy:
   //True if decoder is in a paused state
   bool mAudioPaused;
   bool mVideoPaused;
 
 public:
   OmxDecoder(MediaResource *aResource, AbstractMediaDecoder *aDecoder);
   ~OmxDecoder();
 
-  // MediaResourceManagerClient::EventListener
-  virtual void statusChanged();
+  // OMXCodecProxy::CodecResourceListener
+  virtual void codecReserved();
+  virtual void codecCanceled();
 
   // The MediaExtractor provides essential information for creating OMXCodec
   // instance. Such as video/audio codec, we can retrieve them through the
   // MediaExtractor::getTrackMetaData().
   // In general cases, the extractor is created by a sp<DataSource> which
   // connect to a MediaResource like ChannelMediaResource.
   // Data is read from the MediaResource to create a suitable extractor which
   // extracts data from a container.
   // Note: RTSP requires a custom extractor because it doesn't have a container.
   bool Init(sp<MediaExtractor>& extractor);
 
   // Called after resources(video/audio codec) are allocated, set the
   // mDurationUs and video/audio metadata.
   bool EnsureMetadata();
 
-  // Only called by MediaOmxDecoder, do not call this function arbitrarily.
-  // See bug 1050667.
-  bool IsWaitingMediaResources();
-
-  bool AllocateMediaResources();
+  nsRefPtr<MediaResourcePromise> AllocateMediaResources();
   void ReleaseMediaResources();
   bool SetVideoFormat();
   bool SetAudioFormat();
 
   void ReleaseDecoder();
 
   void GetDuration(int64_t *durationUs) {
     *durationUs = mDurationUs;
--- a/dom/media/omx/RtspOmxReader.cpp
+++ b/dom/media/omx/RtspOmxReader.cpp
@@ -81,27 +81,32 @@ void RtspOmxReader::EnsureActive() {
     mEnsureActiveFromSeek = false;
     mRtspResource->SetSuspend(false);
   }
 
   // Call parent class to set OMXCodec active.
   MediaOmxReader::EnsureActive();
 }
 
-nsresult RtspOmxReader::ReadMetadata(MediaInfo *aInfo, MetadataTags **aTags)
+nsRefPtr<MediaDecoderReader::MetadataPromise>
+RtspOmxReader::AsyncReadMetadata()
 {
   // Send a PLAY command to the RTSP server before reading metadata.
   // Because we might need some decoded samples to ensure we have configuration.
   mRtspResource->DisablePlayoutDelay();
-  EnsureActive();
-  nsresult rv = MediaOmxReader::ReadMetadata(aInfo, aTags);
+
+  nsRefPtr<MediaDecoderReader::MetadataPromise> p =
+    MediaOmxReader::AsyncReadMetadata();
+
+  // Send a PAUSE to the RTSP server because the underlying media resource is
+  // not ready.
+  SetIdle();
 
-  if (rv == NS_OK && !IsWaitingMediaResources()) {
-    mRtspResource->EnablePlayoutDelay();
-  } else if (IsWaitingMediaResources()) {
-    // Send a PAUSE to the RTSP server because the underlying media resource is
-    // not ready.
-    SetIdle();
-  }
-  return rv;
+  return p;
+}
+
+void RtspOmxReader::HandleResourceAllocated()
+{
+  MediaOmxReader::HandleResourceAllocated();
+  mRtspResource->EnablePlayoutDelay();
 }
 
 } // namespace mozilla
--- a/dom/media/omx/RtspOmxReader.h
+++ b/dom/media/omx/RtspOmxReader.h
@@ -61,18 +61,20 @@ public:
   // ChannelMediaResource, it has a "cache" that can store the whole streaming
   // data so the |GetBuffered| function can retrieve useful time ranges.
   virtual media::TimeIntervals GetBuffered() final override {
     return media::TimeIntervals::Invalid();
   }
 
   virtual void SetIdle() override;
 
-  virtual nsresult ReadMetadata(MediaInfo *aInfo, MetadataTags **aTags)
-    final override;
+  virtual nsRefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata()
+    override;
+
+  virtual void HandleResourceAllocated() override;
 
 private:
   // A pointer to RtspMediaResource for calling the Rtsp specific function.
   // The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
   // holds the MediaDecoderStateMachine and RtspMediaResource.
   // And MediaDecoderStateMachine holds this RtspOmxReader.
   RtspMediaResource* mRtspResource;
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1658,16 +1658,20 @@ nsPluginHost::GetSpecialType(const nsACS
     NS_WARNING("You are loading RealPlayer");
     return eSpecialType_RealPlayer;
   }
 
   if (aMIMEType.LowerCaseEqualsASCII("application/pdf")) {
     return eSpecialType_PDF;
   }
 
+  if (aMIMEType.LowerCaseEqualsASCII("application/vnd.unity")) {
+    return eSpecialType_Unity;
+  }
+
   // Java registers variants of its MIME with parameters, e.g.
   // application/x-java-vm;version=1.3
   const nsACString &noParam = Substring(aMIMEType, 0, aMIMEType.FindChar(';'));
 
   // The java mime pref may well not be one of these,
   // e.g. application/x-java-test used in the test suite
   nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
   if ((!javaMIME.IsEmpty() && noParam.LowerCaseEqualsASCII(javaMIME)) ||
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -197,17 +197,19 @@ public:
                      // Binds to the <applet> tag, has various special
                      // rules around opening channels, codebase, ...
                      eSpecialType_Java,
                      // Some IPC quirks
                      eSpecialType_Silverlight,
                      // Native widget quirks
                      eSpecialType_PDF,
                      // Native widget quirks
-                     eSpecialType_RealPlayer };
+                     eSpecialType_RealPlayer,
+                     // Native widget quirks
+                     eSpecialType_Unity };
   static SpecialType GetSpecialType(const nsACString & aMIMEType);
 
   static nsresult PostPluginUnloadEvent(PRLibrary* aLibrary);
 
   void PluginCrashed(nsNPAPIPlugin* plugin,
                      const nsAString& pluginDumpID,
                      const nsAString& browserDumpID);
 
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -27,16 +27,17 @@ using mozilla::gfx::SharedDIBSurface;
 #endif
 #include "gfxSharedImageSurface.h"
 #include "gfxUtils.h"
 #include "gfxAlphaRecovery.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/AutoRestore.h"
+#include "mozilla/StaticPtr.h"
 #include "ImageContainer.h"
 
 using namespace mozilla;
 using mozilla::ipc::ProcessChild;
 using namespace mozilla::plugins;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 using namespace std;
@@ -143,16 +144,18 @@ PluginInstanceChild::PluginInstanceChild
 #if defined(OS_WIN)
     , mPluginWindowHWND(0)
     , mPluginWndProc(0)
     , mPluginParentHWND(0)
     , mCachedWinlessPluginHWND(0)
     , mWinlessPopupSurrogateHWND(0)
     , mWinlessThrottleOldWndProc(0)
     , mWinlessHiddenMsgHWND(0)
+    , mUnityGetMessageHook(NULL)
+    , mUnitySendMessageHook(NULL)
 #endif // OS_WIN
     , mAsyncCallMutex("PluginInstanceChild::mAsyncCallMutex")
 #if defined(MOZ_WIDGET_COCOA)
 #if defined(__i386__)
     , mEventModel(NPEventModelCarbon)
 #endif
     , mShColorSpace(nullptr)
     , mShContext(nullptr)
@@ -189,23 +192,29 @@ PluginInstanceChild::PluginInstanceChild
     mWsInfo.display = DefaultXDisplay();
 #endif
 #endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
 #if defined(OS_WIN)
     memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
 #endif // OS_WIN
 #if defined(OS_WIN)
     InitPopupMenuHook();
+    if (GetQuirks() & PluginModuleChild::QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
+        SetUnityHooks();
+    }
 #endif // OS_WIN
 }
 
 PluginInstanceChild::~PluginInstanceChild()
 {
 #if defined(OS_WIN)
     NS_ASSERTION(!mPluginWindowHWND, "Destroying PluginInstanceChild without NPP_Destroy?");
+    if (GetQuirks() & PluginModuleChild::QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
+        ClearUnityHooks();
+    }
 #endif
 #if defined(MOZ_WIDGET_COCOA)
     if (mShColorSpace) {
         ::CGColorSpaceRelease(mShColorSpace);
     }
     if (mShContext) {
         ::CGContextRelease(mShContext);
     }
@@ -1697,16 +1706,149 @@ PluginInstanceChild::HookSetWindowLongPt
         sUser32Intercept.AddHook("SetWindowLongA", reinterpret_cast<intptr_t>(SetWindowLongAHook),
                                  (void**) &sUser32SetWindowLongAHookStub);
     if (!sUser32SetWindowLongWHookStub)
         sUser32Intercept.AddHook("SetWindowLongW", reinterpret_cast<intptr_t>(SetWindowLongWHook),
                                  (void**) &sUser32SetWindowLongWHookStub);
 #endif
 }
 
+class SetCaptureHookData
+{
+public:
+    explicit SetCaptureHookData(HWND aHwnd)
+        : mHwnd(aHwnd)
+        , mHaveRect(false)
+    {
+        MOZ_ASSERT(aHwnd);
+        mHaveRect = !!GetClientRect(aHwnd, &mCaptureRect);
+    }
+
+    /**
+     * @return true if capture was released
+     */
+    bool HandleMouseMsg(const MSG& aMsg)
+    {
+        // If the window belongs to Unity, the mouse button is up, and the mouse
+        // has moved outside the client rect of the Unity window, release capture.
+        if (aMsg.hwnd != mHwnd || !mHaveRect) {
+            return false;
+        }
+        if (aMsg.message != WM_MOUSEMOVE && aMsg.message != WM_LBUTTONUP) {
+            return false;
+        }
+        if ((aMsg.message == WM_MOUSEMOVE && (aMsg.wParam & MK_LBUTTON))) {
+            return false;
+        }
+        POINT pt = { GET_X_LPARAM(aMsg.lParam), GET_Y_LPARAM(aMsg.lParam) };
+        if (PtInRect(&mCaptureRect, pt)) {
+            return false;
+        }
+        return !!ReleaseCapture();
+    }
+
+    bool IsUnityLosingCapture(const CWPSTRUCT& aInfo) const
+    {
+        return aInfo.message == WM_CAPTURECHANGED &&
+               aInfo.hwnd == mHwnd;
+    }
+
+private:
+    HWND mHwnd;
+    bool mHaveRect;
+    RECT mCaptureRect;
+};
+
+static StaticAutoPtr<SetCaptureHookData> sSetCaptureHookData;
+typedef HWND (WINAPI* User32SetCapture)(HWND);
+static User32SetCapture sUser32SetCaptureHookStub = nullptr;
+
+HWND WINAPI
+PluginInstanceChild::SetCaptureHook(HWND aHwnd)
+{
+    // Don't do anything unless aHwnd belongs to Unity
+    wchar_t className[256] = {0};
+    int numChars = GetClassNameW(aHwnd, className, ArrayLength(className));
+    NS_NAMED_LITERAL_STRING(unityClassName, "Unity.WebPlayer");
+    if (numChars == unityClassName.Length() && unityClassName == className) {
+        sSetCaptureHookData = new SetCaptureHookData(aHwnd);
+    }
+    return sUser32SetCaptureHookStub(aHwnd);
+}
+
+void
+PluginInstanceChild::SetUnityHooks()
+{
+    if (!(GetQuirks() & PluginModuleChild::QUIRK_UNITY_FIXUP_MOUSE_CAPTURE)) {
+        return;
+    }
+
+    sUser32Intercept.Init("user32.dll");
+    if (!sUser32SetCaptureHookStub) {
+        sUser32Intercept.AddHook("SetCapture",
+                                 reinterpret_cast<intptr_t>(SetCaptureHook),
+                                 (void**) &sUser32SetCaptureHookStub);
+    }
+    if (!mUnityGetMessageHook) {
+        mUnityGetMessageHook = SetWindowsHookEx(WH_GETMESSAGE,
+                                                &UnityGetMessageHookProc, NULL,
+                                                GetCurrentThreadId());
+    }
+    if (!mUnitySendMessageHook) {
+        mUnitySendMessageHook = SetWindowsHookEx(WH_CALLWNDPROC,
+                                                 &UnitySendMessageHookProc,
+                                                 NULL, GetCurrentThreadId());
+    }
+}
+
+void
+PluginInstanceChild::ClearUnityHooks()
+{
+    if (mUnityGetMessageHook) {
+        UnhookWindowsHookEx(mUnityGetMessageHook);
+        mUnityGetMessageHook = NULL;
+    }
+    if (mUnitySendMessageHook) {
+        UnhookWindowsHookEx(mUnitySendMessageHook);
+        mUnitySendMessageHook = NULL;
+    }
+    sSetCaptureHookData = nullptr;
+}
+
+LRESULT CALLBACK
+PluginInstanceChild::UnityGetMessageHookProc(int aCode, WPARAM aWparam,
+                                             LPARAM aLParam)
+{
+    if (aCode >= 0) {
+        MSG* info = reinterpret_cast<MSG*>(aLParam);
+        MOZ_ASSERT(info);
+        if (sSetCaptureHookData && sSetCaptureHookData->HandleMouseMsg(*info)) {
+            sSetCaptureHookData = nullptr;
+        }
+    }
+
+    return CallNextHookEx(0, aCode, aWparam, aLParam);
+}
+
+LRESULT CALLBACK
+PluginInstanceChild::UnitySendMessageHookProc(int aCode, WPARAM aWparam,
+                                              LPARAM aLParam)
+{
+    if (aCode >= 0) {
+        CWPSTRUCT* info = reinterpret_cast<CWPSTRUCT*>(aLParam);
+        MOZ_ASSERT(info);
+        if (sSetCaptureHookData &&
+            sSetCaptureHookData->IsUnityLosingCapture(*info)) {
+            sSetCaptureHookData = nullptr;
+        }
+    }
+
+    return CallNextHookEx(0, aCode, aWparam, aLParam);
+}
+
 /* windowless track popup menu helpers */
 
 BOOL
 WINAPI
 PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
                                         UINT uFlags,
                                         int x,
                                         int y,
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -282,16 +282,18 @@ private:
     void SizePluginWindow(int width, int height);
     int16_t WinlessHandleEvent(NPEvent& event);
     void CreateWinlessPopupSurrogate();
     void DestroyWinlessPopupSurrogate();
     void InitPopupMenuHook();
     void SetupFlashMsgThrottle();
     void UnhookWinlessFlashThrottle();
     void HookSetWindowLongPtr();
+    void SetUnityHooks();
+    void ClearUnityHooks();
     static inline bool SetWindowLongHookCheck(HWND hWnd,
                                                 int nIndex,
                                                 LONG_PTR newLong);
     void FlashThrottleMessage(HWND, UINT, WPARAM, LPARAM, bool);
     static LRESULT CALLBACK DummyWindowProc(HWND hWnd,
                                             UINT message,
                                             WPARAM wParam,
                                             LPARAM lParam);
@@ -301,16 +303,19 @@ private:
                                              LPARAM lParam);
     static BOOL WINAPI TrackPopupHookProc(HMENU hMenu,
                                           UINT uFlags,
                                           int x,
                                           int y,
                                           int nReserved,
                                           HWND hWnd,
                                           CONST RECT *prcRect);
+    static HWND WINAPI SetCaptureHook(HWND aHwnd);
+    static LRESULT CALLBACK UnityGetMessageHookProc(int aCode, WPARAM aWparam, LPARAM aLParam);
+    static LRESULT CALLBACK UnitySendMessageHookProc(int aCode, WPARAM aWparam, LPARAM aLParam);
     static BOOL CALLBACK EnumThreadWindowsCallback(HWND hWnd,
                                                    LPARAM aParam);
     static LRESULT CALLBACK WinlessHiddenFlashWndProc(HWND hWnd,
                                                       UINT message,
                                                       WPARAM wParam,
                                                       LPARAM lParam);
 #ifdef _WIN64
     static LONG_PTR WINAPI SetWindowLongPtrAHook(HWND hWnd,
@@ -392,16 +397,18 @@ private:
     WNDPROC mPluginWndProc;
     HWND mPluginParentHWND;
     int mNestedEventLevelDepth;
     HWND mCachedWinlessPluginHWND;
     HWND mWinlessPopupSurrogateHWND;
     nsIntPoint mPluginSize;
     WNDPROC mWinlessThrottleOldWndProc;
     HWND mWinlessHiddenMsgHWND;
+    HHOOK mUnityGetMessageHook;
+    HHOOK mUnitySendMessageHook;
 #endif
 
     friend class ChildAsyncCall;
 
     Mutex mAsyncCallMutex;
     nsTArray<ChildAsyncCall*> mPendingAsyncCalls;
     nsTArray<nsAutoPtr<ChildTimer> > mTimers;
 
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2148,16 +2148,22 @@ PluginModuleChild::InitQuirksModes(const
     NS_NAMED_LITERAL_CSTRING(quicktime, "QuickTime Plugin.plugin");
     if (specialType == nsPluginHost::eSpecialType_Flash) {
         mQuirks |= QUIRK_FLASH_AVOID_CGMODE_CRASHES;
         mQuirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
     } else if (FindInReadable(quicktime, mPluginFilename)) {
         mQuirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
     }
 #endif
+
+#ifdef OS_WIN
+    if (specialType == nsPluginHost::eSpecialType_Unity) {
+        mQuirks |= QUIRK_UNITY_FIXUP_MOUSE_CAPTURE;
+    }
+#endif
 }
 
 bool
 PluginModuleChild::RecvPPluginInstanceConstructor(PPluginInstanceChild* aActor,
                                                   const nsCString& aMimeType,
                                                   const uint16_t& aMode,
                                                   InfallibleTArray<nsCString>&& aNames,
                                                   InfallibleTArray<nsCString>&& aValues)
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -281,16 +281,18 @@ public:
         // in CoreGraphics mode:  The Flash plugin sometimes accesses the
         // CGContextRef we pass to it in NPP_HandleEvent(NPCocoaEventDrawRect)
         // outside of that call.  See bug 804606.
         QUIRK_FLASH_AVOID_CGMODE_CRASHES                = 1 << 10,
         // Work around a Flash bug where it fails to check the error code of a
         // NPN_GetValue(NPNVdocumentOrigin) call before trying to dereference
         // its char* output.
         QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN        = 1 << 11,
+        // Win: Addresses a Unity bug with mouse capture.
+        QUIRK_UNITY_FIXUP_MOUSE_CAPTURE                 = 1 << 12,
     };
 
     int GetQuirks() { return mQuirks; }
 
     const PluginSettings& Settings() const { return mCachedSettings; }
 
 private:
     NPError DoNP_Initialize(const PluginSettings& aSettings);
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -32,16 +32,17 @@ struct RunnableMethodTraits<PluginProces
 {
     static void RetainCallee(PluginProcessParent* obj) { }
     static void ReleaseCallee(PluginProcessParent* obj) { }
 };
 
 PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) :
     GeckoChildProcessHost(GeckoProcessType_Plugin),
     mPluginFilePath(aPluginFilePath),
+    mTaskFactory(this),
     mMainMsgLoop(MessageLoop::current()),
     mRunCompleteTaskImmediately(false)
 {
 }
 
 PluginProcessParent::~PluginProcessParent()
 {
 }
@@ -210,27 +211,27 @@ PluginProcessParent::WaitUntilConnected(
 }
 
 void
 PluginProcessParent::OnChannelConnected(int32_t peer_pid)
 {
     GeckoChildProcessHost::OnChannelConnected(peer_pid);
     if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
         mLaunchCompleteTask->SetLaunchSucceeded();
-        mMainMsgLoop->PostTask(FROM_HERE, NewRunnableMethod(this,
+        mMainMsgLoop->PostTask(FROM_HERE, mTaskFactory.NewRunnableMethod(
                                    &PluginProcessParent::RunLaunchCompleteTask));
     }
 }
 
 void
 PluginProcessParent::OnChannelError()
 {
     GeckoChildProcessHost::OnChannelError();
     if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
-        mMainMsgLoop->PostTask(FROM_HERE, NewRunnableMethod(this,
+        mMainMsgLoop->PostTask(FROM_HERE, mTaskFactory.NewRunnableMethod(
                                    &PluginProcessParent::RunLaunchCompleteTask));
     }
 }
 
 bool
 PluginProcessParent::IsConnected()
 {
     mozilla::MonitorAutoLock lock(mMonitor);
--- a/dom/plugins/ipc/PluginProcessParent.h
+++ b/dom/plugins/ipc/PluginProcessParent.h
@@ -12,16 +12,17 @@
 
 #include "base/file_path.h"
 #include "base/task.h"
 #include "base/thread.h"
 #include "base/waitable_event.h"
 #include "chrome/common/child_process_host.h"
 
 #include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/plugins/TaskFactory.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsIRunnable.h"
 
 namespace mozilla {
 namespace plugins {
 
 class LaunchCompleteTask : public Task
@@ -75,16 +76,17 @@ public:
     virtual void OnChannelError() override;
 
     bool IsConnected();
 
 private:
     void RunLaunchCompleteTask();
 
     std::string mPluginFilePath;
+    TaskFactory<PluginProcessParent> mTaskFactory;
     UniquePtr<LaunchCompleteTask> mLaunchCompleteTask;
     MessageLoop* mMainMsgLoop;
     bool mRunCompleteTaskImmediately;
 
     DISALLOW_EVIL_CONSTRUCTORS(PluginProcessParent);
 };
 
 
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -63,20 +63,18 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     EXPORTS.mozilla.plugins += [
         'PluginInterposeOSX.h',
     ]
 
 if CONFIG['MOZ_ENABLE_QT']:
-    GENERATED_SOURCES += [
-        'moc_NestedLoopTimer.cpp',
-    ]
     SOURCES += [
+        '!moc_NestedLoopTimer.cpp',
         'NestedLoopTimer.cpp',
         'PluginHelperQt.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'BrowserStreamChild.cpp',
     'BrowserStreamParent.cpp',
     'ChildAsyncCall.cpp',
--- a/dom/plugins/test/testplugin/javaplugin/moz.build
+++ b/dom/plugins/test/testplugin/javaplugin/moz.build
@@ -1,10 +1,10 @@
 # -*- 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/.
 
 SharedLibrary('nptestjava')
 
-relative_path = '..'
+relative_path = 'javaplugin'
 include('../testplugin.mozbuild')
--- a/dom/plugins/test/testplugin/secondplugin/moz.build
+++ b/dom/plugins/test/testplugin/secondplugin/moz.build
@@ -1,10 +1,10 @@
 # -*- 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/.
 
 SharedLibrary('npsecondtest')
 
-relative_path = '..'
+relative_path = 'secondplugin'
 include('../testplugin.mozbuild')
--- a/dom/plugins/test/testplugin/testplugin.mozbuild
+++ b/dom/plugins/test/testplugin/testplugin.mozbuild
@@ -1,45 +1,45 @@
 # -*- 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/.
 
 DIST_INSTALL = False
 
-UNIFIED_SOURCES += [ '%s/%s' % (relative_path, p) for p in [
+UNIFIED_SOURCES += [
     'nptest.cpp',
     'nptest_utils.cpp',
-]]
+]
 
 UNIFIED_SOURCES += [
-    'nptest_name.cpp',
+    '%s/nptest_name.cpp' % relative_path,
 ]
 
 toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
 if toolkit == 'cocoa':
     UNIFIED_SOURCES += [
-        relative_path + '/nptest_macosx.mm'
+        'nptest_macosx.mm'
     ]
 elif toolkit in ('gtk2', 'gtk3'):
     UNIFIED_SOURCES += [
-        relative_path + '/nptest_gtk2.cpp',
+        'nptest_gtk2.cpp',
     ]
 elif toolkit == 'android':
     UNIFIED_SOURCES += [
-        relative_path + '/nptest_droid.cpp',
+        'nptest_droid.cpp',
     ]
 elif toolkit == 'qt':
     UNIFIED_SOURCES += [
-        relative_path + '/nptest_qt.cpp',
+        'nptest_qt.cpp',
     ]
 elif toolkit == 'windows':
     UNIFIED_SOURCES += [
-        relative_path + '/nptest_windows.cpp',
+        'nptest_windows.cpp',
     ]
     OS_LIBS += [
         'msimg32',
     ]
 
 # must link statically with the CRT; nptest isn't Gecko code
 USE_STATIC_LIBS = True
 
--- a/dom/system/qt/moz.build
+++ b/dom/system/qt/moz.build
@@ -1,19 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_ENABLE_QT5GEOPOSITION']:
-    GENERATED_SOURCES += [
-        'moc_QTMLocationProvider.cpp',
-    ]
     SOURCES += [
+        '!moc_QTMLocationProvider.cpp',
         'QTMLocationProvider.cpp',
     ]
 
     CXXFLAGS += CONFIG['MOZ_QT_CFLAGS']
 
     LOCAL_INCLUDES += [
         '/dom/geolocation',
     ]
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -9,17 +9,16 @@
 #include "nsIChannel.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIHttpChannel.h"
 #include "nsIInputStreamPump.h"
 #include "nsIIOService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
-#include "nsISerializable.h"
 #include "nsIStreamLoader.h"
 #include "nsIStreamListenerTee.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIURI.h"
 
 #include "jsapi.h"
 #include "nsError.h"
 #include "nsContentPolicyUtils.h"
@@ -417,17 +416,17 @@ private:
 
   ScriptLoadInfo& mLoadInfo;
   uint32_t mIndex;
   nsRefPtr<ScriptLoaderRunnable> mRunnable;
   bool mIsWorkerScript;
   bool mFailed;
   nsCOMPtr<nsIInputStreamPump> mPump;
   nsCOMPtr<nsIURI> mBaseURI;
-  nsCString mSecurityInfo;
+  ChannelInfo mChannelInfo;
 };
 
 NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
 
 class CachePromiseHandler final : public PromiseNativeHandler
 {
 public:
   CachePromiseHandler(ScriptLoaderRunnable* aRunnable,
@@ -584,29 +583,19 @@ private:
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     MOZ_ASSERT(channel == loadInfo.mChannel);
 
     // We synthesize the result code, but its never exposed to content.
     nsRefPtr<InternalResponse> ir =
       new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
     ir->SetBody(mReader);
 
-    // Set the security info of the channel on the response so that it's
+    // Set the channel info of the channel on the response so that it's
     // saved in the cache.
-    nsCOMPtr<nsISupports> infoObj;
-    channel->GetSecurityInfo(getter_AddRefs(infoObj));
-    if (infoObj) {
-      nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj);
-      if (serializable) {
-        ir->SetSecurityInfo(serializable);
-        MOZ_ASSERT(!ir->GetSecurityInfo().IsEmpty());
-      } else {
-        NS_WARNING("A non-serializable object was obtained from nsIChannel::GetSecurityInfo()!");
-      }
-    }
+    ir->InitChannelInfo(channel);
 
     nsRefPtr<Response> response = new Response(mCacheCreator->Global(), ir);
 
     RequestOrUSVString request;
 
     MOZ_ASSERT(!loadInfo.mFullURL.IsEmpty());
     request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
                                     loadInfo.mFullURL.Length());
@@ -960,28 +949,19 @@ private:
     }
 
     // Update the principal of the worker and its base URI if we just loaded the
     // worker's primary script.
     if (IsMainWorkerScript()) {
       // Take care of the base URI first.
       mWorkerPrivate->SetBaseURI(finalURI);
 
-      // Store the security info if needed.
+      // Store the channel info if needed.
       if (mWorkerPrivate->IsServiceWorker()) {
-        nsCOMPtr<nsISupports> infoObj;
-        channel->GetSecurityInfo(getter_AddRefs(infoObj));
-        if (infoObj) {
-          nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj);
-          if (serializable) {
-            mWorkerPrivate->SetSecurityInfo(serializable);
-          } else {
-            NS_WARNING("A non-serializable object was obtained from nsIChannel::GetSecurityInfo()!");
-          }
-        }
+        mWorkerPrivate->InitChannelInfo(channel);
       }
 
       // Now to figure out which principal to give this worker.
       WorkerPrivate* parent = mWorkerPrivate->GetParent();
 
       NS_ASSERTION(mWorkerPrivate->GetPrincipal() || parent,
                    "Must have one of these!");
 
@@ -1042,17 +1022,18 @@ private:
     }
 
     DataReceived();
     return NS_OK;
   }
 
   void
   DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
-                        uint32_t aStringLen, const nsCString& aSecurityInfo)
+                        uint32_t aStringLen,
+                        const ChannelInfo& aChannelInfo)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aIndex < mLoadInfos.Length());
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
     MOZ_ASSERT(loadInfo.mCacheStatus == ScriptLoadInfo::Cached);
 
     // May be null.
     nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
@@ -1070,17 +1051,17 @@ private:
       if (NS_SUCCEEDED(rv)) {
         mWorkerPrivate->SetBaseURI(finalURI);
       }
 
       nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
       MOZ_ASSERT(principal);
       nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
       MOZ_ASSERT(loadGroup);
-      mWorkerPrivate->SetSecurityInfo(aSecurityInfo);
+      mWorkerPrivate->InitChannelInfo(aChannelInfo);
       // Needed to initialize the principal info. This is fine because
       // the cache principal cannot change, unlike the channel principal.
       mWorkerPrivate->SetPrincipal(principal, loadGroup);
     }
 
     if (NS_SUCCEEDED(rv)) {
       DataReceived();
     }
@@ -1424,21 +1405,21 @@ CacheScriptLoader::ResolvedCallback(JSCo
   nsresult rv = UNWRAP_OBJECT(Response, obj, response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Fail(rv);
     return;
   }
 
   nsCOMPtr<nsIInputStream> inputStream;
   response->GetBody(getter_AddRefs(inputStream));
-  mSecurityInfo = response->GetSecurityInfo();
+  mChannelInfo = response->GetChannelInfo();
 
   if (!inputStream) {
     mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
-    mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mSecurityInfo);
+    mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo);
     return;
   }
 
   MOZ_ASSERT(!mPump);
   rv = NS_NewInputStreamPump(getter_AddRefs(mPump), inputStream);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Fail(rv);
     return;
@@ -1484,17 +1465,17 @@ CacheScriptLoader::OnStreamComplete(nsIS
 
   if (NS_FAILED(aStatus)) {
     Fail(aStatus);
     return NS_OK;
   }
 
   mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
 
-  mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mSecurityInfo);
+  mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo);
   return NS_OK;
 }
 
  class ChannelGetterRunnable final : public nsRunnable
 {
   WorkerPrivate* mParentWorker;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   const nsAString& mScriptURL;
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -93,45 +93,43 @@ public:
     return NS_OK;
   }
 };
 
 class FinishResponse final : public nsRunnable
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
   nsRefPtr<InternalResponse> mInternalResponse;
-  nsCString mWorkerSecurityInfo;
+  ChannelInfo mWorkerChannelInfo;
 public:
   FinishResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                  InternalResponse* aInternalResponse,
-                 const nsCString& aWorkerSecurityInfo)
+                 const ChannelInfo& aWorkerChannelInfo)
     : mChannel(aChannel)
     , mInternalResponse(aInternalResponse)
-    , mWorkerSecurityInfo(aWorkerSecurityInfo)
+    , mWorkerChannelInfo(aWorkerChannelInfo)
   {
   }
 
   NS_IMETHOD
   Run()
   {
     AssertIsOnMainThread();
 
-    nsCOMPtr<nsISupports> infoObj;
-    nsAutoCString securityInfo(mInternalResponse->GetSecurityInfo());
-    if (securityInfo.IsEmpty()) {
+    ChannelInfo channelInfo;
+    if (mInternalResponse->GetChannelInfo().IsInitialized()) {
+      channelInfo = mInternalResponse->GetChannelInfo();
+    } else {
       // We are dealing with a synthesized response here, so fall back to the
-      // security info for the worker script.
-      securityInfo = mWorkerSecurityInfo;
+      // channel info for the worker script.
+      channelInfo = mWorkerChannelInfo;
     }
-    nsresult rv = NS_DeserializeObject(securityInfo, getter_AddRefs(infoObj));
-    if (NS_SUCCEEDED(rv)) {
-      rv = mChannel->SetSecurityInfo(infoObj);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
+    nsresult rv = mChannel->SetChannelInfo(&channelInfo);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
 
     mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText());
 
     nsAutoTArray<InternalHeaders::Entry, 5> entries;
     mInternalResponse->UnfilteredHeaders()->GetEntries(entries);
     for (uint32_t i = 0; i < entries.Length(); ++i) {
        mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue);
@@ -164,36 +162,36 @@ public:
 
   void CancelRequest();
 };
 
 struct RespondWithClosure
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
   nsRefPtr<InternalResponse> mInternalResponse;
-  nsCString mWorkerSecurityInfo;
+  ChannelInfo mWorkerChannelInfo;
 
   RespondWithClosure(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                      InternalResponse* aInternalResponse,
-                     const nsCString& aWorkerSecurityInfo)
+                     const ChannelInfo& aWorkerChannelInfo)
     : mInterceptedChannel(aChannel)
     , mInternalResponse(aInternalResponse)
-    , mWorkerSecurityInfo(aWorkerSecurityInfo)
+    , mWorkerChannelInfo(aWorkerChannelInfo)
   {
   }
 };
 
 void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
 {
   nsAutoPtr<RespondWithClosure> data(static_cast<RespondWithClosure*>(aClosure));
   nsCOMPtr<nsIRunnable> event;
   if (NS_SUCCEEDED(aStatus)) {
     event = new FinishResponse(data->mInterceptedChannel,
                                data->mInternalResponse,
-                               data->mWorkerSecurityInfo);
+                               data->mWorkerChannelInfo);
   } else {
     event = new CancelChannelRunnable(data->mInterceptedChannel);
   }
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(event)));
 }
 
 class MOZ_STACK_CLASS AutoCancel
 {
@@ -250,17 +248,17 @@ RespondWithHandler::ResolvedCallback(JSC
     return;
   }
 
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
   nsAutoPtr<RespondWithClosure> closure(
-      new RespondWithClosure(mInterceptedChannel, ir, worker->GetSecurityInfo()));
+      new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo()));
   nsCOMPtr<nsIInputStream> body;
   response->GetBody(getter_AddRefs(body));
   // Errors and redirects may not have a body.
   if (body) {
     response->SetBodyUsed();
 
     nsCOMPtr<nsIOutputStream> responseBody;
     rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorkerScriptCache.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/cache/Cache.h"
 #include "nsIThreadRetargetableRequest.h"
-#include "nsSerializationHelper.h"
 
 #include "nsIPrincipal.h"
 #include "Workers.h"
 
 using mozilla::dom::cache::Cache;
 using mozilla::dom::cache::CacheStorage;
 
 BEGIN_WORKERS_NAMESPACE
@@ -441,19 +440,19 @@ public:
   CacheStorage_()
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mCacheStorage);
     return mCacheStorage;
   }
 
   void
-  SetSecurityInfo(nsISerializable* aSecurityInfo)
+  InitChannelInfo(nsIChannel* aChannel)
   {
-    NS_SerializeToString(aSecurityInfo, mSecurityInfo);
+    mChannelInfo.InitFromChannel(aChannel);
   }
 
 private:
   ~CompareManager()
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(!mCC);
     MOZ_ASSERT(!mCN);
@@ -540,17 +539,17 @@ private:
       Fail(result.StealNSResult());
       return;
     }
 
     nsRefPtr<InternalResponse> ir =
       new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
     ir->SetBody(body);
 
-    ir->SetSecurityInfo(mSecurityInfo);
+    ir->InitChannelInfo(mChannelInfo);
 
     nsRefPtr<Response> response = new Response(aCache->GetGlobalObject(), ir);
 
     RequestOrUSVString request;
     request.SetAsUSVString().Rebind(URL().Data(), URL().Length());
 
     // For now we have to wait until the Put Promise is fulfilled before we can
     // continue since Cache does not yet support starting a read that is being
@@ -572,17 +571,17 @@ private:
 
   nsRefPtr<CompareNetwork> mCN;
   nsRefPtr<CompareCache> mCC;
 
   nsString mURL;
   // Only used if the network script has changed and needs to be cached.
   nsString mNewCacheName;
 
-  nsCString mSecurityInfo;
+  ChannelInfo mChannelInfo;
 
   nsCString mMaxScope;
 
   enum {
     WaitingForOpen,
     WaitingForPut
   } mState;
 
@@ -601,26 +600,17 @@ CompareNetwork::OnStartRequest(nsIReques
     return NS_OK;
   }
 
 #ifdef DEBUG
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   MOZ_ASSERT(channel == mChannel);
 #endif
 
-  nsCOMPtr<nsISupports> infoObj;
-  mChannel->GetSecurityInfo(getter_AddRefs(infoObj));
-  if (infoObj) {
-    nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj);
-    if (serializable) {
-      mManager->SetSecurityInfo(serializable);
-    } else {
-      NS_WARNING("A non-serializable object was obtained from nsIChannel::GetSecurityInfo()!");
-    }
-  }
+  mManager->InitChannelInfo(mChannel);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CompareNetwork::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
                               nsresult aStatusCode)
 {
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -27,17 +27,16 @@
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsIWorkerDebugger.h"
 #include "nsIXPConnect.h"
 #include "nsPerformance.h"
 #include "nsPIDOMWindow.h"
-#include "nsSerializationHelper.h"
 
 #include <algorithm>
 #include "jsfriendapi.h"
 #include "js/MemoryMetrics.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
@@ -4071,27 +4070,16 @@ WorkerPrivateParent<Derived>::SetPrincip
 
   mLoadInfo.mPrincipalInfo = new PrincipalInfo();
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
     PrincipalToPrincipalInfo(aPrincipal, mLoadInfo.mPrincipalInfo)));
 }
 
 template <class Derived>
-void
-WorkerPrivateParent<Derived>::SetSecurityInfo(nsISerializable* aSerializable)
-{
-  MOZ_ASSERT(IsServiceWorker());
-  AssertIsOnMainThread();
-  nsAutoCString securityInfo;
-  NS_SerializeToString(aSerializable, securityInfo);
-  SetSecurityInfo(securityInfo);
-}
-
-template <class Derived>
 JSContext*
 WorkerPrivateParent<Derived>::ParentJSContext() const
 {
   AssertIsOnParentThread();
 
   if (mParent) {
     return mParent->GetJSContext();
   }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -493,34 +493,44 @@ public:
   const nsString&
   ServiceWorkerCacheName() const
   {
     MOZ_ASSERT(IsServiceWorker());
     AssertIsOnMainThread();
     return mLoadInfo.mServiceWorkerCacheName;
   }
 
-  const nsCString&
-  GetSecurityInfo() const
+  const ChannelInfo&
+  GetChannelInfo() const
   {
     MOZ_ASSERT(IsServiceWorker());
-    return mLoadInfo.mSecurityInfo;
+    return mLoadInfo.mChannelInfo;
   }
 
   void
-  SetSecurityInfo(const nsCString& aSecurityInfo)
+  SetChannelInfo(const ChannelInfo& aChannelInfo)
   {
     MOZ_ASSERT(IsServiceWorker());
     AssertIsOnMainThread();
-    MOZ_ASSERT(mLoadInfo.mSecurityInfo.IsEmpty());
-    mLoadInfo.mSecurityInfo = aSecurityInfo;
+    MOZ_ASSERT(!mLoadInfo.mChannelInfo.IsInitialized());
+    MOZ_ASSERT(aChannelInfo.IsInitialized());
+    mLoadInfo.mChannelInfo = aChannelInfo;
   }
 
   void
-  SetSecurityInfo(nsISerializable* aSerializable);
+  InitChannelInfo(nsIChannel* aChannel)
+  {
+    mLoadInfo.mChannelInfo.InitFromChannel(aChannel);
+  }
+
+  void
+  InitChannelInfo(const ChannelInfo& aChannelInfo)
+  {
+    mLoadInfo.mChannelInfo = aChannelInfo;
+  }
 
   // This is used to handle importScripts(). When the worker is first loaded
   // and executed, it happens in a sync loop. At this point it sets
   // mLoadingWorkerScript to true. importScripts() calls that occur during the
   // execution run in nested sync loops and so this continues to return true,
   // leading to these scripts being cached offline.
   // mLoadingWorkerScript is set to false when the top level loop ends.
   // importScripts() in function calls or event handlers are always fetched
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -15,16 +15,17 @@
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #include "nsILoadContext.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsIInterfaceRequestor.h"
+#include "mozilla/dom/ChannelInfo.h"
 
 #define BEGIN_WORKERS_NAMESPACE \
   namespace mozilla { namespace dom { namespace workers {
 #define END_WORKERS_NAMESPACE \
   } /* namespace workers */ } /* namespace dom */ } /* namespace mozilla */
 #define USING_WORKERS_NAMESPACE \
   using namespace mozilla::dom::workers;
 
@@ -239,17 +240,17 @@ struct WorkerLoadInfo
   // Only set if we have a custom overriden load group
   nsRefPtr<InterfaceRequestor> mInterfaceRequestor;
 
   nsAutoPtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
   nsCString mDomain;
 
   nsString mServiceWorkerCacheName;
 
-  nsCString mSecurityInfo;
+  ChannelInfo mChannelInfo;
 
   uint64_t mWindowID;
   uint64_t mServiceWorkerID;
 
   bool mFromWindow;
   bool mEvalAllowed;
   bool mReportCSPViolations;
   bool mXHRParamsAllowed;
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -106,44 +106,45 @@ public:
   uint64_t mLastTotal;
   uint64_t mLastUploadLoaded;
   uint64_t mLastUploadTotal;
   bool mIsSyncXHR;
   bool mLastLengthComputable;
   bool mLastUploadLengthComputable;
   bool mSeenLoadStart;
   bool mSeenUploadLoadStart;
+  bool mOpening;
 
   // Only touched on the main thread.
   bool mUploadEventListenersAttached;
   bool mMainThreadSeenLoadStart;
   bool mInOpen;
   bool mArrayBufferResponseWasTransferred;
 
 public:
   Proxy(XMLHttpRequest* aXHRPrivate, bool aMozAnon, bool aMozSystem)
   : mWorkerPrivate(nullptr), mXMLHttpRequestPrivate(aXHRPrivate),
     mMozAnon(aMozAnon), mMozSystem(aMozSystem),
     mInnerEventStreamId(0), mInnerChannelId(0), mOutstandingSendCount(0),
     mOuterEventStreamId(0), mOuterChannelId(0), mLastLoaded(0), mLastTotal(0),
     mLastUploadLoaded(0), mLastUploadTotal(0), mIsSyncXHR(false),
     mLastLengthComputable(false), mLastUploadLengthComputable(false),
-    mSeenLoadStart(false), mSeenUploadLoadStart(false),
+    mSeenLoadStart(false), mSeenUploadLoadStart(false), mOpening(false),
     mUploadEventListenersAttached(false), mMainThreadSeenLoadStart(false),
     mInOpen(false), mArrayBufferResponseWasTransferred(false)
   { }
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 
   bool
   Init();
 
   void
-  Teardown();
+  Teardown(bool aSendUnpin);
 
   bool
   AddRemoveEventListeners(bool aUpload, bool aAdd);
 
   void
   Reset()
   {
     AssertIsOnMainThread();
@@ -303,17 +304,19 @@ private:
   ~AsyncTeardownRunnable()
   { }
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
 
-    mProxy->Teardown();
+    // This means the XHR was GC'd, so we can't be pinned, and we don't need to
+    // try to unpin.
+    mProxy->Teardown(/* aSendUnpin */ false);
     mProxy = nullptr;
 
     return NS_OK;
   }
 };
 
 class LoadStartDetectionRunnable final : public nsRunnable,
                                          public nsIDOMEventListener
@@ -563,17 +566,17 @@ public:
 
 private:
   ~SyncTeardownRunnable()
   { }
 
   virtual nsresult
   MainThreadRun() override
   {
-    mProxy->Teardown();
+    mProxy->Teardown(/* aSendUnpin */ true);
     MOZ_ASSERT(!mProxy->mSyncLoopTarget);
     return NS_OK;
   }
 };
 
 class SetBackgroundRequestRunnable final :
   public WorkerThreadProxySyncRunnable
 {
@@ -936,33 +939,35 @@ Proxy::Init()
     mXHR = nullptr;
     return false;
   }
 
   return true;
 }
 
 void
-Proxy::Teardown()
+Proxy::Teardown(bool aSendUnpin)
 {
   AssertIsOnMainThread();
 
   if (mXHR) {
     Reset();
 
     // NB: We are intentionally dropping events coming from xhr.abort on the
     // floor.
     AddRemoveEventListeners(false, false);
     mXHR->Abort();
 
     if (mOutstandingSendCount) {
-      nsRefPtr<XHRUnpinRunnable> runnable =
-        new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
-      if (!runnable->Dispatch(nullptr)) {
-        NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
+      if (aSendUnpin) {
+        nsRefPtr<XHRUnpinRunnable> runnable =
+          new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
+        if (!runnable->Dispatch(nullptr)) {
+          NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
+        }
       }
 
       if (mSyncLoopTarget) {
         // We have an unclosed sync loop.  Fix that now.
         nsRefPtr<MainThreadStopSyncLoopRunnable> runnable =
           new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
                                              mSyncLoopTarget.forget(),
                                              false);
@@ -1542,17 +1547,21 @@ SendRunnable::MainThreadRun()
 
     if (NS_FAILED(wvariant->SetAsAString(mStringBody))) {
       MOZ_ASSERT(false, "This should never fail!");
     }
 
     variant = wvariant;
   }
 
-  MOZ_ASSERT(!mProxy->mWorkerPrivate);
+  // Send() has been already called.
+  if (mProxy->mWorkerPrivate) {
+    return NS_ERROR_FAILURE;
+  }
+
   mProxy->mWorkerPrivate = mWorkerPrivate;
 
   MOZ_ASSERT(!mProxy->mSyncLoopTarget);
   mProxy->mSyncLoopTarget.swap(mSyncLoopTarget);
 
   if (mHasUploadListeners) {
     NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
     if (!mProxy->AddRemoveEventListeners(true, true)) {
@@ -1837,16 +1846,22 @@ XMLHttpRequest::Unpin()
 void
 XMLHttpRequest::SendInternal(const nsAString& aStringBody,
                              JSAutoStructuredCloneBuffer&& aBody,
                              nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                              ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
+  // No send() calls when open is running.
+  if (mProxy->mOpening) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
   bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
 
   MaybePin(aRv);
   if (aRv.Failed()) {
     return;
   }
 
   AutoUnpinXHR autoUnpin(this);
@@ -1922,22 +1937,25 @@ XMLHttpRequest::Open(const nsACString& a
 
   mProxy->mOuterEventStreamId++;
 
   nsRefPtr<OpenRunnable> runnable =
     new OpenRunnable(mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
                      mBackgroundRequest, mWithCredentials,
                      mTimeout);
 
+  mProxy->mOpening = true;
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     ReleaseProxy();
+    mProxy->mOpening = false;
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
+  mProxy->mOpening = false;
   mProxy->mIsSyncXHR = !aAsync;
 }
 
 void
 XMLHttpRequest::SetRequestHeader(const nsACString& aHeader,
                                  const nsACString& aValue, ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
--- a/editor/libeditor/nsTextEditRules.cpp
+++ b/editor/libeditor/nsTextEditRules.cpp
@@ -422,22 +422,30 @@ nsTextEditRules::CollapseSelectionToTrai
 {
   // we only need to execute the stuff below if we are a plaintext editor.
   // html editors have a different mechanism for putting in mozBR's
   // (because there are a bunch more places you have to worry about it in html) 
   if (!IsPlaintextEditor()) {
     return NS_OK;
   }
 
+  NS_ENSURE_STATE(mEditor);
+
+  // If there is no selection ranges, we should set to the end of the editor.
+  // This is usually performed in nsTextEditRules::Init(), however, if the
+  // editor is reframed, this may be called by AfterEdit().
+  if (!aSelection->RangeCount()) {
+    mEditor->EndOfDocument();
+  }
+
   // if we are at the end of the textarea, we need to set the
   // selection to stick to the mozBR at the end of the textarea.
   int32_t selOffset;
   nsCOMPtr<nsIDOMNode> selNode;
   nsresult res;
-  NS_ENSURE_STATE(mEditor);
   res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(res, res);
 
   nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
   if (!nodeAsText) return NS_OK; // nothing to do if we're not at a text node
 
   uint32_t length;
   res = nodeAsText->GetLength(&length);
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -270,60 +270,68 @@ DrawTargetCG::OptimizeSourceSurface(Sour
 class UnboundnessFixer
 {
     CGRect mClipBounds;
     CGLayerRef mLayer;
     CGContextRef mLayerCg;
   public:
     UnboundnessFixer() : mLayerCg(nullptr) {}
 
-    CGContextRef Check(CGContextRef baseCg, CompositionOp blend, const Rect* maskBounds = nullptr)
+    CGContextRef Check(DrawTargetCG* dt, CompositionOp blend, const Rect* maskBounds = nullptr)
     {
-      MOZ_ASSERT(baseCg);
+      MOZ_ASSERT(dt->mCg);
       if (!IsOperatorBoundByMask(blend)) {
-        mClipBounds = CGContextGetClipBoundingBox(baseCg);
+        // The clip bounding box will be in user space so we need to clear our transform first
+        CGContextSetCTM(dt->mCg, dt->mOriginalTransform);
+        mClipBounds = CGContextGetClipBoundingBox(dt->mCg);
+
         // If we're entirely clipped out or if the drawing operation covers the entire clip then
         // we don't need to create a temporary surface.
         if (CGRectIsEmpty(mClipBounds) ||
             (maskBounds && maskBounds->Contains(CGRectToRect(mClipBounds)))) {
-          return baseCg;
+          CGContextConcatCTM(dt->mCg, GfxMatrixToCGAffineTransform(dt->mTransform));
+          return dt->mCg;
         }
 
         // TransparencyLayers aren't blended using the blend mode so
         // we are forced to use CGLayers
 
         //XXX: The size here is in default user space units, of the layer relative to the graphics context.
         // is the clip bounds still correct if, for example, we have a scale applied to the context?
-        mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, nullptr);
+        mLayer = CGLayerCreateWithContext(dt->mCg, mClipBounds.size, nullptr);
         mLayerCg = CGLayerGetContext(mLayer);
         // CGContext's default to have the origin at the bottom left
         // so flip it to the top left and adjust for the origin
         // of the layer
         if (MOZ2D_ERROR_IF(!mLayerCg)) {
           return nullptr;
         }
         CGContextTranslateCTM(mLayerCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height);
         CGContextScaleCTM(mLayerCg, 1, -1);
+        CGContextConcatCTM(mLayerCg, GfxMatrixToCGAffineTransform(dt->mTransform));
 
         return mLayerCg;
       } else {
-        return baseCg;
+        return dt->mCg;
       }
     }
 
-    void Fix(CGContextRef baseCg)
+    void Fix(DrawTargetCG *dt)
     {
         if (mLayerCg) {
-            // we pushed a layer so draw it to baseCg
-            MOZ_ASSERT(baseCg);
-            CGContextTranslateCTM(baseCg, 0, mClipBounds.size.height);
-            CGContextScaleCTM(baseCg, 1, -1);
+            // we pushed a layer so draw it to dt->mCg
+            MOZ_ASSERT(dt->mCg);
+            CGContextTranslateCTM(dt->mCg, 0, mClipBounds.size.height);
+            CGContextScaleCTM(dt->mCg, 1, -1);
             mClipBounds.origin.y *= -1;
-            CGContextDrawLayerAtPoint(baseCg, mClipBounds.origin, mLayer);
+            CGContextDrawLayerAtPoint(dt->mCg, mClipBounds.origin, mLayer);
             CGContextRelease(mLayerCg);
+
+            // Reset the transform
+            CGContextConcatCTM(dt->mCg, GfxMatrixToCGAffineTransform(dt->mTransform));
         }
     }
 };
 
 void
 DrawTargetCG::DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
@@ -334,25 +342,23 @@ DrawTargetCG::DrawSurface(SourceSurface 
     return;
   }
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aDest);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp, &aDest);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
   CGContextSetAlpha(cg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-
   CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(aSurfOptions.mFilter));
 
   CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
 
   if (aSurfOptions.mFilter == Filter::POINT) {
     CGImageRef subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
     CGImageRelease(image);
 
@@ -376,17 +382,17 @@ DrawTargetCG::DrawSurface(SourceSurface 
 
     CGContextTranslateCTM(cg, 0, CGRectGetHeight(adjustedDestRect));
     CGContextScaleCTM(cg, 1, -1);
 
     CGContextDrawImage(cg, adjustedDestRect, image);
     CGImageRelease(image);
   }
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
 
   CGContextRestoreGState(mCg);
 }
 
 TemporaryRef<FilterNode>
 DrawTargetCG::CreateFilter(FilterType aType)
 {
   return FilterNodeSoftware::Create(aType);
@@ -889,25 +895,24 @@ DrawTargetCG::MaskSurface(const Pattern 
   }
 
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
 
   CGContextSetAlpha(cg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
   CGImageRef image = GetRetainedImageFromSourceSurface(aMask);
 
   // use a negative-y so that the mask image draws right ways up
   CGContextScaleCTM(cg, 1, -1);
 
   IntSize size = aMask->GetSize();
 
   CGContextClipToMask(cg, CGRectMake(aOffset.x, -(aOffset.y + size.height), size.width, size.height), image);
@@ -919,48 +924,52 @@ DrawTargetCG::MaskSurface(const Pattern 
     DrawGradient(mColorSpace, cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
   } else {
     SetFillFromPattern(cg, mColorSpace, aSource);
     CGContextFillRect(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
   }
 
   CGImageRelease(image);
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
 
   CGContextRestoreGState(mCg);
 }
 
-
+void
+DrawTargetCG::SetTransform(const Matrix &aTransform)
+{
+  mTransform = aTransform;
+  CGContextSetCTM(mCg, mOriginalTransform);
+  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(aTransform));
+}
 
 void
 DrawTargetCG::FillRect(const Rect &aRect,
                        const Pattern &aPattern,
                        const DrawOptions &aDrawOptions)
 {
   if (MOZ2D_ERROR_IF(!mCg)) {
     return;
   }
 
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aRect);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp, &aRect);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
 
   CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-
   if (isGradient(aPattern)) {
     CGContextClipToRect(cg, RectToCGRect(aRect));
     CGRect clipBounds = CGContextGetClipBoundingBox(cg);
     DrawGradient(mColorSpace, cg, aPattern, clipBounds);
   } else if (isNonRepeatingSurface(aPattern)) {
     // SetFillFromPattern can handle this case but using CGContextDrawImage
     // should give us better performance, better output, smaller PDF and
     // matches what cairo does.
@@ -984,17 +993,17 @@ DrawTargetCG::FillRect(const Rect &aRect
 
     CGContextDrawImage(cg, imageRect, image);
     CGImageRelease(image);
   } else {
     SetFillFromPattern(cg, mColorSpace, aPattern);
     CGContextFillRect(cg, RectToCGRect(aRect));
   }
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
   CGContextRestoreGState(mCg);
 }
 
 static Float
 DashPeriodLength(const StrokeOptions& aStrokeOptions)
 {
   Float length = 0;
   for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) {
@@ -1164,26 +1173,24 @@ DrawTargetCG::StrokeLine(const Point &aP
     return;
   }
 
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
   CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-
   CGContextBeginPath(cg);
   CGContextMoveToPoint(cg, p1.x, p1.y);
   CGContextAddLineToPoint(cg, p2.x, p2.y);
 
   SetStrokeOptions(cg, aStrokeOptions);
 
   if (isGradient(aPattern)) {
     CGContextReplacePathWithStrokedPath(cg);
@@ -1191,17 +1198,17 @@ DrawTargetCG::StrokeLine(const Point &aP
     //XXX: should we use EO clip here?
     CGContextClip(cg);
     DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     SetStrokeFromPattern(cg, mColorSpace, aPattern);
     CGContextStrokePath(cg);
   }
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
   CGContextRestoreGState(mCg);
 }
 
 static bool
 IsInteger(Float aValue)
 {
   return floorf(aValue) == aValue;
 }
@@ -1242,17 +1249,17 @@ DrawTargetCG::StrokeRect(const Rect &aRe
     }
   }
 
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
 
   CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
 
   // Work around Quartz bug where antialiasing causes corner pixels to be off by
@@ -1262,18 +1269,16 @@ DrawTargetCG::StrokeRect(const Rect &aRe
   // integers, it maps integers coordinates to integer coordinates.
   bool pixelAlignedStroke = mTransform.IsAllIntegers() &&
     mTransform.PreservesAxisAlignedRectangles() &&
     aPattern.GetType() == PatternType::COLOR &&
     IsPixelAlignedStroke(rect, aStrokeOptions.mLineWidth);
   CGContextSetShouldAntialias(cg,
     aDrawOptions.mAntialiasMode != AntialiasMode::NONE && !pixelAlignedStroke);
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-
   SetStrokeOptions(cg, aStrokeOptions);
 
   if (isGradient(aPattern)) {
     // There's no CGContextClipStrokeRect so we do it by hand
     CGContextBeginPath(cg);
     CGContextAddRect(cg, RectToCGRect(rect));
     CGContextReplacePathWithStrokedPath(cg);
     CGRect extents = CGContextGetPathBoundingBox(cg);
@@ -1291,32 +1296,31 @@ DrawTargetCG::StrokeRect(const Rect &aRe
     CGContextMoveToPoint(cg, rect.x, rect.y);
     CGContextAddLineToPoint(cg, rect.XMost(), rect.y);
     CGContextAddLineToPoint(cg, rect.XMost(), rect.YMost());
     CGContextAddLineToPoint(cg, rect.x, rect.YMost());
     CGContextClosePath(cg);
     CGContextStrokePath(cg);
   }
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
   CGContextRestoreGState(mCg);
 }
 
 
 void
 DrawTargetCG::ClearRect(const Rect &aRect)
 {
   if (MOZ2D_ERROR_IF(!mCg)) {
     return;
   }
 
   MarkChanged();
 
   CGContextSaveGState(mCg);
-  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
 
   CGContextClearRect(mCg, RectToCGRect(aRect));
 
   CGContextRestoreGState(mCg);
 }
 
 void
 DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
@@ -1329,27 +1333,25 @@ DrawTargetCG::Stroke(const Path *aPath, 
     return;
   }
 
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
 
   CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-
 
   CGContextBeginPath(cg);
 
   assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
   const PathCG *cgPath = static_cast<const PathCG*>(aPath);
   CGContextAddPath(cg, cgPath->GetPath());
 
   SetStrokeOptions(cg, aStrokeOptions);
@@ -1362,17 +1364,17 @@ DrawTargetCG::Stroke(const Path *aPath, 
     DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     // XXX: we could put fill mode into the path fill rule if we wanted
 
     SetStrokeFromPattern(cg, mColorSpace, aPattern);
     CGContextStrokePath(cg);
   }
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
   CGContextRestoreGState(mCg);
 }
 
 void
 DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aDrawOptions)
 {
   if (MOZ2D_ERROR_IF(!mCg)) {
     return;
@@ -1381,26 +1383,24 @@ DrawTargetCG::Fill(const Path *aPath, co
   MarkChanged();
 
   assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
 
   CGContextSaveGState(mCg);
 
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
 
   CGContextSetAlpha(cg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-
   CGContextBeginPath(cg);
   // XXX: we could put fill mode into the path fill rule if we wanted
   const PathCG *cgPath = static_cast<const PathCG*>(aPath);
 
   if (isGradient(aPattern)) {
     // setup a clip to draw the gradient through
     CGRect extents;
     if (CGPathIsEmpty(cgPath->GetPath())) {
@@ -1424,17 +1424,17 @@ DrawTargetCG::Fill(const Path *aPath, co
     SetFillFromPattern(cg, mColorSpace, aPattern);
 
     if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
       CGContextEOFillPath(cg);
     else
       CGContextFillPath(cg);
   }
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
   CGContextRestoreGState(mCg);
 }
 
 CGRect ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
 {
   CGFloat x1, x2, y1, y2;
   if (count < 1)
     return CGRectZero;
@@ -1510,29 +1510,27 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFo
         // clamp the components of the result pixel to [0,255] afterwards.)
         mMayContainInvalidPremultipliedData = true;
       }
     }
   }
 
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   UnboundnessFixer fixer;
-  CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
+  CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
 
   CGContextSetAlpha(cg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   if (aDrawOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
     CGContextSetShouldSmoothFonts(cg, aDrawOptions.mAntialiasMode == AntialiasMode::SUBPIXEL);
   }
 
-  CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-
   ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
 
   // This code can execute millions of times in short periods, so we want to
   // avoid heap allocation whenever possible. So we use an inline vector
   // capacity of 64 elements, which is enough to typically avoid heap
   // allocation in ~99% of cases.
   Vector<CGGlyph, 64> glyphs;
   Vector<CGPoint, 64> positions;
@@ -1592,17 +1590,17 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFo
     } else {
       CGContextSetFont(cg, macFont->mFont);
       CGContextSetFontSize(cg, macFont->mSize);
       CGContextShowGlyphsAtPositions(cg, glyphs.begin(), positions.begin(),
                                      aBuffer.mNumGlyphs);
     }
   }
 
-  fixer.Fix(mCg);
+  fixer.Fix(this);
   CGContextRestoreGState(cg);
 }
 
 extern "C" {
 void
 CGContextResetClip(CGContextRef);
 };
 
@@ -1620,16 +1618,17 @@ DrawTargetCG::CopySurface(SourceSurface 
   if (aSurface->GetType() == SurfaceType::COREGRAPHICS_IMAGE ||
       aSurface->GetType() == SurfaceType::COREGRAPHICS_CGCONTEXT ||
       aSurface->GetType() == SurfaceType::DATA) {
     CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
 
     // XXX: it might be more efficient for us to do the copy directly if we have access to the bits
 
     CGContextSaveGState(mCg);
+    CGContextSetCTM(mCg, mOriginalTransform);
 
     // CopySurface ignores the clip, so we need to use private API to temporarily reset it
     CGContextResetClip(mCg);
     CGRect destRect = CGRectMake(aDestination.x, aDestination.y,
                                  aSourceRect.width, aSourceRect.height);
     CGContextClipToRect(mCg, destRect);
 
     CGContextSetBlendMode(mCg, kCGBlendModeCopy);
@@ -1659,16 +1658,17 @@ DrawTargetCG::DrawSurfaceWithShadow(Sour
   }
 
   MarkChanged();
 
   CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
 
   IntSize size = aSurface->GetSize();
   CGContextSaveGState(mCg);
+  CGContextSetCTM(mCg, mOriginalTransform);
   //XXX do we need to do the fixup here?
   CGContextSetBlendMode(mCg, ToBlendMode(aOperator));
 
   CGContextScaleCTM(mCg, 1, -1);
 
   CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + size.height),
                                   size.width, size.height);
 
@@ -1677,17 +1677,16 @@ DrawTargetCG::DrawSurfaceWithShadow(Sour
   // CoreGraphics needs twice sigma as it's amount of blur
   CGContextSetShadowWithColor(mCg, offset, 2*aSigma, color);
   CGColorRelease(color);
 
   CGContextDrawImage(mCg, flippedRect, image);
 
   CGImageRelease(image);
   CGContextRestoreGState(mCg);
-
 }
 
 bool
 DrawTargetCG::Init(BackendType aType,
                    unsigned char* aData,
                    const IntSize &aSize,
                    int32_t aStride,
                    SurfaceFormat aFormat)
@@ -1769,16 +1768,17 @@ DrawTargetCG::Init(BackendType aType,
     gfxCriticalError() << "Failed to create CG context";
     return false;
   }
 
   // CGContext's default to have the origin at the bottom left
   // so flip it to the top left
   CGContextTranslateCTM(mCg, 0, mSize.height);
   CGContextScaleCTM(mCg, 1, -1);
+  mOriginalTransform = CGContextGetCTM(mCg);
   // See Bug 722164 for performance details
   // Medium or higher quality lead to expensive interpolation
   // for canvas we want to use low quality interpolation
   // to have competitive performance with other canvas
   // implementation.
   // XXX: Create input parameter to control interpolation and
   //      use the default for content.
   CGContextSetInterpolationQuality(mCg, kCGInterpolationLow);
@@ -1874,16 +1874,17 @@ DrawTargetCG::Init(CGContextRef cgContex
 
   // CGContext's default to have the origin at the bottom left.
   // However, currently the only use of this function is to construct a
   // DrawTargetCG around a CGContextRef from a cairo quartz surface which
   // already has it's origin adjusted.
   //
   // CGContextTranslateCTM(mCg, 0, mSize.height);
   // CGContextScaleCTM(mCg, 1, -1);
+  mOriginalTransform = CGContextGetCTM(mCg);
 
   mFormat = SurfaceFormat::B8G8R8A8;
   if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) {
     CGColorSpaceRef colorspace;
     CGBitmapInfo bitinfo = CGBitmapContextGetBitmapInfo(mCg);
     colorspace = CGBitmapContextGetColorSpace (mCg);
     if (CGColorSpaceGetNumberOfComponents(colorspace) == 1) {
       mFormat = SurfaceFormat::A8;
@@ -1958,42 +1959,29 @@ DrawTargetCG::Mask(const Pattern &aSourc
 
 void
 DrawTargetCG::PushClipRect(const Rect &aRect)
 {
   if (MOZ2D_ERROR_IF(!mCg)) {
     return;
   }
 
-#ifdef DEBUG
-  mSavedClipBounds.push_back(CGContextGetClipBoundingBox(mCg));
-#endif
-
   CGContextSaveGState(mCg);
 
-  /* We go through a bit of trouble to temporarilly set the transform
-   * while we add the path */
-  CGAffineTransform previousTransform = CGContextGetCTM(mCg);
-  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
   CGContextClipToRect(mCg, RectToCGRect(aRect));
-  CGContextSetCTM(mCg, previousTransform);
 }
 
 
 void
 DrawTargetCG::PushClip(const Path *aPath)
 {
   if (MOZ2D_ERROR_IF(!mCg)) {
     return;
   }
 
-#ifdef DEBUG
-  mSavedClipBounds.push_back(CGContextGetClipBoundingBox(mCg));
-#endif
-
   CGContextSaveGState(mCg);
 
   CGContextBeginPath(mCg);
   assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
 
   const PathCG *cgPath = static_cast<const PathCG*>(aPath);
 
   // Weirdly, CoreGraphics clips empty paths as all shown
@@ -2004,37 +1992,29 @@ DrawTargetCG::PushClip(const Path *aPath
     CGContextClipToRect(mCg, CGRectZero);
   }
 
 
   /* We go through a bit of trouble to temporarilly set the transform
    * while we add the path. XXX: this could be improved if we keep
    * the CTM as resident state on the DrawTarget. */
   CGContextSaveGState(mCg);
-  CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
   CGContextAddPath(mCg, cgPath->GetPath());
   CGContextRestoreGState(mCg);
 
   if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
     CGContextEOClip(mCg);
   else
     CGContextClip(mCg);
 }
 
 void
 DrawTargetCG::PopClip()
 {
   CGContextRestoreGState(mCg);
-
-#ifdef DEBUG
-  MOZ_ASSERT(!mSavedClipBounds.empty(), "Unbalanced PopClip");
-  MOZ_ASSERT(CGRectEqualToRect(mSavedClipBounds.back(), CGContextGetClipBoundingBox(mCg)),
-             "PopClip didn't restore original clip");
-  mSavedClipBounds.pop_back();
-#endif
 }
 
 void
 DrawTargetCG::MarkChanged()
 {
   if (mSnapshot) {
     if (mSnapshot->refCount() > 1) {
       // We only need to worry about snapshots that someone else knows about
@@ -2059,18 +2039,16 @@ BorrowedCGContext::BorrowCGContextFromDr
     if (MOZ2D_ERROR_IF(!cg)) {
       return nullptr;
     }
     cgDT->mCg = nullptr;
 
     // save the state to make it easier for callers to avoid mucking with things
     CGContextSaveGState(cg);
 
-    CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(cgDT->mTransform));
-
     return cg;
   }
   return nullptr;
 }
 
 void
 BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg)
 {
--- a/gfx/2d/DrawTargetCG.h
+++ b/gfx/2d/DrawTargetCG.h
@@ -14,17 +14,17 @@
 #include "SourceSurfaceCG.h"
 #include "GLDefs.h"
 #include "Tools.h"
 
 namespace mozilla {
 namespace gfx {
 
 static inline CGAffineTransform
-GfxMatrixToCGAffineTransform(Matrix m)
+GfxMatrixToCGAffineTransform(const Matrix &m)
 {
   CGAffineTransform t;
   t.a = m._11;
   t.b = m._12;
   t.c = m._21;
   t.d = m._22;
   t.tx = m._31;
   t.ty = m._32;
@@ -108,16 +108,17 @@ private:
   Color mFontSmoothingBackgroundColor;
 };
 
 class DrawTargetCG : public DrawTarget
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCG, override)
   friend class BorrowedCGContext;
+  friend class UnboundnessFixer;
   friend class SourceSurfaceCGBitmapContext;
   DrawTargetCG();
   virtual ~DrawTargetCG();
 
   virtual DrawTargetType GetType() const override;
   virtual BackendType GetBackendType() const override;
   virtual TemporaryRef<SourceSurface> Snapshot() override;
 
@@ -168,16 +169,17 @@ public:
   virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *, uint32_t,
                                                           ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
   virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType) override;
 
   virtual void *GetNativeSurface(NativeSurfaceType) override;
 
   virtual IntSize GetSize() override { return mSize; }
 
+  virtual void SetTransform(const Matrix &aTransform) override;
 
   /* This is for creating good compatible surfaces */
   virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                             const IntSize &aSize,
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const override;
   virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
   CGContextRef GetCGContext() {
@@ -191,30 +193,27 @@ public:
   }
 
 private:
   void MarkChanged();
 
   IntSize mSize;
   CGColorSpaceRef mColorSpace;
   CGContextRef mCg;
+  CGAffineTransform mOriginalTransform;
 
   /**
    * The image buffer, if the buffer is owned by this class.
    * If the DrawTarget was created for a pre-existing buffer or if the buffer's
    * lifetime is managed by CoreGraphics, mData will be null.
    * Data owned by DrawTargetCG will be deallocated in the destructor.
    */
   AlignedArray<uint8_t> mData;
 
   RefPtr<SourceSurfaceCGContext> mSnapshot;
   bool mMayContainInvalidPremultipliedData;
-
-#ifdef DEBUG
-  std::vector<CGRect> mSavedClipBounds;
-#endif
 };
 
 }
 }
 
 #endif
 
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -91,16 +91,17 @@ static const char *sExtensionNames[] = {
     "GL_ARB_robustness",
     "GL_ARB_sampler_objects",
     "GL_ARB_sync",
     "GL_ARB_texture_compression",
     "GL_ARB_texture_float",
     "GL_ARB_texture_non_power_of_two",
     "GL_ARB_texture_rectangle",
     "GL_ARB_texture_storage",
+    "GL_ARB_texture_swizzle",
     "GL_ARB_transform_feedback2",
     "GL_ARB_uniform_buffer_object",
     "GL_ARB_vertex_array_object",
     "GL_EXT_bgra",
     "GL_EXT_blend_minmax",
     "GL_EXT_color_buffer_float",
     "GL_EXT_color_buffer_half_float",
     "GL_EXT_copy_texture",
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -123,16 +123,17 @@ enum class GLFeature {
     texture_3D_compressed,
     texture_3D_copy,
     texture_float,
     texture_float_linear,
     texture_half_float,
     texture_half_float_linear,
     texture_non_power_of_two,
     texture_storage,
+    texture_swizzle,
     transform_feedback2,
     uniform_buffer_object,
     uniform_matrix_nonsquare,
     vertex_array_object,
     EnumMax
 };
 
 enum class ContextProfile : uint8_t {
@@ -384,16 +385,17 @@ public:
         ARB_robustness,
         ARB_sampler_objects,
         ARB_sync,
         ARB_texture_compression,
         ARB_texture_float,
         ARB_texture_non_power_of_two,
         ARB_texture_rectangle,
         ARB_texture_storage,
+        ARB_texture_swizzle,
         ARB_transform_feedback2,
         ARB_uniform_buffer_object,
         ARB_vertex_array_object,
         EXT_bgra,
         EXT_blend_minmax,
         EXT_color_buffer_float,
         EXT_color_buffer_half_float,
         EXT_copy_texture,
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -599,16 +599,25 @@ static const FeatureInfo sFeatureInfoArr
              * Not including GL_EXT_texture_storage here because it
              * doesn't guarantee glTexStorage3D, which is required for
              * WebGL 2.
              */
             GLContext::Extensions_End
         }
     },
     {
+        "texture_swizzle",
+        GLVersion::GL3_3,
+        GLESVersion::ES3,
+        GLContext::ARB_texture_swizzle,
+        {
+            GLContext::Extensions_End
+        }
+    },
+    {
         "transform_feedback2",
         GLVersion::GL4,
         GLESVersion::ES3,
         GLContext::ARB_transform_feedback2,
         {
             GLContext::NV_transform_feedback2,
             GLContext::Extensions_End
         }
--- a/gfx/layers/composite/TextRenderer.cpp
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -85,17 +85,19 @@ TextRenderer::RenderText(const string& a
   // Create a surface to draw our glyphs to.
   RefPtr<DataSourceSurface> textSurf =
     Factory::CreateDataSourceSurface(IntSize(maxWidth, numLines * sCellHeight), sTextureFormat);
   if (NS_WARN_IF(!textSurf)) {
     return;
   }
 
   DataSourceSurface::MappedSurface map;
-  textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map);
+  if (NS_WARN_IF(!textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map))) {
+    return;
+  }
 
   // Initialize the surface to transparent white.
   memset(map.mData, uint8_t(sBackgroundOpacity * 255.0f),
          numLines * sCellHeight * map.mStride);
 
   uint32_t currentXPos = 0;
   uint32_t currentYPos = 0;
 
@@ -146,17 +148,19 @@ TextRenderer::EnsureInitialized()
     return;
   }
 
   mGlyphBitmaps = Factory::CreateDataSourceSurface(IntSize(sTextureWidth, sTextureHeight), sTextureFormat);
   if (NS_WARN_IF(!mGlyphBitmaps)) {
     return;
   }
 
-  mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap);
+  if (NS_WARN_IF(!mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap))) {
+    return;
+  }
 
   png_structp png_ptr = NULL;
   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
 
   png_set_progressive_read_fn(png_ptr, this, info_callback, row_callback, nullptr);
   png_infop info_ptr = NULL;
   info_ptr = png_create_info_struct(png_ptr);
 
--- a/gfx/layers/d3d9/CompositorD3D9.cpp
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -741,17 +741,21 @@ CompositorD3D9::PaintToTarget()
 
   device()->CreateOffscreenPlainSurface(desc.Width, desc.Height,
                                         D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM,
                                         getter_AddRefs(destSurf), nullptr);
 
   device()->GetRenderTargetData(backBuff, destSurf);
 
   D3DLOCKED_RECT rect;
-  destSurf->LockRect(&rect, nullptr, D3DLOCK_READONLY);
+  HRESULT hr = destSurf->LockRect(&rect, nullptr, D3DLOCK_READONLY);
+  if (FAILED(hr) || !rect.pBits) {
+    gfxCriticalError() << "Failed to lock rect in paint to target D3D9 " << hexa(hr);
+    return;
+  }
   RefPtr<DataSourceSurface> sourceSurface =
     Factory::CreateWrappingDataSourceSurface((uint8_t*)rect.pBits,
                                              rect.Pitch,
                                              IntSize(desc.Width, desc.Height),
                                              SurfaceFormat::B8G8R8A8);
   mTarget->CopySurface(sourceSurface,
                        IntRect(0, 0, desc.Width, desc.Height),
                        IntPoint(-mTargetBounds.x, -mTargetBounds.y));
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -187,19 +187,20 @@ TextureSourceD3D9::InitTextures(DeviceMa
 
   RefPtr<IDirect3DTexture9> tmpTexture =
     aDeviceManager->CreateTexture(aSize, aFormat, D3DPOOL_SYSTEMMEM, this);
   if (!tmpTexture) {
     return nullptr;
   }
 
   tmpTexture->GetSurfaceLevel(0, byRef(aSurface));
-  aSurface->LockRect(&aLockedRect, nullptr, 0);
-  if (!aLockedRect.pBits) {
-    NS_WARNING("Could not lock surface");
+  
+  HRESULT hr = aSurface->LockRect(&aLockedRect, nullptr, 0);
+  if (FAILED(hr) || !aLockedRect.pBits) {
+    gfxCriticalError() << "Failed to lock rect initialize texture in D3D9 " << hexa(hr);
     return nullptr;
   }
 
   return result.forget();
 }
 
 /**
  * Helper method for DataToTexture and SurfaceToTexture.
@@ -702,17 +703,21 @@ CairoTextureClientD3D9::BorrowDrawTarget
       mSurface = nullptr;
       return nullptr;
     }
   } else {
     // gfxWindowsSurface don't support transparency so we can't use the d3d9
     // windows surface optimization.
     // Instead we have to use a gfxImageSurface and fallback for font drawing.
     D3DLOCKED_RECT rect;
-    mD3D9Surface->LockRect(&rect, nullptr, 0);
+    HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0);
+    if (FAILED(hr) || !rect.pBits) {
+      gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 " << hexa(hr);
+      return nullptr;
+    }
     mSurface = new gfxImageSurface((uint8_t*)rect.pBits, mSize,
                                    rect.Pitch, SurfaceFormatToImageFormat(mFormat));
     mLockRect = true;
   }
 
   mDrawTarget =
     gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mSurface, mSize);
 
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -24,17 +24,17 @@ UNIFIED_SOURCES += [
     #'gfxTextRunPerfTest.cpp',
     'TestTiledLayerBuffer.cpp',
     'TestVsync.cpp',
 ]
 
 # Because of gkmedia on windows we won't find these
 # symbols in xul.dll.
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
-    UNIFIED_SOURCES += [ '%s/gfx/2d/unittest/%s' % (TOPSRCDIR, p) for p in [
+    UNIFIED_SOURCES += [ '/gfx/2d/unittest/%s' % p for p in [
         'TestBase.cpp',
         'TestBugs.cpp',
         'TestPoint.cpp',
         'TestScaling.cpp',
     ]]
     UNIFIED_SOURCES += [
         'TestMoz2D.cpp',
         'TestRect.cpp',
--- a/intl/unicharutil/util/objs.mozbuild
+++ b/intl/unicharutil/util/objs.mozbuild
@@ -17,11 +17,11 @@ intl_unicharutil_util_lcppsrcs += [
     'IrishCasing.cpp',
     'nsBidiUtils.cpp',
     'nsSpecialCasingData.cpp',
     'nsUnicharUtils.cpp',
     'nsUnicodeProperties.cpp',
 ]
 
 intl_unicharutil_util_cppsrcs = [
-    '%s/intl/unicharutil/util/%s' % (TOPSRCDIR, s) \
+    '/intl/unicharutil/util/%s' % s
         for s in intl_unicharutil_util_lcppsrcs
 ]
--- a/intl/unicharutil/util/standalone/moz.build
+++ b/intl/unicharutil/util/standalone/moz.build
@@ -2,22 +2,15 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     Library('unicharutil_standalone')
 
-intl_unicharutil_util_lcppsrcs = [
-    'nsUnicodeProperties.cpp',
+UNIFIED_SOURCES += [
+    '../nsUnicodeProperties.cpp',
 ]
 
-intl_unicharutil_util_cppsrcs = [
-    '%s/intl/unicharutil/util/%s' % (TOPSRCDIR, s) \
-        for s in intl_unicharutil_util_lcppsrcs
-]
-
-UNIFIED_SOURCES += intl_unicharutil_util_cppsrcs
-
 for var in ('MOZILLA_INTERNAL_API', 'MOZILLA_XPCOMRT_API', 'MOZILLA_EXTERNAL_LINKAGE',
             'NR_SOCKET_IS_VOID_PTR', 'HAVE_STRDUP'):
     DEFINES[var] = True
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -197,21 +197,19 @@ if os_linux:
         'src/base/time_posix.cc',
     ]
     if CONFIG['MOZ_WIDGET_GTK']:
         SOURCES += [
             'src/base/message_pump_glib.cc',
         ]
     if CONFIG['MOZ_ENABLE_QT']:
         SOURCES += [
+            '!moc_message_pump_qt.cc',
             'src/base/message_pump_qt.cc',
         ]
-        GENERATED_SOURCES += [
-            'moc_message_pump_qt.cc',
-        ]
     if not CONFIG['MOZ_NATIVE_LIBEVENT']:
         if CONFIG['OS_TARGET'] != 'Android':
             SOURCES += [
                 'src/third_party/libevent/epoll_sub.c',
             ]
         SOURCES += [
             'src/third_party/libevent/epoll.c',
         ]
@@ -234,21 +232,19 @@ if os_bsd:
             'src/base/process_util_bsd.cc'
         ]
     if CONFIG['MOZ_WIDGET_GTK']:
         SOURCES += [
             'src/base/message_pump_glib.cc',
         ]
     if CONFIG['MOZ_ENABLE_QT']:
         SOURCES += [
+            '!moc_message_pump_qt.cc',
             'src/base/message_pump_qt.cc',
         ]
-        GENERATED_SOURCES += [
-            'moc_message_pump_qt.cc',
-        ]
     if not CONFIG['MOZ_NATIVE_LIBEVENT']:
         SOURCES += [
             'src/third_party/libevent/kqueue.c',
         ]
         LOCAL_INCLUDES += ['src/third_party/libevent/bsd']
 
 ost = CONFIG['OS_TEST']
 if '86' not in ost and 'arm' not in ost and 'mips' not in ost:
--- a/ipc/ipdl/test/cxx/moz.build
+++ b/ipc/ipdl/test/cxx/moz.build
@@ -50,24 +50,21 @@ SOURCES += [
 ]
 
 if CONFIG['OS_ARCH'] == 'Linux':
     SOURCES += [
         'TestSysVShmem.cpp',
     ]
 
 SOURCES += [
+    '!IPDLUnitTests.cpp',
     'IPDLUnitTestProcessChild.cpp',
     'IPDLUnitTestSubprocess.cpp',
 ]
 
-GENERATED_SOURCES += [
-    'IPDLUnitTests.cpp',
-]
-
 IPDL_SOURCES += [
     'PTestActorPunning.ipdl',
     'PTestActorPunningPunned.ipdl',
     'PTestActorPunningSub.ipdl',
     'PTestBadActor.ipdl',
     'PTestBadActorSub.ipdl',
     'PTestBridgeMain.ipdl',
     'PTestBridgeMainSub.ipdl',
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -3735,17 +3735,17 @@ CheckFunctionHead(ModuleCompiler& m, Par
 }
 
 static bool
 CheckArgument(ModuleCompiler& m, ParseNode* arg, PropertyName** name)
 {
     if (!IsDefinition(arg))
         return m.fail(arg, "duplicate argument name not allowed");
 
-    if (arg->pn_dflags & PND_DEFAULT)
+    if (arg->isKind(PNK_ASSIGN))
         return m.fail(arg, "default arguments not allowed");
 
     if (!CheckIdentifier(m, arg, arg->name()))
         return false;
 
     *name = arg->name();
     return true;
 }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -6051,22 +6051,17 @@ BytecodeEmitter::emitYieldStar(ParseNode
 bool
 BytecodeEmitter::emitStatementList(ParseNode* pn, ptrdiff_t top)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
 
     StmtInfoBCE stmtInfo(cx);
     pushStatement(&stmtInfo, STMT_BLOCK, top);
 
-    ParseNode* pnchild = pn->pn_head;
-
-    if (pn->pn_xflags & PNX_DESTRUCT)
-        pnchild = pnchild->pn_next;
-
-    for (ParseNode* pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
+    for (ParseNode* pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
         if (!emitTree(pn2))
             return false;
     }
 
     popStatement();
     return true;
 }
 
@@ -6639,37 +6634,16 @@ BytecodeEmitter::emitLabeledStatement(co
     popStatement();
 
     /* Patch the JSOP_LABEL offset. */
     setJumpOffsetAt(top);
     return true;
 }
 
 bool
-BytecodeEmitter::emitSyntheticStatements(ParseNode* pn, ptrdiff_t top)
-{
-    MOZ_ASSERT(pn->isArity(PN_LIST));
-
-    StmtInfoBCE stmtInfo(cx);
-    pushStatement(&stmtInfo, STMT_SEQ, top);
-
-    ParseNode* pn2 = pn->pn_head;
-    if (pn->pn_xflags & PNX_DESTRUCT)
-        pn2 = pn2->pn_next;
-
-    for (; pn2; pn2 = pn2->pn_next) {
-        if (!emitTree(pn2))
-            return false;
-    }
-
-    popStatement();
-    return true;
-}
-
-bool
 BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional)
 {
     /* Emit the condition, then branch if false to the else part. */
     if (!emitTree(&conditional.condition()))
         return false;
 
     unsigned noteIndex;
     if (!newSrcNote(SRC_COND, &noteIndex))
@@ -6998,45 +6972,69 @@ BytecodeEmitter::emitUnary(ParseNode* pn
     if (!emitTree(pn2))
         return false;
 
     emittingForInit = oldEmittingForInit;
     return emit1(op);
 }
 
 bool
-BytecodeEmitter::emitDefaults(ParseNode* pn)
+BytecodeEmitter::emitDefaultsAndDestructuring(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_ARGSBODY));
 
     ParseNode* pnlast = pn->last();
     for (ParseNode* arg = pn->pn_head; arg != pnlast; arg = arg->pn_next) {
-        if (!(arg->pn_dflags & PND_DEFAULT))
-            continue;
-        if (!bindNameToSlot(arg))
-            return false;
-        if (!emitVarOp(arg, JSOP_GETARG))
-            return false;
-        if (!emit1(JSOP_UNDEFINED))
-            return false;
-        if (!emit1(JSOP_STRICTEQ))
-            return false;
-        // Emit source note to enable ion compilation.
-        if (!newSrcNote(SRC_IF))
-            return false;
-        ptrdiff_t jump;
-        if (!emitJump(JSOP_IFEQ, 0, &jump))
-            return false;
-        if (!emitTree(arg->expr()))
-            return false;
-        if (!emitVarOp(arg, JSOP_SETARG))
-            return false;
-        if (!emit1(JSOP_POP))
-            return false;
-        SET_JUMP_OFFSET(code(jump), offset() - jump);
+        MOZ_ASSERT(arg->isKind(PNK_NAME) || arg->isKind(PNK_ASSIGN));
+        ParseNode* argName = nullptr;
+        ParseNode* defNode = nullptr;
+        ParseNode* destruct = nullptr;
+        if (arg->isKind(PNK_ASSIGN)) {
+            argName = arg->pn_left;
+            defNode = arg->pn_right;
+        } else if (arg->pn_atom == cx->names().empty) {
+            argName = arg;
+            destruct = arg->expr();
+            MOZ_ASSERT(destruct);
+            if (destruct->isKind(PNK_ASSIGN)) {
+                defNode = destruct->pn_right;
+                destruct = destruct->pn_left;
+            }
+        }
+        if (defNode) {
+            if (!bindNameToSlot(argName))
+                return false;
+            if (!emitVarOp(argName, JSOP_GETARG))
+                return false;
+            if (!emit1(JSOP_UNDEFINED))
+                return false;
+            if (!emit1(JSOP_STRICTEQ))
+                return false;
+            // Emit source note to enable ion compilation.
+            if (!newSrcNote(SRC_IF))
+                return false;
+            ptrdiff_t jump;
+            if (!emitJump(JSOP_IFEQ, 0, &jump))
+                return false;
+            if (!emitTree(defNode))
+                return false;
+            if (!emitVarOp(argName, JSOP_SETARG))
+                return false;
+            if (!emit1(JSOP_POP))
+                return false;
+            SET_JUMP_OFFSET(code(jump), offset() - jump);
+        }
+        if (destruct) {
+            if (!emitTree(argName))
+                return false;
+            if (!emitDestructuringOps(destruct, false))
+                 return false;
+            if (!emit1(JSOP_POP))
+                return false;
+        }
     }
 
     return true;
 }
 
 
 bool
 BytecodeEmitter::emitLexicalInitialization(ParseNode* pn, JSOp globalDefOp)
@@ -7202,72 +7200,60 @@ BytecodeEmitter::emitTree(ParseNode* pn)
         break;
 
       case PNK_ARGSBODY:
       {
         RootedFunction fun(cx, sc->asFunctionBox()->function());
         ParseNode* pnlast = pn->last();
 
         // Carefully emit everything in the right order:
-        // 1. Destructuring
-        // 2. Defaults
-        // 3. Functions
+        // 1. Defaults and Destructuring for each argument
+        // 2. Functions
         ParseNode* pnchild = pnlast->pn_head;
-        if (pnlast->pn_xflags & PNX_DESTRUCT) {
-            // Assign the destructuring arguments before defining any functions,
-            // see bug 419662.
-            MOZ_ASSERT(pnchild->isKind(PNK_SEMI));
-            MOZ_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_GLOBALCONST));
-            if (!emitTree(pnchild))
-                return false;
-            pnchild = pnchild->pn_next;
-        }
         bool hasDefaults = sc->asFunctionBox()->hasDefaults();
-        if (hasDefaults) {
-            ParseNode* rest = nullptr;
-            bool restIsDefn = false;
-            if (fun->hasRest()) {
-                MOZ_ASSERT(!sc->asFunctionBox()->argumentsHasLocalBinding());
-
-                // Defaults with a rest parameter need special handling. The
-                // rest parameter needs to be undefined while defaults are being
-                // processed. To do this, we create the rest argument and let it
-                // sit on the stack while processing defaults. The rest
-                // parameter's slot is set to undefined for the course of
-                // default processing.
-                rest = pn->pn_head;
-                while (rest->pn_next != pnlast)
-                    rest = rest->pn_next;
-                restIsDefn = rest->isDefn();
-                if (!emit1(JSOP_REST))
+        ParseNode* rest = nullptr;
+        bool restIsDefn = false;
+        if (fun->hasRest() && hasDefaults) {
+            MOZ_ASSERT(!sc->asFunctionBox()->argumentsHasLocalBinding());
+
+            // Defaults with a rest parameter need special handling. The
+            // rest parameter needs to be undefined while defaults are being
+            // processed. To do this, we create the rest argument and let it
+            // sit on the stack while processing defaults. The rest
+            // parameter's slot is set to undefined for the course of
+            // default processing.
+            rest = pn->pn_head;
+            while (rest->pn_next != pnlast)
+                rest = rest->pn_next;
+            restIsDefn = rest->isDefn();
+            if (!emit1(JSOP_REST))
+                return false;
+            checkTypeSet(JSOP_REST);
+
+            // Only set the rest parameter if it's not aliased by a nested
+            // function in the body.
+            if (restIsDefn) {
+                if (!emit1(JSOP_UNDEFINED))
                     return false;
-                checkTypeSet(JSOP_REST);
-
-                // Only set the rest parameter if it's not aliased by a nested
-                // function in the body.
-                if (restIsDefn) {
-                    if (!emit1(JSOP_UNDEFINED))
-                        return false;
-                    if (!bindNameToSlot(rest))
-                        return false;
-                    if (!emitVarOp(rest, JSOP_SETARG))
-                        return false;
-                    if (!emit1(JSOP_POP))
-                        return false;
-                }
-            }
-            if (!emitDefaults(pn))
-                return false;
-            if (fun->hasRest()) {
-                if (restIsDefn && !emitVarOp(rest, JSOP_SETARG))
+                if (!bindNameToSlot(rest))
+                    return false;
+                if (!emitVarOp(rest, JSOP_SETARG))
                     return false;
                 if (!emit1(JSOP_POP))
                     return false;
             }
         }
+        if (!emitDefaultsAndDestructuring(pn))
+            return false;
+        if (fun->hasRest() && hasDefaults) {
+            if (restIsDefn && !emitVarOp(rest, JSOP_SETARG))
+                return false;
+            if (!emit1(JSOP_POP))
+                return false;
+        }
         for (ParseNode* pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) {
             // Only bind the parameter if it's not aliased by a nested function
             // in the body.
             if (!pn2->isDefn())
                 continue;
             if (!bindNameToSlot(pn2))
                 return false;
             if (pn2->pn_next == pnlast && fun->hasRest() && !hasDefaults) {
@@ -7369,20 +7355,16 @@ BytecodeEmitter::emitTree(ParseNode* pn)
       case PNK_YIELD:
         ok = emitYield(pn);
         break;
 
       case PNK_STATEMENTLIST:
         ok = emitStatementList(pn, top);
         break;
 
-      case PNK_SEQ:
-        ok = emitSyntheticStatements(pn, top);
-        break;
-
       case PNK_SEMI:
         ok = emitStatement(pn);
         break;
 
       case PNK_LABEL:
         ok = emitLabeledStatement(&pn->as<LabeledStatement>());
         break;
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -539,17 +539,16 @@ struct BytecodeEmitter
 
     bool emitCallSiteObject(ParseNode* pn);
     bool emitTemplateString(ParseNode* pn);
     bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs);
 
     bool emitReturn(ParseNode* pn);
     bool emitStatement(ParseNode* pn);
     bool emitStatementList(ParseNode* pn, ptrdiff_t top);
-    bool emitSyntheticStatements(ParseNode* pn, ptrdiff_t top);
 
     bool emitDelete(ParseNode* pn);
     bool emitLogical(ParseNode* pn);
     bool emitUnary(ParseNode* pn);
 
     MOZ_NEVER_INLINE bool emitIncOrDec(ParseNode* pn);
 
     bool emitConditionalExpression(ConditionalExpression& conditional);
@@ -564,17 +563,17 @@ struct BytecodeEmitter
     bool emitForIn(ParseNode* pn, ptrdiff_t top);
     bool emitForInOrOfVariables(ParseNode* pn, bool* letDecl);
     bool emitNormalFor(ParseNode* pn, ptrdiff_t top);
     bool emitWhile(ParseNode* pn, ptrdiff_t top);
 
     bool emitBreak(PropertyName* label);
     bool emitContinue(PropertyName* label);
 
-    bool emitDefaults(ParseNode* pn);
+    bool emitDefaultsAndDestructuring(ParseNode* pn);
     bool emitLexicalInitialization(ParseNode* pn, JSOp globalDefOp);
 
     bool pushInitialConstants(JSOp op, unsigned n);
     bool initializeBlockScopedLocalsFromStack(Handle<StaticBlockObject*> blockObj);
 
     // emitSpread expects the current index (I) of the array, the array itself
     // and the iterator to be on the stack in that order (iterator on the bottom).
     // It will pop the iterator and I, then iterate over the iterator by calling
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -234,32 +234,16 @@ ContainsHoistedDeclaration(ExclusiveCont
       // A case/default node's right half is its statements.  A default node's
       // left half is null; a case node's left half is its expression.
       case PNK_DEFAULT:
         MOZ_ASSERT(!node->pn_left);
       case PNK_CASE:
         MOZ_ASSERT(node->isArity(PN_BINARY));
         return ContainsHoistedDeclaration(cx, node->pn_right, result);
 
-      // PNK_SEQ has two purposes.
-      //
-      // The first is to prepend destructuring operations to the body of a
-      // deprecated function expression closure: irrelevant here, as this
-      // function doesn't recur into PNK_FUNCTION, and this method's sole
-      // caller acts upon statements nested in if-statements not found in
-      // destructuring operations.
-      //
-      // The second is to provide a place for a hoisted declaration to go, in
-      // the bizarre for-in/of loops that have as target a declaration with an
-      // assignment, e.g. |for (var i = 0 in expr)|.  This case is sadly still
-      // relevant, so we can't banish this ParseNodeKind to the unreachable
-      // list and must check every list member.
-      case PNK_SEQ:
-        return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
-
       case PNK_FOR: {
         MOZ_ASSERT(node->isArity(PN_BINARY));
 
         ParseNode* loopHead = node->pn_left;
         MOZ_ASSERT(loopHead->isKind(PNK_FORHEAD) ||
                    loopHead->isKind(PNK_FORIN) ||
                    loopHead->isKind(PNK_FOROF));
 
@@ -282,21 +266,18 @@ ContainsHoistedDeclaration(ExclusiveCont
             // for each? (target in ...), where only target may introduce
             // hoisted declarations.
             //
             //   -- or --
             //
             // for (target of ...), where only target may introduce hoisted
             // declarations.
             //
-            // Either way, if |target| contains a declaration, it's either
-            // |loopHead|'s first kid, *or* that declaration was hoisted to
-            // become a child of an ancestral PNK_SEQ node.  The former case we
-            // detect here.  The latter case is handled by this method
-            // recurring on PNK_SEQ, above.
+            // Either way, if |target| contains a declaration, it's |loopHead|'s
+            // first kid.
             MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
 
             ParseNode* decl = loopHead->pn_kid1;
             if (decl && decl->isKind(PNK_VAR)) {
                 *result = true;
                 return true;
             }
         }
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -48,16 +48,19 @@ class FullParseHandler
      */
     LazyScript * const lazyOuterFunction_;
     size_t lazyInnerFunctionIndex;
 
     const TokenPos& pos() {
         return tokenStream.currentToken().pos;
     }
 
+    inline ParseNode* makeAssignmentFromArg(ParseNode* arg, ParseNode* lhs, ParseNode* rhs);
+    inline void replaceLastFunctionArgument(ParseNode* funcpn, ParseNode* pn);
+
   public:
 
     /*
      * If non-nullptr, points to a syntax parser which can be used for inner
      * functions. Cleared if language features not handled by the syntax parser
      * are encountered, in which case all future activity will use the full
      * parser.
      */
@@ -563,17 +566,18 @@ class FullParseHandler
 
     ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
     }
 
     inline bool addCatchBlock(ParseNode* catchList, ParseNode* letBlock,
                               ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody);
 
-    inline void setLastFunctionArgumentDefault(ParseNode* funcpn, ParseNode* pn);
+    inline bool setLastFunctionArgumentDefault(ParseNode* funcpn, ParseNode* pn);
+    inline void setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseNode* pn);
 
     ParseNode* newFunctionDefinition() {
         return new_<CodeNode>(pos());
     }
     void setFunctionBody(ParseNode* pn, ParseNode* kid) {
         pn->pn_body = kid;
     }
     void setFunctionBox(ParseNode* pn, FunctionBox* funbox) {
@@ -812,22 +816,66 @@ FullParseHandler::addCatchBlock(ParseNod
     if (!catchpn)
         return false;
 
     catchList->append(letBlock);
     letBlock->pn_expr = catchpn;
     return true;
 }
 
+inline ParseNode*
+FullParseHandler::makeAssignmentFromArg(ParseNode* arg, ParseNode* lhs, ParseNode* rhs)
+{
+    return newBinary(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
+}
+
 inline void
+FullParseHandler::replaceLastFunctionArgument(ParseNode* funcpn, ParseNode* pn)
+{
+    funcpn->pn_body->pn_pos.end = pn->pn_pos.end;
+    ParseNode* pnchild = funcpn->pn_body->pn_head;
+    ParseNode* pnlast = funcpn->pn_body->last();
+    MOZ_ASSERT(pnchild);
+    if (pnchild == pnlast) {
+        funcpn->pn_body->pn_head = pn;
+    } else {
+        while (pnchild->pn_next != pnlast) {
+            MOZ_ASSERT(pnchild->pn_next);
+            pnchild = pnchild->pn_next;
+        }
+        pnchild->pn_next = pn;
+    }
+    funcpn->pn_body->pn_tail = &pn->pn_next;
+}
+
+inline bool
 FullParseHandler::setLastFunctionArgumentDefault(ParseNode* funcpn, ParseNode* defaultValue)
 {
     ParseNode* arg = funcpn->pn_body->last();
-    arg->pn_dflags |= PND_DEFAULT;
-    arg->pn_expr = defaultValue;
+    MOZ_ASSERT(arg->isKind(PNK_NAME));
+    ParseNode* lhs = arg->pn_expr ? arg->pn_expr : arg;
+    ParseNode* pn = makeAssignmentFromArg(arg, lhs, defaultValue);
+    if (!pn)
+        return false;
+
+    if (arg->pn_expr)
+        arg->pn_expr = pn;
+    else
+        replaceLastFunctionArgument(funcpn, pn);
+    return true;
+}
+
+inline void
+FullParseHandler::setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseNode* destruct)
+{
+    ParseNode* arg = funcpn->pn_body->last();
+    MOZ_ASSERT(arg->isKind(PNK_NAME));
+    MOZ_ASSERT(!arg->isUsed());
+    MOZ_ASSERT(arg->isDefn());
+    arg->pn_expr = destruct;
 }
 
 inline bool
 FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op)
 {
     if (pn->isUsed()) {
         pn = makeAssignment(pn, init);
         if (!pn)
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -644,17 +644,16 @@ class NameResolver
           case PNK_DIV:
           case PNK_MOD:
           case PNK_COMMA:
           case PNK_NEW:
           case PNK_CALL:
           case PNK_GENEXP:
           case PNK_ARRAY:
           case PNK_STATEMENTLIST:
-          case PNK_SEQ:
           case PNK_ARGSBODY:
           // Initializers for individual variables, and computed property names
           // within destructuring patterns, may contain unnamed functions.
           case PNK_VAR:
           case PNK_CONST:
           case PNK_LET:
           case PNK_GLOBALCONST:
             MOZ_ASSERT(cur->isArity(PN_LIST));
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -482,17 +482,16 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_VAR:
       case PNK_CONST:
       case PNK_GLOBALCONST:
       case PNK_LET:
       case PNK_CATCHLIST:
       case PNK_STATEMENTLIST:
       case PNK_IMPORT_SPEC_LIST:
       case PNK_EXPORT_SPEC_LIST:
-      case PNK_SEQ:
       case PNK_ARGSBODY:
       case PNK_CLASSMETHODLIST:
         return PushListNodeChildren(pn, stack);
 
       // Array comprehension nodes are lists with a single child -- PNK_FOR for
       // comprehensions, PNK_LEXICALSCOPE for legacy comprehensions.  Probably
       // this should be a non-list eventually.
       case PNK_ARRAYCOMP: {
@@ -1064,16 +1063,21 @@ void
 NameNode::dump(int indent)
 {
     if (isKind(PNK_NAME) || isKind(PNK_DOT)) {
         if (isKind(PNK_DOT))
             fprintf(stderr, "(.");
 
         if (!pn_atom) {
             fprintf(stderr, "#<null name>");
+        } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
+            // Dump destructuring parameter.
+            fprintf(stderr, "(#<zero-length name> ");
+            DumpParseTree(expr(), indent + 21);
+            fputc(')', stderr);
         } else {
             JS::AutoCheckCannotGC nogc;
             if (pn_atom->hasLatin1Chars())
                 DumpName(pn_atom->latin1Chars(nogc), pn_atom->length());
             else
                 DumpName(pn_atom->twoByteChars(nogc), pn_atom->length());
         }
 
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -136,17 +136,16 @@ class UpvarCookie
     F(IMPORT) \
     F(IMPORT_SPEC_LIST) \
     F(IMPORT_SPEC) \
     F(EXPORT) \
     F(EXPORT_FROM) \
     F(EXPORT_SPEC_LIST) \
     F(EXPORT_SPEC) \
     F(EXPORT_BATCH_SPEC) \
-    F(SEQ) \
     F(FORIN) \
     F(FOROF) \
     F(FORHEAD) \
     F(FRESHENBLOCK) \
     F(ARGSBODY) \
     F(SPREAD) \
     F(MUTATEPROTO) \
     F(CLASS) \
@@ -235,22 +234,31 @@ enum ParseNodeKind
  *                            object containing arg and var properties.  We
  *                            create the function object at parse (not emit)
  *                            time to specialize arg and var bytecodes early.
  *                          pn_body: PNK_ARGSBODY, ordinarily;
  *                            PNK_LEXICALSCOPE for implicit function in genexpr
  *                          pn_cookie: static level and var index for function
  *                          pn_dflags: PND_* definition/use flags (see below)
  *                          pn_blockid: block id number
- * PNK_ARGSBODY list        list of formal parameters followed by:
+ * PNK_ARGSBODY list        list of formal parameters with
+ *                              PNK_NAME node with non-empty name for
+ *                                SingleNameBinding without Initializer
+ *                              PNK_ASSIGN node for SingleNameBinding with
+ *                                Initializer
+ *                              PNK_NAME node with empty name for destructuring
+ *                                pn_expr: PNK_ARRAY, PNK_OBJECT, or PNK_ASSIGN
+ *                                  PNK_ARRAY or PNK_OBJECT for BindingPattern
+ *                                    without Initializer
+ *                                  PNK_ASSIGN for BindingPattern with
+ *                                    Initializer
+ *                          followed by:
  *                              PNK_STATEMENTLIST node for function body
  *                                statements,
- *                              PNK_RETURN for expression closure, or
- *                              PNK_SEQ for expression closure with
- *                                destructured formal parameters
+ *                              PNK_RETURN for expression closure
  *                          pn_count: 1 + number of formal parameters
  *                          pn_tree: PNK_ARGSBODY or PNK_STATEMENTLIST node
  * PNK_SPREAD   unary       pn_kid: expression being spread
  *
  * <Statements>
  * PNK_STATEMENTLIST list   pn_head: list of pn_count statements
  * PNK_IF       ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else or null.
  *                            In body of a comprehension or desugared generator
@@ -684,35 +692,34 @@ class ParseNode
 #define PND_CONST               0x02    /* const binding (orthogonal to let) */
 #define PND_ASSIGNED            0x04    /* set if ever LHS of assignment */
 #define PND_PLACEHOLDER         0x08    /* placeholder definition for lexdep */
 #define PND_BOUND               0x10    /* bound to a stack or global slot */
 #define PND_DEOPTIMIZED         0x20    /* former pn_used name node, pn_lexdef
                                            still valid, but this use no longer
                                            optimizable via an upvar opcode */
 #define PND_CLOSED              0x40    /* variable is closed over */
-#define PND_DEFAULT             0x80    /* definition is an arg with a default */
+// 0x80 is available
 #define PND_IMPLICITARGUMENTS  0x100    /* the definition is a placeholder for
                                            'arguments' that has been converted
                                            into a definition after the function
                                            body has been parsed. */
 #define PND_EMITTEDFUNCTION    0x200    /* hoisted function that was emitted */
 
     static_assert(PND_EMITTEDFUNCTION < (1 << NumDefinitionFlagBits), "Not enough bits");
 
 /* Flags to propagate from uses to definition. */
 #define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
 
 /* PN_LIST pn_xflags bits. */
 #define PNX_POPVAR      0x01            /* PNK_VAR or PNK_CONST last result
                                            needs popping */
 #define PNX_FUNCDEFS    0x02            /* contains top-level function statements */
 #define PNX_SETCALL     0x04            /* call expression in lvalue context */
-#define PNX_DESTRUCT    0x08            /* code evaluating destructuring
-                                           arguments occurs before function body */
+/* 0x08 is available */
 #define PNX_ARRAYHOLESPREAD 0x10        /* one or more of
                                            1. array initialiser has holes
                                            2. array initializer has spread node */
 #define PNX_NONCONST    0x20            /* initialiser has non-constants */
 
     static_assert(PNX_NONCONST < (1 << NumListFlagBits), "Not enough bits");
 
     unsigned frameLevel() const {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1569,17 +1569,17 @@ Parser<ParseHandler>::bindDestructuringA
         return false;
 
     return pc->define(parser->tokenStream, name, data->pn, Definition::VAR);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                                        Node* listp, Node funcpn, bool* hasRest)
+                                        Node funcpn, bool* hasRest)
 {
     FunctionBox* funbox = pc->sc->asFunctionBox();
 
     *hasRest = false;
 
     bool parenFreeArrow = false;
     if (kind == Arrow) {
         TokenKind tt;
@@ -1616,17 +1616,16 @@ Parser<ParseHandler>::functionArguments(
         if (!tokenStream.matchToken(&matched, TOK_RP))
             return false;
         if (!matched)
             hasArguments = true;
     }
     if (hasArguments) {
         bool hasDefaults = false;
         Node duplicatedArg = null();
-        Node list = null();
         bool disallowDuplicateArgs = kind == Arrow || kind == Method || kind == ClassConstructor;
 
         if (kind == Getter) {
             report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
             return false;
         }
 
         while (true) {
@@ -1645,62 +1644,47 @@ Parser<ParseHandler>::functionArguments(
               {
                 /* See comment below in the TOK_NAME case. */
                 disallowDuplicateArgs = true;
                 if (duplicatedArg) {
                     report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
                     return false;
                 }
 
-                if (hasDefaults) {
-                    report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
-                    return false;
-                }
-
                 funbox->hasDestructuringArgs = true;
 
                 /*
                  * A destructuring formal parameter turns into one or more
                  * local variables initialized from properties of a single
                  * anonymous positional parameter, so here we must tweak our
                  * binder and its data.
                  */
                 BindData<ParseHandler> data(context);
                 data.pn = ParseHandler::null();
                 data.op = JSOP_DEFVAR;
                 data.binder = bindDestructuringArg;
-                Node lhs = destructuringExprWithoutYield(yieldHandling, &data, tt,
-                                                         JSMSG_YIELD_IN_DEFAULT);
-                if (!lhs)
+                Node destruct = destructuringExprWithoutYield(yieldHandling, &data, tt,
+                                                              JSMSG_YIELD_IN_DEFAULT);
+                if (!destruct)
                     return false;
 
                 /*
-                 * Synthesize a destructuring assignment from the single
-                 * anonymous positional parameter into the destructuring
-                 * left-hand-side expression and accumulate it in list.
+                 * Make a single anonymous positional parameter, and store
+                 * destructuring expression into the node.
                  */
                 HandlePropertyName name = context->names().empty;
-                Node rhs = newName(name);
-                if (!rhs)
-                    return false;
-
-                if (!pc->define(tokenStream, name, rhs, Definition::ARG))
+                Node arg = newName(name);
+                if (!arg)
                     return false;
 
-                Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs);
-                if (!item)
+                handler.addFunctionArgument(funcpn, arg);
+                if (!pc->define(tokenStream, name, arg, Definition::ARG))
                     return false;
-                if (list) {
-                    handler.addList(list, item);
-                } else {
-                    list = handler.newDeclarationList(PNK_VAR, item);
-                    if (!list)
-                        return false;
-                    *listp = list;
-                }
+
+                handler.setLastFunctionArgumentDestructuring(funcpn, destruct);
                 break;
               }
 
               case TOK_YIELD:
                 if (!checkYieldNameValidity())
                     return false;
                 MOZ_ASSERT(yieldHandling == YieldIsName);
                 goto TOK_NAME;
@@ -1738,61 +1722,60 @@ Parser<ParseHandler>::functionArguments(
               case TOK_NAME:
               {
                 if (parenFreeArrow)
                     funbox->setStart(tokenStream);
 
                 RootedPropertyName name(context, tokenStream.currentName());
                 if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
                     return false;
-
-                bool matched;
-                if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
-                    return false;
-                if (matched) {
-                    // A default argument without parentheses would look like:
-                    // a = expr => body, but both operators are right-associative, so
-                    // that would have been parsed as a = (expr => body) instead.
-                    // Therefore it's impossible to get here with parenFreeArrow.
-                    MOZ_ASSERT(!parenFreeArrow);
-
-                    if (*hasRest) {
-                        report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
-                        return false;
-                    }
-                    disallowDuplicateArgs = true;
-                    if (duplicatedArg) {
-                        report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
-                        return false;
-                    }
-                    if (!hasDefaults) {
-                        hasDefaults = true;
-
-                        // The Function.length property is the number of formals
-                        // before the first default argument.
-                        funbox->length = pc->numArgs() - 1;
-                    }
-                    Node def_expr = assignExprWithoutYield(yieldHandling, JSMSG_YIELD_IN_DEFAULT);
-                    if (!def_expr)
-                        return false;
-                    handler.setLastFunctionArgumentDefault(funcpn, def_expr);
-                }
-
                 break;
               }
 
               default:
                 report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
                 return false;
             }
 
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
+                return false;
+            if (matched) {
+                // A default argument without parentheses would look like:
+                // a = expr => body, but both operators are right-associative, so
+                // that would have been parsed as a = (expr => body) instead.
+                // Therefore it's impossible to get here with parenFreeArrow.
+                MOZ_ASSERT(!parenFreeArrow);
+
+                if (*hasRest) {
+                    report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
+                    return false;
+                }
+                disallowDuplicateArgs = true;
+                if (duplicatedArg) {
+                    report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
+                    return false;
+                }
+                if (!hasDefaults) {
+                    hasDefaults = true;
+
+                    // The Function.length property is the number of formals
+                    // before the first default argument.
+                    funbox->length = pc->numArgs() - 1;
+                }
+                Node def_expr = assignExprWithoutYield(yieldHandling, JSMSG_YIELD_IN_DEFAULT);
+                if (!def_expr)
+                    return false;
+                if (!handler.setLastFunctionArgumentDefault(funcpn, def_expr))
+                    return false;
+            }
+
             if (parenFreeArrow || kind == Setter)
                 break;
 
-            bool matched;
             if (!tokenStream.matchToken(&matched, TOK_COMMA))
                 return false;
             if (!matched)
                 break;
         }
 
         if (!parenFreeArrow) {
             TokenKind tt;
@@ -2233,57 +2216,31 @@ Parser<ParseHandler>::functionDef(InHand
     }
 
     return pn;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::finishFunctionDefinition(ParseNode* pn, FunctionBox* funbox,
-                                                   ParseNode* prelude, ParseNode* body)
+                                                   ParseNode* body)
 {
     pn->pn_pos.end = pos().end;
 
-    /*
-     * If there were destructuring formal parameters, prepend the initializing
-     * comma expression that we synthesized to body. If the body is a return
-     * node, we must make a special PNK_SEQ node, to prepend the destructuring
-     * code without bracing the decompilation of the function body.
-     */
-    if (prelude) {
-        if (!body->isArity(PN_LIST)) {
-            ParseNode* block;
-
-            block = handler.newList(PNK_SEQ, body);
-            if (!block)
-                return false;
-            body = block;
-        }
-
-        ParseNode* item = handler.new_<UnaryNode>(PNK_SEMI, JSOP_NOP,
-                                                  TokenPos(body->pn_pos.begin, body->pn_pos.begin),
-                                                  prelude);
-        if (!item)
-            return false;
-
-        body->prepend(item);
-        body->pn_xflags |= PNX_DESTRUCT;
-    }
-
     MOZ_ASSERT(pn->pn_funbox == funbox);
     MOZ_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY));
     pn->pn_body->append(body);
 
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox* funbox,
-                                                     Node prelude, Node body)
+                                                     Node body)
 {
     // The LazyScript for a lazily parsed function needs to be constructed
     // while its ParseContext and associated lexdeps and inner functions are
     // still available.
 
     if (funbox->inWith)
         return abortIfSyntaxParser();
 
@@ -2541,19 +2498,18 @@ bool
 Parser<ParseHandler>::functionArgsAndBodyGeneric(InHandling inHandling,
                                                  YieldHandling yieldHandling, Node pn,
                                                  HandleFunction fun, FunctionSyntaxKind kind)
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
-    Node prelude = null();
     bool hasRest;
-    if (!functionArguments(yieldHandling, kind, &prelude, pn, &hasRest))
+    if (!functionArguments(yieldHandling, kind, pn, &hasRest))
         return false;
 
     FunctionBox* funbox = pc->sc->asFunctionBox();
 
     fun->setArgCount(pc->numArgs());
     if (hasRest)
         fun->setHasRest();
 
@@ -2619,17 +2575,17 @@ Parser<ParseHandler>::functionArgsAndBod
 #endif
         if (tokenStream.hadError())
             return false;
         funbox->bufEnd = pos().end;
         if (kind == Statement && !MatchOrInsertSemicolon(tokenStream))
             return false;
     }
 
-    return finishFunctionDefinition(pn, funbox, prelude, body);
+    return finishFunctionDefinition(pn, funbox, body);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::checkYieldNameValidity()
 {
     // In star generators and in JS >= 1.7, yield is a keyword.  Otherwise in
     // strict mode, yield is a future reserved word.
@@ -4790,20 +4746,16 @@ Parser<FullParseHandler>::forStatement(Y
     //
     //   * forLetImpliedBlock is the node for the implicit block scope.
     //   * forLetDecl is the node for the decl 'let/const <pattern>'.
     //
     // Otherwise both are null.
     ParseNode* forLetImpliedBlock = nullptr;
     ParseNode* forLetDecl = nullptr;
 
-    // If non-null, the node for the decl 'var v = expr1' in the weirdo form
-    // 'for (var v = expr1 in expr2) stmt'.
-    ParseNode* hoistedVar = nullptr;
-
     /*
      * We can be sure that it's a for/in loop if there's still an 'in'
      * keyword here, even if JavaScript recognizes 'in' as an operator,
      * as we've excluded 'in' from being parsed in RelExpr by setting
      * pc->parsingForInit.
      */
     StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */
     ParseNode* pn2;      /* forHead->pn_kid2 */
@@ -5006,24 +4958,16 @@ Parser<FullParseHandler>::forStatement(Y
     if (blockObj)
         PopStatementPC(tokenStream, pc);
     PopStatementPC(tokenStream, pc);
 
     ParseNode* forLoop = handler.newForStatement(begin, forHead, body, iflags);
     if (!forLoop)
         return null();
 
-    if (hoistedVar) {
-        ParseNode* pnseq = handler.newList(PNK_SEQ, hoistedVar);
-        if (!pnseq)
-            return null();
-        pnseq->pn_pos = forLoop->pn_pos;
-        pnseq->append(forLoop);
-        return pnseq;
-    }
     if (forLetImpliedBlock) {
         forLetImpliedBlock->pn_expr = forLoop;
         forLetImpliedBlock->pn_pos = forLoop->pn_pos;
         return handler.newLetBlock(forLetDecl, forLetImpliedBlock, forLoop->pn_pos);
     }
     return forLoop;
 }
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -593,17 +593,17 @@ class Parser : private JS::AutoGCRooter,
 
     bool methodDefinition(YieldHandling yieldHandling, PropListType listType, Node propList,
                           Node propname, FunctionSyntaxKind kind, GeneratorKind generatorKind,
                           bool isStatic, JSOp Op);
 
     /*
      * Additional JS parsers.
      */
-    bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind, Node* list,
+    bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
                            Node funcpn, bool* hasRest);
 
     Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name,
                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
                      InvokedPrediction invoked = PredictUninvoked);
     bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun,
                              FunctionSyntaxKind kind, GeneratorKind generatorKind,
                              Directives inheritedDirectives, Directives* newDirectives);
@@ -659,17 +659,17 @@ class Parser : private JS::AutoGCRooter,
 
     bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
     bool matchInOrOf(bool* isForInp, bool* isForOfp);
 
     bool checkFunctionArguments();
     bool makeDefIntoUse(Definition* dn, Node pn, JSAtom* atom);
     bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind,
                                  bool* pbodyProcessed);
-    bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node prelude, Node body);
+    bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
 
     bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach,
                                 ParseNodeKind headKind);
     bool checkForHeadConstInitializers(Node pn1);
     bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
     bool checkStrictAssignment(Node lhs);
     bool checkStrictBinding(PropertyName* name, Node pn);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -236,17 +236,18 @@ class SyntaxParseHandler
         return NodeGetProp;
     }
 
     Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeLValue; }
 
     bool addCatchBlock(Node catchList, Node letBlock,
                        Node catchName, Node catchGuard, Node catchBody) { return true; }
 
-    void setLastFunctionArgumentDefault(Node funcpn, Node pn) {}
+    bool setLastFunctionArgumentDefault(Node funcpn, Node pn) { return true; }
+    void setLastFunctionArgumentDestructuring(Node funcpn, Node pn) {}
     Node newFunctionDefinition() { return NodeHoistableDeclaration; }
     void setFunctionBody(Node pn, Node kid) {}
     void setFunctionBox(Node pn, FunctionBox* funbox) {}
     void addFunctionArgument(Node pn, Node argpn) {}
 
     Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) {
         return NodeGeneric;
     }
--- a/js/src/jit-test/tests/arguments/defaults-bound-to-function.js
+++ b/js/src/jit-test/tests/arguments/defaults-bound-to-function.js
@@ -27,8 +27,16 @@ function k(a=(b=42), b=8) {
     function a() { return 43; }
 }
 assertEq(k(), 42);
 function l(a=8, b=a) {
     return b;
     function a() { return 42; }
 }
 assertEq(l(), 8);
+function m([a, b]=[1, 2], c=a) {
+  function a() { return 42; }
+  assertEq(typeof a, "function");
+  assertEq(a(), 42);
+  assertEq(b, 2);
+  assertEq(c, 1);
+}
+m();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/defaults-destructuring-array.js
@@ -0,0 +1,17 @@
+function f1(a, bIs, [b]=[3]) {
+    assertEq(a, 1);
+    assertEq(b, bIs);
+}
+assertEq(f1.length, 2);
+f1(1, 3);
+f1(1, 42, [42]);
+f1(1, 3, undefined);
+
+function f2(a, bIs, [b]=[]) {
+    assertEq(a, 1);
+    assertEq(b, bIs);
+}
+assertEq(f2.length, 2);
+f2(1, undefined);
+f2(1, 42, [42]);
+f2(1, undefined, undefined);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/defaults-destructuring-expression-closure.js
@@ -0,0 +1,19 @@
+function f1(a, bIs, cIs, dIs, {b}={b: 3}, c=4, [d]=[5]) (
+  assertEq(a, 1),
+  assertEq(b, bIs),
+  assertEq(c, cIs),
+  assertEq(d, dIs)
+);
+assertEq(f1.length, 4);
+f1(1, 3, 4, 5);
+f1(1, 42, 43, 44, {b: 42}, 43, [44]);
+
+let f2 = (a, bIs, cIs, dIs, {b}={b: 3}, c=4, [d]=[5]) => (
+  assertEq(a, 1),
+  assertEq(b, bIs),
+  assertEq(c, cIs),
+  assertEq(d, dIs)
+);
+assertEq(f2.length, 4);
+f2(1, 3, 4, 5);
+f2(1, 42, 43, 44, {b: 42}, 43, [44]);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/defaults-destructuring-function-expression.js
@@ -0,0 +1,9 @@
+let f = function(a, bIs, cIs, dIs, {b}={b: 3}, c=4, [d]=[5]) {
+  assertEq(a, 1);
+  assertEq(b, bIs);
+  assertEq(c, cIs);
+  assertEq(d, dIs);
+};
+assertEq(f.length, 4);
+f(1, 3, 4, 5);
+f(1, 42, 43, 44, {b: 42}, 43, [44]);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/defaults-destructuring-mixed-default-value.js
@@ -0,0 +1,55 @@
+function f1(a=1,
+            [b, c=(assertEq(a, 2), a=3, 42)]=[(assertEq(a, 1), a=2, 43)],
+            {d, e:e=(assertEq(a, 4), a=5, 44)}={d: (assertEq(a, 3), a=4, 45)},
+            f=(assertEq(a, 5), a=6, 46)) {
+  assertEq(a, 6);
+  assertEq(b, 43);
+  assertEq(c, 42);
+  assertEq(d, 45);
+  assertEq(e, 44);
+  assertEq(f, 46);
+}
+assertEq(f1.length, 0);
+f1();
+
+function f2(a=1,
+            [b, c=assertEq(false)]=[(assertEq(a, 1), a=2, 42), (assertEq(a, 2), a=3, 43)],
+            {d, e:e=assertEq(false)}={d: (assertEq(a, 3), a=4, 44), e: (assertEq(a, 4), a=5, 45)},
+            f=(assertEq(a, 5), a=6, 46)) {
+  assertEq(a, 6);
+  assertEq(b, 42);
+  assertEq(c, 43);
+  assertEq(d, 44);
+  assertEq(e, 45);
+  assertEq(f, 46);
+}
+assertEq(f2.length, 0);
+f2();
+
+function f3(a=1,
+            [b, c=(assertEq(a, 1), a=2, 42)]=[assertEq(false)],
+            {d, e:e=(assertEq(a, 2), a=3, 43)}={d: assertEq(false)},
+            f=(assertEq(a, 3), a=4, 44)) {
+  assertEq(a, 4);
+  assertEq(b, 8);
+  assertEq(c, 42);
+  assertEq(d, 9);
+  assertEq(e, 43);
+  assertEq(f, 44);
+}
+assertEq(f3.length, 0);
+f3(undefined, [8], {d: 9});
+
+function f4(a=1,
+            [b, c=assertEq(false)]=[assertEq(false), assertEq(false)],
+            {d, e:e=assertEq(false)}={d: assertEq(false), e: assertEq(false)},
+            f=(assertEq(a, 1), a=2, 42)) {
+  assertEq(a, 2);
+  assertEq(b, 8);
+  assertEq(c, 9);
+  assertEq(d, 10);
+  assertEq(e, 11);
+  assertEq(f, 42);
+}
+assertEq(f4.length, 0);
+f4(undefined, [8, 9], {d: 10, e: 11});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/defaults-destructuring-mixed.js
@@ -0,0 +1,29 @@
+function f1(a, bIs, cIs, dIs, b=3, {c}={c: 4}, [d]=[5]) {
+    assertEq(a, 1);
+    assertEq(b, bIs);
+    assertEq(c, cIs);
+    assertEq(d, dIs);
+}
+assertEq(f1.length, 4);
+f1(1, 3, 4, 5);
+f1(1, 42, 4, 5, 42);
+f1(1, 42, 43, 5, 42, {c: 43});
+f1(1, 42, 43, 44, 42, {c: 43}, [44]);
+f1(1, 3, 4, 5, undefined);
+f1(1, 42, 4, 5, 42, undefined);
+f1(1, 3, 42, 5, undefined, {c: 42});
+f1(1, 3, 4, 42, undefined, undefined, [42]);
+
+function f2(a, bIs, cIs, dIs, eIs, {b}={b: 3}, [c]=[b], d=c, {ee: e}={ee: d}) {
+  assertEq(a, 1);
+  assertEq(b, bIs);
+  assertEq(c, cIs);
+  assertEq(d, dIs);
+  assertEq(e, eIs);
+}
+assertEq(f2.length, 5);
+f2(1, 3, 3, 3, 3);
+f2(1, 42, 42, 42, 42, {b: 42});
+f2(1, 42, 43, 43, 43, {b: 42}, [43]);
+f2(1, 42, 43, 44, 44, {b: 42}, [43], 44);
+f2(1, 42, 43, 44, 45, {b: 42}, [43], 44, {ee: 45});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/defaults-destructuring-object.js
@@ -0,0 +1,27 @@
+function f1(a, bIs, cIs, {b}={b: 3}, {cc: c}={cc: 4}) {
+    assertEq(a, 1);
+    assertEq(b, bIs);
+    assertEq(c, cIs);
+}
+assertEq(f1.length, 3);
+f1(1, 3, 4);
+f1(1, 42, 4, {b: 42});
+f1(1, 42, 4, {b: 42}, undefined);
+f1(1, 42, 43, {b: 42}, {cc: 43});
+f1(1, 3, 4, undefined);
+f1(1, 3, 4, undefined, undefined);
+f1(1, 3, 43, undefined, {cc: 43});
+
+function f2(a, bIs, cIs, {b}={}, {cc: c}={}) {
+    assertEq(a, 1);
+    assertEq(b, bIs);
+    assertEq(c, cIs);
+}
+assertEq(f2.length, 3);
+f2(1, undefined, undefined);
+f2(1, 42, undefined, {b: 42});
+f2(1, 42, undefined, {b: 42}, undefined);
+f2(1, 42, 43, {b: 42}, {cc: 43});
+f2(1, undefined, undefined, undefined);
+f2(1, undefined, undefined, undefined, undefined);
+f2(1, undefined, 43, undefined, {cc: 43});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/defaults-destructuring-with-rest.js
@@ -0,0 +1,26 @@
+load(libdir + "asserts.js");
+load(libdir + "eqArrayHelper.js");
+
+function f1(a, bIs, [b]=[3], ...rest) {
+    assertEq(a, 1);
+    assertEq(bIs, b);
+    assertEqArray(rest, []);
+}
+assertEq(f1.length, 2);
+f1(1, 3);
+f1(1, 42, [42]);
+
+function f2([a]=[rest], ...rest) {
+    assertEq(a, undefined);
+}
+f2();
+
+function f3([a]=[rest], ...rest) {
+    assertEq(a, 1);
+    assertEqArray(rest, [2, 3, 4]);
+}
+f3([1], 2, 3, 4);
+
+function f4([a]=rest, ...rest) {
+}
+assertThrowsInstanceOf(f4, TypeError);
--- a/js/src/jit-test/tests/arguments/defaults-invalid-syntax.js
+++ b/js/src/jit-test/tests/arguments/defaults-invalid-syntax.js
@@ -1,20 +1,14 @@
 load(libdir + "asserts.js");
 
 assertThrowsInstanceOf(function () {
     eval("function f(...rest=23) {}");
 }, SyntaxError);
 assertThrowsInstanceOf(function () {
-    eval("function f([a]=4) {}");
-}, SyntaxError);
-assertThrowsInstanceOf(function () {
-    eval("function f(a=4, [b]) {}");
-}, SyntaxError);
-assertThrowsInstanceOf(function () {
     eval("function f(a=yield 24) {}");
 }, SyntaxError);
 assertThrowsInstanceOf(function () {
     eval("function f(a={a : 19 + (yield 24).prop}) {}");
 }, SyntaxError);
 assertThrowsInstanceOf(function () {
     eval("function f(a=1,a=1) {}");
 }, SyntaxError);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/destructuring-after-defaults.js
@@ -0,0 +1,28 @@
+load(libdir + "asserts.js");
+
+function f1(a, bIs, cIs, dIs, b=1, [c], {d}) {
+    assertEq(a, 1);
+    assertEq(b, bIs);
+    assertEq(c, cIs);
+    assertEq(d, dIs);
+}
+assertEq(f1.length, 4);
+f1(1, 1, 42, 43, undefined, [42], {d: 43});
+f1(1, 42, 43, 44, 42, [43], {d: 44});
+assertThrowsInstanceOf(function () {
+  f1(1, 1, 1, 1);
+}, TypeError);
+
+function f2(a=(assertEq(a, undefined), assertEq(b, undefined),
+               assertEq(c, undefined), assertEq(d, undefined), 1),
+            [b],
+            c=(assertEq(a, 1), assertEq(b, 42),
+               assertEq(c, undefined), assertEq(d, undefined), 2),
+            {d}) {
+  assertEq(a, 1);
+  assertEq(b, 42);
+  assertEq(c, 2);
+  assertEq(d, 43);
+}
+assertEq(f2.length, 0);
+f2(undefined, [42], undefined, {d: 43});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/destructuring-with-rest.js
@@ -0,0 +1,21 @@
+load(libdir + "eqArrayHelper.js");
+
+function f1(a, bIs, [b], ...rest) {
+    assertEq(a, 1);
+    assertEq(bIs, b);
+    assertEqArray(rest, []);
+}
+assertEq(f1.length, 3);
+f1(1, 3, [3]);
+f1(1, 42, [42]);
+
+function f2([a], ...rest) {
+    assertEq(a, undefined);
+}
+f2([]);
+
+function f3([a], ...rest) {
+    assertEq(a, 1);
+    assertEqArray(rest, [2, 3, 4]);
+}
+f3([1], 2, 3, 4);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2179,16 +2179,22 @@ CodeGenerator::visitNurseryObject(LNurse
     // Store a dummy JSObject pointer. We will fix it up on the main thread,
     // in JitCode::fixupNurseryObjects. The low bit is set to distinguish
     // it from a real JSObject pointer.
     JSObject* ptr = reinterpret_cast<JSObject*>((uintptr_t(index) << 1) | 1);
     masm.movePtr(ImmGCPtr(IonNurseryPtr(ptr)), output);
 }
 
 void
+CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir)
+{
+    // No-op.
+}
+
+void
 CodeGenerator::visitSlots(LSlots* lir)
 {
     Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots());
     masm.loadPtr(slots, ToRegister(lir->output()));
 }
 
 void
 CodeGenerator::visitLoadSlotT(LLoadSlotT* lir)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -103,16 +103,17 @@ class CodeGenerator : public CodeGenerat
     void visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool);
     void visitRegExpReplace(LRegExpReplace* lir);
     void visitStringReplace(LStringReplace* lir);
     void visitLambda(LLambda* lir);
     void visitLambdaArrow(LLambdaArrow* lir);
     void visitLambdaForSingleton(LLambdaForSingleton* lir);
     void visitPointer(LPointer* lir);
     void visitNurseryObject(LNurseryObject* lir);
+    void visitKeepAliveObject(LKeepAliveObject* lir);
     void visitSlots(LSlots* lir);
     void visitLoadSlotT(LLoadSlotT* lir);
     void visitLoadSlotV(LLoadSlotV* lir);
     void visitStoreSlotT(LStoreSlotT* lir);
     void visitStoreSlotV(LStoreSlotV* lir);
     void visitElements(LElements* lir);
     void visitConvertElementsToDoubles(LConvertElementsToDoubles* lir);
     void visitMaybeToDoubleElement(LMaybeToDoubleElement* lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1496,16 +1496,23 @@ OptimizeMIR(MIRGenerator* mir)
         // code motion after this pass could incorrectly move a load or store
         // before its bounds check.
         if (!EliminateRedundantChecks(graph))
             return false;
         IonSpewPass("Bounds Check Elimination");
         AssertGraphCoherency(graph);
     }
 
+    if (!mir->compilingAsmJS()) {
+        AutoTraceLog log(logger, TraceLogger_AddKeepAliveInstructions);
+        AddKeepAliveInstructions(graph);
+        IonSpewPass("Add KeepAlive Instructions");
+        AssertGraphCoherency(graph);
+    }
+
     return true;
 }
 
 LIRGraph*
 GenerateLIR(MIRGenerator* mir)
 {
     MIRGraph& graph = mir->graph();
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2724,16 +2724,121 @@ jit::EliminateRedundantChecks(MIRGraph& 
     for (size_t i = 0; i < eliminateList.length(); i++) {
         MDefinition* def = eliminateList[i];
         def->block()->discardDef(def);
     }
 
     return true;
 }
 
+static bool
+NeedsKeepAlive(MInstruction* slotsOrElements, MInstruction* use)
+{
+    MOZ_ASSERT(slotsOrElements->type() == MIRType_Elements ||
+               slotsOrElements->type() == MIRType_Slots);
+
+    if (slotsOrElements->block() != use->block())
+        return true;
+
+    MBasicBlock* block = use->block();
+    MInstructionIterator iter(block->begin(slotsOrElements));
+    MOZ_ASSERT(*iter == slotsOrElements);
+    ++iter;
+
+    while (true) {
+        if (*iter == use)
+            return false;
+
+        switch (iter->op()) {
+          case MDefinition::Op_Nop:
+          case MDefinition::Op_Constant:
+          case MDefinition::Op_KeepAliveObject:
+          case MDefinition::Op_Unbox:
+          case MDefinition::Op_LoadSlot:
+          case MDefinition::Op_StoreSlot:
+          case MDefinition::Op_LoadFixedSlot:
+          case MDefinition::Op_StoreFixedSlot:
+          case MDefinition::Op_LoadElement:
+          case MDefinition::Op_StoreElement:
+          case MDefinition::Op_InitializedLength:
+          case MDefinition::Op_ArrayLength:
+          case MDefinition::Op_BoundsCheck:
+            iter++;
+            break;
+          default:
+            return true;
+        }
+    }
+
+    MOZ_CRASH("Unreachable");
+}
+
+void
+jit::AddKeepAliveInstructions(MIRGraph& graph)
+{
+    for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) {
+        MBasicBlock* block = *i;
+
+        for (MInstructionIterator insIter(block->begin()); insIter != block->end(); insIter++) {
+            MInstruction* ins = *insIter;
+            if (ins->type() != MIRType_Elements && ins->type() != MIRType_Slots)
+                continue;
+
+            MDefinition* ownerObject;
+            switch (ins->op()) {
+              case MDefinition::Op_ConstantElements:
+                continue;
+              case MDefinition::Op_ConvertElementsToDoubles:
+                // EliminateRedundantChecks should have replaced all uses.
+                MOZ_ASSERT(!ins->hasUses());
+                continue;
+              case MDefinition::Op_Elements:
+              case MDefinition::Op_TypedArrayElements:
+              case MDefinition::Op_TypedObjectElements:
+                MOZ_ASSERT(ins->numOperands() == 1);
+                ownerObject = ins->getOperand(0);
+                break;
+              case MDefinition::Op_Slots:
+                ownerObject = ins->toSlots()->object();
+                break;
+              default:
+                MOZ_CRASH("Unexpected op");
+            }
+
+            MOZ_ASSERT(ownerObject->type() == MIRType_Object);
+
+            for (MUseDefIterator uses(ins); uses; uses++) {
+                MInstruction* use = uses.def()->toInstruction();
+
+                if (use->isStoreElementHole()) {
+                    // StoreElementHole has an explicit object operand. If GVN
+                    // is disabled, we can get different unbox instructions with
+                    // the same object as input, so we check for that case.
+                    MOZ_ASSERT_IF(!use->toStoreElementHole()->object()->isUnbox() && !ownerObject->isUnbox(),
+                                  use->toStoreElementHole()->object() == ownerObject);
+                    continue;
+                }
+
+                if (use->isInArray()) {
+                    // See StoreElementHole case above.
+                    MOZ_ASSERT_IF(!use->toInArray()->object()->isUnbox() && !ownerObject->isUnbox(),
+                                  use->toInArray()->object() == ownerObject);
+                    continue;
+                }
+
+                if (!NeedsKeepAlive(ins, use))
+                    continue;
+
+                MKeepAliveObject* keepAlive = MKeepAliveObject::New(graph.alloc(), ownerObject);
+                use->block()->insertAfter(use, keepAlive);
+            }
+        }
+    }
+}
+
 bool
 LinearSum::multiply(int32_t scale)
 {
     for (size_t i = 0; i < terms_.length(); i++) {
         if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale))
             return false;
     }
     return SafeMul(scale, constant_, &constant_);
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -78,16 +78,19 @@ void
 AssertGraphCoherency(MIRGraph& graph);
 
 void
 AssertExtendedGraphCoherency(MIRGraph& graph);
 
 bool
 EliminateRedundantChecks(MIRGraph& graph);
 
+void
+AddKeepAliveInstructions(MIRGraph& graph);
+
 class MDefinition;
 
 // Simple linear sum of the form 'n' or 'x + n'.
 struct SimpleLinearSum
 {
     MDefinition* term;
     int32_t constant;
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -4041,16 +4041,30 @@ class LLambdaArrow : public LInstruction
     const LDefinition* temp() {
         return getTemp(0);
     }
     const MLambdaArrow* mir() const {
         return mir_->toLambdaArrow();
     }
 };
 
+class LKeepAliveObject : public LInstructionHelper<0, 1, 0>
+{
+  public:
+    LIR_HEADER(KeepAliveObject)
+
+    explicit LKeepAliveObject(const LAllocation& object) {
+        setOperand(0, object);
+    }
+
+    const LAllocation* object() {
+        return getOperand(0);
+    }
+};
+
 // Load the "slots" member out of a JSObject.
 //   Input: JSObject pointer
 //   Output: slots pointer
 class LSlots : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(Slots)
 
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -186,16 +186,17 @@
     _(RegExpExec)                   \
     _(RegExpTest)                   \
     _(RegExpReplace)                \
     _(StringReplace)                \
     _(Substr)                       \
     _(Lambda)                       \
     _(LambdaArrow)                  \
     _(LambdaForSingleton)           \
+    _(KeepAliveObject)              \
     _(Slots)                        \
     _(Elements)                     \
     _(ConvertElementsToDoubles)     \
     _(MaybeToDoubleElement)         \
     _(MaybeCopyElementsForWrite)    \
     _(LoadSlotV)                    \
     _(LoadSlotT)                    \
     _(StoreSlotV)                   \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2167,16 +2167,25 @@ LIRGenerator::visitLambdaArrow(MLambdaAr
 
     LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()), temp());
     useBox(lir, LLambdaArrow::ThisValue, ins->thisDef());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitKeepAliveObject(MKeepAliveObject* ins)
+{
+    MDefinition* obj = ins->object();
+    MOZ_ASSERT(obj->type() == MIRType_Object);
+
+    add(new(alloc()) LKeepAliveObject(useKeepalive(obj)), ins);
+}
+
+void
 LIRGenerator::visitSlots(MSlots* ins)
 {
     define(new(alloc()) LSlots(useRegisterAtStart(ins->object())), ins);
 }
 
 void
 LIRGenerator::visitElements(MElements* ins)
 {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -157,16 +157,17 @@ class LIRGenerator : public LIRGenerator
     void visitToObjectOrNull(MToObjectOrNull* convert);
     void visitRegExp(MRegExp* ins);
     void visitRegExpExec(MRegExpExec* ins);
     void visitRegExpTest(MRegExpTest* ins);
     void visitRegExpReplace(MRegExpReplace* ins);
     void visitStringReplace(MStringReplace* ins);
     void visitLambda(MLambda* ins);
     void visitLambdaArrow(MLambdaArrow* ins);
+    void visitKeepAliveObject(MKeepAliveObject* ins);
     void visitSlots(MSlots* ins);
     void visitElements(MElements* ins);
     void visitConstantElements(MConstantElements* ins);
     void visitConvertElementsToDoubles(MConvertElementsToDoubles* ins);
     void visitMaybeToDoubleElement(MMaybeToDoubleElement* ins);
     void visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins);
     void visitLoadSlot(MLoadSlot* ins);
     void visitFunctionEnvironment(MFunctionEnvironment* ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8207,16 +8207,39 @@ class MSetTypedObjectOffset
 
     AliasSet getAliasSet() const override {
         // This affects the result of MTypedObjectElements,
         // which is described as a load of ObjectFields.
         return AliasSet::Store(AliasSet::ObjectFields);
     }
 };
 
+class MKeepAliveObject
+  : public MUnaryInstruction,
+    public SingleObjectPolicy::Data
+{
+    explicit MKeepAliveObject(MDefinition* object)
+      : MUnaryInstruction(object)
+    {
+        setResultType(MIRType_None);
+        setGuard();
+    }
+
+  public:
+    INSTRUCTION_HEADER(KeepAliveObject)
+
+    static MKeepAliveObject* New(TempAllocator& alloc, MDefinition* object) {
+        return new(alloc) MKeepAliveObject(object);
+    }
+
+    MDefinition* object() const {
+        return getOperand(0);
+    }
+};
+
 // Perform !-operation
 class MNot
   : public MUnaryInstruction,
     public TestPolicy::Data
 {
     bool operandMightEmulateUndefined_;
     bool operandIsNeverNaN_;
 
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -137,16 +137,17 @@ namespace jit {
     _(LimitedTruncate)                                                      \
     _(RegExp)                                                               \
     _(RegExpExec)                                                           \
     _(RegExpTest)                                                           \
     _(RegExpReplace)                                                        \
     _(StringReplace)                                                        \
     _(Lambda)                                                               \
     _(LambdaArrow)                                                          \
+    _(KeepAliveObject)                                                      \
     _(Slots)                                                                \
     _(Elements)                                                             \
     _(ConstantElements)                                                     \
     _(ConvertElementsToDoubles)                                             \
     _(MaybeToDoubleElement)                                                 \
     _(MaybeCopyElementsForWrite)                                            \
     _(LoadSlot)                                                             \
     _(StoreSlot)                                                            \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -354,21 +354,27 @@ LAllocation
 LIRGeneratorShared::useStorableAtStart(MDefinition* mir)
 {
     return useRegisterOrConstantAtStart(mir);
 }
 
 #endif
 
 LAllocation
+LIRGeneratorShared::useKeepalive(MDefinition* mir)
+{
+    return use(mir, LUse(LUse::KEEPALIVE));
+}
+
+LAllocation
 LIRGeneratorShared::useKeepaliveOrConstant(MDefinition* mir)
 {
     if (mir->isConstant())
         return LAllocation(mir->toConstant()->vp());
-    return use(mir, LUse(LUse::KEEPALIVE));
+    return useKeepalive(mir);
 }
 
 LUse
 LIRGeneratorShared::useFixed(MDefinition* mir, Register reg)
 {
     return use(mir, LUse(reg));
 }
 
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -98,16 +98,17 @@ class LIRGeneratorShared : public MDefin
     // slots on X86, and only registers on ARM.
     inline LAllocation useAny(MDefinition* mir);
     inline LAllocation useAnyOrConstant(MDefinition* mir);
     // "Storable" is architecture dependend, and will include registers and
     // constants on X86 and only registers on ARM.  This is a generic "things
     // we can expect to write into memory in 1 instruction".
     inline LAllocation useStorable(MDefinition* mir);
     inline LAllocation useStorableAtStart(MDefinition* mir);
+    inline LAllocation useKeepalive(MDefinition* mir);
     inline LAllocation useKeepaliveOrConstant(MDefinition* mir);
     inline LAllocation useRegisterOrConstant(MDefinition* mir);
     inline LAllocation useRegisterOrConstantAtStart(MDefinition* mir);
     inline LAllocation useRegisterOrZeroAtStart(MDefinition* mir);
     inline LAllocation useRegisterOrNonDoubleConstant(MDefinition* mir);
 
     inline LUse useRegisterForTypedLoad(MDefinition* mir, MIRType type);
 
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -1767,17 +1767,17 @@ class ASTSerializer
 
     BinaryOperator binop(ParseNodeKind kind, JSOp op);
     UnaryOperator unop(ParseNodeKind kind, JSOp op);
     AssignmentOperator aop(JSOp op);
 
     bool statements(ParseNode* pn, NodeVector& elts);
     bool expressions(ParseNode* pn, NodeVector& elts);
     bool leftAssociate(ParseNode* pn, MutableHandleValue dst);
-    bool functionArgs(ParseNode* pn, ParseNode* pnargs, ParseNode* pndestruct, ParseNode* pnbody,
+    bool functionArgs(ParseNode* pn, ParseNode* pnargs, ParseNode* pnbody,
                       NodeVector& args, NodeVector& defaults, MutableHandleValue rest);
 
     bool sourceElement(ParseNode* pn, MutableHandleValue dst);
 
     bool declaration(ParseNode* pn, MutableHandleValue dst);
     bool variableDeclaration(ParseNode* pn, bool lexical, MutableHandleValue dst);
     bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
     bool letBlock(ParseNode* pn, MutableHandleValue dst);
@@ -2541,38 +2541,16 @@ ASTSerializer::statement(ParseNode* pn, 
                        ? head->pn_kid1
                        : nullptr,
                        &init) &&
                optExpression(head->pn_kid2, &test) &&
                optExpression(head->pn_kid3, &update) &&
                builder.forStatement(init, test, update, stmt, &pn->pn_pos, dst);
       }
 
-      /* Synthesized by the parser when a for-in loop contains a variable initializer. */
-      case PNK_SEQ:
-      {
-        LOCAL_ASSERT(pn->pn_count == 2);
-
-        ParseNode* prelude = pn->pn_head;
-        ParseNode* loop = prelude->pn_next;
-
-        LOCAL_ASSERT(prelude->isKind(PNK_VAR) && loop->isKind(PNK_FOR));
-
-        RootedValue var(cx);
-        if (!variableDeclaration(prelude, false, &var))
-            return false;
-
-        ParseNode* head = loop->pn_left;
-        MOZ_ASSERT(head->isKind(PNK_FORIN));
-
-        RootedValue stmt(cx);
-
-        return statement(loop->pn_right, &stmt) && forIn(loop, head, var, stmt, dst);
-      }
-
       case PNK_BREAK:
       case PNK_CONTINUE:
       {
         RootedValue label(cx);
         RootedAtom pnAtom(cx, pn->pn_atom);
         return optIdentifier(pnAtom, nullptr, &label) &&
                (pn->isKind(PNK_BREAK)
                 ? builder.breakStatement(label, &pn->pn_pos, dst)
@@ -3429,131 +3407,90 @@ ASTSerializer::functionArgsAndBody(Parse
     if (pn->isKind(PNK_ARGSBODY)) {
         pnargs = pn;
         pnbody = pn->last();
     } else {
         pnargs = nullptr;
         pnbody = pn;
     }
 
-    ParseNode* pndestruct;
-
-    /* Extract the destructuring assignments. */
-    if (pnbody->isArity(PN_LIST) && (pnbody->pn_xflags & PNX_DESTRUCT)) {
-        ParseNode* head = pnbody->pn_head;
-        LOCAL_ASSERT(head && head->isKind(PNK_SEMI));
-
-        pndestruct = head->pn_kid;
-        LOCAL_ASSERT(pndestruct);
-        LOCAL_ASSERT(pndestruct->isKind(PNK_VAR));
-    } else {
-        pndestruct = nullptr;
-    }
-
     /* Serialize the arguments and body. */
     switch (pnbody->getKind()) {
       case PNK_RETURN: /* expression closure, no destructured args */
-        return functionArgs(pn, pnargs, nullptr, pnbody, args, defaults, rest) &&
+        return functionArgs(pn, pnargs, pnbody, args, defaults, rest) &&
                expression(pnbody->pn_left, body);
 
-      case PNK_SEQ:    /* expression closure with destructured args */
-      {
-        ParseNode* pnstart = pnbody->pn_head->pn_next;
-        LOCAL_ASSERT(pnstart && pnstart->isKind(PNK_RETURN));
-
-        return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) &&
-               expression(pnstart->pn_left, body);
-      }
-
       case PNK_STATEMENTLIST:     /* statement closure */
       {
-        ParseNode* pnstart = (pnbody->pn_xflags & PNX_DESTRUCT)
-                               ? pnbody->pn_head->pn_next
-                               : pnbody->pn_head;
+        ParseNode* pnstart = pnbody->pn_head;
 
         // Skip over initial yield in generator.
         if (pnstart && pnstart->isKind(PNK_YIELD)) {
             MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD);
             pnstart = pnstart->pn_next;
         }
 
-        return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) &&
+        return functionArgs(pn, pnargs, pnbody, args, defaults, rest) &&
                functionBody(pnstart, &pnbody->pn_pos, body);
       }
 
       default:
         LOCAL_NOT_REACHED("unexpected function contents");
     }
 }
 
 bool
-ASTSerializer::functionArgs(ParseNode* pn, ParseNode* pnargs, ParseNode* pndestruct,
+ASTSerializer::functionArgs(ParseNode* pn, ParseNode* pnargs,
                             ParseNode* pnbody, NodeVector& args, NodeVector& defaults,
                             MutableHandleValue rest)
 {
-    uint32_t i = 0;
-    ParseNode* arg = pnargs ? pnargs->pn_head : nullptr;
-    ParseNode* destruct = pndestruct ? pndestruct->pn_head : nullptr;
+    if (!pnargs)
+        return true;
+
     RootedValue node(cx);
     bool defaultsNull = true;
     MOZ_ASSERT(defaults.empty(),
                "must be initially empty for it to be proper to clear this "
                "when there are no defaults");
 
-    /*
-     * Arguments are found in potentially two different places: 1) the
-     * argsbody sequence (which ends with the body node), or 2) a
-     * destructuring initialization at the beginning of the body. Loop
-     * |arg| through the argsbody and |destruct| through the initial
-     * destructuring assignments, stopping only when we've exhausted
-     * both.
-     */
-    while ((arg && arg != pnbody) || destruct) {
-        if (destruct && destruct->pn_right->frameSlot() == i) {
-            if (!pattern(destruct->pn_left, &node) ||
-                !args.append(node) || !defaults.append(NullValue()))
-            {
-                return false;
+    for (ParseNode* arg = pnargs->pn_head; arg && arg != pnbody; arg = arg->pn_next) {
+        MOZ_ASSERT(arg->isKind(PNK_NAME) || arg->isKind(PNK_ASSIGN));
+        ParseNode* argName = nullptr;
+        ParseNode* defNode = nullptr;
+        if (arg->isKind(PNK_ASSIGN)) {
+            argName = arg->pn_left;
+            defNode = arg->pn_right;
+        } else if (arg->pn_atom == cx->names().empty) {
+            ParseNode* destruct = arg->expr();
+            if (destruct->isKind(PNK_ASSIGN)) {
+                defNode = destruct->pn_right;
+                destruct = destruct->pn_left;
             }
-            destruct = destruct->pn_next;
-        } else if (arg && arg != pnbody) {
-            /*
-             * We don't check that arg->frameSlot() == i since we
-             * can't call that method if the arg def has been turned
-             * into a use, e.g.:
-             *
-             *     function(a) { function a() { } }
-             *
-             * There's no other way to ask a non-destructuring arg its
-             * index in the formals list, so we rely on the ability to
-             * ask destructuring args their index above.
-             */
-            MOZ_ASSERT(arg->isKind(PNK_NAME) || arg->isKind(PNK_ASSIGN));
-            ParseNode* argName = arg->isKind(PNK_NAME) ? arg : arg->pn_left;
+            if (!pattern(destruct, &node) || !args.append(node))
+                return false;
+        } else {
+            argName = arg;
+        }
+        if (argName) {
             if (!identifier(argName, &node))
                 return false;
             if (rest.isUndefined() && arg->pn_next == pnbody)
                 rest.setObject(node.toObject());
             else if (!args.append(node))
                 return false;
-            if (arg->pn_dflags & PND_DEFAULT) {
-                defaultsNull = false;
-                ParseNode* expr = arg->expr();
-                RootedValue def(cx);
-                if (!expression(expr, &def) || !defaults.append(def))
-                    return false;
-            } else {
-                if (!defaults.append(NullValue()))
-                    return false;
-            }
-            arg = arg->pn_next;
+        }
+        if (defNode) {
+            defaultsNull = false;
+            RootedValue def(cx);
+            if (!expression(defNode, &def) || !defaults.append(def))
+                return false;
         } else {
-            LOCAL_NOT_REACHED("missing function argument");
+            if (!defaults.append(NullValue()))
+                return false;
         }
-        ++i;
     }
     MOZ_ASSERT(!rest.isUndefined());
 
     if (defaultsNull)
         defaults.clear();
 
     return true;
 }
--- a/js/src/tests/js1_8_5/reflect-parse/declarations.js
+++ b/js/src/tests/js1_8_5/reflect-parse/declarations.js
@@ -27,16 +27,22 @@ assertDecl("function foo(...rest) { }",
 assertDecl("function foo(a=4) { }", funDecl(ident("foo"), [ident("a")], blockStmt([]), [lit(4)]));
 assertDecl("function foo(a, b=4) { }", funDecl(ident("foo"), [ident("a"), ident("b")], blockStmt([]), [null, lit(4)]));
 assertDecl("function foo(a, b=4, ...rest) { }",
            funDecl(ident("foo"), [ident("a"), ident("b")], blockStmt([]), [null, lit(4), null], ident("rest")));
 assertDecl("function foo(a=(function () {})) { function a() {} }",
            funDecl(ident("foo"), [ident("a")], blockStmt([funDecl(ident("a"), [], blockStmt([]))]),
                    [funExpr(null, [], blockStmt([]))]));
 
+// Bug 1018628: default paremeter for destructuring
+assertDecl("function f(a=1, [x,y]=[2,3]) { }",
+           funDecl(ident("f"),
+                   [ident("a"), arrPatt([ident("x"), ident("y")])],
+                   blockStmt([]),
+                   [lit(1), arrExpr([lit(2), lit(3)])]));
 
 // Bug 591437: rebound args have their defs turned into uses
 assertDecl("function f(a) { function a() { } }",
            funDecl(ident("f"), [ident("a")], blockStmt([funDecl(ident("a"), [], blockStmt([]))])));
 assertDecl("function f(a,b,c) { function b() { } }",
            funDecl(ident("f"), [ident("a"),ident("b"),ident("c")], blockStmt([funDecl(ident("b"), [], blockStmt([]))])));
 assertDecl("function f(a,[x,y]) { function a() { } }",
            funDecl(ident("f"),
--- a/js/src/vm/TraceLoggingTypes.h
+++ b/js/src/vm/TraceLoggingTypes.h
@@ -50,16 +50,17 @@
     _(LICM)                                           \
     _(RangeAnalysis)                                  \
     _(LoopUnrolling)                                  \
     _(EffectiveAddressAnalysis)                       \
     _(AlignmentMaskAnalysis)                          \
     _(EliminateDeadCode)                              \
     _(EdgeCaseAnalysis)                               \
     _(EliminateRedundantChecks)                       \
+    _(AddKeepAliveInstructions)                       \
     _(GenerateLIR)                                    \
     _(RegisterAllocation)                             \
     _(GenerateCode)
 
 #define TRACELOGGER_LOG_ITEMS(_)                      \
     _(Bailout)                                        \
     _(Disable)                                        \
     _(Enable)                                         \
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -219,18 +219,27 @@ RestyleTracker::ProcessOneRestyle(Elemen
     changeList.AppendChange(primaryFrame, aElement, aChangeHint);
     mRestyleManager->ProcessRestyledFrames(changeList);
   }
 }
 
 void
 RestyleTracker::DoProcessRestyles()
 {
-  PROFILER_LABEL("RestyleTracker", "ProcessRestyles",
-    js::ProfileEntry::Category::CSS);
+  nsAutoCString docURL;
+  if (profiler_is_active()) {
+    nsIURI *uri = Document()->GetDocumentURI();
+    if (uri) {
+      uri->GetSpec(docURL);
+    } else {
+      docURL = "N/A";
+    }
+  }
+  PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles",
+                        js::ProfileEntry::Category::CSS, "(%s)", docURL.get());
 
   bool isTimelineRecording = false;
   nsDocShell* docShell =
     static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
   if (docShell) {
     docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
   }
 
new file mode 100644
--- /dev/null
+++ b/layout/forms/crashtests/997709-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html class="reftest-print"><body><div style="position: fixed;">
+<div style="page-break-after: always"></div>
+<select style="display:flex; position: fixed;"><option>A</select>
+</div></body></html>
--- a/layout/forms/crashtests/crashtests.list
+++ b/layout/forms/crashtests/crashtests.list
@@ -54,10 +54,11 @@ load 626014.xhtml
 load 639733.xhtml
 asserts(0-1) load 669767.html
 load 682684.xhtml
 load 865602.html
 load 944198.html
 load 949891.xhtml
 load 959311.html
 load 960277-2.html
+load 997709-1.html
 load 1102791.html
 load 1140216.html
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -1280,18 +1280,18 @@ nsComboboxDisplayFrame::Reflow(nsPresCon
     state.SetComputedHeight(mComboBox->mListControlFrame->GetHeightOfARow());
   }
   nscoord computedWidth = mComboBox->mDisplayWidth -
     state.ComputedPhysicalBorderPadding().LeftRight();
   if (computedWidth < 0) {
     computedWidth = 0;
   }
   state.SetComputedWidth(computedWidth);
-
-  return nsBlockFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
+  nsBlockFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
+  aStatus = NS_FRAME_COMPLETE; // this type of frame can't be split
 }
 
 void
 nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                          const nsRect&           aDirtyRect,
                                          const nsDisplayListSet& aLists)
 {
   nsDisplayListCollection set;
@@ -1320,17 +1320,18 @@ nsComboboxControlFrame::CreateFrameFor(n
   // Get PresShell
   nsIPresShell *shell = PresContext()->PresShell();
   nsStyleSet *styleSet = shell->StyleSet();
 
   // create the style contexts for the anonymous block frame and text frame
   nsRefPtr<nsStyleContext> styleContext;
   styleContext = styleSet->
     ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozDisplayComboboxControlFrame,
-                             mStyleContext);
+                             mStyleContext,
+                             nsStyleSet::eSkipParentDisplayBasedStyleFixup);
 
   nsRefPtr<nsStyleContext> textStyleContext;
   textStyleContext = styleSet->ResolveStyleForNonElement(mStyleContext);
 
   // Start by creating our anonymous block frame
   mDisplayFrame = new (shell) nsComboboxDisplayFrame(styleContext, this);
   mDisplayFrame->Init(mContent, this, nullptr);
 
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1003441.xul
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="setTimeout(boom,0,1)">
+
+<splitter style="all: inherit;">
+    <box/>
+    <box/>
+    <box/>
+    <box/>
+    <iframe id="a" src="aaa"/>
+</splitter>
+
+<script id="script" xmlns="http://www.w3.org/1999/xhtml">
+
+    <![CDATA[//<![CDATA[
+
+var doc = document;
+function boom(i) {
+  if (i>6)
+    return;
+  var x=doc.getElementsByTagName('*');
+  if (x[i] && x[i+1]) {
+    var temp = x[i+1].getAttribute('style');
+    x[i+1].setAttribute('style', x[i].getAttribute('style'));
+    x[i].setAttribute('style', temp);
+  } else { 
+    return;
+  }
+  i++;
+  setTimeout(boom,50,i);
+}
+
+//]]>
+
+    </script>
+
+    </window>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -554,16 +554,17 @@ load 946167-1.html
 load 947158.html
 test-pref(layout.css.sticky.enabled,true) load 949932.html
 load 970710.html
 load 973701-1.xhtml
 load 973701-2.xhtml
 load 986899.html
 load 1001233.html
 load 1001258-1.html
+load 1003441.xul
 pref(layout.css.grid.enabled,true) load 1015562.html
 asserts(1-2) load 1015563-1.html
 asserts(1-2) load 1015563-2.html
 asserts(0-300) load 1015844.html # bug 574889
 load outline-on-frameset.xhtml
 pref(font.size.inflation.minTwips,200) load 1032450.html
 load 1037903.html
 load 1039454-1.html
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -932,17 +932,17 @@ public:
   NS_IMETHOD Run()
   {
     // Flush frames, to ensure any pending display:none changes are made.
     // Note it can be unsafe to flush if we've destroyed the presentation
     // for some other reason, like if we're shutting down.
     if (!mPresShell->IsDestroying()) {
       mPresShell->FlushPendingNotifications(Flush_Frames);
     }
-    nsIFrame* frame = mFrameElement->GetPrimaryFrame();
+    nsSubDocumentFrame* frame = do_QueryFrame(mFrameElement->GetPrimaryFrame());
     if ((!frame && mHideViewerIfFrameless) ||
         mPresShell->IsDestroying()) {
       // Either the frame element has no nsIFrame or the presshell is being
       // destroyed. Hide the nsFrameLoader, which destroys the presentation,
       // and clear our references to the stashed presentation.
       mFrameLoader->SetDetachedSubdocView(nullptr, nullptr);
       mFrameLoader->Hide();
     }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/select/997709-2-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE HTML>
+<html><body>
+<select style="display:block"><option>A</select>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/select/997709-2.html
@@ -0,0 +1,4 @@
+<!DOCTYPE HTML>
+<html><body>
+<select style="display:flex"><option>A</select>
+</body></html>
--- a/layout/reftests/forms/select/reftest.list
+++ b/layout/reftests/forms/select/reftest.list
@@ -1,7 +1,8 @@
 skip-if(B2G||Mulet) == out-of-bounds-selectedindex.html out-of-bounds-selectedindex-ref.html # test for bug 471741 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == multiple.html multiple-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == boguskids.html boguskids-ref.html
 == dynamic-boguskids.html boguskids-ref.html
 == option-children.html option-children-ref.html
 fuzzy(1,4) == padding-button-placement.html padding-button-placement-ref.html
 HTTP(../..) == vertical-centering.html vertical-centering-ref.html
+== 997709-2.html 997709-2-ref.html
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -1829,17 +1829,18 @@ nsStyleSet::ProbePseudoElementStyle(Elem
     }
   }
 
   return result.forget();
 }
 
 already_AddRefed<nsStyleContext>
 nsStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag,
-                                     nsStyleContext* aParentContext)
+                                     nsStyleContext* aParentContext,
+                                     uint32_t aFlags)
 {
   NS_ENSURE_FALSE(mInShutdown, nullptr);
 
 #ifdef DEBUG
     bool isAnonBox = nsCSSAnonBoxes::IsAnonBox(aPseudoTag)
 #ifdef MOZ_XUL
                  && !nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag)
 #endif
@@ -1866,17 +1867,17 @@ nsStyleSet::ResolveAnonymousBoxStyle(nsI
     }
     for (uint32_t i = 0, i_end = importantRules.Length(); i != i_end; ++i) {
       ruleWalker.Forward(importantRules[i]);
     }
   }
 
   return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr,
                     aPseudoTag, nsCSSPseudoElements::ePseudo_AnonBox,
-                    nullptr, eNoFlags);
+                    nullptr, aFlags);
 }
 
 #ifdef MOZ_XUL
 already_AddRefed<nsStyleContext>
 nsStyleSet::ResolveXULTreePseudoStyle(Element* aParentElement,
                                       nsIAtom* aPseudoTag,
                                       nsStyleContext* aParentContext,
                                       nsICSSPseudoComparator* aComparator)
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -187,20 +187,40 @@ class nsStyleSet
                           nsStyleContext* aParentContext);
   already_AddRefed<nsStyleContext>
   ProbePseudoElementStyle(mozilla::dom::Element* aParentElement,
                           nsCSSPseudoElements::Type aType,
                           nsStyleContext* aParentContext,
                           TreeMatchContext& aTreeMatchContext,
                           mozilla::dom::Element* aPseudoElement = nullptr);
 
+  /**
+   * Bit-flags that can be passed to ResolveAnonymousBoxStyle and GetContext
+   * in their parameter 'aFlags'.
+   */
+  enum {
+    eNoFlags =          0,
+    eIsLink =           1 << 0,
+    eIsVisitedLink =    1 << 1,
+    eDoAnimation =      1 << 2,
+
+    // Indicates that we should skip the flex/grid item specific chunk of
+    // ApplyStyleFixups().  This is useful if our parent has "display: flex"
+    // or "display: grid" but we can tell we're not going to honor that (e.g. if
+    // it's the outer frame of a button widget, and we're the inline frame for
+    // the button's label).
+    eSkipParentDisplayBasedStyleFixup = 1 << 3
+  };
+
   // Get a style context for an anonymous box.  aPseudoTag is the
-  // pseudo-tag to use and must be non-null.
+  // pseudo-tag to use and must be non-null.  aFlags will be forwarded
+  // to a GetContext call internally.
   already_AddRefed<nsStyleContext>
-  ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, nsStyleContext* aParentContext);
+  ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, nsStyleContext* aParentContext,
+                           uint32_t aFlags = eNoFlags);
 
 #ifdef MOZ_XUL
   // Get a style context for a XUL tree pseudo.  aPseudoTag is the
   // pseudo-tag to use and must be non-null.  aParentContent must be
   // non-null.  aComparator must be non-null.
   already_AddRefed<nsStyleContext>
   ResolveXULTreePseudoStyle(mozilla::dom::Element* aParentElement,
                             nsIAtom* aPseudoTag,
@@ -428,33 +448,16 @@ class nsStyleSet
   // aPseudoElement must follow the same rules as for
   // ResolvePseudoElementStyle, and be null for non-pseudo-element cases
   nsRuleNode* RuleNodeWithReplacement(mozilla::dom::Element* aElement,
                                       mozilla::dom::Element* aPseudoElement,
                                       nsRuleNode* aOldRuleNode,
                                       nsCSSPseudoElements::Type aPseudoType,
                                       nsRestyleHint aReplacements);
 
-  /**
-   * Bit-flags that can be passed to GetContext() in its parameter 'aFlags'.
-   */
-  enum {
-    eNoFlags =          0,
-    eIsLink =           1 << 0,
-    eIsVisitedLink =    1 << 1,
-    eDoAnimation =      1 << 2,
-
-    // Indicates that we should skip the flex/grid item specific chunk of
-    // ApplyStyleFixups().  This is useful if our parent has "display: flex"
-    // or "display: grid" but we can tell we're not going to honor that (e.g. if
-    // it's the outer frame of a button widget, and we're the inline frame for
-    // the button's label).
-    eSkipParentDisplayBasedStyleFixup = 1 << 3
-  };
-
   already_AddRefed<nsStyleContext>
   GetContext(nsStyleContext* aParentContext,
              nsRuleNode* aRuleNode,
              nsRuleNode* aVisitedRuleNode,
              nsIAtom* aPseudoTag,
              nsCSSPseudoElements::Type aPseudoType,
              mozilla::dom::Element* aElementForAnimation,
              uint32_t aFlags);
--- a/layout/tools/reftest/reftest-analyzer.xhtml
+++ b/layout/tools/reftest/reftest-analyzer.xhtml
@@ -241,17 +241,17 @@ function process_log(contents) {
           random: (random == "(EXPECTED RANDOM)"),
           skip: (extra == " (SKIP)"),
           url: url,
           images: [],
           imageLabels: []
         });
       continue;
     }
-    match = line.match(/^(?:  |)IMAGE([^:]*): (data:.*)$/);
+    match = line.match(/IMAGE([^:]*): (data:.*)$/);
     if (match) {
       var item = gTestItems[gTestItems.length - 1];
       item.images.push(match[2]);
       item.imageLabels.push(match[1]);
     }
   }
 
   build_viewer();
--- a/media/libnestegg/README_MOZILLA
+++ b/media/libnestegg/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the nestegg
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The nestegg git repository is: git://github.com/kinetiknz/nestegg.git
 
-The git commit ID used was 493fb475842db86de657101ffac9a06a0cf688de.
+The git commit ID used was d90e2ccac66e90c4a6f25318f8092b8596c97a67.
--- a/media/libnestegg/src/nestegg.c
+++ b/media/libnestegg/src/nestegg.c
@@ -2219,25 +2219,33 @@ nestegg_track_codec_id(nestegg * ctx, un
 }
 
 int
 nestegg_track_codec_data_count(nestegg * ctx, unsigned int track,
                                unsigned int * count)
 {
   struct track_entry * entry;
   struct ebml_binary codec_private;
+  int codec_id;
   unsigned char * p;
 
   *count = 0;
 
   entry = ne_find_track_entry(ctx, track);
   if (!entry)
     return -1;
 
-  if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS)
+  codec_id = nestegg_track_codec_id(ctx, track);
+
+  if (codec_id == NESTEGG_CODEC_OPUS) {
+    *count = 1;
+    return 0;
+  }
+
+  if (codec_id != NESTEGG_CODEC_VORBIS)
     return -1;
 
   if (ne_get_binary(entry->codec_private, &codec_private) != 0)
     return -1;
 
   if (codec_private.length < 1)
     return -1;
 
--- a/media/libopus/moz.build
+++ b/media/libopus/moz.build
@@ -84,19 +84,19 @@ else:
         'silk/fixed',
     ]
     UNIFIED_SOURCES += silk_sources_fixed
 # for webrtc
     UNIFIED_SOURCES += opus_sources_float
 
 if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_AS']:
     SOURCES += celt_sources_arm
-    GENERATED_SOURCES += [ '%s.%s' % (f, CONFIG['ASM_SUFFIX']) for f in [
-        'celt_pitch_xcorr_arm-gnu',
-    ]]
+    SOURCES += [
+        '!celt_pitch_xcorr_arm-gnu.%s' % CONFIG['ASM_SUFFIX']
+    ]
     # -Os is significantly slower, enable -O3 unless optimization is disabled
     if CONFIG['MOZ_OPTIMIZE']:
         CFLAGS += [
             '-O3',
         ]
         CXXFLAGS += [
             '-O3',
         ]
--- a/media/libtheora/moz.build
+++ b/media/libtheora/moz.build
@@ -81,17 +81,17 @@ if CONFIG['GNU_AS']:
                     'OC_ARM_ASM_MEDIA',
                     'OC_ARM_ASM_NEON'):
             DEFINES[var] = True
         # The Android NDK doesn't pre-define anything to indicate the OS it's
         # on, so do it for them.
         if CONFIG['OS_TARGET'] == 'Android':
             DEFINES['__linux__'] = True
 
-        GENERATED_SOURCES += [ '%s.%s' % (f, CONFIG['ASM_SUFFIX']) for f in [
+        SOURCES += [ '!%s.%s' % (f, CONFIG['ASM_SUFFIX']) for f in [
             'armbits-gnu',
             'armfrag-gnu',
             'armidct-gnu',
             'armloop-gnu',
         ]]
 
         # These flags are a lie; they're just used to enable the requisite
         # opcodes; actual arch detection is done at runtime.
--- a/media/libvpx/moz.build
+++ b/media/libvpx/moz.build
@@ -48,22 +48,21 @@ if CONFIG['VPX_X86_ASM']:
     #postproc is only enabled on x86 with asm
     SOURCES += files['VP8_POSTPROC']
 
 arm_asm_files = []
 if CONFIG['VPX_ARM_ASM']:
     arm_asm_files += files['ARM_ASM']
 
     if CONFIG['VPX_AS_CONVERSION']:
-        GENERATED_SOURCES += [ "%s.%s" % (f, CONFIG['VPX_ASM_SUFFIX'])
-            for f in sorted(arm_asm_files) if f.endswith('.asm')
-        ]
-        SOURCES += [
-            f for f in sorted(arm_asm_files) if not f.endswith('.asm')
-        ]
+        SOURCES += sorted([
+            "!%s.%s" % (f, CONFIG['VPX_ASM_SUFFIX'])
+            if f.endswith('.asm') else f
+            for f in sorted(arm_asm_files)
+        ])
     else:
         SOURCES += sorted(arm_asm_files)
 
     for f in SOURCES:
         if f.endswith('.c') and 'neon' in f:
             SOURCES[f].flags += ['-march=armv7-a', '-mthumb', '-mfloat-abi=softfp', '-mfpu=neon']
 
 # boolhuff_armv5te.asm defines the same functions as boolhuff.c instead of
--- a/media/mtransport/common.build
+++ b/media/mtransport/common.build
@@ -26,17 +26,17 @@ mtransport_lcppsrcs = [
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     mtransport_lcppsrcs += [
         'gonk_addrs.cpp',
     ]
 
 mtransport_cppsrcs = [
-    '%s/media/mtransport/%s' % (TOPSRCDIR, s) for s in sorted(mtransport_lcppsrcs)
+    '/media/mtransport/%s' % s for s in sorted(mtransport_lcppsrcs)
 ]
 
 LOCAL_INCLUDES += [
     '/media/mtransport/',
     '/media/mtransport/third_party/',
     '/media/mtransport/third_party/nICEr/src/crypto',
     '/media/mtransport/third_party/nICEr/src/ice',
     '/media/mtransport/third_party/nICEr/src/net',
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -1,16 +1,21 @@
 # -*- 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/.
 
 TEST_DIRS += ['tests']
 
+# On win we build two mfbt libs - mfbt linked to crt dlls here and in
+# staticruntime we build a statically linked mfbt lib.
+if CONFIG['OS_ARCH'] == 'WINNT':
+    DIRS += ['staticruntime']
+
 Library('mfbt')
 
 EXPORTS.mozilla = [
     'Alignment.h',
     'AllocPolicy.h',
     'AlreadyAddRefed.h',
     'Array.h',
     'ArrayUtils.h',
@@ -90,41 +95,22 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla += [
         'WindowsVersion.h',
     ]
 elif CONFIG['OS_ARCH'] == 'Linux':
     EXPORTS.mozilla += [
         'LinuxSignal.h',
     ]
 
-UNIFIED_SOURCES = [
-    'double-conversion/bignum-dtoa.cc',
-    'double-conversion/bignum.cc',
-    'double-conversion/cached-powers.cc',
-    'double-conversion/diy-fp.cc',
-    'double-conversion/double-conversion.cc',
-    'double-conversion/fast-dtoa.cc',
-    'double-conversion/fixed-dtoa.cc',
-    'double-conversion/strtod.cc',
-    'FloatingPoint.cpp',
-    'HashFunctions.cpp',
-    'JSONWriter.cpp',
-    'Poison.cpp',
-    'SHA1.cpp',
-    'TaggedAnonymousMemory.cpp',
-    'unused.cpp',
-]
+include('objs.mozbuild')
+
+UNIFIED_SOURCES += mfbt_src_cppsrcs
 
 DEFINES['IMPL_MFBT'] = True
 
-# Compression.cpp cannot be built in unified mode because it pulls in Windows system headers.
-# Decimal.cpp doesn't build in unified mode with gcc.
-SOURCES += [
-    'Compression.cpp',
-    'decimal/Decimal.cpp',
-]
+SOURCES += mfbt_nonunified_src_cppsrcs
 
 DISABLE_STL_WRAPPING = True
 
 # Suppress warnings in third-party code.
 if CONFIG['GNU_CXX']:
     # TODO: Remove this LZ4 warning suppression after bug 993267 is fixed.
-    SOURCES['Compression.cpp'].flags += ['-Wno-unused-function']
+    SOURCES['/mfbt/Compression.cpp'].flags += ['-Wno-unused-function']
new file mode 100644
--- /dev/null
+++ b/mfbt/objs.mozbuild
@@ -0,0 +1,38 @@
+# -*- 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/.
+
+mfbt_src_lcppsrcs = [
+    'double-conversion/bignum-dtoa.cc',
+    'double-conversion/bignum.cc',
+    'double-conversion/cached-powers.cc',
+    'double-conversion/diy-fp.cc',
+    'double-conversion/double-conversion.cc',
+    'double-conversion/fast-dtoa.cc',
+    'double-conversion/fixed-dtoa.cc',
+    'double-conversion/strtod.cc',
+    'FloatingPoint.cpp',
+    'HashFunctions.cpp',
+    'JSONWriter.cpp',
+    'Poison.cpp',
+    'SHA1.cpp',
+    'TaggedAnonymousMemory.cpp',
+    'unused.cpp',
+]
+
+mfbt_src_cppsrcs = [
+    '/mfbt/%s' % s for s in mfbt_src_lcppsrcs
+]
+
+# Compression.cpp cannot be built in unified mode because it pulls in Windows system headers.
+# Decimal.cpp doesn't build in unified mode with gcc.
+mfbt_nonunified_src_lcppsrcs = [
+    'Compression.cpp',
+    'decimal/Decimal.cpp',
+]
+
+mfbt_nonunified_src_cppsrcs = [
+    '/mfbt/%s' % s for s in mfbt_nonunified_src_lcppsrcs
+]
new file mode 100644
--- /dev/null
+++ b/mfbt/staticruntime/moz.build
@@ -0,0 +1,24 @@
+# -*- 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/.
+
+Library('mfbt_staticruntime')
+
+include('../objs.mozbuild')
+
+UNIFIED_SOURCES += mfbt_src_cppsrcs
+
+DEFINES['IMPL_MFBT'] = True
+
+SOURCES += mfbt_nonunified_src_cppsrcs
+
+USE_STATIC_LIBS = True
+
+DISABLE_STL_WRAPPING = True
+
+# Suppress warnings in third-party code.
+if CONFIG['GNU_CXX']:
+    # TODO: Remove this LZ4 warning suppression after bug 993267 is fixed.
+    SOURCES['/mfbt/Compression.cpp'].flags += ['-Wno-unused-function']
--- a/modules/libjar/InterceptedJARChannel.cpp
+++ b/modules/libjar/InterceptedJARChannel.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set expandtab ts=2 sw=2 sts=2 cin: */
 /* 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 "InterceptedJARChannel.h"
 #include "nsIPipe.h"
+#include "mozilla/dom/ChannelInfo.h"
 
 using namespace mozilla::net;
 
 NS_IMPL_ISUPPORTS(InterceptedJARChannel, nsIInterceptedChannel)
 
 InterceptedJARChannel::InterceptedJARChannel(nsJARChannel* aChannel,
                                              nsINetworkInterceptController* aController,
                                              bool aIsNavigation)
@@ -94,23 +95,23 @@ InterceptedJARChannel::Cancel()
   nsresult rv = mChannel->Cancel(NS_BINDING_ABORTED);
   NS_ENSURE_SUCCESS(rv, rv);
   mResponseBody = nullptr;
   mChannel = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-InterceptedJARChannel::SetSecurityInfo(nsISupports* aSecurityInfo)
+InterceptedJARChannel::SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo)
 {
   if (!mChannel) {
     return NS_ERROR_FAILURE;
   }
 
-  return mChannel->OverrideSecurityInfo(aSecurityInfo);
+  return aChannelInfo->ResurrectInfoOnChannel(mChannel);
 }
 
 void
 InterceptedJARChannel::NotifyController()
 {
   nsresult rv = NS_NewPipe(getter_AddRefs(mSynthesizedInput),
                            getter_AddRefs(mResponseBody),
                            0, UINT32_MAX, true, true);
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -56,16 +56,18 @@ public:
     NS_DECL_NSIREMOTEOPENFILELISTENER
     NS_DECL_NSITHREADRETARGETABLEREQUEST
     NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 
     nsJARChannel();
 
     nsresult Init(nsIURI *uri);
 
+    nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo);
+
 private:
     virtual ~nsJARChannel();
 
     nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
     nsresult LookupFile(bool aAllowAsync);
     nsresult OpenLocalFile();
     void NotifyError(nsresult aError);
     void FireOnProgress(uint64_t aProgress);
@@ -82,17 +84,16 @@ private:
     bool ShouldIntercept();
     nsresult ContinueAsyncOpen();
     // Discard the prior interception and continue with the original network
     // request.
     void ResetInterception();
     // Override this channel's pending response with a synthesized one. The
     // content will be asynchronously read from the pump.
     void OverrideWithSynthesizedResponse(nsIInputStream* aSynthesizedInput);
-    nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo);
 
     nsCString                       mSpec;
 
     bool                            mOpened;
 
     nsCOMPtr<nsIJARURI>             mJarURI;
     nsCOMPtr<nsIURI>                mOriginalURI;
     nsCOMPtr<nsIURI>                mAppURI;
--- a/netwerk/base/Dashboard.cpp
+++ b/netwerk/base/Dashboard.cpp
@@ -293,34 +293,38 @@ LookupHelper::OnLookupComplete(nsICancel
     mThread->Dispatch(event, NS_DISPATCH_NORMAL);
 
     return NS_OK;
 }
 
 nsresult
 LookupHelper::ConstructAnswer(LookupArgument *aArgument)
 {
-
     nsIDNSRecord *aRecord = aArgument->mRecord;
     AutoSafeJSContext cx;
 
     mozilla::dom::DNSLookupDict dict;
     dict.mAddress.Construct();
 
     Sequence<nsString> &addresses = dict.mAddress.Value();
 
     if (NS_SUCCEEDED(mStatus)) {
         dict.mAnswer = true;
         bool hasMore;
         aRecord->HasMore(&hasMore);
         while (hasMore) {
-           nsCString nextAddress;
-           aRecord->GetNextAddrAsString(nextAddress);
-           CopyASCIItoUTF16(nextAddress, *addresses.AppendElement());
-           aRecord->HasMore(&hasMore);
+            nsString* nextAddress = addresses.AppendElement();
+            if (!nextAddress) {
+                return NS_ERROR_OUT_OF_MEMORY;
+            }
+
+            nsCString nextAddressASCII;
+            aRecord->GetNextAddrAsString(nextAddressASCII);
+            CopyASCIItoUTF16(nextAddressASCII, *nextAddress);
+            aRecord->HasMore(&hasMore);
         }
     } else {
         dict.mAnswer = false;
         GetErrorString(mStatus, dict.mError);
     }
 
     JS::RootedValue val(cx);
     if (!ToJSValue(cx, dict, &val)) {
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -93,17 +93,16 @@ XPIDL_SOURCES += [
     'nsIResumableChannel.idl',
     'nsISecretDecoderRing.idl',
     'nsISecureBrowserUI.idl',
     'nsISecurityEventSink.idl',
     'nsISecurityInfoProvider.idl',
     'nsISerializationHelper.idl',
     'nsIServerSocket.idl',
     'nsISimpleStreamListener.idl',
-    'nsISiteSecurityService.idl',
     'nsISocketTransport.idl',
     'nsISocketTransportService.idl',
     'nsISpeculativeConnect.idl',
     'nsIStandardURL.idl',
     'nsIStreamingProtocolController.idl',
     'nsIStreamingProtocolService.idl',
     'nsIStreamListener.idl',
     'nsIStreamListenerTee.idl',
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -4,24 +4,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIChannel;
 interface nsIOutputStream;
 interface nsIURI;
 
+%{C++
+namespace mozilla {
+namespace dom {
+class ChannelInfo;
+}
+}
+%}
+
+[ptr] native ChannelInfo(mozilla::dom::ChannelInfo);
+
 /**
  * Interface to allow implementors of nsINetworkInterceptController to control the behaviour
  * of intercepted channels without tying implementation details of the interception to
  * the actual channel. nsIInterceptedChannel is expected to be implemented by objects
  * which do not implement nsIChannel.
  */
 
-[scriptable, uuid(2fc1170c-4f9d-4c9e-8e5d-2d351dbe03f2)]
+[scriptable, uuid(f2c07a6b-366d-4ef4-85ab-a77f4bcb1646)]
 interface nsIInterceptedChannel : nsISupports
 {
     /**
      * Instruct a channel that has been intercepted to continue with the original
      * network request.
      */
     void resetInterception();
 
@@ -62,19 +72,20 @@ interface nsIInterceptedChannel : nsISup
     readonly attribute nsIChannel channel;
 
     /**
      * True if the underlying request was caused by a navigation attempt.
      */
     readonly attribute bool isNavigation;
 
     /**
-     * This method allows to override the security info for the channel.
+     * This method allows to override the channel info for the channel.
      */
-    void setSecurityInfo(in nsISupports securityInfo);
+    [noscript]
+    void setChannelInfo(in ChannelInfo channelInfo);
 };
 
 /**
  * Interface to allow consumers to attach themselves to a channel's
  * notification callbacks/loadgroup and determine if a given channel
  * request should be intercepted before any network request is initiated.
  */
 
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -8,16 +8,17 @@
 
 #include "InterceptedChannel.h"
 #include "nsInputStreamPump.h"
 #include "nsIPipe.h"
 #include "nsIStreamListener.h"
 #include "nsHttpChannel.h"
 #include "HttpChannelChild.h"
 #include "nsHttpResponseHead.h"
+#include "mozilla/dom/ChannelInfo.h"
 
 namespace mozilla {
 namespace net {
 
 extern nsresult
 DoAddCacheEntryHeaders(nsHttpChannel *self,
                        nsICacheEntry *entry,
                        nsHttpRequestHead *requestHead,
@@ -228,23 +229,23 @@ InterceptedChannelChrome::Cancel()
   // we need to use AsyncAbort instead of Cancel since there's no active pump
   // to cancel which will provide OnStart/OnStopRequest to the channel.
   nsresult rv = mChannel->AsyncAbort(NS_BINDING_ABORTED);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-InterceptedChannelChrome::SetSecurityInfo(nsISupports* aSecurityInfo)
+InterceptedChannelChrome::SetChannelInfo(dom::ChannelInfo* aChannelInfo)
 {
   if (!mChannel) {
     return NS_ERROR_FAILURE;
   }
 
-  return mChannel->OverrideSecurityInfo(aSecurityInfo);
+  return aChannelInfo->ResurrectInfoOnChannel(mChannel);
 }
 
 InterceptedChannelContent::InterceptedChannelContent(HttpChannelChild* aChannel,
                                                      nsINetworkInterceptController* aController,
                                                      nsIStreamListener* aListener)
 : InterceptedChannelBase(aController, aChannel->IsNavigation())
 , mChannel(aChannel)
 , mStreamListener(aListener)
@@ -335,19 +336,19 @@ InterceptedChannelContent::Cancel()
   nsresult rv = mChannel->AsyncAbort(NS_BINDING_ABORTED);
   NS_ENSURE_SUCCESS(rv, rv);
   mChannel = nullptr;
   mStreamListener = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-InterceptedChannelContent::SetSecurityInfo(nsISupports* aSecurityInfo)
+InterceptedChannelContent::SetChannelInfo(dom::ChannelInfo* aChannelInfo)
 {
   if (!mChannel) {
     return NS_ERROR_FAILURE;
   }
 
-  return mChannel->OverrideSecurityInfo(aSecurityInfo);
+  return aChannelInfo->ResurrectInfoOnChannel(mChannel);
 }
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/InterceptedChannel.h
+++ b/netwerk/protocol/http/InterceptedChannel.h
@@ -77,17 +77,17 @@ public:
                            nsICacheEntry* aEntry);
 
   NS_IMETHOD ResetInterception() override;
   NS_IMETHOD FinishSynthesizedResponse() override;
   NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
   NS_IMETHOD SynthesizeStatus(uint16_t aStatus, const nsACString& aReason) override;
   NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
   NS_IMETHOD Cancel() override;
-  NS_IMETHOD SetSecurityInfo(nsISupports* aSecurityInfo) override;
+  NS_IMETHOD SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) override;
 
   virtual void NotifyController() override;
 };
 
 class InterceptedChannelContent : public InterceptedChannelBase
 {
   // The actual channel being intercepted.
   nsRefPtr<HttpChannelChild> mChannel;
@@ -104,17 +104,17 @@ public:
                             nsIStreamListener* aListener);
 
   NS_IMETHOD ResetInterception() override;
   NS_IMETHOD FinishSynthesizedResponse() override;
   NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
   NS_IMETHOD SynthesizeStatus(uint16_t aStatus, const nsACString& aReason) override;
   NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
   NS_IMETHOD Cancel() override;
-  NS_IMETHOD SetSecurityInfo(nsISupports* aSecurityInfo) override;
+  NS_IMETHOD SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) override;
 
   virtual void NotifyController() override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // InterceptedChannel_h
--- a/netwerk/protocol/http/TunnelUtils.h
+++ b/netwerk/protocol/http/TunnelUtils.h
@@ -10,16 +10,17 @@
 #include "mozilla/Attributes.h"
 #include "nsAHttpTransaction.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsISocketTransport.h"
 #include "nsITimer.h"
 #include "NullHttpTransaction.h"
 #include "mozilla/TimeStamp.h"
+#include "prio.h"
 
 // a TLSFilterTransaction wraps another nsAHttpTransaction but
 // applies a encode/decode filter of TLS onto the ReadSegments
 // and WriteSegments data. It is not used for basic https://
 // but it is used for supplemental TLS tunnels - such as those
 // needed by CONNECT tunnels in HTTP/2 or even CONNECT tunnels when
 // the underlying proxy connection is already running TLS
 //
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -6,16 +6,17 @@
 
 #ifndef nsHttpConnectionInfo_h__
 #define nsHttpConnectionInfo_h__
 
 #include "nsHttp.h"
 #include "nsProxyInfo.h"
 #include "nsCOMPtr.h"
 #include "nsStringFwd.h"
+#include "mozilla/Logging.h"
 
 extern PRLogModuleInfo *gHttpLog;
 
 //-----------------------------------------------------------------------------
 // nsHttpConnectionInfo - holds the properties of a connection
 //-----------------------------------------------------------------------------
 
 // http:// uris through a proxy will all share the same CI, because they can
--- a/netwerk/standalone/moz.build
+++ b/netwerk/standalone/moz.build
@@ -13,32 +13,32 @@ src_list = [
 
 netwerk_base_src = [
     'nsDNSPrefetch.cpp',
     'nsNetAddr.cpp',
     'nsSocketTransportService2.cpp',
     'nsURLHelper.cpp',
 ]
 src_list += [
-    '%s/netwerk/base/%s' % (TOPSRCDIR, s) for s in netwerk_base_src
+    '/netwerk/base/%s' % s for s in netwerk_base_src
 ]
 
 netwerk_dns_src = [
     'nsHostResolver.cpp',
     'DNS.cpp',
     'DNSListenerProxy.cpp',
     'GetAddrInfo.cpp',
     'nameprep.c',
     'nsDNSService2.cpp',
     'nsIDNService.cpp',
     'punycode.c',
     'race.c',
 ]
 src_list += [
-    '%s/netwerk/dns/%s' % (TOPSRCDIR, s) for s in netwerk_dns_src
+    '/netwerk/dns/%s' % s for s in netwerk_dns_src
 ]
 
 SOURCES += sorted(src_list)
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES = [
     '../base',
--- a/netwerk/system/qt/moz.build
+++ b/netwerk/system/qt/moz.build
@@ -1,23 +1,20 @@
 # -*- 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/.
 
 SOURCES += [
+    '!moc_nsQtNetworkManager.cpp',
     'nsQtNetworkLinkService.cpp',
     'nsQtNetworkManager.cpp',
 ]
 
-GENERATED_SOURCES += [
-    'moc_nsQtNetworkManager.cpp',
-]
-
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/netwerk/base',
 ]
 
 CXXFLAGS += CONFIG['MOZ_QT_CFLAGS']
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -449,28 +449,36 @@ class RecursiveMakeBackend(CommonBackend
                 '.s': 'ASFILES',
                 '.c': 'CSRCS',
                 '.m': 'CMSRCS',
                 '.mm': 'CMMSRCS',
                 '.cpp': 'CPPSRCS',
                 '.rs': 'RSSRCS',
                 '.S': 'SSRCS',
             }
-            var = suffix_map[obj.canonical_suffix]
+            variables = [suffix_map[obj.canonical_suffix]]
+            if isinstance(obj, GeneratedSources):
+                variables.append('GARBAGE')
+                base = backend_file.objdir
+            else:
+                base = backend_file.srcdir
             for f in sorted(obj.files):
-                backend_file.write('%s += %s\n' % (var, f))
+                f = mozpath.relpath(f, base)
+                for var in variables:
+                    backend_file.write('%s += %s\n' % (var, f))
         elif isinstance(obj, HostSources):
             suffix_map = {
                 '.c': 'HOST_CSRCS',
                 '.mm': 'HOST_CMMSRCS',
                 '.cpp': 'HOST_CPPSRCS',
             }
             var = suffix_map[obj.canonical_suffix]
             for f in sorted(obj.files):
-                backend_file.write('%s += %s\n' % (var, f))
+                backend_file.write('%s += %s\n' % (
+                    var, mozpath.relpath(f, backend_file.srcdir)))
         elif isinstance(obj, VariablePassthru):
             # Sorted so output is consistent and we don't bump mtimes.
             for k, v in sorted(obj.variables.items()):
                 if isinstance(v, list):
                     for item in v:
                         backend_file.write('%s += %s\n' % (k, item))
                 elif isinstance(v, bool):
                     if v:
@@ -926,17 +934,18 @@ class RecursiveMakeBackend(CommonBackend
         for path, files in obj.objdir_files.iteritems():
             prefix = 'TEST_HARNESS_%s' % path.replace('/', '_')
             backend_file.write("""
 %(prefix)s_FILES := %(files)s
 %(prefix)s_DEST := %(dest)s
 INSTALL_TARGETS += %(prefix)s
 """ % { 'prefix': prefix,
         'dest': '$(DEPTH)/_tests/%s' % path,
-        'files': ' '.join(files) })
+        'files': ' '.join(mozpath.relpath(f, backend_file.objdir)
+                          for f in files) })
 
     def _process_resources(self, obj, resources, backend_file):
         dep_path = mozpath.join(self.environment.topobjdir, '_build_manifests', '.deps', 'install')
 
         # Resources need to go in the 'res' subdirectory of $(DIST)/bin, so we
         # specify a root namespace of 'res'.
         for source, dest, flags in self._walk_hierarchy(obj, resources,
                                                         namespace='res'):
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -206,43 +206,16 @@ class Context(KeyedDefaultDict):
         if not isinstance(value, stored_type):
             if issubclass(stored_type, ContextDerivedValue):
                 value = stored_type(self, value)
             else:
                 value = stored_type(value)
 
         return KeyedDefaultDict.__setitem__(self, key, value)
 
-    def resolve_path(self, path):
-        """Resolves a path using moz.build conventions.
-
-        Paths may be relative to the current srcdir or objdir, or to the
-        environment's topsrcdir or topobjdir.  Different resolution contexts
-        are denoted by characters at the beginning of the path:
-
-            * '/' - relative to topsrcdir;
-            * '!/' - relative to topobjdir;
-            * '!' - relative to objdir; and
-            * any other character - relative to srcdir.
-        """
-        if path.startswith('/'):
-            resolved = mozpath.join(self.config.topsrcdir, path[1:])
-        elif path.startswith('!/'):
-            resolved = mozpath.join(self.config.topobjdir, path[2:])
-        elif path.startswith('!'):
-            resolved = mozpath.join(self.objdir, path[1:])
-        else:
-            resolved = mozpath.join(self.srcdir, path)
-
-        return mozpath.normpath(resolved)
-
-    @staticmethod
-    def is_objdir_path(path):
-        return path[0] == '!'
-
     def update(self, iterable={}, **kwargs):
         """Like dict.update(), but using the context's setitem.
 
         This function is transactional: if setitem fails for one of the values,
         the context is not updated at all."""
         if isinstance(iterable, dict):
             iterable = iterable.items()
 
@@ -328,96 +301,174 @@ def Enum(*values):
                 return default
             if value in values:
                 return value
             raise ValueError('Invalid value. Allowed values are: %s'
                              % ', '.join(repr(v) for v in values))
     return EnumClass
 
 
-class SourcePath(ContextDerivedValue, UserString):
+class PathMeta(type):
+    """Meta class for the Path family of classes.
+
+    It handles calling __new__ and __init__ with the right arguments
+    in cases where a Path is instantiated with another instance of
+    Path instead of having received a context.
+
+    It also makes Path(context, value) instantiate one of the
+    subclasses depending on the value, allowing callers to do
+    standard type checking (isinstance(path, ObjDirPath)) instead
+    of checking the value itself (path.startswith('!')).
+    """
+    def __call__(cls, context, value=None):
+        if isinstance(context, Path):
+            assert value is None
+            value = context
+            context = context.context
+        else:
+            assert isinstance(context, Context)
+            if isinstance(value, Path):
+                context = value.context
+        if not issubclass(cls, (SourcePath, ObjDirPath)):
+            cls = ObjDirPath if value.startswith('!') else SourcePath
+        return super(PathMeta, cls).__call__(context, value)
+
+class Path(ContextDerivedValue, unicode):
     """Stores and resolves a source path relative to a given context
 
     This class is used as a backing type for some of the sandbox variables.
-    It expresses paths relative to a context. Paths starting with a '/'
-    are considered relative to the topsrcdir, and other paths relative
-    to the current source directory for the associated context.
+    It expresses paths relative to a context. Supported paths are:
+      - '/topsrcdir/relative/paths'
+      - 'srcdir/relative/paths'
+      - '!/topobjdir/relative/paths'
+      - '!objdir/relative/paths'
     """
+    __metaclass__ = PathMeta
+
     def __new__(cls, context, value=None):
-        if not isinstance(context, Context) and value is None:
-            return unicode(context)
-        return super(SourcePath, cls).__new__(cls)
+        return super(Path, cls).__new__(cls, value)
 
     def __init__(self, context, value=None):
+        # Only subclasses should be instantiated.
+        assert self.__class__ != Path
         self.context = context
         self.srcdir = context.srcdir
-        self.value = value
+
+    def join(self, *p):
+        """ContextDerived equivalent of mozpath.join(self, *p), returning a
+        new Path instance.
+        """
+        return Path(self.context, mozpath.join(self, *p))
+
+    def __cmp__(self, other):
+        if isinstance(other, Path) and self.srcdir != other.srcdir:
+            return cmp(self.full_path, other.full_path)
+        return cmp(unicode(self), other)
+
+    # __cmp__ is not enough because unicode has __eq__, __ne__, etc. defined
+    # and __cmp__ is only used for those when they don't exist.
+    def __eq__(self, other):
+        return self.__cmp__(other) == 0
+
+    def __ne__(self, other):
+        return self.__cmp__(other) != 0
+
+    def __lt__(self, other):
+        return self.__cmp__(other) < 0
 
-    @memoized_property
-    def data(self):
-        """Serializes the path for UserString."""
-        if self.value.startswith('/'):
-            ret = None
+    def __gt__(self, other):
+        return self.__cmp__(other) > 0
+
+    def __le__(self, other):
+        return self.__cmp__(other) <= 0
+
+    def __ge__(self, other):
+        return self.__cmp__(other) >= 0
+
+    def __repr__(self):
+        return '<%s (%s)%s>' % (self.__class__.__name__, self.srcdir, self)
+
+    def __hash__(self):
+        return hash(self.full_path)
+
+
+class SourcePath(Path):
+    """Like Path, but limited to paths in the source directory."""
+    def __init__(self, context, value):
+        if value.startswith('!'):
+            raise ValueError('Object directory paths are not allowed')
+        super(SourcePath, self).__init__(context, value)
+
+        if value.startswith('/'):
+            path = None
             # If the path starts with a '/' and is actually relative to an
             # external source dir, use that as base instead of topsrcdir.
-            if self.context.config.external_source_dir:
-                ret = mozpath.join(self.context.config.external_source_dir,
-                    self.value[1:])
-            if not ret or not os.path.exists(ret):
-                ret = mozpath.join(self.context.config.topsrcdir,
-                    self.value[1:])
+            if context.config.external_source_dir:
+                path = mozpath.join(context.config.external_source_dir,
+                                    value[1:])
+            if not path or not os.path.exists(path):
+                path = mozpath.join(context.config.topsrcdir,
+                                    value[1:])
         else:
-            ret = mozpath.join(self.srcdir, self.value)
-        return mozpath.normpath(ret)
-
-    def __unicode__(self):
-        # UserString doesn't implement a __unicode__ function at all, so add
-        # ours.
-        return self.data
+            path = mozpath.join(self.srcdir, value)
+        self.full_path = mozpath.normpath(path)
 
     @memoized_property
     def translated(self):
         """Returns the corresponding path in the objdir.
 
         Ideally, we wouldn't need this function, but the fact that both source
         path under topsrcdir and the external source dir end up mixed in the
         objdir (aka pseudo-rework), this is needed.
         """
-        if self.value.startswith('/'):
-            ret = mozpath.join(self.context.config.topobjdir, self.value[1:])
-        else:
-            ret = mozpath.join(self.context.objdir, self.value)
-        return mozpath.normpath(ret)
+        return ObjDirPath(self.context, '!%s' % self).full_path
+
 
-    def join(self, *p):
-        """Lazy mozpath.join(self, *p), returning a new SourcePath instance.
+class ObjDirPath(Path):
+    """Like Path, but limited to paths in the object directory."""
+    def __init__(self, context, value=None):
+        if not value.startswith('!'):
+            raise ValueError('Source paths are not allowed')
+        super(ObjDirPath, self).__init__(context, value)
 
-        In an ideal world, this wouldn't be required, but with the
-        external_source_dir business, and the fact that comm-central and
-        mozilla-central have directories in common, resolving a SourcePath
-        before doing mozpath.join doesn't work out properly.
-        """
-        return SourcePath(self.context, mozpath.join(self.value, *p))
+        if value.startswith('!/'):
+            path = mozpath.join(context.config.topobjdir,value[2:])
+        else:
+            path = mozpath.join(context.objdir, value[1:])
+        self.full_path = mozpath.normpath(path)
 
 
 @memoize
-def ContextDerivedTypedList(type, base_class=List):
+def ContextDerivedTypedList(klass, base_class=List):
     """Specialized TypedList for use with ContextDerivedValue types.
     """
-    assert issubclass(type, ContextDerivedValue)
-    class _TypedList(ContextDerivedValue, TypedList(type, base_class)):
+    assert issubclass(klass, ContextDerivedValue)
+    class _TypedList(ContextDerivedValue, TypedList(klass, base_class)):
         def __init__(self, context, iterable=[]):
-            class _Type(type):
-                def __new__(cls, obj):
-                    return type(context, obj)
-            self.TYPE = _Type
+            self.context = context
             super(_TypedList, self).__init__(iterable)
 
+        def normalize(self, e):
+            if not isinstance(e, klass):
+                e = klass(self.context, e)
+            return e
+
     return _TypedList
 
+@memoize
+def ContextDerivedTypedListWithItems(type, base_class=List):
+    """Specialized TypedList for use with ContextDerivedValue types.
+    """
+    class _TypedListWithItems(ContextDerivedTypedList(type, base_class)):
+        def __getitem__(self, name):
+            name = self.normalize(name)
+            return super(_TypedListWithItems, self).__getitem__(name)
+
+    return _TypedListWithItems
+
 
 BugzillaComponent = TypedNamedTuple('BugzillaComponent',
                         [('product', unicode), ('component', unicode)])
 
 
 class Files(SubContext):
     """Metadata attached to files.
 
@@ -581,53 +632,37 @@ VARIABLES = {
 
     'ANDROID_ECLIPSE_PROJECT_TARGETS': (dict, dict,
         """Defines Android Eclipse project targets.
 
         This variable should not be populated directly. Instead, it should
         populated by calling add_android_eclipse{_library}_project().
         """, 'export'),
 
-    'SOURCES': (StrictOrderingOnAppendListWithFlagsFactory({'no_pgo': bool, 'flags': List}), list,
+    'SOURCES': (ContextDerivedTypedListWithItems(Path, StrictOrderingOnAppendListWithFlagsFactory({'no_pgo': bool, 'flags': List})), list,
         """Source code files.
 
         This variable contains a list of source code files to compile.
         Accepts assembler, C, C++, Objective C/C++.
         """, None),
 
-    'GENERATED_SOURCES': (StrictOrderingOnAppendList, list,
-        """Generated source code files.
-
-        This variable contains a list of generated source code files to
-        compile. Accepts assembler, C, C++, Objective C/C++.
-        """, None),
-
     'FILES_PER_UNIFIED_FILE': (int, int,
         """The number of source files to compile into each unified source file.
 
         """, 'None'),
 
-    'UNIFIED_SOURCES': (StrictOrderingOnAppendList, list,
+    'UNIFIED_SOURCES': (ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), list,
         """Source code files that can be compiled together.
 
         This variable contains a list of source code files to compile,
         that can be concatenated all together and built as a single source
         file. This can help make the build faster and reduce the debug info
         size.
         """, None),
 
-    'GENERATED_UNIFIED_SOURCES': (StrictOrderingOnAppendList, list,
-        """Generated source code files that can be compiled together.
-
-        This variable contains a list of generated source code files to
-        compile, that can be concatenated all together, with UNIFIED_SOURCES,
-        and built as a single source file. This can help make the build faster
-        and reduce the debug info size.
-        """, None),
-
     'GENERATED_FILES': (StrictOrderingOnAppendListWithFlagsFactory({
                 'script': unicode,
                 'inputs': list }), list,
         """Generic generated files.
 
         This variable contains a list of files for the build system to
         generate at export time. The generation method may be declared
         with optional ``script`` and ``inputs`` flags on individual entries.
@@ -833,17 +868,17 @@ VARIABLES = {
         This variable only has an effect when building with MSVC.
         """, None),
 
     'GENERATED_INCLUDES' : (StrictOrderingOnAppendList, list,
         """Directories generated by the build system to be searched for include
         files by the compiler.
         """, None),
 
-    'HOST_SOURCES': (StrictOrderingOnAppendList, list,
+    'HOST_SOURCES': (ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), list,
         """Source code files to compile with the host compiler.
 
         This variable contains a list of source code files to compile.
         with the host compiler.
         """, None),
 
     'IS_COMPONENT': (bool, bool,
         """Whether the library contains a binary XPCOM component manifest.
@@ -1824,14 +1859,24 @@ DEPRECATION_HINTS = {
         Please use
 
             DIST_INSTALL = False
 
         instead of
 
             NO_DIST_INSTALL = True
     ''',
+
+    'GENERATED_SOURCES': '''
+        Please use
+
+            SOURCES += [ '!foo.cpp' ]
+
+        instead of
+
+            GENERATED_SOURCES += [ 'foo.cpp']
+    ''',
 }
 
 # Make sure that all template variables have a deprecation hint.
 for name in TEMPLATE_VARIABLES:
     if name not in DEPRECATION_HINTS:
         raise RuntimeError('Missing deprecation hint for %s' % name)
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -73,16 +73,20 @@ from .data import (
     WebIDLFile,
     XPIDLFile,
 )
 
 from .reader import SandboxValidationError
 
 from .context import (
     Context,
+    ObjDirPath,
+    SourcePath,
+    ObjDirPath,
+    Path,
     SubContext,
     TemplateContext,
 )
 
 
 class TreeMetadataEmitter(LoggingMixin):
     """Converts the executed mozbuild files into data structures.
 
@@ -707,25 +711,47 @@ class TreeMetadataEmitter(LoggingMixin):
         sub = cls(context)
         sub.input_path = mozpath.join(context.srcdir, '%s.in' % path)
         sub.output_path = mozpath.join(context.objdir, path)
         sub.relpath = path
 
         return sub
 
     def _process_sources(self, context, passthru):
+        sources = defaultdict(list)
+        gen_sources = defaultdict(list)
+        all_flags = {}
         for symbol in ('SOURCES', 'HOST_SOURCES', 'UNIFIED_SOURCES'):
-            for src in (context[symbol] or []):
-                if not os.path.exists(mozpath.join(context.srcdir, src)):
+            srcs = sources[symbol]
+            gen_srcs = gen_sources[symbol]
+            context_srcs = context.get(symbol, [])
+            for f in context_srcs:
+                full_path = f.full_path
+                if isinstance(f, SourcePath):
+                    srcs.append(full_path)
+                else:
+                    assert isinstance(f, ObjDirPath)
+                    gen_srcs.append(full_path)
+                if symbol == 'SOURCES':
+                    flags = context_srcs[f]
+                    if flags:
+                        all_flags[full_path] = flags
+
+                if isinstance(f, SourcePath) and not os.path.exists(full_path):
                     raise SandboxValidationError('File listed in %s does not '
-                        'exist: \'%s\'' % (symbol, src), context)
+                        'exist: \'%s\'' % (symbol, full_path), context)
+
+        # HOST_SOURCES and UNIFIED_SOURCES only take SourcePaths, so
+        # there should be no g