Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 07 Apr 2015 16:28:57 -0400
changeset 238148 7bcd3974f95b3c86e322a603acb404801cf88b78
parent 238147 eb88757dd17bc793f9eda2f171d4ed7665dfcbc0 (current diff)
parent 237996 078128c2600a0c695f8a16bf561b171a523daedd (diff)
child 238149 cb97221434c4834bdb1752b0172e8d6bdacfe4a5
push id58133
push userryanvm@gmail.com
push dateWed, 08 Apr 2015 16:59:36 +0000
treeherdermozilla-inbound@8911c111a6ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.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 m-c to fx-team. a=merge
dom/media/fmp4/AVCCDecoderModule.cpp
dom/media/fmp4/AVCCDecoderModule.h
layout/analysis/pixel-conversion.js
layout/analysis/simple-match.js
testing/taskcluster/tasks/builds/b2g_dolphin_user.yml
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Merge day clobber
\ No newline at end of file
+Hitting failures with Taskcluster-based jobs that seem like needs-clobber.
+
--- a/README.txt
+++ b/README.txt
@@ -22,8 +22,9 @@ You can download nightly development bui
 Keep in mind that nightly builds, which are used by Mozilla developers for
 testing, may be buggy. Firefox nightlies, for example, can be found at:
 
     ftp://ftp.mozilla.org/pub/firefox/nightly/latest-trunk/
             - or -
     http://nightly.mozilla.org/
 
 
+>>
--- a/b2g/app/ua-update.json.in
+++ b/b2g/app/ua-update.json.in
@@ -12,18 +12,16 @@
   // bug 826514, estadao.com.br
   "estadao.com.br": "\\(Mobile#(Android; Mobile",
   // bug 826711, bb.com.br
   "bb.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827622, bing.com
   "bing.com": "\\(Mobile#(Android; Mobile",
   // bug 827626, magazineluiza.com.br
   "magazineluiza.com.br": "\\(Mobile#(Android; Mobile",
-  // bug 827628, groupon.com.br
-  "groupon.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827633, hao123.com
   "hao123.com": "\\(Mobile#(Android; Mobile",
   // bug 827573, webmotors.com.br
   "webmotors.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827670, elpais.com.co
   "elpais.com.co": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 827674, avianca.com
   "avianca.com": "\\(Mobile#(Android; Mobile",
--- a/b2g/components/AlertsHelper.jsm
+++ b/b2g/components/AlertsHelper.jsm
@@ -20,16 +20,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "notificationStorage",
+                                   "@mozilla.org/notificationStorage;1",
+                                   "nsINotificationStorage");
+
 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
   return Cc["@mozilla.org/parentprocessmessagemanager;1"]
          .getService(Ci.nsIMessageListenerManager);
 });
 
 function debug(str) {
   //dump("=*= AlertsHelper.jsm : " + str + "\n");
 }
@@ -152,20 +156,23 @@ let AlertsHelper = {
               timestamp: listener.timestamp,
               data: listener.dataObj
             },
             Services.io.newURI(listener.target, null, null),
             Services.io.newURI(listener.manifestURL, null, null)
           );
         }
       }
+      if (detail.type === kDesktopNotificationClose && listener.dbId) {
+        notificationStorage.delete(listener.manifestURL, listener.dbId);
+      }
     }
 
     // we"re done with this notification
-    if (topic === kTopicAlertFinished) {
+    if (detail.type === kDesktopNotificationClose) {
       delete this._listeners[uid];
     }
   },
 
   registerListener: function(alertId, cookie, alertListener) {
     this._listeners[alertId] = { observer: alertListener, cookie: cookie };
   },
 
@@ -280,16 +287,17 @@ let AlertsHelper = {
     let listener = {
       mm: aMessage.target,
       title: data.title,
       text: data.text,
       manifestURL: details.manifestURL,
       imageURL: data.imageURL,
       lang: details.lang || undefined,
       id: details.id || undefined,
+      dbId: details.dbId || undefined,
       dir: details.dir || undefined,
       tag: details.tag || undefined,
       timestamp: details.timestamp || undefined,
       dataObj: dataObject || undefined
     };
     this.registerAppListener(data.uid, listener);
     this.showNotification(data.imageURL, data.title, data.text,
                           details.textClickable, null, data.uid, details.dir,
--- a/b2g/components/AlertsService.js
+++ b/b2g/components/AlertsService.js
@@ -149,17 +149,16 @@ AlertsService.prototype = {
               clicked: (topic === kTopicAlertClickCallback),
               title: listener.title,
               body: listener.text,
               imageURL: listener.imageURL,
               lang: listener.lang,
               dir: listener.dir,
               id: listener.id,
               tag: listener.tag,
-              dbId: listener.dbId,
               timestamp: listener.timestamp,
               data: listener.dataObj || undefined,
             },
             Services.io.newURI(data.target, null, null),
             Services.io.newURI(listener.manifestURL, null, null)
           );
         }
       }
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3b3407510d6e2c60242e8b9b5f2bc1783ca0a0e4"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
@@ -130,12 +130,12 @@
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="libnfcemu" path="external/libnfcemu" remote="b2g" revision="125ccf9bd5986c7728ea44508b3e1d1185ac028b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c5f8d282efe4a4e8b1e31a37300944e338e60e4f"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3b3407510d6e2c60242e8b9b5f2bc1783ca0a0e4"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
@@ -123,17 +123,17 @@
   <remote fetch="https://git.mozilla.org/external/t2m-foxfone" name="t2m"/>
   <default remote="caf" revision="LNX.LA.3.5.2.1.1" sync-j="4"/>
   <!-- Flame specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="2501e5940ba69ece7654ff85611c76ae5bda299c"/>
   <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="b83fc73de7b64594cd74b33e498bf08332b5d87b"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="0865bc4134b67220df4058625fba29305d6b10c3"/>
   <project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="fda40423ffa573dc6cafd3780515010cb2a086be"/>
-  <project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="26e78a979f3090dc196219e268467620b6c40ec5"/>
+  <project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="d5e53ed6f22fa06052351dc03510af9473af01ea"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="d61fc97258c8b0c362430dd2eb195dcc4d266f14"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="5b71e40213f650459e95d35b6f14af7e88d8ab62"/>
   <project name="platform_external_libnfc-nci" path="external/libnfc-nci" remote="t2m" revision="4186bdecb4dae911b39a8202252cc2310d91b0be"/>
   <project name="platform_external_libnfc-pn547" path="external/libnfc-pn547" remote="b2g" revision="5bb999b84b8adc14f6bea004d523ba258dea8188"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="65f5144987afff35a932262c0c5fad6ecce0c04a"/>
   <project name="platform/frameworks/base" path="frameworks/base" revision="da8e6bc53c8bc669da0bb627904d08aa293f2497"/>
   <project name="platform/frameworks/native" path="frameworks/native" revision="a46a9f1ac0ed5662d614c277cbb14eb3f332f365"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="7196881a0e9dd7bfbbcf0af64c8064e70f0fa094"/>
@@ -141,13 +141,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="2a1ded216a91bf62a72b1640cf01ab4998f37028"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="a74adcf8d88320d936daa8d20ce88ca0107fb916"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="9883ea57b0668d8f60dba025d4522dfa69a1fbfa"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="a558dc844bf5144fc38603fd8f4df8d9557052a5"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="57ee1320ed7b4a1a1274d8f3f6c177cd6b9becb2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
   <project name="platform/system/core" path="system/core" revision="42839aedcf70bf6bc92a3b7ea4a5cc9bf9aef3f9"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4c187c1f3a0dffd8e51a961735474ea703535b99"/>
 </manifest>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
@@ -140,13 +140,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="fa9ffd47948eb24466de227e48fe9c4a7c5e7711"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="cd76b19aafd4229ccf83853d02faef8c51ca8b34"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
   <project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>
 </manifest>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "c710bac533b76635161315bf907d004e000549cb", 
+        "git_revision": "84cbd4391fb7175d5380fa72c04d68873ce77e6d", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "89ec488d9617c749588db3c2e283debfa2f95596", 
+    "revision": "a7d22a159f8c412c7d8ebb371025b17563265c06", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
@@ -125,17 +125,17 @@
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Nexus 4 specific things -->
   <project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="b0a528d839cfd9d170d092fe3743b5252b4243a6"/>
   <project name="platform/hardware/qcom/bt" path="hardware/qcom/bt" revision="380945eaa249a2dbdde0daa4c8adb8ca325edba6"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="6f3b0272cefaffeaed2a7d2bb8f633059f163ddc"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="16da8262c997a5a0d797885788a64a0771b26910"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="689b476ba3eb46c34b81343295fe144a0e81a18e"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c710bac533b76635161315bf907d004e000549cb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
@@ -151,10 +151,10 @@
   <project name="hardware_qcom_display" path="hardware/qcom/display" remote="b2g" revision="c43952000d57f08b93a0e4fb77052871ce587976"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="028649652cd8f8f18cfb47d34bd78c435eb030ca"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="758a80fbb178b5663d4edbb46944b2dc553cb1ca"/>
   <project name="platform/hardware/qcom/msm8x74" path="hardware/qcom/msm8x74" revision="aa0124820e22302149b1f2db603a9a72f1972527"/>
   <project name="platform/hardware/qcom/power" path="hardware/qcom/power" revision="37499eb89f31233135ca73b830b067ab24dc1be2"/>
   <project name="platform/hardware/qcom/sensors" path="hardware/qcom/sensors" revision="3724fd91ef5183684d97e2bf1d7ff948faabe090"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2e54754cc0529d26ccac37ed291600048adbf6c0"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="71dfa8228ad0d6cdf6bac0426ac59404ab74b7f3"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
 </manifest>
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -547,18 +547,17 @@ nsContextMenu.prototype = {
       aNode = gContextMenuContentData.event.target;
       aRangeParent = gContextMenuContentData.event.rangeParent;
       aRangeOffset = gContextMenuContentData.event.rangeOffset;
       editFlags = gContextMenuContentData.editFlags;
     }
 
     const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     if (aNode.namespaceURI == xulNS ||
-        aNode.nodeType == Node.DOCUMENT_NODE ||
-        this.isDisabledForEvents(aNode)) {
+        aNode.nodeType == Node.DOCUMENT_NODE) {
       this.shouldDisplay = false;
       return;
     }
 
     // Initialize contextual info.
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
@@ -1529,26 +1528,16 @@ nsContextMenu.prototype = {
     return "contextMenu.target     = " + this.target + "\n" +
            "contextMenu.onImage    = " + this.onImage + "\n" +
            "contextMenu.onLink     = " + this.onLink + "\n" +
            "contextMenu.link       = " + this.link + "\n" +
            "contextMenu.inFrame    = " + this.inFrame + "\n" +
            "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
   },
 
-  isDisabledForEvents: function(aNode) {
-    let ownerDoc = aNode.ownerDocument;
-    return
-      ownerDoc.defaultView &&
-      ownerDoc.defaultView
-              .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-              .getInterface(Components.interfaces.nsIDOMWindowUtils)
-              .isNodeDisabledForEvents(aNode);
-  },
-
   isTargetATextBox: function(node) {
     if (node instanceof HTMLInputElement)
       return node.mozIsTextField(false);
 
     return (node instanceof HTMLTextAreaElement);
   },
 
   // Determines whether or not the separator with the specified ID should be
--- a/configure.in
+++ b/configure.in
@@ -45,17 +45,17 @@ dnl ====================================
 _SUBDIR_HOST_CFLAGS="$HOST_CFLAGS"
 _SUBDIR_HOST_CXXFLAGS="$HOST_CXXFLAGS"
 _SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS"
 _SUBDIR_CONFIG_ARGS="$ac_configure_args"
 
 dnl Set the version number of the libs included with mozilla
 dnl ========================================================
 MOZJPEG=62
-MOZPNG=10616
+MOZPNG=10617
 NSPR_VERSION=4
 NSPR_MINVER=4.10.8
 NSS_VERSION=3
 
 dnl Set the minimum version of toolkit libs used by mozilla
 dnl ========================================================
 GLIB_VERSION=2.22
 # 2_26 is the earliest version we can set GLIB_VERSION_MIN_REQUIRED.
--- a/dom/apps/OfflineCacheInstaller.jsm
+++ b/dom/apps/OfflineCacheInstaller.jsm
@@ -41,42 +41,53 @@ function enableOfflineCacheForApp(aPrinc
   Services.perms.addFromPrincipal(aPrincipal, 'offline-app',
                                   Ci.nsIPermissionManager.ALLOW_ACTION);
   // Prevent cache from being evicted:
   Services.perms.addFromPrincipal(aPrincipal, 'pin-app',
                                   Ci.nsIPermissionManager.ALLOW_ACTION);
 }
 
 
-function storeCache(applicationCache, url, file, itemType) {
+function storeCache(applicationCache, url, file, itemType, metadata) {
   let storage =
     Services.cache2.appCacheStorage(LoadContextInfo.default, applicationCache);
   let uri = Services.io.newURI(url, null, null);
+  let nowGMT = new Date().toGMTString();
+  metadata = metadata || {};
+  metadata.lastFetched = metadata.lastFetched || nowGMT;
+  metadata.lastModified = metadata.lastModified || nowGMT;
   storage.asyncOpenURI(uri, "", nsICacheStorage.OPEN_TRUNCATE, {
     onCacheEntryAvailable:
       function (cacheEntry, isNew, appCache, result) {
-        cacheEntry.setMetaDataElement('request-method', 'GET');
-        cacheEntry.setMetaDataElement('response-head', 'HTTP/1.1 200 OK\r\n');
+        cacheEntry.setMetaDataElement("request-method", "GET");
+        cacheEntry.setMetaDataElement("response-head",
+          "HTTP/1.1 200 OK\r\n" +
+          "Date: " + metadata.lastFetched + "\r\n" +
+          "Last-Modified: " + metadata.lastModified + "\r\n" +
+          "Cache-Control: no-cache\r\n");
 
         let outputStream = cacheEntry.openOutputStream(0);
 
-        // Input-Output stream machinery in order to push nsIFile content into cache
-        let inputStream = Cc['@mozilla.org/network/file-input-stream;1']
+        // Input-Output stream machinery in order to push nsIFile content into
+        // cache
+        let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
                             .createInstance(Ci.nsIFileInputStream);
         inputStream.init(file, 1, -1, null);
-        let bufferedOutputStream = Cc['@mozilla.org/network/buffered-output-stream;1']
-                                     .createInstance(Ci.nsIBufferedOutputStream);
+        let bufferedOutputStream =
+          Cc["@mozilla.org/network/buffered-output-stream;1"]
+            .createInstance(Ci.nsIBufferedOutputStream);
         bufferedOutputStream.init(outputStream, 1024);
         bufferedOutputStream.writeFrom(inputStream, inputStream.available());
         bufferedOutputStream.flush();
         bufferedOutputStream.close();
         inputStream.close();
 
+        cacheEntry.setExpirationTime(0);
         cacheEntry.markValid();
-        debug (file.path + ' -> ' + url + ' (' + itemType + ')');
+        debug (file.path + " -> " + url + " (" + itemType + ")");
         applicationCache.markEntry(url, itemType);
         cacheEntry.close();
       }
   });
 }
 
 function readFile(aFile, aPrincipal, aCallback) {
 
@@ -201,77 +212,98 @@ function parseAppCache(app, path, conten
   };
 }
 
 function installCache(app) {
   if (!app.cachePath) {
     return;
   }
 
-  let cacheDir = makeFile(app.cachePath)
+  let cacheDir = makeFile(app.cachePath);
   cacheDir.append(app.appId);
+
+  let resourcesMetadata = cacheDir.clone();
+  resourcesMetadata.append('resources_metadata.json');
+
   cacheDir.append('cache');
   if (!cacheDir.exists())
     return;
 
   let cacheManifest = cacheDir.clone();
   cacheManifest.append('manifest.appcache');
   if (!cacheManifest.exists())
     return;
 
   let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
-                    app.origin, app.localId, false);
-
-  enableOfflineCacheForApp(principal);
-
-  // Get the url for the manifest.
-  let appcacheURL = app.appcache_path;
+      app.origin, app.localId, false);
 
-  // The group ID contains application id and 'f' for not being hosted in
-  // a browser element, but a mozbrowser iframe.
-  // See netwerk/cache/nsDiskCacheDeviceSQL.cpp: AppendJARIdentifier
-  let groupID = appcacheURL + '#' + app.localId+ '+f';
-  let applicationCache = applicationCacheService.createApplicationCache(groupID);
-  applicationCache.activate();
+  // If the build has been correctly configured, this should not happen!
+  // If we install the cache anyway, it won't be updateable. If we don't install
+  // it, the application won't be useable offline.
+  let metadataLoaded;
+  if (!resourcesMetadata.exists()) {
+    // Not debug, since this is something that should be logged always!
+    dump("OfflineCacheInstaller: App " + app.appId + " does have an app cache" +
+         " but does not have a resources_metadata.json file!");
+    metadataLoaded = Promise.resolve({});
+  } else {
+    metadataLoaded = new Promise(
+      (resolve, reject) =>
+        readFile(resourcesMetadata, principal, content => resolve(JSON.parse(content))));
+  }
 
-  readFile(cacheManifest, principal, function readAppCache(content) {
-    let entries = parseAppCache(app, cacheManifest.path, content);
+  metadataLoaded.then(function(metadata) {
+    enableOfflineCacheForApp(principal);
+
+    // Get the url for the manifest.
+    let appcacheURL = app.appcache_path;
+
+    // The group ID contains application id and 'f' for not being hosted in
+    // a browser element, but a mozbrowser iframe.
+    // See netwerk/cache/nsDiskCacheDeviceSQL.cpp: AppendJARIdentifier
+    let groupID = appcacheURL + '#' + app.localId+ '+f';
+    let applicationCache = applicationCacheService.createApplicationCache(groupID);
+    applicationCache.activate();
+
+    readFile(cacheManifest, principal, function readAppCache(content) {
+      let entries = parseAppCache(app, cacheManifest.path, content);
 
-    entries.urls.forEach(function processCachedFile(url) {
-      // Get this nsIFile from cache folder for this URL
-      // We have absolute urls, so remove the origin part to locate the
-      // files.
-      let path = url.replace(app.origin.spec, '');
-      let file = cacheDir.clone();
-      let paths = path.split('/');
-      paths.forEach(file.append);
+      entries.urls.forEach(function processCachedFile(url) {
+        // Get this nsIFile from cache folder for this URL
+        // We have absolute urls, so remove the origin part to locate the
+        // files.
+        let path = url.replace(app.origin.spec, '');
+        let file = cacheDir.clone();
+        let paths = path.split('/');
+        paths.forEach(file.append);
 
-      if (!file.exists()) {
-        let msg = 'File ' + file.path + ' exists in the manifest but does ' +
-                  'not points to a real file.';
-        throw new Error(msg);
-      }
+        if (!file.exists()) {
+          let msg = 'File ' + file.path + ' exists in the manifest but does ' +
+                    'not points to a real file.';
+          throw new Error(msg);
+        }
 
-      let itemType = nsIApplicationCache.ITEM_EXPLICIT;
-      if (entries.fallbacks.indexOf(url) > -1) {
-        debug('add fallback: ' + url + '\n');
-        itemType |= nsIApplicationCache.ITEM_FALLBACK;
-      }
-      storeCache(applicationCache, url, file, itemType);
-    });
+        let itemType = nsIApplicationCache.ITEM_EXPLICIT;
+        if (entries.fallbacks.indexOf(url) > -1) {
+          debug('add fallback: ' + url + '\n');
+          itemType |= nsIApplicationCache.ITEM_FALLBACK;
+        }
+        storeCache(applicationCache, url, file, itemType, metadata[path]);
+      });
 
-    let array = new MutableArray();
-    entries.namespaces.forEach(function processNamespace([type, spec, data]) {
-      debug('add namespace: ' + type + ' - ' + spec + ' - ' + data + '\n');
-      array.appendElement(new Namespace(type, spec, data), false);
+      let array = new MutableArray();
+      entries.namespaces.forEach(function processNamespace([type, spec, data]) {
+        debug('add namespace: ' + type + ' - ' + spec + ' - ' + data + '\n');
+        array.appendElement(new Namespace(type, spec, data), false);
+      });
+      applicationCache.addNamespaces(array);
+
+      storeCache(applicationCache, appcacheURL, cacheManifest,
+                 nsIApplicationCache.ITEM_MANIFEST);
     });
-    applicationCache.addNamespaces(array);
-
-    storeCache(applicationCache, appcacheURL, cacheManifest,
-               nsIApplicationCache.ITEM_MANIFEST);
   });
 }
 
 
 // Public API
 
 this.OfflineCacheInstaller = {
   installCache: installCache
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -120,18 +120,17 @@ public:
 
   nsresult ParseURL(const nsAString& aURL);
   nsresult InitializeConnection();
 
   // These methods when called can release the WebSocket object
   void FailConnection(uint16_t reasonCode,
                       const nsACString& aReasonString = EmptyCString());
   nsresult CloseConnection(uint16_t reasonCode,
-                           const nsACString& aReasonString = EmptyCString(),
-                           bool aCanceling = false);
+                           const nsACString& aReasonString = EmptyCString());
   nsresult Disconnect();
   void DisconnectInternal();
 
   nsresult ConsoleError();
   nsresult PrintErrorOnConsole(const char* aBundleURI,
                                const char16_t* aError,
                                const char16_t** aFormatStrings,
                                uint32_t aFormatStringsLen);
@@ -380,48 +379,16 @@ WebSocketImpl::PrintErrorOnConsole(const
   rv = console->LogMessage(errorObject);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 namespace {
 
-class CloseRunnable final : public WorkerMainThreadRunnable
-{
-public:
-  CloseRunnable(WebSocketImpl* aImpl, uint16_t aReasonCode,
-                const nsACString& aReasonString)
-    : WorkerMainThreadRunnable(aImpl->mWorkerPrivate)
-    , mImpl(aImpl)
-    , mReasonCode(aReasonCode)
-    , mReasonString(aReasonString)
-    , mRv(NS_ERROR_FAILURE)
-  { }
-
-  bool MainThreadRun() override
-  {
-    mRv = mImpl->mChannel->Close(mReasonCode, mReasonString);
-    return true;
-  }
-
-  nsresult ErrorCode() const
-  {
-    return mRv;
-  }
-
-private:
-  // A raw pointer because this runnable is sync.
-  WebSocketImpl* mImpl;
-
-  uint16_t mReasonCode;
-  const nsACString& mReasonString;
-  nsresult mRv;
-};
-
 class CancelWebSocketRunnable final : public nsRunnable
 {
 public:
   CancelWebSocketRunnable(nsIWebSocketChannel* aChannel, uint16_t aReasonCode,
                           const nsACString& aReasonString)
     : mChannel(aChannel)
     , mReasonCode(aReasonCode)
     , mReasonString(aReasonString)
@@ -464,21 +431,19 @@ public:
 private:
   WebSocketImpl* mImpl;
 };
 
 } // anonymous namespace
 
 nsresult
 WebSocketImpl::CloseConnection(uint16_t aReasonCode,
-                               const nsACString& aReasonString,
-                               bool aCanceling)
+                               const nsACString& aReasonString)
 {
   AssertIsOnTargetThread();
-  MOZ_ASSERT(!NS_IsMainThread() || !aCanceling);
 
   if (mDisconnectingOrDisconnected) {
     return NS_OK;
   }
 
   // If this method is called because the worker is going away, we will not
   // receive the OnStop() method and we have to disconnect the WebSocket and
   // release the WorkerFeature.
@@ -495,26 +460,19 @@ WebSocketImpl::CloseConnection(uint16_t 
     mWebSocket->SetReadyState(WebSocket::CLOSING);
 
     // The channel has to be closed on the main-thread.
 
     if (NS_IsMainThread()) {
       return mChannel->Close(aReasonCode, aReasonString);
     }
 
-    if (aCanceling) {
-      nsRefPtr<CancelWebSocketRunnable> runnable =
-        new CancelWebSocketRunnable(mChannel, aReasonCode, aReasonString);
-      return NS_DispatchToMainThread(runnable);
-    }
-
-    nsRefPtr<CloseRunnable> runnable =
-      new CloseRunnable(this, aReasonCode, aReasonString);
-    runnable->Dispatch(mWorkerPrivate->GetJSContext());
-    return runnable->ErrorCode();
+    nsRefPtr<CancelWebSocketRunnable> runnable =
+      new CancelWebSocketRunnable(mChannel, aReasonCode, aReasonString);
+    return NS_DispatchToMainThread(runnable);
   }
 
   // No channel, but not disconnected: canceled or failed early
   //
   MOZ_ASSERT(readyState == WebSocket::CONNECTING,
              "Should only get here for early websocket cancel/error");
 
   // Server won't be sending us a close code, so use what's passed in here.
@@ -2028,17 +1986,17 @@ public:
 
     if (aStatus >= Canceling) {
       {
         MutexAutoLock lock(mWebSocketImpl->mMutex);
         mWebSocketImpl->mWorkerShuttingDown = true;
       }
 
       mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY,
-                                      EmptyCString(), true);
+                                      EmptyCString());
     }
 
     return true;
   }
 
   bool Suspend(JSContext* aCx) override
   {
     {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -335,29 +335,28 @@ nsFrameLoader::ReallyStartLoadingInterna
     js::ProfileEntry::Category::OTHER);
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mRemoteFrame) {
-    if (!mRemoteBrowser) {
-      TryRemoteBrowser();
-
-      if (!mRemoteBrowser) {
+    if (!mRemoteBrowser && !TryRemoteBrowser()) {
         NS_WARNING("Couldn't create child process for iframe.");
         return NS_ERROR_FAILURE;
-      }
     }
 
-    if (mRemoteBrowserShown || ShowRemoteFrame(ScreenIntSize(0, 0))) {
-      // FIXME get error codes from child
-      mRemoteBrowser->LoadURL(mURIToLoad);
-    } else {
+    // Execute pending frame scripts before loading URL
+    EnsureMessageManager();
+
+    // FIXME get error codes from child
+    mRemoteBrowser->LoadURL(mURIToLoad);
+    
+    if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) {
       NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n");
     }
 
     return NS_OK;
   }
 
   NS_ASSERTION(mDocShell,
                "MaybeCreateDocShell succeeded with a null mDocShell");
@@ -827,23 +826,19 @@ nsFrameLoader::MarginsChanged(uint32_t a
 }
 
 bool
 nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
                                nsSubDocumentFrame *aFrame)
 {
   NS_ASSERTION(mRemoteFrame, "ShowRemote only makes sense on remote frames.");
 
-  if (!mRemoteBrowser) {
-    TryRemoteBrowser();
-
-    if (!mRemoteBrowser) {
-      NS_ERROR("Couldn't create child process.");
-      return false;
-    }
+  if (!mRemoteBrowser && !TryRemoteBrowser()) {
+    NS_ERROR("Couldn't create child process.");
+    return false;
   }
 
   // FIXME/bug 589337: Show()/Hide() is pretty expensive for
   // cross-process layers; need to figure out what behavior we really
   // want here.  For now, hack.
   if (!mRemoteBrowserShown) {
     if (!mOwnerContent ||
         !mOwnerContent->GetComposedDoc()) {
@@ -2257,38 +2252,41 @@ nsFrameLoader::TryRemoteBrowser()
     rv = context.SetTabContextForBrowserFrame(containingApp);
   } else {
     rv = context.SetTabContextForNormalFrame();
   }
   NS_ENSURE_TRUE(rv, false);
 
   nsCOMPtr<Element> ownerElement = mOwnerContent;
   mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement, openerContentParent);
-  if (mRemoteBrowser) {
-    mChildID = mRemoteBrowser->Manager()->ChildID();
-    nsCOMPtr<nsIDocShellTreeItem> rootItem;
-    parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
-    nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
-    nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
-
-    if (rootChromeWin) {
-      nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
-      rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
-      mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
-    }
-
-    mContentParent = mRemoteBrowser->Manager();
-
-    if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
-                                   nsGkAtoms::mozpasspointerevents,
-                                   nsGkAtoms::_true,
-                                   eCaseMatters)) {
-      unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
-    }
+  if (!mRemoteBrowser) {
+    return false;
   }
+
+  mContentParent = mRemoteBrowser->Manager();
+  mChildID = mRemoteBrowser->Manager()->ChildID();
+
+  nsCOMPtr<nsIDocShellTreeItem> rootItem;
+  parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
+  nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
+  nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
+
+  if (rootChromeWin) {
+    nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+    rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+    mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
+  }
+
+  if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
+                                 nsGkAtoms::mozpasspointerevents,
+                                 nsGkAtoms::_true,
+                                 eCaseMatters)) {
+    unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
+  }
+
   return true;
 }
 
 mozilla::dom::PBrowserParent*
 nsFrameLoader::GetRemoteBrowser()
 {
   return mRemoteBrowser;
 }
@@ -2523,17 +2521,17 @@ nsFrameLoader::EnsureMessageManager()
         mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                    nsGkAtoms::forcemessagemanager,
                                    nsGkAtoms::_true, eCaseMatters))) {
     return NS_OK;
   }
 
   bool useRemoteProcess = ShouldUseRemoteProcess();
   if (mMessageManager) {
-    if (useRemoteProcess && mRemoteBrowserShown) {
+    if (useRemoteProcess && mRemoteBrowser) {
       mMessageManager->InitWithCallback(this);
     }
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
     do_QueryInterface(GetOwnerDoc()->GetWindow());
   nsCOMPtr<nsIMessageBroadcaster> parentManager;
@@ -2548,17 +2546,17 @@ nsFrameLoader::EnsureMessageManager()
     }
 
     if (!parentManager) {
       chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
     }
   }
 
   if (useRemoteProcess) {
-    mMessageManager = new nsFrameMessageManager(mRemoteBrowserShown ? this : nullptr,
+    mMessageManager = new nsFrameMessageManager(mRemoteBrowser ? this : nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
                                                 MM_CHROME);
   } else {
     mMessageManager = new nsFrameMessageManager(nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
                                                 MM_CHROME);
 
     mChildMessageManager =
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -255,16 +255,17 @@ NS_IMETHODIMP_(nsIContent *)
 nsInProcessTabChildGlobal::GetOwnerContent()
 {
   return mOwner;
 }
 
 nsresult
 nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor)
 {
+  aVisitor.mForceContentDispatch = true;
   aVisitor.mCanHandle = true;
 
 #ifdef DEBUG
   if (mOwner) {
     nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(mOwner);
     nsRefPtr<nsFrameLoader> fl = owner->GetFrameLoader();
     if (fl) {
       NS_ASSERTION(this == fl->GetTabChildGlobalAsEventTarget(),
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -1790,16 +1790,29 @@ nsXMLHttpRequest::Open(const nsACString&
     }
   }
 
   ChangeState(XML_HTTP_REQUEST_OPENED);
 
   return rv;
 }
 
+void
+nsXMLHttpRequest::PopulateNetworkInterfaceId()
+{
+  if (mNetworkInterfaceId.IsEmpty()) {
+    return;
+  }
+  nsCOMPtr<nsIHttpChannelInternal> channel(do_QueryInterface(mChannel));
+  if (!channel) {
+    return;
+  }
+  channel->SetNetworkInterfaceId(mNetworkInterfaceId);
+}
+
 /*
  * "Copy" from a stream.
  */
 NS_METHOD
 nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
                                    void* closure,
                                    const char* fromRawSegment,
                                    uint32_t toOffset,
@@ -2607,16 +2620,18 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
   return Send(aBody, Nullable<RequestBody>());
 }
 
 nsresult
 nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
 {
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
+  PopulateNetworkInterfaceId();
+
   nsresult rv = CheckInnerWindowCorrectness();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Return error if we're already processing a request
   if (XML_HTTP_REQUEST_SENT & mState) {
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/base/nsXMLHttpRequest.h
+++ b/dom/base/nsXMLHttpRequest.h
@@ -423,16 +423,21 @@ private:
   }
   nsresult Send(const RequestBody& aBody)
   {
     return Send(Nullable<RequestBody>(aBody));
   }
 
   bool IsDeniedCrossSiteRequest();
 
+  // Tell our channel what network interface ID we were told to use.
+  // If it's an HTTP channel and we were told to use a non-default
+  // interface ID.
+  void PopulateNetworkInterfaceId();
+
 public:
   void Send(JSContext* /*aCx*/, ErrorResult& aRv)
   {
     aRv = Send(Nullable<RequestBody>());
   }
   void Send(JSContext* /*aCx*/,
             const mozilla::dom::ArrayBuffer& aArrayBuffer,
             ErrorResult& aRv)
@@ -533,16 +538,26 @@ public:
   bool MozAnon();
   bool MozSystem();
 
   nsIChannel* GetChannel()
   {
     return mChannel;
   }
 
+  void GetNetworkInterfaceId(nsACString& aId) const
+  {
+    aId = mNetworkInterfaceId;
+  }
+
+  void SetNetworkInterfaceId(const nsACString& aId)
+  {
+    mNetworkInterfaceId = aId;
+  }
+
   // We need a GetInterface callable from JS for chrome JS
   void GetInterface(JSContext* aCx, nsIJSID* aIID,
                     JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
 
   // This creates a trusted readystatechange event, which is not cancelable and
   // doesn't bubble.
   nsresult CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent);
   void DispatchProgressEvent(mozilla::DOMEventTargetHelper* aTarget,
@@ -757,16 +772,20 @@ protected:
   // Ordering of OnProgress and OnDataAvailable is undefined.
   int64_t mLoadTransferred;
   nsCOMPtr<nsITimer> mProgressNotifier;
   void HandleProgressTimerCallback();
 
   bool mIsSystem;
   bool mIsAnon;
 
+  // A platform-specific identifer to represent the network interface
+  // that this request is associated with.
+  nsCString mNetworkInterfaceId;
+
   /**
    * Close the XMLHttpRequest's channels and dispatch appropriate progress
    * events.
    *
    * @param aType The progress event type.
    * @param aFlag A XML_HTTP_REQUEST_* state flag defined in
    *              nsXMLHttpRequest.cpp.
    */
--- a/dom/canvas/CanvasImageCache.cpp
+++ b/dom/canvas/CanvasImageCache.cpp
@@ -294,17 +294,17 @@ CanvasImageCache::Lookup(Element* aImage
 
   nsCOMPtr<imgIRequest> request;
   entry->mData->mILC->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(request));
   if (request != entry->mData->mRequest)
     return nullptr;
 
   gImageCache->MarkUsed(entry->mData);
 
-  *aSize = gfx::ToIntSize(entry->mData->mSize);
+  *aSize = entry->mData->mSize;
   return entry->mData->mSourceSurface;
 }
 
 SourceSurface*
 CanvasImageCache::SimpleLookup(Element* aImage)
 {
   if (!gImageCache)
     return nullptr;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4315,17 +4315,17 @@ CanvasRenderingContext2D::DrawImage(cons
     if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
       // Spec says to silently do nothing if the element is still loading.
       if (!res.mIsStillLoading) {
         error.Throw(NS_ERROR_NOT_AVAILABLE);
       }
       return;
     }
 
-    imgSize = gfx::ToIntSize(res.mSize);
+    imgSize = res.mSize;
 
     // Scale sw/sh based on aspect ratio
     if (image.IsHTMLVideoElement()) {
       HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
       int32_t displayWidth = video->VideoWidth();
       int32_t displayHeight = video->VideoHeight();
       sw *= (double)imgSize.width / (double)displayWidth;
       sh *= (double)imgSize.height / (double)displayHeight;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -50,17 +50,16 @@
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
-#include "nsDirectoryServiceDefs.h"
 #elif defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #include "mozilla/SandboxInfo.h"
 #elif defined(XP_MACOSX)
 #include "mozilla/Sandbox.h"
 #endif
 #endif
 
@@ -1075,86 +1074,16 @@ ContentChild::AllocPBackgroundChild(Tran
 
 PProcessHangMonitorChild*
 ContentChild::AllocPProcessHangMonitorChild(Transport* aTransport,
                                             ProcessId aOtherProcess)
 {
     return CreateHangMonitorChild(aTransport, aOtherProcess);
 }
 
-#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
-static void
-SetUpSandboxEnvironment()
-{
-    // Set up a low integrity temp directory. This only makes sense if the
-    // delayed integrity level for the content process is INTEGRITY_LEVEL_LOW.
-    nsresult rv;
-    nsCOMPtr<nsIProperties> directoryService =
-        do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    nsCOMPtr<nsIFile> lowIntegrityTemp;
-    rv = directoryService->Get(NS_WIN_LOW_INTEGRITY_TEMP, NS_GET_IID(nsIFile),
-                               getter_AddRefs(lowIntegrityTemp));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    // Undefine returns a failure if the property is not already set.
-    unused << directoryService->Undefine(NS_OS_TEMP_DIR);
-    rv = directoryService->Set(NS_OS_TEMP_DIR, lowIntegrityTemp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    // Set TEMP and TMP environment variables.
-    nsAutoString lowIntegrityTempPath;
-    rv = lowIntegrityTemp->GetPath(lowIntegrityTempPath);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    bool setOK = SetEnvironmentVariableW(L"TEMP", lowIntegrityTempPath.get());
-    NS_WARN_IF_FALSE(setOK, "Failed to set TEMP to low integrity temp path");
-    setOK = SetEnvironmentVariableW(L"TMP", lowIntegrityTempPath.get());
-    NS_WARN_IF_FALSE(setOK, "Failed to set TMP to low integrity temp path");
-}
-
-void
-ContentChild::CleanUpSandboxEnvironment()
-{
-    // Sandbox environment is only currently a low integrity temp, which only
-    // makes sense for sandbox pref level 1 (and will eventually not be needed
-    // at all, once all file access is via chrome/broker process).
-    if (Preferences::GetInt("security.sandbox.content.level") != 1) {
-        return;
-    }
-
-    nsresult rv;
-    nsCOMPtr<nsIProperties> directoryService =
-        do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    nsCOMPtr<nsIFile> lowIntegrityTemp;
-    rv = directoryService->Get(NS_WIN_LOW_INTEGRITY_TEMP, NS_GET_IID(nsIFile),
-                               getter_AddRefs(lowIntegrityTemp));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-    }
-
-    // Don't check the return value as the directory will only have been created
-    // if it has been used.
-    unused << lowIntegrityTemp->Remove(/* aRecursive */ true);
-}
-#endif
-
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
 
 #include <stdlib.h>
 
 static bool
 GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath, nsCString &aAppDir)
 {
   nsAutoCString appPath;
@@ -1263,22 +1192,16 @@ ContentChild::RecvSetProcessSandbox()
     // Otherwise, sandboxing is best-effort.
     if (!SandboxInfo::Get().CanSandboxContent()) {
         return true;
     }
 #endif
     SetContentProcessSandbox();
 #elif defined(XP_WIN)
     mozilla::SandboxTarget::Instance()->StartSandbox();
-    // Sandbox environment is only currently a low integrity temp, which only
-    // makes sense for sandbox pref level 1 (and will eventually not be needed
-    // at all, once all file access is via chrome/broker process).
-    if (Preferences::GetInt("security.sandbox.content.level") == 1) {
-        SetUpSandboxEnvironment();
-    }
 #elif defined(XP_MACOSX)
     StartMacOSContentSandbox();
 #endif
 #endif
     return true;
 }
 
 bool
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -130,21 +130,16 @@ public:
     PImageBridgeChild*
     AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) override;
 
     PProcessHangMonitorChild*
     AllocPProcessHangMonitorChild(Transport* aTransport,
                                   ProcessId aOtherProcess) override;
 
-#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
-    // Cleans up any resources used by the process when sandboxed.
-    void CleanUpSandboxEnvironment();
-#endif
-
     virtual bool RecvSetProcessSandbox() override;
 
     PBackgroundChild*
     AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
                           override;
 
     virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                               const IPCTabContext& aContext,
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -3,42 +3,122 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ipc/IOThreadChild.h"
 
 #include "ContentProcess.h"
 
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+#include "mozilla/Preferences.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#endif
+
 using mozilla::ipc::IOThreadChild;
 
 namespace mozilla {
 namespace dom {
 
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+static already_AddRefed<nsIFile>
+GetLowIntegrityTemp()
+{
+  MOZ_ASSERT(nsDirectoryService::gService,
+    "GetLowIntegrityTemp relies on nsDirectoryService being initialized");
+
+  // A low integrity temp only currently makes sense for sandbox pref level 1.
+  if (Preferences::GetInt("security.sandbox.content.level") != 1) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIFile> lowIntegrityTemp;
+  nsresult rv = nsDirectoryService::gService->Get(NS_WIN_LOW_INTEGRITY_TEMP,
+                                                  NS_GET_IID(nsIFile),
+                                                  getter_AddRefs(lowIntegrityTemp));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  return lowIntegrityTemp.forget();
+}
+
+static void
+SetUpSandboxEnvironment()
+{
+  MOZ_ASSERT(nsDirectoryService::gService,
+    "SetUpSandboxEnvironment relies on nsDirectoryService being initialized");
+
+  // Setup to use a low integrity temp if available.
+  nsCOMPtr<nsIFile> lowIntegrityTemp = GetLowIntegrityTemp();
+  if (!lowIntegrityTemp) {
+    return;
+  }
+
+  // Undefine returns a failure if the property is not already set.
+  unused << nsDirectoryService::gService->Undefine(NS_OS_TEMP_DIR);
+  nsresult rv = nsDirectoryService::gService->Set(NS_OS_TEMP_DIR, lowIntegrityTemp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  // Set TEMP and TMP environment variables.
+  nsAutoString lowIntegrityTempPath;
+  rv = lowIntegrityTemp->GetPath(lowIntegrityTempPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  bool setOK = SetEnvironmentVariableW(L"TEMP", lowIntegrityTempPath.get());
+  NS_WARN_IF_FALSE(setOK, "Failed to set TEMP to low integrity temp path");
+  setOK = SetEnvironmentVariableW(L"TMP", lowIntegrityTempPath.get());
+  NS_WARN_IF_FALSE(setOK, "Failed to set TMP to low integrity temp path");
+}
+
+static void
+CleanUpSandboxEnvironment()
+{
+  // Remove low integrity temp if it exists.
+  nsCOMPtr<nsIFile> lowIntegrityTemp = GetLowIntegrityTemp();
+  if (!lowIntegrityTemp) {
+    return;
+  }
+
+  // Don't check the return value as the directory will only have been created
+  // if it has been used.
+  unused << lowIntegrityTemp->Remove(/* aRecursive */ true);
+}
+#endif
+
 void
 ContentProcess::SetAppDir(const nsACString& aPath)
 {
   mXREEmbed.SetAppDir(aPath);
 }
 
 bool
 ContentProcess::Init()
 {
     mContent.Init(IOThreadChild::message_loop(),
                          ParentPid(),
                          IOThreadChild::channel());
     mXREEmbed.Start();
     mContent.InitXPCOM();
+
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+    SetUpSandboxEnvironment();
+#endif
     
     return true;
 }
 
 void
 ContentProcess::CleanUp()
 {
 #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
-    mContent.CleanUpSandboxEnvironment();
+    CleanUpSandboxEnvironment();
 #endif
     mXREEmbed.Stop();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1688,16 +1688,20 @@ TabChild::IsRootContentDocument()
 
     return !HasAppOwnerApp();
 }
 
 bool
 TabChild::RecvLoadURL(const nsCString& aURI,
                       const BrowserConfiguration& aConfiguration)
 {
+    if (!InitTabChildGlobal()) {
+      return false;
+    }
+
     SetProcessNameToAppName();
 
     nsresult rv = WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(),
                                            nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
                                            nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER,
                                            nullptr, nullptr, nullptr);
     if (NS_FAILED(rv)) {
         NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?");
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -261,17 +261,16 @@ TabParent::TabParent(nsIContentParent* a
   , mIMECompositionStart(0)
   , mIMESeqno(0)
   , mIMECompositionRectOffset(0)
   , mRect(0, 0, 0, 0)
   , mDimensions(0, 0)
   , mOrientation(0)
   , mDPI(0)
   , mDefaultScale(0)
-  , mShown(false)
   , mUpdatedDimensions(false)
   , mChromeOffset(0, 0)
   , mManager(aManager)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
@@ -749,23 +748,16 @@ TabParent::LoadURL(nsIURI* aURI)
 
     if (mCreatingWindow) {
         // Don't send the message if the child wants to load its own URL.
         MOZ_ASSERT(mDelayedURL.IsEmpty());
         mDelayedURL = spec;
         return;
     }
 
-    if (!mShown) {
-        NS_WARNING(nsPrintfCString("TabParent::LoadURL(%s) called before "
-                                   "Show(). Ignoring LoadURL.\n",
-                                   spec.get()).get());
-        return;
-    }
-
     uint32_t appId = OwnOrContainingAppId();
     if (mSendOfflineStatus && NS_IsAppOffline(appId)) {
       // If the app is offline in the parent process
       // pass that state to the child process as well
       unused << SendAppOfflineStatus(appId, true);
     }
     mSendOfflineStatus = false;
 
@@ -819,18 +811,16 @@ TabParent::LoadURL(nsIURI* aURI)
             }
         }
     }
 }
 
 void
 TabParent::Show(const ScreenIntSize& size, bool aParentIsActive)
 {
-    // sigh
-    mShown = true;
     mDimensions = size;
     if (mIsDestroyed) {
         return;
     }
 
     TextureFactoryIdentifier textureFactoryIdentifier;
     uint64_t layersId = 0;
     bool success = false;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -424,17 +424,16 @@ protected:
     LayoutDeviceIntRect mIMECaretRect;
     LayoutDeviceIntRect mIMEEditorRect;
 
     nsIntRect mRect;
     ScreenIntSize mDimensions;
     ScreenOrientation mOrientation;
     float mDPI;
     CSSToLayoutDeviceScale mDefaultScale;
-    bool mShown;
     bool mUpdatedDimensions;
     nsIntPoint mChromeOffset;
 
 private:
     already_AddRefed<nsFrameLoader> GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
     layout::RenderFrameParent* GetRenderFrame();
     nsRefPtr<nsIContentParent> mManager;
     void TryCacheDPIAndScale();
deleted file mode 100644
--- a/dom/media/fmp4/AVCCDecoderModule.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 "AVCCDecoderModule.h"
-#include "ImageContainer.h"
-#include "MediaTaskQueue.h"
-#include "mp4_demuxer/DecoderData.h"
-#include "mp4_demuxer/AnnexB.h"
-#include "mp4_demuxer/H264.h"
-
-namespace mozilla
-{
-
-class AVCCMediaDataDecoder : public MediaDataDecoder {
-public:
-
-  AVCCMediaDataDecoder(PlatformDecoderModule* aPDM,
-                       const mp4_demuxer::VideoDecoderConfig& aConfig,
-                       layers::LayersBackend aLayersBackend,
-                       layers::ImageContainer* aImageContainer,
-                       FlushableMediaTaskQueue* aVideoTaskQueue,
-                       MediaDataDecoderCallback* aCallback);
-
-  virtual ~AVCCMediaDataDecoder();
-
-  virtual nsresult Init() override;
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
-  virtual bool IsWaitingMediaResources() override;
-  virtual bool IsDormantNeeded() override;
-  virtual void AllocateMediaResources() override;
-  virtual void ReleaseMediaResources() override;
-  virtual bool IsHardwareAccelerated() const override;
-
-private:
-  // Will create the required MediaDataDecoder if we have a AVC SPS.
-  // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
-  // will set mError accordingly.
-  nsresult CreateDecoder();
-  nsresult CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample);
-  nsresult CheckForSPSChange(mp4_demuxer::MP4Sample* aSample);
-  void UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData);
-
-  nsRefPtr<PlatformDecoderModule> mPDM;
-  mp4_demuxer::VideoDecoderConfig mCurrentConfig;
-  layers::LayersBackend mLayersBackend;
-  nsRefPtr<layers::ImageContainer> mImageContainer;
-  nsRefPtr<FlushableMediaTaskQueue> mVideoTaskQueue;
-  MediaDataDecoderCallback* mCallback;
-  nsRefPtr<MediaDataDecoder> mDecoder;
-  nsresult mLastError;
-};
-
-AVCCMediaDataDecoder::AVCCMediaDataDecoder(PlatformDecoderModule* aPDM,
-                                           const mp4_demuxer::VideoDecoderConfig& aConfig,
-                                           layers::LayersBackend aLayersBackend,
-                                           layers::ImageContainer* aImageContainer,
-                                           FlushableMediaTaskQueue* aVideoTaskQueue,
-                                           MediaDataDecoderCallback* aCallback)
-  : mPDM(aPDM)
-  , mCurrentConfig(aConfig)
-  , mLayersBackend(aLayersBackend)
-  , mImageContainer(aImageContainer)
-  , mVideoTaskQueue(aVideoTaskQueue)
-  , mCallback(aCallback)
-  , mDecoder(nullptr)
-  , mLastError(NS_OK)
-{
-  CreateDecoder();
-}
-
-AVCCMediaDataDecoder::~AVCCMediaDataDecoder()
-{
-}
-
-nsresult
-AVCCMediaDataDecoder::Init()
-{
-  if (mDecoder) {
-    return mDecoder->Init();
-  }
-  return mLastError;
-}
-
-nsresult
-AVCCMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
-{
-  if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
-    return NS_ERROR_FAILURE;
-  }
-  nsresult rv;
-  if (!mDecoder) {
-    // It is not possible to create an AVCC H264 decoder without SPS.
-    // As such, creation will fail if the extra_data just extracted doesn't
-    // contain a SPS.
-    rv = CreateDecoderAndInit(aSample);
-    if (rv == NS_ERROR_NOT_INITIALIZED) {
-      // We are missing the required SPS to create the decoder.
-      // Ignore for the time being, the MP4Sample will be dropped.
-      return NS_OK;
-    }
-  } else {
-    rv = CheckForSPSChange(aSample);
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aSample->extra_data = mCurrentConfig.extra_data;
-
-  return mDecoder->Input(aSample);
-}
-
-nsresult
-AVCCMediaDataDecoder::Flush()
-{
-  if (mDecoder) {
-    return mDecoder->Flush();
-  }
-  return mLastError;
-}
-
-nsresult
-AVCCMediaDataDecoder::Drain()
-{
-  if (mDecoder) {
-    return mDecoder->Drain();
-  }
-  return mLastError;
-}
-
-nsresult
-AVCCMediaDataDecoder::Shutdown()
-{
-  if (mDecoder) {
-    nsresult rv = mDecoder->Shutdown();
-    mDecoder = nullptr;
-    return rv;
-  }
-  return NS_OK;
-}
-
-bool
-AVCCMediaDataDecoder::IsWaitingMediaResources()
-{
-  if (mDecoder) {
-    return mDecoder->IsWaitingMediaResources();
-  }
-  return MediaDataDecoder::IsWaitingMediaResources();
-}
-
-bool
-AVCCMediaDataDecoder::IsDormantNeeded()
-{
-  return true;
-}
-
-void
-AVCCMediaDataDecoder::AllocateMediaResources()
-{
-  // Nothing to do, decoder will be allocated on the fly when required.
-}
-
-void
-AVCCMediaDataDecoder::ReleaseMediaResources()
-{
-  Shutdown();
-}
-
-nsresult
-AVCCMediaDataDecoder::CreateDecoder()
-{
-  if (!mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.extra_data)) {
-    // nothing found yet, will try again later
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-  UpdateConfigFromExtraData(mCurrentConfig.extra_data);
-
-  mDecoder = mPDM->CreateVideoDecoder(mCurrentConfig,
-                                      mLayersBackend,
-                                      mImageContainer,
-                                      mVideoTaskQueue,
-                                      mCallback);
-  if (!mDecoder) {
-    mLastError = NS_ERROR_FAILURE;
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-nsresult
-AVCCMediaDataDecoder::CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample)
-{
-  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
-    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
-  if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-  UpdateConfigFromExtraData(extra_data);
-
-  nsresult rv = CreateDecoder();
-  NS_ENSURE_SUCCESS(rv, rv);
-  return Init();
-}
-
-bool
-AVCCMediaDataDecoder::IsHardwareAccelerated() const
-{
-  if (mDecoder) {
-    return mDecoder->IsHardwareAccelerated();
-  }
-  return MediaDataDecoder::IsHardwareAccelerated();
-}
-
-nsresult
-AVCCMediaDataDecoder::CheckForSPSChange(mp4_demuxer::MP4Sample* aSample)
-{
-  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
-    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
-  if (!mp4_demuxer::AnnexB::HasSPS(extra_data) ||
-      mp4_demuxer::AnnexB::CompareExtraData(extra_data,
-                                            mCurrentConfig.extra_data)) {
-    return NS_OK;
-  }
-  // The SPS has changed, signal to flush the current decoder and create a
-  // new one.
-  mDecoder->Flush();
-  ReleaseMediaResources();
-  return CreateDecoderAndInit(aSample);
-}
-
-void
-AVCCMediaDataDecoder::UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData)
-{
-  mp4_demuxer::SPSData spsdata;
-  if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata) &&
-      spsdata.pic_width > 0 && spsdata.pic_height > 0) {
-    mp4_demuxer::H264::EnsureSPSIsSane(spsdata);
-    mCurrentConfig.image_width = spsdata.pic_width;
-    mCurrentConfig.image_height = spsdata.pic_height;
-    mCurrentConfig.display_width = spsdata.display_width;
-    mCurrentConfig.display_height = spsdata.display_height;
-  }
-  mCurrentConfig.extra_data = aExtraData;
-}
-
-// AVCCDecoderModule
-
-AVCCDecoderModule::AVCCDecoderModule(PlatformDecoderModule* aPDM)
-: mPDM(aPDM)
-{
-  MOZ_ASSERT(aPDM);
-}
-
-AVCCDecoderModule::~AVCCDecoderModule()
-{
-}
-
-nsresult
-AVCCDecoderModule::Startup()
-{
-  return mPDM->Startup();
-}
-
-already_AddRefed<MediaDataDecoder>
-AVCCDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                                      layers::LayersBackend aLayersBackend,
-                                      layers::ImageContainer* aImageContainer,
-                                      FlushableMediaTaskQueue* aVideoTaskQueue,
-                                      MediaDataDecoderCallback* aCallback)
-{
-  nsRefPtr<MediaDataDecoder> decoder;
-
-  if ((!aConfig.mime_type.EqualsLiteral("video/avc") &&
-       !aConfig.mime_type.EqualsLiteral("video/mp4")) ||
-      !mPDM->DecoderNeedsAVCC(aConfig)) {
-    // There is no need for an AVCC wrapper for non-AVC content.
-    decoder = mPDM->CreateVideoDecoder(aConfig,
-                                       aLayersBackend,
-                                       aImageContainer,
-                                       aVideoTaskQueue,
-                                       aCallback);
-  } else {
-    decoder = new AVCCMediaDataDecoder(mPDM,
-                                       aConfig,
-                                       aLayersBackend,
-                                       aImageContainer,
-                                       aVideoTaskQueue,
-                                       aCallback);
-  }
-  return decoder.forget();
-}
-
-already_AddRefed<MediaDataDecoder>
-AVCCDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
-                                      FlushableMediaTaskQueue* aAudioTaskQueue,
-                                      MediaDataDecoderCallback* aCallback)
-{
-  return mPDM->CreateAudioDecoder(aConfig,
-                                  aAudioTaskQueue,
-                                  aCallback);
-}
-
-bool
-AVCCDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
-{
-  return mPDM->SupportsAudioMimeType(aMimeType);
-}
-
-bool
-AVCCDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
-{
-  return mPDM->SupportsVideoMimeType(aMimeType);
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/fmp4/AVCCDecoderModule.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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_AVCCDecoderModule_h
-#define mozilla_AVCCDecoderModule_h
-
-#include "PlatformDecoderModule.h"
-
-namespace mozilla {
-
-class AVCCMediaDataDecoder;
-
-// AVCCDecoderModule is a PlatformDecoderModule wrapper used to ensure that
-// only AVCC format is fed to the underlying PlatformDecoderModule.
-// The AVCCDecoderModule allows playback of content where the SPS NAL may not be
-// provided in the init segment (e.g. AVC3 or Annex B)
-// AVCCDecoderModule will monitor the input data, and will delay creation of the
-// MediaDataDecoder until a SPS and PPS NALs have been extracted.
-//
-// AVCC-only decoder modules are AppleVideoDecoder and EMEH264Decoder.
-
-class AVCCDecoderModule : public PlatformDecoderModule {
-public:
-  explicit AVCCDecoderModule(PlatformDecoderModule* aPDM);
-  virtual ~AVCCDecoderModule();
-
-  virtual nsresult Startup() override;
-
-  virtual already_AddRefed<MediaDataDecoder>
-  CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                     layers::LayersBackend aLayersBackend,
-                     layers::ImageContainer* aImageContainer,
-                     FlushableMediaTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
-
-  virtual already_AddRefed<MediaDataDecoder>
-  CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
-                     FlushableMediaTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
-
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
-  virtual bool SupportsVideoMimeType(const nsACString& aMimeType) override;
-
-private:
-  nsRefPtr<PlatformDecoderModule> mPDM;
-};
-
-} // namespace mozilla
-
-#endif // mozilla_AVCCDecoderModule_h
--- a/dom/media/fmp4/BlankDecoderModule.cpp
+++ b/dom/media/fmp4/BlankDecoderModule.cpp
@@ -233,21 +233,27 @@ public:
     nsRefPtr<MediaDataDecoder> decoder =
       new BlankMediaDataDecoder<BlankAudioDataCreator>(creator,
                                                        aAudioTaskQueue,
                                                        aCallback);
     return decoder.forget();
   }
 
   virtual bool
-  SupportsAudioMimeType(const nsACString& aMimeType) override
+  SupportsMimeType(const nsACString& aMimeType) override
   {
     return true;
   }
 
+  ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
+  {
+    return kNeedNone;
+  }
+
 };
 
 already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule()
 {
   nsRefPtr<PlatformDecoderModule> pdm = new BlankDecoderModule();
   return pdm.forget();
 }
 
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -153,16 +153,17 @@ MP4Reader::MP4Reader(AbstractMediaDecode
   , mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mDemuxerInitialized(false)
   , mFoundSPSForTelemetry(false)
   , mIsEncrypted(false)
   , mAreDecodersSetup(false)
   , mIndexReady(false)
+  , mLastSeenEnd(-1)
   , mDemuxerMonitor("MP4 Demuxer")
 #if defined(MP4_READER_DORMANT_HEURISTIC)
   , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
 #endif
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(MP4Reader);
 }
@@ -338,26 +339,26 @@ MP4Reader::ExtractCryptoInitData(nsTArra
   }
 }
 
 bool
 MP4Reader::IsSupportedAudioMimeType(const nsACString& aMimeType)
 {
   return (aMimeType.EqualsLiteral("audio/mpeg") ||
           aMimeType.EqualsLiteral("audio/mp4a-latm")) &&
-         mPlatform->SupportsAudioMimeType(aMimeType);
+         mPlatform->SupportsMimeType(aMimeType);
 }
 
 bool
 MP4Reader::IsSupportedVideoMimeType(const nsACString& aMimeType)
 {
   return (aMimeType.EqualsLiteral("video/mp4") ||
           aMimeType.EqualsLiteral("video/avc") ||
           aMimeType.EqualsLiteral("video/x-vnd.on2.vp6")) &&
-         mPlatform->SupportsVideoMimeType(aMimeType);
+         mPlatform->SupportsMimeType(aMimeType);
 }
 
 void
 MP4Reader::PreReadMetadata()
 {
   if (mPlatform) {
     RequestCodecResource();
   }
@@ -508,19 +509,20 @@ MP4Reader::EnsureDecodersSetup()
     mPlatform = PlatformDecoderModule::Create();
     NS_ENSURE_TRUE(mPlatform, false);
   }
 
   if (HasAudio()) {
     NS_ENSURE_TRUE(IsSupportedAudioMimeType(mDemuxer->AudioConfig().mime_type),
                    false);
 
-    mAudio.mDecoder = mPlatform->CreateAudioDecoder(mDemuxer->AudioConfig(),
-                                                    mAudio.mTaskQueue,
-                                                    mAudio.mCallback);
+    mAudio.mDecoder =
+      mPlatform->CreateDecoder(mDemuxer->AudioConfig(),
+                               mAudio.mTaskQueue,
+                               mAudio.mCallback);
     NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, false);
     nsresult rv = mAudio.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   if (HasVideo()) {
     NS_ENSURE_TRUE(IsSupportedVideoMimeType(mDemuxer->VideoConfig().mime_type),
                    false);
@@ -529,21 +531,22 @@ MP4Reader::EnsureDecodersSetup()
       mVideo.mDecoder =
         mSharedDecoderManager->CreateVideoDecoder(mPlatform,
                                                   mDemuxer->VideoConfig(),
                                                   mLayersBackendType,
                                                   mDecoder->GetImageContainer(),
                                                   mVideo.mTaskQueue,
                                                   mVideo.mCallback);
     } else {
-      mVideo.mDecoder = mPlatform->CreateVideoDecoder(mDemuxer->VideoConfig(),
-                                                      mLayersBackendType,
-                                                      mDecoder->GetImageContainer(),
-                                                      mVideo.mTaskQueue,
-                                                      mVideo.mCallback);
+      mVideo.mDecoder =
+        mPlatform->CreateDecoder(mDemuxer->VideoConfig(),
+                                 mVideo.mTaskQueue,
+                                 mVideo.mCallback,
+                                 mLayersBackendType,
+                                 mDecoder->GetImageContainer());
     }
     NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false);
     nsresult rv = mVideo.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   mAreDecodersSetup = true;
   return true;
@@ -595,17 +598,17 @@ MP4Reader::GetNextKeyframeTime()
 
 void
 MP4Reader::DisableHardwareAcceleration()
 {
   if (HasVideo() && mSharedDecoderManager) {
     mSharedDecoderManager->DisableHardwareAcceleration();
 
     const VideoDecoderConfig& video = mDemuxer->VideoConfig();
-    if (!mSharedDecoderManager->Recreate(video, mLayersBackendType, mDecoder->GetImageContainer())) {
+    if (!mSharedDecoderManager->Recreate(video)) {
       MonitorAutoLock mon(mVideo.mMonitor);
       mVideo.mError = true;
       if (mVideo.HasPromise()) {
         mVideo.RejectPromise(DECODE_ERROR, __func__);
       }
     } else {
       MonitorAutoLock lock(mVideo.mMonitor);
       ScheduleUpdate(kVideo);
@@ -1153,9 +1156,44 @@ MP4Reader::SetSharedDecoderManager(Share
 }
 
 bool
 MP4Reader::VideoIsHardwareAccelerated() const
 {
   return mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated();
 }
 
+void
+MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mShutdown) {
+    return;
+  }
+
+  if (mLastSeenEnd < 0) {
+    MonitorAutoLock mon(mDemuxerMonitor);
+    mLastSeenEnd = mDecoder->GetResource()->GetLength();
+    if (mLastSeenEnd < 0) {
+      // We dont have a length. Demuxer would have been blocking already.
+      return;
+    }
+  }
+  int64_t end = aOffset + aLength;
+  if (end <= mLastSeenEnd) {
+    return;
+  }
+  mLastSeenEnd = end;
+
+  if (HasVideo()) {
+    auto& decoder = GetDecoderData(kVideo);
+    MonitorAutoLock lock(decoder.mMonitor);
+    decoder.mDemuxEOS = false;
+  }
+  if (HasAudio()) {
+    auto& decoder = GetDecoderData(kAudio);
+    MonitorAutoLock lock(decoder.mMonitor);
+    decoder.mDemuxEOS = false;
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -72,16 +72,17 @@ public:
   virtual void ReadUpdatedMetadata(MediaInfo* aInfo) override;
 
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) override;
 
   virtual bool IsMediaSeekable() override;
 
   virtual int64_t GetEvictionOffset(double aTime) override;
+  virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) override;
 
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) override;
 
   // For Media Resource Management
   virtual void SetIdle() override;
   virtual bool IsWaitingMediaResources() override;
   virtual bool IsDormantNeeded() override;
   virtual void ReleaseMediaResources() override;
@@ -282,16 +283,17 @@ private:
   bool mFoundSPSForTelemetry;
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
 
   bool mAreDecodersSetup;
 
   bool mIndexReady;
+  int64_t mLastSeenEnd;
   Monitor mDemuxerMonitor;
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 
 #if defined(MP4_READER_DORMANT_HEURISTIC)
   const bool mDormantEnabled;
 #endif
 };
 
--- a/dom/media/fmp4/PlatformDecoderModule.cpp
+++ b/dom/media/fmp4/PlatformDecoderModule.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "PlatformDecoderModule.h"
-#include "AVCCDecoderModule.h"
 
 #ifdef XP_WIN
 #include "WMFDecoderModule.h"
 #endif
 #ifdef MOZ_FFMPEG
 #include "FFmpegRuntimeLinker.h"
 #endif
 #ifdef MOZ_APPLEMEDIA
@@ -27,16 +26,19 @@
 #include "mozilla/Preferences.h"
 #ifdef MOZ_EME
 #include "EMEDecoderModule.h"
 #include "mozilla/CDMProxy.h"
 #endif
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 
+#include "mp4_demuxer/DecoderData.h"
+#include "H264Converter.h"
+
 namespace mozilla {
 
 extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
 
 bool PlatformDecoderModule::sUseBlankDecoder = false;
 bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
 bool PlatformDecoderModule::sGonkDecoderEnabled = false;
 bool PlatformDecoderModule::sAndroidMCDecoderEnabled = false;
@@ -102,20 +104,17 @@ PlatformDecoderModule::CreateCDMWrapper(
     // decrypted output of the CDM.
     pdm = Create();
     if (!pdm) {
       return nullptr;
     }
   }
 
   nsRefPtr<PlatformDecoderModule> emepdm(
-    new AVCCDecoderModule(new EMEDecoderModule(aProxy,
-                                               pdm,
-                                               cdmDecodesAudio,
-                                               cdmDecodesVideo)));
+    new EMEDecoderModule(aProxy, pdm, cdmDecodesAudio, cdmDecodesVideo));
   return emepdm.forget();
 }
 #endif
 
 /* static */
 already_AddRefed<PlatformDecoderModule>
 PlatformDecoderModule::Create()
 {
@@ -146,55 +145,81 @@ PlatformDecoderModule::CreatePDM()
 #ifdef XP_WIN
   nsRefPtr<PlatformDecoderModule> m(new WMFDecoderModule());
   return m.forget();
 #endif
 #ifdef MOZ_FFMPEG
   if (sFFmpegDecoderEnabled) {
     nsRefPtr<PlatformDecoderModule> m = FFmpegRuntimeLinker::CreateDecoderModule();
     if (m) {
-      nsRefPtr<PlatformDecoderModule> m2(new AVCCDecoderModule(m));
-      return m2.forget();
+      return m.forget();
     }
   }
 #endif
 #ifdef MOZ_APPLEMEDIA
-  nsRefPtr<PlatformDecoderModule> m(new AVCCDecoderModule(new AppleDecoderModule()));
+  nsRefPtr<PlatformDecoderModule> m(new AppleDecoderModule());
   return m.forget();
 #endif
 #ifdef MOZ_GONK_MEDIACODEC
   if (sGonkDecoderEnabled) {
     nsRefPtr<PlatformDecoderModule> m(new GonkDecoderModule());
     return m.forget();
   }
 #endif
 #ifdef MOZ_WIDGET_ANDROID
   if(sAndroidMCDecoderEnabled){
     nsRefPtr<PlatformDecoderModule> m(new AndroidDecoderModule());
     return m.forget();
   }
 #endif
   if (sGMPDecoderEnabled) {
-    nsRefPtr<PlatformDecoderModule> m(new AVCCDecoderModule(new GMPDecoderModule()));
+    nsRefPtr<PlatformDecoderModule> m(new GMPDecoderModule());
     return m.forget();
   }
   return nullptr;
 }
 
-bool
-PlatformDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
+already_AddRefed<MediaDataDecoder>
+PlatformDecoderModule::CreateDecoder(const mp4_demuxer::TrackConfig& aConfig,
+                                     FlushableMediaTaskQueue* aTaskQueue,
+                                     MediaDataDecoderCallback* aCallback,
+                                     layers::LayersBackend aLayersBackend,
+                                     layers::ImageContainer* aImageContainer)
 {
-  return aMimeType.EqualsLiteral("audio/mp4a-latm");
+  nsRefPtr<MediaDataDecoder> m;
+
+  if (aConfig.IsAudioConfig()) {
+    m = CreateAudioDecoder(static_cast<const mp4_demuxer::AudioDecoderConfig&>(aConfig),
+                           aTaskQueue,
+                           aCallback);
+    return m.forget();
+  }
+
+  if (!aConfig.IsVideoConfig()) {
+    return nullptr;
+  }
+
+  if (H264Converter::IsH264(aConfig)) {
+    m = new H264Converter(this,
+                          static_cast<const mp4_demuxer::VideoDecoderConfig&>(aConfig),
+                          aLayersBackend,
+                          aImageContainer,
+                          aTaskQueue,
+                          aCallback);
+  } else {
+    m = CreateVideoDecoder(static_cast<const mp4_demuxer::VideoDecoderConfig&>(aConfig),
+                           aLayersBackend,
+                           aImageContainer,
+                           aTaskQueue,
+                           aCallback);
+  }
+  return m.forget();
 }
 
 bool
-PlatformDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
+PlatformDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
-  return aMimeType.EqualsLiteral("video/mp4") || aMimeType.EqualsLiteral("video/avc");
-}
-
-bool
-PlatformDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
-{
-  return false;
+  return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
+    aMimeType.EqualsLiteral("video/mp4") ||
+    aMimeType.EqualsLiteral("video/avc");
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/PlatformDecoderModule.h
+++ b/dom/media/fmp4/PlatformDecoderModule.h
@@ -9,16 +9,17 @@
 
 #include "MediaDecoderReader.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include <queue>
 
 namespace mp4_demuxer {
+class TrackConfig;
 class VideoDecoderConfig;
 class AudioDecoderConfig;
 class MP4Sample;
 }
 
 class nsIThreadPool;
 
 namespace mozilla {
@@ -81,67 +82,86 @@ public:
   // that we use on on aTaskQueue to decode the decrypted stream.
   // This is called on the decode task queue.
   static already_AddRefed<PlatformDecoderModule>
   CreateCDMWrapper(CDMProxy* aProxy,
                    bool aHasAudio,
                    bool aHasVideo);
 #endif
 
-  // Creates an H.264 decoder. The layers backend is passed in so that
+  // Creates a decoder.
+  // See CreateVideoDecoder and CreateAudioDecoder for implementation details.
+  virtual already_AddRefed<MediaDataDecoder>
+  CreateDecoder(const mp4_demuxer::TrackConfig& aConfig,
+                FlushableMediaTaskQueue* aTaskQueue,
+                MediaDataDecoderCallback* aCallback,
+                layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE,
+                layers::ImageContainer* aImageContainer = nullptr);
+
+  // An audio decoder module must support AAC by default.
+  // A video decoder must support H264 by default.
+  // If more codecs are to be supported, SupportsMimeType will have
+  // to be extended
+  virtual bool SupportsMimeType(const nsACString& aMimeType);
+
+  enum ConversionRequired {
+    kNeedNone,
+    kNeedAVCC,
+    kNeedAnnexB,
+  };
+
+  // Indicates that the decoder requires a specific format.
+  // The PlatformDecoderModule will convert the demuxed data accordingly before
+  // feeding it to MediaDataDecoder::Input.
+  virtual ConversionRequired DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const = 0;
+
+  virtual void DisableHardwareAcceleration() {}
+
+  virtual bool SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const {
+    return true;
+  }
+
+protected:
+  PlatformDecoderModule() {}
+  virtual ~PlatformDecoderModule() {}
+
+  friend class H264Converter;
+  // Creates a Video decoder. The layers backend is passed in so that
   // decoders can determine whether hardware accelerated decoding can be used.
   // Asynchronous decoding of video should be done in runnables dispatched
   // to aVideoTaskQueue. If the task queue isn't needed, the decoder should
   // not hold a reference to it.
   // Output and errors should be returned to the reader via aCallback.
   // On Windows the task queue's threads in have MSCOM initialized with
   // COINIT_MULTITHREADED.
   // Returns nullptr if the decoder can't be created.
   // It is safe to store a reference to aConfig.
   // This is called on the decode task queue.
   virtual already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                    layers::LayersBackend aLayersBackend,
-                    layers::ImageContainer* aImageContainer,
-                    FlushableMediaTaskQueue* aVideoTaskQueue,
-                    MediaDataDecoderCallback* aCallback) = 0;
+                     layers::LayersBackend aLayersBackend,
+                     layers::ImageContainer* aImageContainer,
+                     FlushableMediaTaskQueue* aVideoTaskQueue,
+                     MediaDataDecoderCallback* aCallback) = 0;
 
   // Creates an Audio decoder with the specified properties.
   // Asynchronous decoding of audio should be done in runnables dispatched to
   // aAudioTaskQueue. If the task queue isn't needed, the decoder should
   // not hold a reference to it.
   // Output and errors should be returned to the reader via aCallback.
   // Returns nullptr if the decoder can't be created.
   // On Windows the task queue's threads in have MSCOM initialized with
   // COINIT_MULTITHREADED.
   // It is safe to store a reference to aConfig.
   // This is called on the decode task queue.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) = 0;
 
-  // An audio decoder module must support AAC by default.
-  // If more audio codec is to be supported, SupportsAudioMimeType will have
-  // to be extended
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType);
-  virtual bool SupportsVideoMimeType(const nsACString& aMimeType);
-
-  // Indicates if the video decoder requires AVCC format.
-  virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig);
-
-  virtual void DisableHardwareAcceleration() {}
-
-  virtual bool SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const {
-    return true;
-  }
-
-protected:
-  PlatformDecoderModule() {}
-  virtual ~PlatformDecoderModule() {}
   // Caches pref media.fragmented-mp4.use-blank-decoder
   static bool sUseBlankDecoder;
   static bool sFFmpegDecoderEnabled;
   static bool sGonkDecoderEnabled;
   static bool sAndroidMCDecoderPreferred;
   static bool sAndroidMCDecoderEnabled;
   static bool sGMPDecoderEnabled;
 };
@@ -208,17 +228,16 @@ public:
   // this function returns, the decoder must be ready to accept new input
   // for decoding. This function is called when the demuxer seeks, before
   // decoding resumes after the seek.
   // While the reader calls Flush(), it ignores all output sent to it;
   // it is safe (but pointless) to send output while Flush is called.
   // The MP4Reader will not call Input() while it's calling Flush().
   virtual nsresult Flush() = 0;
 
-
   // Causes all complete samples in the pipeline that can be decoded to be
   // output. If the decoder can't produce samples from the current output,
   // it drops the input samples. The decoder may be holding onto samples
   // that are required to decode samples that it expects to get in future.
   // This is called when the demuxer reaches end of stream.
   // The MP4Reader will not call Input() while it's calling Drain().
   // This function is asynchronous. The MediaDataDecoder must call
   // MediaDataDecoderCallback::DrainComplete() once all remaining
@@ -239,13 +258,22 @@ public:
     return false;
   };
   virtual bool IsDormantNeeded() {
     return false;
   };
   virtual void AllocateMediaResources() {}
   virtual void ReleaseMediaResources() {}
   virtual bool IsHardwareAccelerated() const { return false; }
+
+  // ConfigurationChanged will be called to inform the video or audio decoder
+  // that the format of the next input sample is about to change.
+  // If video decoder, aConfig will be a VideoDecoderConfig object.
+  // If audio decoder, aConfig will be a AudioDecoderConfig object.
+  virtual nsresult ConfigurationChanged(const mp4_demuxer::TrackConfig& aConfig)
+  {
+    return NS_OK;
+  }
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -78,25 +78,28 @@ SharedDecoderManager::CreateVideoDecoder
   PlatformDecoderModule* aPDM,
   const mp4_demuxer::VideoDecoderConfig& aConfig,
   layers::LayersBackend aLayersBackend,
   layers::ImageContainer* aImageContainer,
   FlushableMediaTaskQueue* aVideoTaskQueue,
   MediaDataDecoderCallback* aCallback)
 {
   if (!mDecoder) {
+    mLayersBackend = aLayersBackend;
+    mImageContainer = aImageContainer;
     // We use the manager's task queue for the decoder, rather than the one
     // passed in, so that none of the objects sharing the decoder can shutdown
     // the task queue while we're potentially still using it for a *different*
     // object also sharing the decoder.
-    mDecoder = aPDM->CreateVideoDecoder(aConfig,
-                                        aLayersBackend,
-                                        aImageContainer,
-                                        mTaskQueue,
-                                        mCallback);
+    mDecoder =
+      aPDM->CreateDecoder(aConfig,
+                          mTaskQueue,
+                          mCallback,
+                          mLayersBackend,
+                          mImageContainer);
     if (!mDecoder) {
       mPDM = nullptr;
       return nullptr;
     }
     mPDM = aPDM;
     nsresult rv = mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
@@ -108,27 +111,25 @@ SharedDecoderManager::CreateVideoDecoder
 void
 SharedDecoderManager::DisableHardwareAcceleration()
 {
   MOZ_ASSERT(mPDM);
   mPDM->DisableHardwareAcceleration();
 }
 
 bool
-SharedDecoderManager::Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                               layers::LayersBackend aLayersBackend,
-                               layers::ImageContainer* aImageContainer)
+SharedDecoderManager::Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig)
 {
   mDecoder->Flush();
   mDecoder->Shutdown();
-  mDecoder = mPDM->CreateVideoDecoder(aConfig,
-                                      aLayersBackend,
-                                      aImageContainer,
-                                      mTaskQueue,
-                                      mCallback);
+  mDecoder = mPDM->CreateDecoder(aConfig,
+                                 mTaskQueue,
+                                 mCallback,
+                                 mLayersBackend,
+                                 mImageContainer);
   if (!mDecoder) {
     return false;
   }
   nsresult rv = mDecoder->Init();
   return rv == NS_OK;
 }
 
 void
--- a/dom/media/fmp4/SharedDecoderManager.h
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -37,26 +37,26 @@ public:
   void SetIdle(MediaDataDecoder* aProxy);
   void ReleaseMediaResources();
   void Shutdown();
 
   friend class SharedDecoderProxy;
   friend class SharedDecoderCallback;
 
   void DisableHardwareAcceleration();
-  bool Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig,
-                layers::LayersBackend aLayersBackend,
-                layers::ImageContainer* aImageContainer);
+  bool Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig);
 
 private:
   virtual ~SharedDecoderManager();
   void DrainComplete();
 
   nsRefPtr<PlatformDecoderModule> mPDM;
   nsRefPtr<MediaDataDecoder> mDecoder;
+  layers::LayersBackend mLayersBackend;
+  nsRefPtr<layers::ImageContainer> mImageContainer;
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   SharedDecoderProxy* mActiveProxy;
   MediaDataDecoderCallback* mActiveCallback;
   nsAutoPtr<MediaDataDecoderCallback> mCallback;
   bool mWaitForInternalDrain;
   Monitor mMonitor;
   bool mDecoderReleasedResources;
 };
--- a/dom/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp
@@ -58,20 +58,16 @@ public:
     return InitDecoder(mSurfaceTexture->JavaSurface());
   }
 
   void Cleanup() override {
     mGLContext = nullptr;
   }
 
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override {
-    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
     return MediaCodecDataDecoder::Input(aSample);
   }
 
   bool WantCopy() {
     // Allocating a texture is incredibly slow on PowerVR
     return mGLContext->Vendor() != GLVendor::Imagination;
   }
 
@@ -243,18 +239,23 @@ public:
                                              numChannels,
                                              sampleRate);
     mCallback->Output(data);
     return NS_OK;
   }
 };
 
 
-bool AndroidDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType) {
-  return static_cast<bool>(CreateDecoder(aMimeType));
+bool AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType)
+{
+  if (aMimeType.EqualsLiteral("video/mp4") ||
+      aMimeType.EqualsLiteral("video/avc")) {
+    return true;
+  }
+  return static_cast<bool>(mozilla::CreateDecoder(aMimeType));
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateVideoDecoder(
                                 const mp4_demuxer::VideoDecoderConfig& aConfig,
                                 layers::LayersBackend aLayersBackend,
                                 layers::ImageContainer* aImageContainer,
                                 FlushableMediaTaskQueue* aVideoTaskQueue,
@@ -291,16 +292,26 @@ AndroidDecoderModule::CreateAudioDecoder
 
   nsRefPtr<MediaDataDecoder> decoder =
     new AudioDataDecoder(aConfig, format, aCallback);
 
   return decoder.forget();
 
 }
 
+PlatformDecoderModule::ConversionRequired
+AndroidDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
+{
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAnnexB;
+  } else {
+    return kNeedNone;
+  }
+}
+
 MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
                                              const nsACString& aMimeType,
                                              MediaFormat::Param aFormat,
                                              MediaDataDecoderCallback* aCallback)
   : mType(aType)
   , mMimeType(aMimeType)
   , mFormat(aFormat)
   , mCallback(aCallback)
--- a/dom/media/fmp4/android/AndroidDecoderModule.h
+++ b/dom/media/fmp4/android/AndroidDecoderModule.h
@@ -30,17 +30,20 @@ public:
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
 
   AndroidDecoderModule() {}
   virtual ~AndroidDecoderModule() {}
 
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
+  virtual bool SupportsMimeType(const nsACString& aMimeType) override;
+
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 };
 
 class MediaCodecDataDecoder : public MediaDataDecoder {
 public:
 
   MediaCodecDataDecoder(MediaData::Type aType,
                         const nsACString& aMimeType,
                         widget::sdk::MediaFormat::Param aFormat,
--- a/dom/media/fmp4/apple/AppleDecoderModule.cpp
+++ b/dom/media/fmp4/apple/AppleDecoderModule.cpp
@@ -187,20 +187,25 @@ AppleDecoderModule::CreateAudioDecoder(c
                                        MediaDataDecoderCallback* aCallback)
 {
   nsRefPtr<MediaDataDecoder> decoder =
     new AppleATDecoder(aConfig, aAudioTaskQueue, aCallback);
   return decoder.forget();
 }
 
 bool
-AppleDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
+AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
-  return aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/mpeg");
+  return aMimeType.EqualsLiteral("audio/mpeg") ||
+    PlatformDecoderModule::SupportsMimeType(aMimeType);
 }
 
-bool
-AppleDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
+PlatformDecoderModule::ConversionRequired
+AppleDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
-  return true;
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/apple/AppleDecoderModule.h
+++ b/dom/media/fmp4/apple/AppleDecoderModule.h
@@ -27,19 +27,20 @@ public:
                      MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
-  virtual bool
-  DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
+  virtual bool SupportsMimeType(const nsACString& aMimeType) override;
+
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 
   static void Init();
   static nsresult CanDecode();
 
 private:
   friend class InitTask;
   friend class LinkTask;
   friend class UnlinkTask;
--- a/dom/media/fmp4/eme/EMEDecoderModule.cpp
+++ b/dom/media/fmp4/eme/EMEDecoderModule.cpp
@@ -248,21 +248,22 @@ EMEDecoderModule::CreateVideoDecoder(con
                                                 aConfig,
                                                 aLayersBackend,
                                                 aImageContainer,
                                                 aVideoTaskQueue,
                                                 wrapper->Callback()));
     return wrapper.forget();
   }
 
-  nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateVideoDecoder(aConfig,
-                                                              aLayersBackend,
-                                                              aImageContainer,
-                                                              aVideoTaskQueue,
-                                                              aCallback));
+  nsRefPtr<MediaDataDecoder> decoder(
+    mPDM->CreateDecoder(aConfig,
+                        aVideoTaskQueue,
+                        aCallback,
+                        aLayersBackend,
+                        aImageContainer));
   if (!decoder) {
     return nullptr;
   }
 
   if (!aConfig.crypto.valid) {
     return decoder.forget();
   }
 
@@ -281,32 +282,35 @@ EMEDecoderModule::CreateAudioDecoder(con
     nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aAudioTaskQueue);
     wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy,
                                                 aConfig,
                                                 aAudioTaskQueue,
                                                 wrapper->Callback()));
     return wrapper.forget();
   }
 
-  nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAudioDecoder(aConfig,
-                                                              aAudioTaskQueue,
-                                                              aCallback));
+  nsRefPtr<MediaDataDecoder> decoder(
+    mPDM->CreateDecoder(aConfig, aAudioTaskQueue, aCallback));
   if (!decoder) {
     return nullptr;
   }
 
   if (!aConfig.crypto.valid) {
     return decoder.forget();
   }
 
   nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
                                                          aCallback,
                                                          mProxy));
   return emeDecoder.forget();
 }
 
-bool
-EMEDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
+PlatformDecoderModule::ConversionRequired
+EMEDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
-  return mCDMDecodesVideo && aConfig.crypto.valid;
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/eme/EMEDecoderModule.h
+++ b/dom/media/fmp4/eme/EMEDecoderModule.h
@@ -37,18 +37,18 @@ public:
                     MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual bool
-  DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 
 private:
   nsRefPtr<CDMProxy> mProxy;
   // Will be null if CDM has decoding capability.
   nsRefPtr<PlatformDecoderModule> mPDM;
   // We run the PDM on its own task queue.
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   bool mCDMDecodesAudio;
--- a/dom/media/fmp4/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/fmp4/ffmpeg/FFmpegDecoderModule.h
@@ -46,28 +46,31 @@ public:
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override
   {
     nsRefPtr<MediaDataDecoder> decoder =
       new FFmpegAudioDecoder<V>(aAudioTaskQueue, aCallback, aConfig);
     return decoder.forget();
   }
 
-  virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override
+  virtual bool SupportsMimeType(const nsACString& aMimeType) override
   {
-    return FFmpegAudioDecoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
+    return FFmpegAudioDecoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE ||
+      FFmpegH264Decoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
   }
 
-  virtual bool SupportsVideoMimeType(const nsACString& aMimeType) override
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override
   {
-    return FFmpegH264Decoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
-  }
-
-  virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override
-  {
-    return true;
+    if (aConfig.IsVideoConfig() &&
+        (aConfig.mime_type.EqualsLiteral("video/avc") ||
+         aConfig.mime_type.EqualsLiteral("video/mp4"))) {
+      return PlatformDecoderModule::kNeedAVCC;
+    } else {
+      return kNeedNone;
+    }
   }
 
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDecoderModule_h__
--- a/dom/media/fmp4/gmp/GMPDecoderModule.cpp
+++ b/dom/media/fmp4/gmp/GMPDecoderModule.cpp
@@ -70,16 +70,20 @@ GMPDecoderModule::CreateAudioDecoder(con
 
   nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback);
   wrapper->SetProxyTarget(new GMPAudioDecoder(aConfig,
                                               aAudioTaskQueue,
                                               wrapper->Callback()));
   return wrapper.forget();
 }
 
-bool
-GMPDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
+PlatformDecoderModule::ConversionRequired
+GMPDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
   // GMPVideoCodecType::kGMPVideoCodecH264 specifies that encoded frames must be in AVCC format.
-  return true;
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/gmp/GMPDecoderModule.h
+++ b/dom/media/fmp4/gmp/GMPDecoderModule.h
@@ -26,14 +26,15 @@ public:
                      MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 };
 
 } // namespace mozilla
 
 #endif // GMPDecoderModule_h_
--- a/dom/media/fmp4/gonk/GonkDecoderModule.cpp
+++ b/dom/media/fmp4/gonk/GonkDecoderModule.cpp
@@ -46,9 +46,19 @@ GonkDecoderModule::CreateAudioDecoder(co
                                       MediaDataDecoderCallback* aCallback)
 {
   nsRefPtr<MediaDataDecoder> decoder =
   new GonkMediaDataDecoder(new GonkAudioDecoderManager(aAudioTaskQueue, aConfig),
                            aAudioTaskQueue, aCallback);
   return decoder.forget();
 }
 
+PlatformDecoderModule::ConversionRequired
+GonkDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
+{
+  if (aConfig.IsVideoConfig()) {
+    return kNeedAVCC;
+  } else {
+    return kNeedNone;
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/gonk/GonkDecoderModule.h
+++ b/dom/media/fmp4/gonk/GonkDecoderModule.h
@@ -26,13 +26,16 @@ public:
 
   // Decode thread.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   static void Init();
+
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
@@ -447,30 +447,16 @@ GonkVideoDecoderManager::SendSampleToOMX
   }
 
   return mDecoder->Input(reinterpret_cast<const uint8_t*>(aSample->data),
                          aSample->size,
                          aSample->composition_timestamp,
                          0);
 }
 
-bool
-GonkVideoDecoderManager::PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample)
-{
-  if (aSample != nullptr) {
-    // We must prepare samples in AVC Annex B.
-    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
-      GVDM_LOG("Failed to convert sample to annex B!");
-      return false;
-    }
-  }
-
-  return true;
-}
-
 void
 GonkVideoDecoderManager::ClearQueueFrameTime()
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   mFrameTimeInfo.Clear();
 }
 
 nsresult
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
@@ -53,18 +53,16 @@ public:
 
   virtual void AllocateMediaResources();
 
   virtual void ReleaseMediaResources();
 
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
 
 protected:
-  virtual bool PerformFormatSpecificProcess(mp4_demuxer::MP4Sample* aSample) override;
-
   virtual android::status_t SendSampleToOMX(mp4_demuxer::MP4Sample* aSample) override;
 
 private:
   struct FrameInfo
   {
     int32_t mWidth = 0;
     int32_t mHeight = 0;
     int32_t mStride = 0;
--- a/dom/media/fmp4/moz.build
+++ b/dom/media/fmp4/moz.build
@@ -1,30 +1,30 @@
 # -*- 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 += [
-    'AVCCDecoderModule.h',
     'MP4Decoder.h',
     'MP4Reader.h',
     'MP4Stream.h',
     'PlatformDecoderModule.h',
     'SharedDecoderManager.h',
+    'wrappers/H264Converter.h'
 ]
 
 UNIFIED_SOURCES += [
-    'AVCCDecoderModule.cpp',
     'BlankDecoderModule.cpp',
     'MP4Decoder.cpp',
     'MP4Stream.cpp',
     'PlatformDecoderModule.cpp',
     'SharedDecoderManager.cpp',
+    'wrappers/H264Converter.cpp'
 ]
 
 SOURCES += [
     'MP4Reader.cpp',
 ]
 
 DIRS += ['gmp']
 
--- a/dom/media/fmp4/wmf/WMFDecoderModule.cpp
+++ b/dom/media/fmp4/wmf/WMFDecoderModule.cpp
@@ -116,29 +116,36 @@ bool
 WMFDecoderModule::SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const
 {
   // If DXVA is enabled, but we're not going to use it for this specific config, then
   // we can't use the shared decoder.
   return !sDXVAEnabled || ShouldUseDXVA(aConfig);
 }
 
 bool
-WMFDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
+WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
   return aMimeType.EqualsLiteral("video/mp4") ||
          aMimeType.EqualsLiteral("video/avc") ||
          aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
-         aMimeType.EqualsLiteral("video/webm; codecs=vp9");
+         aMimeType.EqualsLiteral("video/webm; codecs=vp9") ||
+         aMimeType.EqualsLiteral("audio/mp4a-latm") ||
+         aMimeType.EqualsLiteral("audio/mpeg");
 }
 
-bool
-WMFDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
+PlatformDecoderModule::ConversionRequired
+WMFDecoderModule::DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const
 {
-  return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
-         aMimeType.EqualsLiteral("audio/mpeg");
+  if (aConfig.IsVideoConfig() &&
+      (aConfig.mime_type.EqualsLiteral("video/avc") ||
+       aConfig.mime_type.EqualsLiteral("video/mp4"))) {
+    return kNeedAnnexB;
+  } else {
+    return kNeedNone;
+  }
 }
 
 static bool
 ClassesRootRegKeyExists(const nsAString& aRegKeyPath)
 {
   nsresult rv;
 
   nsCOMPtr<nsIWindowsRegKey> regKey =
--- a/dom/media/fmp4/wmf/WMFDecoderModule.h
+++ b/dom/media/fmp4/wmf/WMFDecoderModule.h
@@ -26,26 +26,28 @@ public:
                      FlushableMediaTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  bool SupportsVideoMimeType(const nsACString& aMimeType) override;
-  bool SupportsAudioMimeType(const nsACString& aMimeType) override;
+  bool SupportsMimeType(const nsACString& aMimeType) override;
 
   virtual void DisableHardwareAcceleration() override
   {
     sDXVAEnabled = false;
   }
 
   virtual bool SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig& aConfig) const override;
 
+  virtual ConversionRequired
+  DecoderNeedsConversion(const mp4_demuxer::TrackConfig& aConfig) const override;
+
   // Accessors that report whether we have the required MFTs available
   // on the system to play various codecs. Windows Vista doesn't have the
   // H.264/AAC decoders if the "Platform Update Supplement for Windows Vista"
   // is not installed.
   static bool HasAAC();
   static bool HasH264();
 
   // Called on main thread.
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
@@ -236,22 +236,16 @@ WMFVideoMFTManager::Init()
 
 HRESULT
 WMFVideoMFTManager::Input(mp4_demuxer::MP4Sample* aSample)
 {
   if (!mDecoder) {
     // This can happen during shutdown.
     return E_FAIL;
   }
-  if (mStreamType != VP8 && mStreamType != VP9) {
-    // We must prepare samples in AVC Annex B.
-    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
-      return E_FAIL;
-    }
-  }
   // Forward sample data to the decoder.
   const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
   uint32_t length = aSample->size;
   return mDecoder->Input(data, length, aSample->composition_timestamp);
 }
 
 HRESULT
 WMFVideoMFTManager::ConfigureVideoFrameGeometry()
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/wrappers/H264Converter.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "H264Converter.h"
+#include "ImageContainer.h"
+#include "MediaTaskQueue.h"
+#include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/H264.h"
+
+namespace mozilla
+{
+
+  // H264 AnnexB or AVCC handler
+#include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/H264.h"
+
+H264Converter::H264Converter(PlatformDecoderModule* aPDM,
+                             const mp4_demuxer::VideoDecoderConfig& aConfig,
+                             layers::LayersBackend aLayersBackend,
+                             layers::ImageContainer* aImageContainer,
+                             FlushableMediaTaskQueue* aVideoTaskQueue,
+                             MediaDataDecoderCallback* aCallback)
+  : mPDM(aPDM)
+  , mCurrentConfig(aConfig)
+  , mLayersBackend(aLayersBackend)
+  , mImageContainer(aImageContainer)
+  , mVideoTaskQueue(aVideoTaskQueue)
+  , mCallback(aCallback)
+  , mDecoder(nullptr)
+  , mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC)
+  , mLastError(NS_OK)
+{
+  CreateDecoder();
+}
+
+H264Converter::~H264Converter()
+{
+}
+
+nsresult
+H264Converter::Init()
+{
+  if (mDecoder) {
+    return mDecoder->Init();
+  }
+  return mLastError;
+}
+
+nsresult
+H264Converter::Input(mp4_demuxer::MP4Sample* aSample)
+{
+  if (!mNeedAVCC) {
+    if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
+      return NS_ERROR_FAILURE;
+    }
+  } else {
+    if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  nsresult rv;
+  if (!mDecoder) {
+    // It is not possible to create an AVCC H264 decoder without SPS.
+    // As such, creation will fail if the extra_data just extracted doesn't
+    // contain a SPS.
+    rv = CreateDecoderAndInit(aSample);
+    if (rv == NS_ERROR_NOT_INITIALIZED) {
+      // We are missing the required SPS to create the decoder.
+      // Ignore for the time being, the MP4Sample will be dropped.
+      return NS_OK;
+    }
+  } else {
+    rv = CheckForSPSChange(aSample);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aSample->extra_data = mCurrentConfig.extra_data;
+
+  return mDecoder->Input(aSample);
+}
+
+nsresult
+H264Converter::Flush()
+{
+  if (mDecoder) {
+    return mDecoder->Flush();
+  }
+  return mLastError;
+}
+
+nsresult
+H264Converter::Drain()
+{
+  if (mDecoder) {
+    return mDecoder->Drain();
+  }
+  return mLastError;
+}
+
+nsresult
+H264Converter::Shutdown()
+{
+  if (mDecoder) {
+    nsresult rv = mDecoder->Shutdown();
+    mDecoder = nullptr;
+    return rv;
+  }
+  return NS_OK;
+}
+
+bool
+H264Converter::IsWaitingMediaResources()
+{
+  if (mDecoder) {
+    return mDecoder->IsWaitingMediaResources();
+  }
+  return MediaDataDecoder::IsWaitingMediaResources();
+}
+
+bool
+H264Converter::IsDormantNeeded()
+{
+  if (mNeedAVCC) {
+    return true;
+  }
+  return mDecoder ?
+    mDecoder->IsDormantNeeded() : MediaDataDecoder::IsDormantNeeded();
+}
+
+void
+H264Converter::AllocateMediaResources()
+{
+  if (mNeedAVCC) {
+    // Nothing to do, decoder will be allocated on the fly when required.
+    return;
+  }
+  if (mDecoder) {
+    mDecoder->AllocateMediaResources();
+  }
+}
+
+void
+H264Converter::ReleaseMediaResources()
+{
+  if (mNeedAVCC) {
+    Shutdown();
+    return;
+  }
+  if (mDecoder) {
+    mDecoder->ReleaseMediaResources();
+  }
+}
+
+bool
+H264Converter::IsHardwareAccelerated() const
+{
+  if (mDecoder) {
+    return mDecoder->IsHardwareAccelerated();
+  }
+  return MediaDataDecoder::IsHardwareAccelerated();
+}
+
+nsresult
+H264Converter::CreateDecoder()
+{
+  if (mNeedAVCC && !mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.extra_data)) {
+    // nothing found yet, will try again later
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  UpdateConfigFromExtraData(mCurrentConfig.extra_data);
+
+  mDecoder = mPDM->CreateVideoDecoder(mCurrentConfig,
+                                      mLayersBackend,
+                                      mImageContainer,
+                                      mVideoTaskQueue,
+                                      mCallback);
+  if (!mDecoder) {
+    mLastError = NS_ERROR_FAILURE;
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+nsresult
+H264Converter::CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample)
+{
+  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
+    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
+  if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  UpdateConfigFromExtraData(extra_data);
+
+  nsresult rv = CreateDecoder();
+  NS_ENSURE_SUCCESS(rv, rv);
+  return Init();
+}
+
+nsresult
+H264Converter::CheckForSPSChange(mp4_demuxer::MP4Sample* aSample)
+{
+  nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
+    mp4_demuxer::AnnexB::ExtractExtraData(aSample);
+  if (!mp4_demuxer::AnnexB::HasSPS(extra_data) ||
+      mp4_demuxer::AnnexB::CompareExtraData(extra_data,
+                                            mCurrentConfig.extra_data)) {
+        return NS_OK;
+      }
+  if (!mNeedAVCC) {
+    UpdateConfigFromExtraData(extra_data);
+    mDecoder->ConfigurationChanged(mCurrentConfig);
+    return NS_OK;
+  }
+  // The SPS has changed, signal to flush the current decoder and create a
+  // new one.
+  mDecoder->Flush();
+  ReleaseMediaResources();
+  return CreateDecoderAndInit(aSample);
+}
+
+void
+H264Converter::UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData)
+{
+  mp4_demuxer::SPSData spsdata;
+  if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata) &&
+      spsdata.pic_width > 0 && spsdata.pic_height > 0) {
+    mp4_demuxer::H264::EnsureSPSIsSane(spsdata);
+    mCurrentConfig.image_width = spsdata.pic_width;
+    mCurrentConfig.image_height = spsdata.pic_height;
+    mCurrentConfig.display_width = spsdata.display_width;
+    mCurrentConfig.display_height = spsdata.display_height;
+  }
+  mCurrentConfig.extra_data = aExtraData;
+}
+
+/* static */
+bool
+H264Converter::IsH264(const mp4_demuxer::TrackConfig& aConfig)
+{
+  return aConfig.mime_type.EqualsLiteral("video/avc") ||
+    aConfig.mime_type.EqualsLiteral("video/mp4");
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/wrappers/H264Converter.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_H264Converter_h
+#define mozilla_H264Converter_h
+
+#include "PlatformDecoderModule.h"
+
+namespace mozilla {
+
+// H264Converter is a MediaDataDecoder wrapper used to ensure that
+// only AVCC or AnnexB is fed to the underlying MediaDataDecoder.
+// The H264Converter allows playback of content where the SPS NAL may not be
+// provided in the init segment (e.g. AVC3 or Annex B)
+// H264Converter will monitor the input data, and will delay creation of the
+// MediaDataDecoder until a SPS and PPS NALs have been extracted.
+
+class H264Converter : public MediaDataDecoder {
+public:
+
+  H264Converter(PlatformDecoderModule* aPDM,
+                const mp4_demuxer::VideoDecoderConfig& aConfig,
+                layers::LayersBackend aLayersBackend,
+                layers::ImageContainer* aImageContainer,
+                FlushableMediaTaskQueue* aVideoTaskQueue,
+                MediaDataDecoderCallback* aCallback);
+  virtual ~H264Converter();
+
+  virtual nsresult Init() override;
+  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
+  virtual nsresult Flush() override;
+  virtual nsresult Drain() override;
+  virtual nsresult Shutdown() override;
+  virtual bool IsWaitingMediaResources() override;
+  virtual bool IsDormantNeeded() override;
+  virtual void AllocateMediaResources() override;
+  virtual void ReleaseMediaResources() override;
+  virtual bool IsHardwareAccelerated() const override;
+
+  // Return true if mimetype is H.264.
+  static bool IsH264(const mp4_demuxer::TrackConfig& aConfig);
+
+private:
+  // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
+  // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
+  // will set mError accordingly.
+  nsresult CreateDecoder();
+  nsresult CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample);
+  nsresult CheckForSPSChange(mp4_demuxer::MP4Sample* aSample);
+  void UpdateConfigFromExtraData(mp4_demuxer::ByteBuffer* aExtraData);
+
+  nsRefPtr<PlatformDecoderModule> mPDM;
+  mp4_demuxer::VideoDecoderConfig mCurrentConfig;
+  layers::LayersBackend mLayersBackend;
+  nsRefPtr<layers::ImageContainer> mImageContainer;
+  nsRefPtr<FlushableMediaTaskQueue> mVideoTaskQueue;
+  MediaDataDecoderCallback* mCallback;
+  nsRefPtr<MediaDataDecoder> mDecoder;
+  bool mNeedAVCC;
+  nsresult mLastError;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_H264Converter_h
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -560,18 +560,18 @@ class GMPStorageTest : public GMPDecrypt
   }
 
   void TestGetNodeId()
   {
     AssertIsOnGMPThread();
 
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
-    const nsString origin1 = NS_LITERAL_STRING("example1.com");
-    const nsString origin2 = NS_LITERAL_STRING("example2.org");
+    const nsString origin1 = NS_LITERAL_STRING("http://example1.com");
+    const nsString origin2 = NS_LITERAL_STRING("http://example2.org");
 
     nsCString PBnodeId1 = GetNodeId(origin1, origin2, true);
     nsCString PBnodeId2 = GetNodeId(origin1, origin2, true);
 
     // Node ids for the same origins should be the same in PB mode.
     EXPECT_TRUE(PBnodeId1.Equals(PBnodeId2));
 
     nsCString PBnodeId3 = GetNodeId(origin2, origin1, true);
@@ -597,18 +597,18 @@ class GMPStorageTest : public GMPDecrypt
       this, &GMPStorageTest::TestGetNodeId_Continuation, nodeId1), thread);
   }
 
   void TestGetNodeId_Continuation(nsCString aNodeId1) {
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Once we clear storage, the node ids generated for the same origin-pair
     // should be different.
-    const nsString origin1 = NS_LITERAL_STRING("example1.com");
-    const nsString origin2 = NS_LITERAL_STRING("example2.org");
+    const nsString origin1 = NS_LITERAL_STRING("http://example1.com");
+    const nsString origin2 = NS_LITERAL_STRING("http://example2.org");
     nsCString nodeId3 = GetNodeId(origin1, origin2, false);
     EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
 
     SetFinished();
   }
 
   class CreateDecryptorDone : public GetGMPDecryptorCallback
   {
@@ -702,18 +702,18 @@ class GMPStorageTest : public GMPDecrypt
       GeckoMediaPluginService::GetGeckoMediaPluginService();
 
     // Send a message to the fake GMP for it to run its own tests internally.
     // It sends us a "test-storage complete" message when its passed, or
     // some other message if its tests fail.
     Expect(NS_LITERAL_CSTRING("test-storage complete"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate storage data for some sites.
    * 2. Forget about one of the sites.
    * 3. Check if the storage data for the forgotten site are erased correctly.
@@ -723,32 +723,32 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_AnotherSite);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   void TestForgetThisSite_AnotherSite() {
     Shutdown();
 
     // Generate storage data for another site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example3.com"),
+                    NS_LITERAL_STRING("http://example4.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   struct NodeInfo {
     explicit NodeInfo(const nsACString& aSite) : siteToForget(aSite) {}
     nsCString siteToForget;
     nsTArray<nsCString> expectedRemainingNodeIds;
@@ -766,17 +766,17 @@ class GMPStorageTest : public GMPDecrypt
       }
     }
   private:
     NodeInfo* mNodeInfo;
   };
 
   void TestForgetThisSite_CollectSiteInfo() {
     nsAutoPtr<NodeInfo> siteInfo(
-        new NodeInfo(NS_LITERAL_CSTRING("example1.com")));
+        new NodeInfo(NS_LITERAL_CSTRING("http://example1.com")));
     // Collect nodeIds that are expected to remain for later comparison.
     EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo));
     // Invoke "Forget this site" on the main thread.
     NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<nsAutoPtr<NodeInfo>>(
         this, &GMPStorageTest::TestForgetThisSite_Forget, siteInfo));
   }
 
   void TestForgetThisSite_Forget(nsAutoPtr<NodeInfo> aSiteInfo) {
@@ -856,18 +856,18 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory1_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
 }
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t| to clear recent history.
@@ -878,18 +878,18 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory2_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t+1| to clear recent history.
@@ -900,18 +900,18 @@ class GMPStorageTest : public GMPDecrypt
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory3_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
+                    NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   class MaxMTimeFinder {
   public:
     MaxMTimeFinder() : mMaxTime(0) {}
     void operator()(nsIFile* aFile) {
@@ -1014,76 +1014,76 @@ class GMPStorageTest : public GMPDecrypt
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestCrossOriginStorage_RecordStoredContinuation));
 
     nsCString update("store crossOriginTestRecordId ");
     update.AppendInt((int64_t)t);
 
     // Open decryptor on one, origin, write a record, and test that that
     // record can't be read on another origin.
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example3.com"),
+                    NS_LITERAL_STRING("http://example4.com"),
                     false,
                     update);
   }
 
   void TestCrossOriginStorage_RecordStoredContinuation() {
     // Close the old decryptor, and create a new one on a different origin,
     // and try to read the record.
     Shutdown();
 
     Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example5.com"),
-                    NS_LITERAL_STRING("example6.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example5.com"),
+                    NS_LITERAL_STRING("http://example6.com"),
                     false,
                     NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
   }
 
   void TestPBStorage() {
     // Send the decryptor the message "store recordid $time"
     // Wait for the decrytor to send us "stored recordid $time"
     nsCString response("stored pbdata test-pb-data");
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestPBStorage_RecordStoredContinuation));
 
     // Open decryptor on one, origin, write a record, close decryptor,
     // open another, and test that record can be read, close decryptor,
     // then send pb-last-context-closed notification, then open decryptor
     // and check that it can't read that data; it should have been purged.
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"),
+                    NS_LITERAL_STRING("http://pb2.com"),
                     true,
                     NS_LITERAL_CSTRING("store pbdata test-pb-data"));
   }
 
   void TestPBStorage_RecordStoredContinuation() {
     Shutdown();
 
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::TestPBStorage_RecordRetrievedContinuation));
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"),
+                    NS_LITERAL_STRING("http://pb2.com"),
                     true,
                     NS_LITERAL_CSTRING("retrieve pbdata"));
   }
 
   void TestPBStorage_RecordRetrievedContinuation() {
     Shutdown();
     SimulatePBModeExit();
 
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"),
+                    NS_LITERAL_STRING("http://pb2.com"),
                     true,
                     NS_LITERAL_CSTRING("retrieve pbdata"));
   }
 
   void NextAsyncShutdownTimeoutTest(nsIRunnable* aContinuation)
   {
     if (mDecryptor) {
       Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
@@ -1103,30 +1103,30 @@ class GMPStorageTest : public GMPDecrypt
         NS_NewRunnableMethod(this, aCallback)));
 
     CreateDecryptor(aOrigin1, aOrigin2, false, continuation);
   }
 
   void TestAsyncShutdownTimeout() {
     // Create decryptors that timeout in their async shutdown.
     // If the gtest hangs on shutdown, test fails!
-    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
-                                  NS_LITERAL_STRING("example8.com"),
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example7.com"),
+                                  NS_LITERAL_STRING("http://example8.com"),
                                   &GMPStorageTest::TestAsyncShutdownTimeout2);
   };
 
   void TestAsyncShutdownTimeout2() {
-    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
-                                  NS_LITERAL_STRING("example10.com"),
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example9.com"),
+                                  NS_LITERAL_STRING("http://example10.com"),
                                   &GMPStorageTest::TestAsyncShutdownTimeout3);
   };
 
   void TestAsyncShutdownTimeout3() {
-    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
-                                  NS_LITERAL_STRING("example12.com"),
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example11.com"),
+                                  NS_LITERAL_STRING("http://example12.com"),
                                   &GMPStorageTest::SetFinished);
   };
 
   void TestAsyncShutdownStorage() {
     // Instruct the GMP to write a token (the current timestamp, so it's
     // unique) during async shutdown, then shutdown the plugin, re-create
     // it, and check that the token was successfully stored.
     auto t = time(0);
@@ -1139,18 +1139,18 @@ class GMPStorageTest : public GMPDecrypt
     // the token.
     nsCString response("shutdown-token received ");
     response.Append(token);
     Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
 
     // Test that a GMP can write to storage during shutdown, and retrieve
     // that written data in a subsequent session.
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example13.com"),
+                    NS_LITERAL_STRING("http://example14.com"),
                     false,
                     update);
   }
 
   void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
     ShutdownThen(NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken));
   }
@@ -1158,42 +1158,42 @@ class GMPStorageTest : public GMPDecrypt
   void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
     // Create a new instance of the plugin, retrieve the token written
     // during shutdown and verify it is correct.
     nsCString response("retrieved shutdown-token ");
     response.Append(aToken);
     Expect(response,
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example13.com"),
+                    NS_LITERAL_STRING("http://example14.com"),
                     false,
                     NS_LITERAL_CSTRING("retrieve-shutdown-token"));
   }
 
 #if defined(XP_WIN)
   void TestOutputProtection() {
     Shutdown();
 
     Expect(NS_LITERAL_CSTRING("OP tests completed"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example15.com"),
-                    NS_LITERAL_STRING("example16.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example15.com"),
+                    NS_LITERAL_STRING("http://example16.com"),
                     false,
                     NS_LITERAL_CSTRING("test-op-apis"));
   }
 #endif
 
   void TestPluginVoucher() {
     Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
-    CreateDecryptor(NS_LITERAL_STRING("example17.com"),
-                    NS_LITERAL_STRING("example18.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://example17.com"),
+                    NS_LITERAL_STRING("http://example18.com"),
                     false,
                     NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
   }
 
   void TestGetRecordNamesInMemoryStorage() {
     TestGetRecordNames(true);
   }
 
@@ -1232,18 +1232,18 @@ class GMPStorageTest : public GMPDecrypt
       nsIRunnable* continuation = nullptr;
       if (i + 1 == num) {
         continuation =
           NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames);
       }
       Expect(response, continuation);
     }
 
-    CreateDecryptor(NS_LITERAL_STRING("foo.com"),
-                    NS_LITERAL_STRING("bar.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://foo.com"),
+                    NS_LITERAL_STRING("http://bar.com"),
                     aPrivateBrowsing,
                     Move(updates));
   }
 
   void TestGetRecordNames_QueryNames() {
     nsCString response("record-names ");
     response.Append(mRecordNames);
     Expect(response,
@@ -1285,18 +1285,18 @@ class GMPStorageTest : public GMPDecrypt
     response.AppendLiteral(" ");
     response.Append(data);
     Expect(response, NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
     nsCString update("store ");
     update.Append(longRecordName);
     update.AppendLiteral(" ");
     update.Append(data);
-    CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
-                    NS_LITERAL_STRING("baz.com"),
+    CreateDecryptor(NS_LITERAL_STRING("http://fuz.com"),
+                    NS_LITERAL_STRING("http://baz.com"),
                     false,
                     update);
   }
 
   void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
     mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
   }
 
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -163,22 +163,26 @@ MediaSourceReader::RequestAudioData()
     case SOURCE_NEW:
       GetAudioReader()->ResetDecode();
       mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0)
                               ->RefableThen(GetTaskQueue(), __func__, this,
                                             &MediaSourceReader::CompleteAudioSeekAndDoRequest,
                                             &MediaSourceReader::CompleteAudioSeekAndRejectPromise));
       break;
     case SOURCE_NONE:
-      if (mLastAudioTime) {
+      if (!mLastAudioTime) {
+        // This is the first call to RequestAudioData.
+        // Fallback to using decoder with earliest data.
+        mAudioSourceDecoder = FirstDecoder(MediaData::AUDIO_DATA);
+      }
+      if (mLastAudioTime || !mAudioSourceDecoder) {
         CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
         break;
       }
-      // Fallback to using first reader
-      mAudioSourceDecoder = mAudioTrack->Decoders()[0];
+      // Fallback to getting first frame from first decoder.
     default:
       DoAudioRequest();
       break;
   }
   return p;
 }
 
 void MediaSourceReader::DoAudioRequest()
@@ -333,22 +337,26 @@ MediaSourceReader::RequestVideoData(bool
     case SOURCE_NEW:
       GetVideoReader()->ResetDecode();
       mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0)
                              ->RefableThen(GetTaskQueue(), __func__, this,
                                            &MediaSourceReader::CompleteVideoSeekAndDoRequest,
                                            &MediaSourceReader::CompleteVideoSeekAndRejectPromise));
       break;
     case SOURCE_NONE:
-      if (mLastVideoTime) {
+      if (!mLastVideoTime) {
+        // This is the first call to RequestVideoData.
+        // Fallback to using decoder with earliest data.
+        mVideoSourceDecoder = FirstDecoder(MediaData::VIDEO_DATA);
+      }
+      if (mLastVideoTime || !mVideoSourceDecoder) {
         CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
         break;
       }
-      // Fallback to using first reader.
-      mVideoSourceDecoder = mVideoTrack->Decoders()[0];
+      // Fallback to getting first frame from first decoder.
     default:
       DoVideoRequest();
       break;
   }
 
   return p;
 }
 
@@ -1028,16 +1036,46 @@ MediaSourceReader::GetBuffered(dom::Time
 
     intersectionRanges->Intersection(sourceRanges);
   }
 
   MSE_DEBUG("ranges=%s", DumpTimeRanges(intersectionRanges).get());
   return NS_OK;
 }
 
+already_AddRefed<SourceBufferDecoder>
+MediaSourceReader::FirstDecoder(MediaData::Type aType)
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  TrackBuffer* trackBuffer =
+    aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
+  MOZ_ASSERT(trackBuffer);
+  const nsTArray<nsRefPtr<SourceBufferDecoder>>& decoders = trackBuffer->Decoders();
+  if (decoders.IsEmpty()) {
+    return nullptr;
+  }
+
+  nsRefPtr<SourceBufferDecoder> firstDecoder;
+  double lowestStartTime = PositiveInfinity<double>();
+
+  for (uint32_t i = 0; i < decoders.Length(); ++i) {
+    nsRefPtr<TimeRanges> r = new TimeRanges();
+    decoders[i]->GetBuffered(r);
+    double start = r->GetStartTime();
+    if (start < 0) {
+      continue;
+    }
+    if (start < lowestStartTime) {
+      firstDecoder = decoders[i];
+      lowestStartTime = start;
+    }
+  }
+  return firstDecoder.forget();
+}
+
 nsRefPtr<MediaDecoderReader::WaitForDataPromise>
 MediaSourceReader::WaitForData(MediaData::Type aType)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   nsRefPtr<WaitForDataPromise> p = WaitPromise(aType).Ensure(__func__);
   MaybeNotifyHaveData();
   return p;
@@ -1048,23 +1086,33 @@ MediaSourceReader::MaybeNotifyHaveData()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   bool haveAudio = false, haveVideo = false;
   bool ended = IsEnded();
   // If we are in ended mode, we will resolve any pending wait promises.
   // The next Request*Data will handle END_OF_STREAM or going back into waiting
   // mode.
   if (!IsSeeking() && mAudioTrack) {
-    haveAudio = HaveData(mLastAudioTime, MediaData::AUDIO_DATA);
+    if (!mLastAudioTime) {
+      nsRefPtr<SourceBufferDecoder> d = FirstDecoder(MediaData::AUDIO_DATA);
+      haveAudio = !!d;
+    } else {
+      haveAudio = HaveData(mLastAudioTime, MediaData::AUDIO_DATA);
+    }
     if (ended || haveAudio) {
       WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
     }
   }
   if (!IsSeeking() && mVideoTrack) {
-    haveVideo = HaveData(mLastVideoTime, MediaData::VIDEO_DATA);
+    if (!mLastVideoTime) {
+      nsRefPtr<SourceBufferDecoder> d = FirstDecoder(MediaData::VIDEO_DATA);
+      haveVideo = !!d;
+    } else {
+      haveVideo = HaveData(mLastVideoTime, MediaData::VIDEO_DATA);
+    }
     if (ended || haveVideo) {
       WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
     }
   }
   MSE_DEBUG("isSeeking=%d haveAudio=%d, haveVideo=%d ended=%d",
             IsSeeking(), haveAudio, haveVideo, ended);
 }
 
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -218,16 +218,17 @@ private:
   void CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime /* microseconds */);
 
   // Return a decoder from the set available in aTrackDecoders that has data
   // available in the range requested by aTarget.
   already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget /* microseconds */,
                                                       int64_t aTolerance /* microseconds */,
                                                       const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
   bool HaveData(int64_t aTarget, MediaData::Type aType);
+  already_AddRefed<SourceBufferDecoder> FirstDecoder(MediaData::Type aType);
 
   void AttemptSeek();
   bool IsSeeking() { return mPendingSeekTime != -1; }
 
   bool IsNearEnd(MediaData::Type aType, int64_t aTime /* microseconds */);
   int64_t LastSampleTime(MediaData::Type aType);
 
   nsRefPtr<SourceBufferDecoder> mAudioSourceDecoder;
--- a/dom/media/webm/IntelWebMVideoDecoder.cpp
+++ b/dom/media/webm/IntelWebMVideoDecoder.cpp
@@ -111,17 +111,17 @@ IntelWebMVideoDecoder::Create(WebMReader
   return decoder.forget();
 }
 
 bool
 IntelWebMVideoDecoder::IsSupportedVideoMimeType(const nsACString& aMimeType)
 {
   return (aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
           aMimeType.EqualsLiteral("video/webm; codecs=vp9")) &&
-         mPlatform->SupportsVideoMimeType(aMimeType);
+         mPlatform->SupportsMimeType(aMimeType);
 }
 
 nsresult
 IntelWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
 {
   mPlatform = PlatformDecoderModule::Create();
   if (!mPlatform) {
     return NS_ERROR_FAILURE;
@@ -142,21 +142,22 @@ IntelWebMVideoDecoder::Init(unsigned int
   default:
     return NS_ERROR_FAILURE;
   }
 
   const VideoDecoderConfig& video = *mDecoderConfig;
   if (!IsSupportedVideoMimeType(video.mime_type)) {
     return NS_ERROR_FAILURE;
   }
-  mMediaDataDecoder = mPlatform->CreateVideoDecoder(video,
-                                                    mReader->GetLayersBackendType(),
-                                                    mReader->GetDecoder()->GetImageContainer(),
-                                                    mTaskQueue,
-                                                    this);
+  mMediaDataDecoder =
+    mPlatform->CreateDecoder(video,
+                             mTaskQueue,
+                             this,
+                             mReader->GetLayersBackendType(),
+                             mReader->GetDecoder()->GetImageContainer());
   if (!mMediaDataDecoder) {
     return NS_ERROR_FAILURE;
   }
   nsresult rv = mMediaDataDecoder->Init();
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -465,18 +465,19 @@ MediaEngineGonkVideoSource::Notify(const
 void
 MediaEngineGonkVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
   MOZ_ASSERT(NS_IsMainThread());
 
   ICameraControl::Configuration config;
   config.mMode = ICameraControl::kPictureMode;
   config.mPreviewSize.width = aCapability.width;
   config.mPreviewSize.height = aCapability.height;
+  config.mPictureSize.width = aCapability.width;
+  config.mPictureSize.height = aCapability.height;
   mCameraControl->Start(&config);
-  mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, config.mPreviewSize);
 
   hal::RegisterScreenConfigurationObserver(this);
 }
 
 void
 MediaEngineGonkVideoSource::StopImpl() {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/network/tests/test_udpsocket.html
+++ b/dom/network/tests/test_udpsocket.html
@@ -316,16 +316,19 @@ function testOpenWithoutClose() {
   info('test for open without close');
 
   let closed = [];
   for (let i = 0; i < 50; i++) {
     let socket = new UDPSocket();
     closed.push(socket.closed);
   }
 
+  SpecialPowers.gc();
+  info('all unrefereced socket should be closed right after GC');
+
   return Promise.all(closed);
 }
 
 function testBFCache() {
   info('test for bfcache behavior');
 
   let socket = new UDPSocket();
 
--- a/dom/nfc/gonk/Nfc.js
+++ b/dom/nfc/gonk/Nfc.js
@@ -117,17 +117,18 @@ EventMsgTable[NfcResponseType.MAKE_READ_
 EventMsgTable[NfcResponseType.FORMAT_RSP] = "NFC:FormatResponse";
 EventMsgTable[NfcResponseType.TRANSCEIVE_RSP] = "NFC:TransceiveResponse";
 
 // Should be consistent with NfcNotificationType defined in NfcOptions.webidl.
 const NfcNotificationType = {
   INITIALIZED: "initialized",
   TECH_DISCOVERED: "techDiscovered",
   TECH_LOST: "techLost",
-  HCI_EVENT_TRANSACTION: "hciEventTransaction"
+  HCI_EVENT_TRANSACTION: "hciEventTransaction",
+  NDEF_RECEIVED: "ndefReceived"
 };
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
@@ -635,24 +636,17 @@ Nfc.prototype = {
         // Update the upper layers with a session token (alias)
         message.sessionToken =
           SessionHelper.registerSession(message.sessionId, message.isP2P);
         // Do not expose the actual session to the content
         let sessionId = message.sessionId;
         delete message.sessionId;
 
         if (SessionHelper.isP2PSession(sessionId)) {
-          if (message.records) {
-            // TODO: Bug 1082493.
-            // This event should be sent to the focus app, but before Bug 1082493
-            // is landed we forward this to System app.
-            gMessageManager.callDefaultFoundHandler(message);
-          } else {
-            gMessageManager.onPeerEvent(NFC.PEER_EVENT_FOUND, message.sessionToken);
-          }
+          gMessageManager.onPeerEvent(NFC.PEER_EVENT_FOUND, message.sessionToken);
         } else {
           gMessageManager.onTagFound(message);
         }
         break;
       case NfcNotificationType.TECH_LOST:
         // Update the upper layers with a session token (alias)
         message.sessionToken = SessionHelper.getToken(message.sessionId);
         if (SessionHelper.isP2PSession(message.sessionId)) {
@@ -661,16 +655,25 @@ Nfc.prototype = {
           gMessageManager.onTagLost(message.sessionToken);
         }
 
         SessionHelper.unregisterSession(message.sessionId);
         break;
       case NfcNotificationType.HCI_EVENT_TRANSACTION:
         this.notifyHCIEventTransaction(message);
         break;
+      case NfcNotificationType.NDEF_RECEIVED:
+        message.sessionToken = SessionHelper.getToken(message.sessionId);
+        delete message.sessionId;
+        message.isP2P = true;
+        // TODO: Bug 1082493.
+        // This event should be sent to the focus app, but before Bug 1082493
+        // is landed we forward this to System app.
+        gMessageManager.callDefaultFoundHandler(message);
+        break;
       case NfcResponseType.CHANGE_RF_STATE_RSP:
         this.sendNfcResponse(message);
 
         if (!message.errorMsg) {
           this.rfState = message.rfState;
           gMessageManager.onRFStateChanged(this.rfState);
         }
         if (this.rfState == NFC.NFC_RF_STATE_IDLE) {
--- a/dom/nfc/gonk/NfcGonkMessage.h
+++ b/dom/nfc/gonk/NfcGonkMessage.h
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NfcGonkMessage_h
 #define NfcGonkMessage_h
 
 namespace mozilla {
 
 #define NFCD_MAJOR_VERSION 1
-#define NFCD_MINOR_VERSION 21
+#define NFCD_MINOR_VERSION 22
 
 enum NfcTechlogy {
   NDEF = 0,
   NDEFWritable,
   NDEFFormattable,
   P2P,
 };
 
@@ -34,18 +34,11 @@ enum NfcErrorCode {
   NotInitialized = 13,
   InitializeFail = 14,
   DeinitializeFail = 15,
   NotSupported = 16,
   FailEnableLowPowerMode = 17,
   FailDisableLowPowerMode = 18,
 };
 
-enum SecureElementOrigin {
-  SIM = 0,
-  ESE = 1,
-  ASSD = 2,
-  OriginEndGuard = 3
-};
-
 } // namespace mozilla
 
 #endif // NfcGonkMessage_h
--- a/dom/nfc/gonk/NfcMessageHandler.cpp
+++ b/dom/nfc/gonk/NfcMessageHandler.cpp
@@ -100,16 +100,19 @@ NfcMessageHandler::ProcessNotification(i
       result = TechDiscoveredNotification(aParcel, aOptions);
       break;
     case NfcNotificationType::TechLost:
       result = TechLostNotification(aParcel, aOptions);
       break;
     case NfcNotificationType::HciEventTransaction:
       result = HCIEventTransactionNotification(aParcel, aOptions);
       break;
+    case NfcNotificationType::NdefReceived:
+      result = NDEFReceivedNotification(aParcel, aOptions);
+      break;
     default:
       result = false;
       break;
   }
 
   return result;
 }
 
@@ -301,16 +304,28 @@ NfcMessageHandler::HCIEventTransactionNo
   int32_t payloadLength = aParcel.readInt32();
   aOptions.mPayload.AppendElements(
     static_cast<const uint8_t*>(aParcel.readInplace(payloadLength)), payloadLength);
 
   return true;
 }
 
 bool
+NfcMessageHandler::NDEFReceivedNotification(const Parcel& aParcel, EventOptions& aOptions)
+{
+  aOptions.mSessionId = aParcel.readInt32();
+  int32_t ndefMsgCount = aParcel.readInt32();
+  if (ndefMsgCount != 0) {
+    ReadNDEFMessage(aParcel, aOptions);
+  }
+
+  return true;
+}
+
+bool
 NfcMessageHandler::ReadNDEFMessage(const Parcel& aParcel, EventOptions& aOptions)
 {
   int32_t recordCount = aParcel.readInt32();
   aOptions.mRecords.SetCapacity(recordCount);
 
   for (int i = 0; i < recordCount; i++) {
     int32_t tnf = aParcel.readInt32();
     NDEFRecordStruct record;
--- a/dom/nfc/gonk/NfcMessageHandler.h
+++ b/dom/nfc/gonk/NfcMessageHandler.h
@@ -36,16 +36,17 @@ private:
   bool TransceiveRequest(android::Parcel& aParcel, const CommandOptions& options);
   bool TransceiveResponse(const android::Parcel& aParcel, EventOptions& aOptions);
 
   bool ProcessNotification(int32_t aType, const android::Parcel& aParcel, EventOptions& aOptions);
   bool InitializeNotification(const android::Parcel& aParcel, EventOptions& aOptions);
   bool TechDiscoveredNotification(const android::Parcel& aParcel, EventOptions& aOptions);
   bool TechLostNotification(const android::Parcel& aParcel, EventOptions& aOptions);
   bool HCIEventTransactionNotification(const android::Parcel& aParcel, EventOptions& aOptions);
+  bool NDEFReceivedNotification(const android::Parcel& aParcel, EventOptions& aOptions);
 
   bool ReadNDEFMessage(const android::Parcel& aParcel, EventOptions& aOptions);
   bool WriteNDEFMessage(android::Parcel& aParcel, const CommandOptions& aOptions);
   bool ReadTransceiveResponse(const android::Parcel& aParcel, EventOptions& aOptions);
 private:
   nsTArray<nsString> mRequestIdQueue;
 };
 
--- a/dom/nfc/gonk/NfcService.cpp
+++ b/dom/nfc/gonk/NfcService.cpp
@@ -20,22 +20,16 @@
 #define NS_NFCSERVICE_CID \
   { 0x584c9a21, 0x4e17, 0x43b7, {0xb1, 0x6a, 0x87, 0xa0, 0x42, 0xef, 0xd4, 0x64} }
 #define NS_NFCSERVICE_CONTRACTID "@mozilla.org/nfc/service;1"
 
 using namespace android;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
-static const nsLiteralString SEOriginString[] = {
-  NS_LITERAL_STRING("SIM"),
-  NS_LITERAL_STRING("eSE"),
-  NS_LITERAL_STRING("ASSD")
-};
-
 namespace mozilla {
 
 static NfcService* gNfcService;
 
 NS_IMPL_ISUPPORTS(NfcService, nsINfcService)
 
 void
 assertIsNfcServiceThread()
@@ -186,20 +180,20 @@ public:
     }
 
     COPY_OPT_FIELD(mMaxNDEFSize, -1)
     COPY_OPT_FIELD(mIsReadOnly, -1)
     COPY_OPT_FIELD(mIsFormatable, -1)
 
     // HCI Event Transaction parameters.
     if (mEvent.mOriginType != -1) {
-      MOZ_ASSERT(mEvent.mOriginType < SecureElementOrigin::OriginEndGuard);
+      MOZ_ASSERT(static_cast<HCIEventOrigin>(mEvent.mOriginType) < HCIEventOrigin::EndGuard_);
 
       event.mOrigin.Construct();
-      event.mOrigin.Value().Assign(SEOriginString[mEvent.mOriginType]);
+      event.mOrigin.Value().AssignASCII(HCIEventOriginValues::strings[mEvent.mOriginType].value);
       event.mOrigin.Value().AppendInt(mEvent.mOriginIndex, 16 /* radix */);
     }
 
     if (mEvent.mAid.Length() > 0) {
       event.mAid.Construct();
       event.mAid.Value().Init(Uint8Array::Create(cx, mEvent.mAid.Length(), mEvent.mAid.Elements()));
     }
 
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -108,17 +108,17 @@ CreateDrawTargetForSurface(gfxASurface *
     format = SurfaceFormat::A8;
   } else if (aSurface->GetContentType() == gfxContentType::COLOR) {
     format = SurfaceFormat::B8G8R8X8;
   } else {
     format = SurfaceFormat::B8G8R8A8;
   }
   RefPtr<DrawTarget> drawTarget =
     Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(),
-                                             ToIntSize(gfxIntSize(aSurface->GetSize())),
+                                             aSurface->GetSize(),
                                              &format);
   aSurface->SetData(&kDrawTarget, drawTarget, nullptr);
   return drawTarget;
 }
 
 PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
                                          const nsCString& aMimeType,
                                          const uint16_t& aMode,
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1436,45 +1436,23 @@ PromiseWorkerProxy::StoreISupports(nsISu
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsMainThreadPtrHandle<nsISupports> supports(
     new nsMainThreadPtrHolder<nsISupports>(aSupports));
   mSupportsArray.AppendElement(supports);
 }
 
-namespace {
-
-class PromiseWorkerProxyControlRunnable final
-  : public WorkerControlRunnable
+bool
+PromiseWorkerProxyControlRunnable::WorkerRun(JSContext* aCx,
+                                             WorkerPrivate* aWorkerPrivate)
 {
-  nsRefPtr<PromiseWorkerProxy> mProxy;
-
-public:
-  PromiseWorkerProxyControlRunnable(WorkerPrivate* aWorkerPrivate,
-                                    PromiseWorkerProxy* aProxy)
-    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-    , mProxy(aProxy)
-  {
-    MOZ_ASSERT(aProxy);
-  }
-
-  virtual bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    mProxy->CleanUp(aCx);
-    return true;
-  }
-
-private:
-  ~PromiseWorkerProxyControlRunnable()
-  {}
-};
-
-} // anonymous namespace
+  mProxy->CleanUp(aCx);
+  return true;
+}
 
 void
 PromiseWorkerProxy::RunCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue,
                                 RunCallbackFunc aFunc)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -6,16 +6,18 @@
 #define mozilla_dom_PromiseWorkerProxy_h
 
 // Required for Promise::PromiseTaskSync.
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/workers/bindings/WorkerFeature.h"
 #include "nsProxyRelease.h"
 
+#include "WorkerRunnable.h"
+
 namespace mozilla {
 namespace dom {
 
 class Promise;
 
 namespace workers {
 class WorkerPrivate;
 }
@@ -48,18 +50,22 @@ class WorkerPrivate;
 //
 // PromiseWorkerProxy can also be used in situations where there is no main
 // thread Promise, or where special handling is required on the worker thread
 // for promise resolution. Create a PromiseWorkerProxy as in steps 1 and
 // 2 above. When the main thread is ready to resolve the worker thread promise,
 // dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
 // worker. This might be null! In the WorkerRunnable's WorkerRun() use
 // GetWorkerPromise() to access the Promise and resolve/reject it. Then call
-// CleanUp() on the worker
-// thread.
+// CleanUp() on the worker thread.
+//
+// IMPORTANT: Dispatching the runnable to the worker thread may fail causing
+// the promise to leak. To successfully release the promise on the
+// worker thread in this case, use |PromiseWorkerProxyControlRunnable| to
+// dispatch a control runnable that will deref the object on the correct thread.
 
 class PromiseWorkerProxy : public PromiseNativeHandler,
                            public workers::WorkerFeature
 {
   friend class PromiseWorkerProxyRunnable;
 
   // This overrides the non-threadsafe refcounting in PromiseNativeHandler.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseWorkerProxy, override)
@@ -73,16 +79,27 @@ public:
   workers::WorkerPrivate* GetWorkerPrivate() const;
 
   Promise* GetWorkerPromise() const;
 
   void StoreISupports(nsISupports* aSupports);
 
   void CleanUp(JSContext* aCx);
 
+  Mutex& GetCleanUpLock()
+  {
+    return mCleanUpLock;
+  }
+
+  bool IsClean() const
+  {
+    mCleanUpLock.AssertCurrentThreadOwns();
+    return mCleanedUp;
+  }
+
 protected:
   virtual void ResolvedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
   virtual void RejectedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
   virtual bool Notify(JSContext* aCx, workers::Status aStatus) override;
@@ -114,12 +131,36 @@ private:
   // Aimed to keep objects alive when doing the structured-clone read/write,
   // which can be added by calling StoreISupports() on the main thread.
   nsTArray<nsMainThreadPtrHandle<nsISupports>> mSupportsArray;
 
   // Ensure the worker and the main thread won't race to access |mCleanedUp|.
   Mutex mCleanUpLock;
 };
 
+// Helper runnable used for releasing the proxied promise when the worker
+// is not accepting runnables and the promise object would leak.
+// See the instructions above.
+class PromiseWorkerProxyControlRunnable final : public workers::WorkerControlRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mProxy;
+
+public:
+  PromiseWorkerProxyControlRunnable(workers::WorkerPrivate* aWorkerPrivate,
+                                    PromiseWorkerProxy* aProxy)
+    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mProxy(aProxy)
+  {
+    MOZ_ASSERT(aProxy);
+  }
+
+  virtual bool
+  WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override;
+
+private:
+  ~PromiseWorkerProxyControlRunnable()
+  {}
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PromiseWorkerProxy_h
--- a/dom/system/gonk/moz.build
+++ b/dom/system/gonk/moz.build
@@ -93,16 +93,17 @@ if CONFIG['MOZ_B2G_RIL']:
         'RILContentHelper.manifest', # TODO: Bug 815526, deprecate RILContentHelper.
         'RILSystemMessengerHelper.js',
         'RILSystemMessengerHelper.manifest',
     ]
     EXTRA_JS_MODULES += [
         'ril_consts.js',
         'ril_worker.js',
         'ril_worker_buf_object.js',
+        'ril_worker_telephony_request_queue.js',
         'RILSystemMessenger.jsm',
     ]
     if not CONFIG['DISABLE_MOZ_RIL_GEOLOC']:
         EXTRA_COMPONENTS += [
             'RadioInterfaceLayer.js',
             'RadioInterfaceLayer.manifest',
         ]
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -32,22 +32,24 @@
  * - nsIRadioInterfaceLayer -> postMessage() -> RIL -> Buf -> postRILMessage()
  *
  * Note: The code below is purposely lean on abstractions to be as lean in
  * terms of object allocations. As a result, it may look more like C than
  * JavaScript, and that's intended.
  */
 
 /* global BufObject */
+/* global TelephonyRequestQueue */
 
 "use strict";
 
 importScripts("ril_consts.js");
 importScripts("resource://gre/modules/workers/require.js");
 importScripts("ril_worker_buf_object.js");
+importScripts("ril_worker_telephony_request_queue.js");
 
 // set to true in ril_consts.js to see debug messages
 let DEBUG = DEBUG_WORKER;
 let GLOBAL = this;
 
 if (!this.debug) {
   // Debugging stub that goes nowhere.
   this.debug = function debug(message) {
@@ -79,144 +81,16 @@ let RILQUIRKS_SEND_STK_PROFILE_DOWNLOAD;
 // Ril quirk to attach data registration on demand.
 let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND;
 
 // Ril quirk to control the uicc/data subscription.
 let RILQUIRKS_SUBSCRIPTION_CONTROL;
 
 let RILQUIRKS_SIGNAL_EXTRA_INT32;
 
-const TELEPHONY_REQUESTS = [
-  REQUEST_GET_CURRENT_CALLS,
-  REQUEST_ANSWER,
-  REQUEST_CONFERENCE,
-  REQUEST_DIAL,
-  REQUEST_DIAL_EMERGENCY_CALL,
-  REQUEST_HANGUP,
-  REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
-  REQUEST_HANGUP_WAITING_OR_BACKGROUND,
-  REQUEST_SEPARATE_CONNECTION,
-  REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
-  REQUEST_UDUB
-];
-
-function TelephonyRequestEntry(request, callback) {
-  this.request = request;
-  this.callback = callback;
-}
-
-function TelephonyRequestQueue(ril) {
-  this.ril = ril;
-  this.currentQueue = null;  // Point to the current running queue.
-
-  this.queryQueue = [];
-  this.controlQueue = [];
-}
-TelephonyRequestQueue.prototype = {
-  ril: null,
-
-  _getQueue: function(request) {
-    return (request === REQUEST_GET_CURRENT_CALLS) ? this.queryQueue
-                                                   : this.controlQueue;
-  },
-
-  _getAnotherQueue: function(queue) {
-    return (this.queryQueue === queue) ? this.controlQueue : this.queryQueue;
-  },
-
-  _find: function(queue, request) {
-    for (let i = 0; i < queue.length; ++i) {
-      if (queue[i].request === request) {
-        return i;
-      }
-    }
-    return -1;
-  },
-
-  _startQueue: function(queue) {
-    if (queue.length === 0) {
-      return;
-    }
-
-    // We only need to keep one entry for queryQueue.
-    if (queue === this.queryQueue) {
-      queue.splice(1, queue.length - 1);
-    }
-
-    this.currentQueue = queue;
-    for (let entry of queue) {
-      this._executeEntry(entry);
-    }
-  },
-
-  _executeEntry: function(entry) {
-    if (DEBUG) this.debug("execute " + this._getRequestName(entry.request));
-    entry.callback();
-  },
-
-  _getRequestName: function(request) {
-    let method = this.ril[request];
-    return (typeof method === 'function') ? method.name : "";
-  },
-
-  debug: function(msg) {
-    this.ril.context.debug("[TeleQ] " + msg);
-  },
-
-  isValidRequest: function(request) {
-    return TELEPHONY_REQUESTS.indexOf(request) !== -1;
-  },
-
-  push: function(request, callback) {
-    if (!this.isValidRequest(request)) {
-      if (DEBUG) {
-        this.debug("Error: " + this._getRequestName(request) +
-                   " is not a telephony request");
-      }
-      return;
-    }
-
-    if (DEBUG) this.debug("push " + this._getRequestName(request));
-    let entry = new TelephonyRequestEntry(request, callback);
-    let queue = this._getQueue(request);
-    queue.push(entry);
-
-    // Try to run the request.
-    if (this.currentQueue === queue) {
-      this._executeEntry(entry);
-    } else if (!this.currentQueue) {
-      this._startQueue(queue);
-    }
-  },
-
-  pop: function(request) {
-    if (!this.isValidRequest(request)) {
-      if (DEBUG) {
-        this.debug("Error: " + this._getRequestName(request) +
-                   " is not a telephony request");
-      }
-      return;
-    }
-
-    if (DEBUG) this.debug("pop " + this._getRequestName(request));
-    let queue = this._getQueue(request);
-    let index = this._find(queue, request);
-    if (index === -1) {
-      throw new Error("Cannot find the request in telephonyRequestQueue.");
-    } else {
-      queue.splice(index, 1);
-    }
-
-    if (queue.length === 0) {
-      this.currentQueue = null;
-      this._startQueue(this._getAnotherQueue(queue));
-    }
-  }
-};
-
 /**
  * The RIL state machine.
  *
  * This object communicates with rild via parcels and with the main thread
  * via post messages. It maintains state about the radio, ICC, calls, etc.
  * and acts upon state changes accordingly.
  */
 function RilObject(aContext) {
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/ril_worker_telephony_request_queue.js
@@ -0,0 +1,157 @@
+/* global DEBUG, DEBUG_WORKER */
+/* global REQUEST_GET_CURRENT_CALLS */
+/* global REQUEST_ANSWER, REQUEST_CONFERENCE, REQUEST_DIAL */
+/* global REQUEST_DIAL_EMERGENCY_CALL, REQUEST_HANGUP */
+/* global REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND */
+/* global REQUEST_HANGUP_WAITING_OR_BACKGROUND */
+/* global REQUEST_SEPARATE_CONNECTION */
+/* global REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, REQUEST_UDUB */
+
+"use strict";
+
+(function(exports) {
+
+  const TELEPHONY_REQUESTS = [
+    REQUEST_GET_CURRENT_CALLS,
+    REQUEST_ANSWER,
+    REQUEST_CONFERENCE,
+    REQUEST_DIAL,
+    REQUEST_DIAL_EMERGENCY_CALL,
+    REQUEST_HANGUP,
+    REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
+    REQUEST_HANGUP_WAITING_OR_BACKGROUND,
+    REQUEST_SEPARATE_CONNECTION,
+    REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
+    REQUEST_UDUB
+  ];
+
+  // Set to true in ril_consts.js to see debug messages
+  let DEBUG = DEBUG_WORKER;
+
+  /**
+   * Queue entry; only used in the queue.
+   */
+  let TelephonyRequestEntry = function(request, callback) {
+    this.request = request;
+    this.callback = callback;
+  };
+
+  let TelephonyRequestQueue = function(ril) {
+    this.ril = ril;
+    this.currentQueue = null;  // Point to the current running queue.
+
+    this.queryQueue = [];
+    this.controlQueue = [];
+  };
+
+  TelephonyRequestQueue.prototype._getQueue = function(request) {
+    return (request === REQUEST_GET_CURRENT_CALLS) ? this.queryQueue
+                                                   : this.controlQueue;
+  };
+
+  TelephonyRequestQueue.prototype._getAnotherQueue = function(queue) {
+    return (this.queryQueue === queue) ? this.controlQueue : this.queryQueue;
+  };
+
+  TelephonyRequestQueue.prototype._find = function(queue, request) {
+    for (let i = 0; i < queue.length; ++i) {
+      if (queue[i].request === request) {
+        return i;
+      }
+    }
+    return -1;
+  };
+
+  TelephonyRequestQueue.prototype._startQueue = function(queue) {
+    if (queue.length === 0) {
+      return;
+    }
+
+    // We only need to keep one entry for queryQueue.
+    if (queue === this.queryQueue) {
+      queue.splice(1, queue.length - 1);
+    }
+
+    this.currentQueue = queue;
+    for (let entry of queue) {
+      this._executeEntry(entry);
+    }
+  };
+
+  TelephonyRequestQueue.prototype._executeEntry = function(entry) {
+    if (DEBUG) {
+      this.debug("execute " + this._getRequestName(entry.request));
+    }
+    entry.callback();
+  };
+
+  TelephonyRequestQueue.prototype._getRequestName = function(request) {
+    let method = this.ril[request];
+    return (typeof method === 'function') ? method.name : "";
+  };
+
+  TelephonyRequestQueue.prototype.debug = function(msg) {
+    this.ril.context.debug("[TeleQ] " + msg);
+  };
+
+  TelephonyRequestQueue.prototype.isValidRequest = function(request) {
+    return TELEPHONY_REQUESTS.indexOf(request) !== -1;
+  };
+
+  TelephonyRequestQueue.prototype.push = function(request, callback) {
+    if (!this.isValidRequest(request)) {
+      if (DEBUG) {
+        this.debug("Error: " + this._getRequestName(request) +
+                   " is not a telephony request");
+      }
+      return;
+    }
+
+    if (DEBUG) {
+      this.debug("push " + this._getRequestName(request));
+    }
+    let entry = new TelephonyRequestEntry(request, callback);
+    let queue = this._getQueue(request);
+    queue.push(entry);
+
+    // Try to run the request.
+    if (this.currentQueue === queue) {
+      this._executeEntry(entry);
+    } else if (!this.currentQueue) {
+      this._startQueue(queue);
+    }
+  };
+
+  TelephonyRequestQueue.prototype.pop = function(request) {
+    if (!this.isValidRequest(request)) {
+      if (DEBUG) {
+        this.debug("Error: " + this._getRequestName(request) +
+                   " is not a telephony request");
+      }
+      return;
+    }
+
+    if (DEBUG) {
+      this.debug("pop " + this._getRequestName(request));
+    }
+    let queue = this._getQueue(request);
+    let index = this._find(queue, request);
+    if (index === -1) {
+      throw new Error("Cannot find the request in telephonyRequestQueue.");
+    } else {
+      queue.splice(index, 1);
+    }
+
+    if (queue.length === 0) {
+      this.currentQueue = null;
+      this._startQueue(this._getAnotherQueue(queue));
+    }
+  };
+
+
+  // Before we make sure to form it as a module would not add extra
+  // overhead of module loading, we need to define it in this way
+  // rather than 'module.exports' it as a module component.
+  exports.TelephonyRequestQueue = TelephonyRequestQueue;
+})(self); // in worker self is the global
+
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -1116,16 +1116,17 @@ TelephonyService.prototype = {
       aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
       return;
     }
 
     let callNum = Object.keys(this._currentCalls[aClientId]).length;
     if (callNum !== 1) {
       this._hangUpBackground(aClientId, aCallback);
     } else {
+      call.hangUpLocal = true;
       this._sendToRilWorker(aClientId, "udub", null,
                             this._defaultCallbackHandler.bind(this, aCallback));
     }
   },
 
   _hangUpForeground: function(aClientId, aCallback) {
     let calls = this._getCallsWithState(aClientId, nsITelephonyService.CALL_STATE_CONNECTED);
     calls.forEach(call => call.hangUpLocal = true);
--- a/dom/webidl/Client.webidl
+++ b/dom/webidl/Client.webidl
@@ -17,16 +17,18 @@ interface Client {
   void postMessage(any message, optional sequence<Transferable> transfer);
 };
 
 [Exposed=ServiceWorker]
 interface WindowClient : Client {
   readonly attribute VisibilityState visibilityState;
   readonly attribute boolean focused;
   readonly attribute FrameType frameType;
+
+  [Throws]
   Promise<WindowClient> focus();
 };
 
 enum FrameType {
   "auxiliary",
   "top-level",
   "nested",
   "none"
--- a/dom/webidl/NfcOptions.webidl
+++ b/dom/webidl/NfcOptions.webidl
@@ -34,17 +34,27 @@ enum NfcResponseType {
 
 /**
  * Type of the Notification used in NfcEventOptions.
  */
 enum NfcNotificationType {
   "initialized",
   "techDiscovered",
   "techLost",
-  "hciEventTransaction"
+  "hciEventTransaction",
+  "ndefReceived",
+};
+
+/**
+ * The source of HCI Transaction Event.
+ */
+enum HCIEventOrigin {
+  "SIM",
+  "eSE",
+  "ASSD"
 };
 
 dictionary NfcCommandOptions
 {
   required NfcRequestType type;
 
   long sessionId;
   required DOMString requestId;
--- a/dom/webidl/XMLHttpRequest.webidl
+++ b/dom/webidl/XMLHttpRequest.webidl
@@ -135,14 +135,19 @@ interface XMLHttpRequest : XMLHttpReques
   // Mozilla-specific stuff
 
   [ChromeOnly, SetterThrows=Workers]
   attribute boolean mozBackgroundRequest;
 
   [ChromeOnly, Exposed=Window]
   readonly attribute MozChannel? channel;
 
+  // A platform-specific identifer to represent the network interface 
+  // which the HTTP request would occur on.
+  [ChromeOnly, Exposed=Window]
+  attribute ByteString? networkInterfaceId;
+
   [Throws, ChromeOnly, Exposed=Window]
   any getInterface(IID iid);
 
   readonly attribute boolean mozAnon;
   readonly attribute boolean mozSystem;
 };
--- a/dom/workers/ServiceWorkerClient.h
+++ b/dom/workers/ServiceWorkerClient.h
@@ -45,20 +45,20 @@ class ServiceWorkerClient : public nsISu
                             public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClient)
 
   ServiceWorkerClient(nsISupports* aOwner,
                       const ServiceWorkerClientInfo& aClientInfo)
-    : mOwner(aOwner),
-      mId(aClientInfo.mClientId),
-      mWindowId(aClientInfo.mWindowId),
-      mUrl(aClientInfo.mUrl)
+    : mOwner(aOwner)
+    , mId(aClientInfo.mClientId)
+    , mUrl(aClientInfo.mUrl)
+    , mWindowId(aClientInfo.mWindowId)
   {
     MOZ_ASSERT(aOwner);
   }
 
   nsISupports*
   GetParentObject() const
   {
     return mOwner;
@@ -84,17 +84,19 @@ public:
 
 protected:
   virtual ~ServiceWorkerClient()
   { }
 
 private:
   nsCOMPtr<nsISupports> mOwner;
   nsString mId;
+  nsString mUrl;
+
+protected:
   uint64_t mWindowId;
-  nsString mUrl;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkerclient_h
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
 
 #include "ServiceWorkerClient.h"
 #include "ServiceWorkerClients.h"
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerWindowClient.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -36,210 +37,103 @@ ServiceWorkerClients::ServiceWorkerClien
 JSObject*
 ServiceWorkerClients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ClientsBinding::Wrap(aCx, this, aGivenProto);
 }
 
 namespace {
 
-// Helper class used for passing the promise between threads while
-// keeping the worker alive.
-class PromiseHolder final : public WorkerFeature
-{
-  friend class MatchAllRunnable;
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseHolder)
-
-public:
-  PromiseHolder(WorkerPrivate* aWorkerPrivate,
-                Promise* aPromise)
-    : mWorkerPrivate(aWorkerPrivate),
-      mPromise(aPromise),
-      mCleanUpLock("promiseHolderCleanUpLock"),
-      mClean(false)
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(mPromise);
-
-    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this))) {
-      // Worker has been canceled and will go away.
-      // The ResolvePromiseWorkerRunnable won't run, so we can set mPromise to
-      // nullptr.
-      mPromise = nullptr;
-      mClean = true;
-    }
-  }
-
-  Promise*
-  GetPromise() const
-  {
-    return mPromise;
-  }
-
-  void
-  Clean()
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    MutexAutoLock lock(mCleanUpLock);
-    if (mClean) {
-      return;
-    }
-
-    mPromise = nullptr;
-    mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
-    mClean = true;
-  }
-
-  bool
-  Notify(JSContext* aCx, Status aStatus)
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (aStatus > Running) {
-      Clean();
-    }
-
-    return true;
-  }
-
-private:
-  ~PromiseHolder()
-  {
-    MOZ_ASSERT(mClean);
-  }
-
-  WorkerPrivate* mWorkerPrivate;
-  nsRefPtr<Promise> mPromise;
-
-  // Used to prevent race conditions on |mClean| and to ensure that either a
-  // Notify() call or a dispatch back to the worker thread occurs before
-  // this object is released.
-  Mutex mCleanUpLock;
-
-  bool mClean;
-};
-
 class ResolvePromiseWorkerRunnable final : public WorkerRunnable
 {
-  nsRefPtr<PromiseHolder> mPromiseHolder;
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
   nsTArray<ServiceWorkerClientInfo> mValue;
 
 public:
   ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
-                               PromiseHolder* aPromiseHolder,
+                               PromiseWorkerProxy* aPromiseProxy,
                                nsTArray<ServiceWorkerClientInfo>& aValue)
     : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-      mPromiseHolder(aPromiseHolder)
+      mPromiseProxy(aPromiseProxy)
   {
     AssertIsOnMainThread();
     mValue.SwapElements(aValue);
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
-    Promise* promise = mPromiseHolder->GetPromise();
+    Promise* promise = mPromiseProxy->GetWorkerPromise();
     MOZ_ASSERT(promise);
 
     nsTArray<nsRefPtr<ServiceWorkerClient>> ret;
     for (size_t i = 0; i < mValue.Length(); i++) {
       ret.AppendElement(nsRefPtr<ServiceWorkerClient>(
             new ServiceWorkerWindowClient(promise->GetParentObject(),
                                           mValue.ElementAt(i))));
     }
     promise->MaybeResolve(ret);
 
     // release the reference on the worker thread.
-    mPromiseHolder->Clean();
+    mPromiseProxy->CleanUp(aCx);
 
     return true;
   }
 };
 
-class ReleasePromiseRunnable final : public MainThreadWorkerControlRunnable
-{
-  nsRefPtr<PromiseHolder> mPromiseHolder;
-
-public:
-  ReleasePromiseRunnable(WorkerPrivate* aWorkerPrivate,
-                         PromiseHolder* aPromiseHolder)
-    : MainThreadWorkerControlRunnable(aWorkerPrivate),
-      mPromiseHolder(aPromiseHolder)
-  { }
-
-private:
-  ~ReleasePromiseRunnable()
-  { }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    mPromiseHolder->Clean();
-
-    return true;
-  }
-
-};
-
 class MatchAllRunnable final : public nsRunnable
 {
   WorkerPrivate* mWorkerPrivate;
-  nsRefPtr<PromiseHolder> mPromiseHolder;
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
   nsCString mScope;
 public:
   MatchAllRunnable(WorkerPrivate* aWorkerPrivate,
-                   PromiseHolder* aPromiseHolder,
+                   PromiseWorkerProxy* aPromiseProxy,
                    const nsCString& aScope)
     : mWorkerPrivate(aWorkerPrivate),
-      mPromiseHolder(aPromiseHolder),
+      mPromiseProxy(aPromiseProxy),
       mScope(aScope)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
   }
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
 
-    MutexAutoLock lock(mPromiseHolder->mCleanUpLock);
-    if (mPromiseHolder->mClean) {
+    MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
+    if (mPromiseProxy->IsClean()) {
       // Don't resolve the promise if it was already released.
       return NS_OK;
     }
 
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     nsTArray<ServiceWorkerClientInfo> result;
 
     swm->GetAllClients(mScope, result);
     nsRefPtr<ResolvePromiseWorkerRunnable> r =
-      new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseHolder, result);
+      new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseProxy, result);
 
     AutoSafeJSContext cx;
     if (r->Dispatch(cx)) {
       return NS_OK;
     }
 
     // Dispatch to worker thread failed because the worker is shutting down.
     // Use a control runnable to release the runnable on the worker thread.
-    nsRefPtr<ReleasePromiseRunnable> releaseRunnable =
-      new ReleasePromiseRunnable(mWorkerPrivate, mPromiseHolder);
+    nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
+      new PromiseWorkerProxyControlRunnable(mWorkerPrivate, mPromiseProxy);
 
     if (!releaseRunnable->Dispatch(cx)) {
-      NS_RUNTIMEABORT("Failed to dispatch PromiseHolder control runnable.");
+      NS_RUNTIMEABORT("Failed to dispatch MatchAll promise control runnable.");
     }
 
     return NS_OK;
   }
 };
 
 } // anonymous namespace
 
@@ -259,26 +153,26 @@ ServiceWorkerClients::MatchAll(const Cli
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  nsRefPtr<PromiseHolder> promiseHolder = new PromiseHolder(workerPrivate,
-                                                            promise);
-  if (!promiseHolder->GetPromise()) {
+  nsRefPtr<PromiseWorkerProxy> promiseProxy =
+    PromiseWorkerProxy::Create(workerPrivate, promise);
+  if (!promiseProxy->GetWorkerPromise()) {
     // Don't dispatch if adding the worker feature failed.
     return promise.forget();
   }
 
   nsRefPtr<MatchAllRunnable> r =
     new MatchAllRunnable(workerPrivate,
-                         promiseHolder,
+                         promiseProxy,
                          NS_ConvertUTF16toUTF8(scope));
   nsresult rv = NS_DispatchToMainThread(r);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
   }
 
   return promise.forget();
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -2,33 +2,151 @@
 /* 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 "ServiceWorkerWindowClient.h"
 
 #include "mozilla/dom/ClientBinding.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::workers;
 
 JSObject*
 ServiceWorkerWindowClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return WindowClientBinding::Wrap(aCx, this, aGivenProto);
 }
 
+namespace {
+
+// Passing a null clientInfo will reject the promise with InvalidAccessError.
+class ResolveOrRejectPromiseRunnable final : public WorkerRunnable
+{
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
+  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+
+public:
+  ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
+                                 PromiseWorkerProxy* aPromiseProxy,
+                                 UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mPromiseProxy(aPromiseProxy)
+    , mClientInfo(Move(aClientInfo))
+  {
+    AssertIsOnMainThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    Promise* promise = mPromiseProxy->GetWorkerPromise();
+    MOZ_ASSERT(promise);
+
+    if (mClientInfo) {
+      nsRefPtr<ServiceWorkerWindowClient> client =
+        new ServiceWorkerWindowClient(promise->GetParentObject(), *mClientInfo);
+      promise->MaybeResolve(client);
+    } else {
+      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    }
+
+    // Release the reference on the worker thread.
+    mPromiseProxy->CleanUp(aCx);
+
+    return true;
+  }
+};
+
+class ClientFocusRunnable final : public nsRunnable
+{
+  uint64_t mWindowId;
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
+
+public:
+  ClientFocusRunnable(uint64_t aWindowId, PromiseWorkerProxy* aPromiseProxy)
+    : mWindowId(aWindowId)
+    , mPromiseProxy(aPromiseProxy)
+  {
+    MOZ_ASSERT(mPromiseProxy);
+    MOZ_ASSERT(mPromiseProxy->GetWorkerPromise());
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+    nsGlobalWindow* window = nsGlobalWindow::GetOuterWindowWithId(mWindowId);
+    UniquePtr<ServiceWorkerClientInfo> clientInfo;
+
+    if (window) {
+      ErrorResult result;
+      //FIXME(catalinb): Bug 1144660 - check if we are allowed to focus here.
+      window->Focus(result);
+      clientInfo.reset(new ServiceWorkerClientInfo(window->GetDocument()));
+    }
+
+    DispatchResult(Move(clientInfo));
+    return NS_OK;
+  }
+
+private:
+  void
+  DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+  {
+    WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+
+    nsRefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
+      new ResolveOrRejectPromiseRunnable(workerPrivate, mPromiseProxy,
+                                         Move(aClientInfo));
+
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    JSContext* cx = jsapi.cx();
+    if (!resolveRunnable->Dispatch(cx)) {
+      nsRefPtr<PromiseWorkerProxyControlRunnable> controlRunnable =
+        new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
+      if (!controlRunnable->Dispatch(cx)) {
+        NS_RUNTIMEABORT("Failed to dispatch Focus promise control runnable.");
+      }
+    }
+  }
+};
+
+} // anonymous namespace
+
 already_AddRefed<Promise>
-ServiceWorkerWindowClient::Focus() const
+ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
 {
-  ErrorResult result;
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+  workerPrivate->AssertIsOnWorkerThread();
+
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   MOZ_ASSERT(global);
 
-  nsRefPtr<Promise> promise = Promise::Create(global, result);
-  if (NS_WARN_IF(result.Failed())) {
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
+  nsRefPtr<PromiseWorkerProxy> promiseProxy =
+    PromiseWorkerProxy::Create(workerPrivate, promise);
+  if (!promiseProxy->GetWorkerPromise()) {
+    // Don't dispatch if adding the worker feature failed.
+    return promise.forget();
+  }
+
+  nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
+                                                            promiseProxy);
+  aRv = NS_DispatchToMainThread(r);
+  if (NS_WARN_IF(aRv.Failed())) {
+    promise->MaybeReject(aRv.ErrorCode());
+  }
+
   return promise.forget();
 }
--- a/dom/workers/ServiceWorkerWindowClient.h
+++ b/dom/workers/ServiceWorkerWindowClient.h
@@ -42,17 +42,17 @@ public:
 
   mozilla::dom::FrameType
   FrameType() const
   {
     return mFrameType;
   }
 
   already_AddRefed<Promise>
-  Focus() const;
+  Focus(ErrorResult& aRv) const;
 
 private:
   ~ServiceWorkerWindowClient()
   { }
 
   mozilla::dom::VisibilityState mVisibilityState;
   bool mFocused;
   mozilla::dom::FrameType mFrameType;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5893,21 +5893,20 @@ WorkerPrivate::NotifyFeatures(JSContext*
   AssertIsOnWorkerThread();
 
   NS_ASSERTION(aStatus > Running, "Bad status!");
 
   if (aStatus >= Closing) {
     CancelAllTimeouts(aCx);
   }
 
-  nsAutoTArray<WorkerFeature*, 30> features;
-  features.AppendElements(mFeatures);
-
-  for (uint32_t index = 0; index < features.Length(); index++) {
-    if (!features[index]->Notify(aCx, aStatus)) {
+  nsTObserverArray<WorkerFeature*>::ForwardIterator iter(mFeatures);
+  while (iter.HasMore()) {
+    WorkerFeature* feature = iter.GetNext();
+    if (!feature->Notify(aCx, aStatus)) {
       NS_WARNING("Failed to notify feature!");
     }
   }
 
   nsAutoTArray<ParentType*, 10> children;
   children.AppendElements(mChildWorkers);
 
   for (uint32_t index = 0; index < children.Length(); index++) {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -20,16 +20,17 @@
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
+#include "nsTObserverArray.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 
 #include "Queue.h"
 #include "WorkerFeature.h"
 
 class JSAutoStructuredCloneBuffer;
 class nsIChannel;
 class nsIDocument;
@@ -847,17 +848,17 @@ class WorkerPrivate : public WorkerPriva
   nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
   nsRefPtr<WorkerThread> mThread;
   PRThread* mPRThread;
 
   // Things touched on worker thread only.
   nsRefPtr<WorkerGlobalScope> mScope;
   nsRefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
   nsTArray<ParentType*> mChildWorkers;
-  nsTArray<WorkerFeature*> mFeatures;
+  nsTObserverArray<WorkerFeature*> mFeatures;
   nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
   uint32_t mDebuggerEventLoopLevel;
 
   struct SyncLoopInfo
   {
     explicit SyncLoopInfo(EventTarget* aEventTarget);
 
     nsRefPtr<EventTarget> mEventTarget;
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/client_focus_worker.js
@@ -0,0 +1,15 @@
+onmessage = function(e) {
+  if (!e.source) {
+    dump("ERROR: message doesn't have a source.");
+  }
+
+  // The client should be a window client
+  if (e.source instanceof WindowClient) {
+    // this will dispatch a focus event on the client
+    e.source.focus().then(function(client) {
+      client.postMessage(client.focused);
+    });
+  } else {
+    dump("ERROR: client should be a WindowClient");
+  }
+};
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -12,16 +12,17 @@ support-files =
   simpleregister/index.html
   simpleregister/ready.html
   controller/index.html
   unregister/index.html
   unregister/unregister.html
   workerUpdate/update.html
   sw_clients/simple.html
   sw_clients/service_worker_controlled.html
+  sw_clients/focus_stealing_client.html
   match_all_worker.js
   match_all_advanced_worker.js
   worker_unregister.js
   worker_update.js
   message_posting_worker.js
   fetch/index.html
   fetch/fetch_worker_script.js
   fetch/fetch_tests.js
@@ -58,16 +59,17 @@ support-files =
   serviceworker_not_sharedworker.js
   match_all_client/match_all_client_id.html
   match_all_client_id_worker.js
   source_message_posting_worker.js
   scope/scope_worker.js
   redirect_serviceworker.sjs
   importscript.sjs
   importscript_worker.js
+  client_focus_worker.js
 
 [test_unregister.html]
 [test_installation_simple.html]
 [test_fetch_event.html]
 skip-if = true # Bug 1136780
 [test_https_fetch.html]
 [test_https_fetch_cloned_response.html]
 [test_match_all.html]
@@ -84,8 +86,9 @@ skip-if = true # Bug 1136780
 [test_match_all_client_properties.html]
 [test_close.html]
 [test_serviceworker_interfaces.html]
 [test_serviceworker_not_sharedworker.html]
 [test_match_all_client_id.html]
 [test_sandbox_intercept.html]
 [test_request_context.html]
 [test_importscript.html]
+[test_client_focus.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html
@@ -0,0 +1,33 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1130686 - Test service worker client.focus: client </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <!--
+    We load this as an iframe to blur the main test window.
+  -->
+</head>
+<body>/
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  if (!parent) {
+    info("error: sw_clients/focus_stealing_client.html shouldn't be launched directly!");
+  }
+
+  window.onload = function() {
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage("READY", "*");
+    });
+  }
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_client_focus.html
@@ -0,0 +1,94 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1130686 - Test service worker client.focus </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<!--
+  This test checks that client.focus is able to restore focus to the main window
+  when an iframe is holding the focus.
+-->
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  var registration;
+  var worker;
+
+  function start() {
+    return navigator.serviceWorker.register("client_focus_worker.js",
+                                            { scope: "./sw_clients/focus_stealing_client.html" })
+      .then((swr) => registration = swr);
+  }
+
+  function unregister() {
+    return registration.unregister().then(function(result) {
+      ok(result, "Unregister should return true.");
+    });
+  }
+
+  function loseFocus() {
+    var p = new Promise(function(res, rej) {
+      window.onmessage = function(e) {
+        if (e.data == "READY") {
+          ok(true, "iframe created.");
+          iframe.contentWindow.focus();
+        }
+      }
+      window.onblur = function() {
+        ok(true, "blurred");
+        res();
+      }
+    });
+
+    content = document.getElementById("content");
+    ok(content, "parent exists.");
+
+    iframe = document.createElement("iframe");
+    content.appendChild(iframe);
+
+    iframe.setAttribute('src', "sw_clients/focus_stealing_client.html");
+    return p;
+  }
+
+  function testFocus() {
+    var p = new Promise(function(res, rej) {
+      navigator.serviceWorker.onmessage = function(e) {
+        ok(e.data, "client object is marked as focused.");
+        ok(document.hasFocus(), "document has focus.");
+        res();
+      }
+    });
+
+    ok(registration.active, "active worker exists.");
+    registration.active.postMessage("go");
+    return p;
+  }
+
+  function runTest() {
+     start()
+      .then(loseFocus)
+      .then(testFocus)
+      .then(unregister)
+      .catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+      }).then(SimpleTest.finish);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -291,17 +291,17 @@ GLContextProviderCGL::CreateHeadless(boo
 }
 
 already_AddRefed<GLContext>
 GLContextProviderCGL::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 static nsRefPtr<GLContext> gGlobalContext;
 
 GLContext*
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -899,17 +899,17 @@ already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
     if (!glContext)
         return nullptr;
 
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 // Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
 // and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
 // and 3) each EGL context eats 750k on B2G (bug 813783)
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1229,17 +1229,17 @@ already_AddRefed<GLContext>
 GLContextProviderGLX::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
     if (!glContext)
         return nullptr;
 
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 GLContext*
 GLContextProviderGLX::GetGlobalContext()
 {
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -635,25 +635,25 @@ GLContextProviderWGL::CreateHeadless(boo
         return nullptr;
     }
 
     nsRefPtr<GLContext> retGL = glContext.get();
     return retGL.forget();
 }
 
 already_AddRefed<GLContext>
-GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size,
+GLContextProviderWGL::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& caps,
                                       bool requireCompatProfile)
 {
     nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
     if (!glContext)
         return nullptr;
 
-    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+    if (!glContext->InitOffscreen(size, caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 static nsRefPtr<GLContextWGL> gGlobalContext;
 
 GLContext *
--- a/gfx/layers/TextureDIB.cpp
+++ b/gfx/layers/TextureDIB.cpp
@@ -121,17 +121,17 @@ DIBTextureHost::DIBTextureHost(TextureFl
   : TextureHost(aFlags)
   , mIsLocked(false)
 {
   // We added an extra ref for transport, so we shouldn't AddRef now.
   mSurface =
     dont_AddRef(reinterpret_cast<gfxWindowsSurface*>(aDescriptor.surface()));
   MOZ_ASSERT(mSurface);
 
-  mSize = ToIntSize(mSurface->GetSize());
+  mSize = mSurface->GetSize();
   mFormat = ImageFormatToSurfaceFormat(
     gfxPlatform::GetPlatform()->OptimalFormatForContent(mSurface->GetContentType()));
 }
 
 bool
 DIBTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
 {
   if (!mTextureSource) {
--- a/gfx/layers/YCbCrImageDataSerializer.cpp
+++ b/gfx/layers/YCbCrImageDataSerializer.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 #include <string.h>                     // for memcpy
-#include "gfx2DGlue.h"                  // for ToIntSize
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface, Factory
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Logging.h"        // for gfxDebug
 #include "mozilla/gfx/Types.h"
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "yuv_convert.h"                // for ConvertYCbCrToRGB32, etc
 
 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -634,17 +634,17 @@ Transform(const gfxImageSurface* aDest,
           RefPtr<DataSourceSurface> aSrc,
           const gfx3DMatrix& aTransform,
           gfxPoint aDestOffset)
 {
   if (aTransform.IsSingular()) {
     return;
   }
 
-  IntSize destSize = ToIntSize(aDest->GetSize());
+  IntSize destSize = aDest->GetSize();
   SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
                                            destSize.height,
                                            kBGRA_8888_SkColorType,
                                            kPremul_SkAlphaType);
   SkBitmap destBitmap;
   destBitmap.setInfo(destInfo, aDest->Stride());
   destBitmap.setPixels((uint32_t*)aDest->Data());
   SkCanvas destCanvas(destBitmap);
@@ -692,17 +692,17 @@ BasicLayerManager_Matrix3DToPixman(const
 }
 
 static void
 Transform(const gfxImageSurface* aDest,
           RefPtr<DataSourceSurface> aSrc,
           const gfx3DMatrix& aTransform,
           gfxPoint aDestOffset)
 {
-  IntSize destSize = ToIntSize(aDest->GetSize());
+  IntSize destSize = aDest->GetSize();
   pixman_image_t* dest = pixman_image_create_bits(aDest->Format() == gfxImageFormat::ARGB32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
                                                   destSize.width,
                                                   destSize.height,
                                                   (uint32_t*)aDest->Data(),
                                                   aDest->Stride());
 
   IntSize srcSize = aSrc->GetSize();
   pixman_image_t* src = pixman_image_create_bits(aSrc->GetFormat() == SurfaceFormat::B8G8R8A8 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
--- a/gfx/layers/basic/TextureClientX11.cpp
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -139,14 +139,14 @@ TextureClientX11::BorrowDrawTarget()
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mLocked);
 
   if (!mSurface) {
     return nullptr;
   }
 
   if (!mDrawTarget) {
-    IntSize size = ToIntSize(mSurface->GetSize());
+    IntSize size = mSurface->GetSize();
     mDrawTarget = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
   }
 
   return mDrawTarget;
 }
--- a/gfx/layers/basic/X11TextureSourceBasic.cpp
+++ b/gfx/layers/basic/X11TextureSourceBasic.cpp
@@ -16,17 +16,17 @@ X11TextureSourceBasic::X11TextureSourceB
   : mCompositor(aCompositor),
     mSurface(aSurface)
 {
 }
 
 IntSize
 X11TextureSourceBasic::GetSize() const
 {
-  return ToIntSize(mSurface->GetSize());
+  return mSurface->GetSize();
 }
 
 SurfaceFormat
 X11TextureSourceBasic::GetFormat() const
 {
   gfxContentType type = mSurface->GetContentType();
   return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
 }
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -253,27 +253,25 @@ ClientTiledPaintedLayer::UseProgressiveD
 
   if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) {
     // This layer is fixed-position and so even if it does have a scrolling
     // ancestor it will likely be entirely on-screen all the time, so we
     // should draw it all at once
     return false;
   }
 
-  // XXX We probably want to disable progressive drawing for non active APZ layers in the future
-  //     but we should wait for a proper test case before making this change.
-#if 0 //!defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
-  LayerMetricsWrapper scrollAncestor;
-  GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
-  MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty
-  const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
-  if (!IsScrollingOnCompositor(parentMetrics)) {
-    return false;
+  if (gfxPrefs::AsyncPanZoomEnabled()) {
+    LayerMetricsWrapper scrollAncestor;
+    GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+    MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty
+    const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
+    if (!IsScrollingOnCompositor(parentMetrics)) {
+      return false;
+    }
   }
-#endif
 
   return true;
 }
 
 bool
 ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
                                             const nsIntRegion& aVisibleRegion,
                                             LayerManager::DrawPaintedLayerCallback aCallback,
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -800,17 +800,17 @@ ApplyAsyncTransformToScrollbarForContent
     if (aScrollbarIsDescendant) {
       xTranslation *= metrics.GetPresShellResolution();
     }
 
     scrollbarTransform.PostScale(xScale, 1.f, 1.f);
     scrollbarTransform.PostTranslate(xTranslation, 0, 0);
   }
 
-  Matrix4x4 transform = scrollbarTransform * aScrollbar->GetTransform();
+  Matrix4x4 transform = aScrollbar->GetLocalTransform() * scrollbarTransform;
 
   if (aScrollbarIsDescendant) {
     // If the scrollbar layer is a child of the content it is a scrollbar for,
     // then we need to make a couple of adjustments to the scrollbar's transform.
     //
     //  - First, the content's resolution applies to the scrollbar as well.
     //    Since we don't actually want the scroll thumb's size to vary with
     //    the zoom (other than its length reflecting the fraction of the
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -339,17 +339,18 @@ RenderLayers(ContainerT* aContainer,
 
   for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
     PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
     LayerComposite* layerToRender = preparedData.mLayer;
     const RenderTargetIntRect& clipRect = preparedData.mClipRect;
     Layer* layer = layerToRender->GetLayer();
 
     gfxRGBA color;
-    if (LayerHasCheckerboardingAPZC(layer, &color)) {
+    if ((layer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+        LayerHasCheckerboardingAPZC(layer, &color)) {
       // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
       // and only fill in that area. However the layer bounds takes into account the base translation
       // for the painted layer whereas the checkerboard region does not. One does not simply
       // intersect areas in different coordinate spaces. So we do this a little more permissively
       // and only fill in the background when we know there is checkerboard, which in theory
       // should only occur transiently.
       nsIntRect layerBounds = layer->GetLayerBounds();
       EffectChain effectChain(layer);
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -2,17 +2,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 "TextureHost.h"
 
 #include "CompositableHost.h"           // for CompositableHost
 #include "LayersLogging.h"              // for AppendToString
-#include "gfx2DGlue.h"                  // for ToIntSize
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface, Factory
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
--- a/gfx/layers/composite/X11TextureHost.cpp
+++ b/gfx/layers/composite/X11TextureHost.cpp
@@ -78,13 +78,13 @@ X11TextureHost::GetFormat() const
   }
 #endif
   return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
 }
 
 IntSize
 X11TextureHost::GetSize() const
 {
-  return ToIntSize(mSurface->GetSize());
+  return mSurface->GetSize();
 }
 
 }
 }
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1453,28 +1453,34 @@ CompositorD3D11::PaintToTarget()
 }
 
 void
 CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
 {
   if (SUCCEEDED(hr)) {
     return;
   }
-  // XXX - It would be nice to use gfxCriticalError, but it needs to
-  // be made to work off the main thread first.
-  //MOZ_ASSERT(aSeverity != DebugAssert);
 
   if (aSeverity == Critical) {
     MOZ_CRASH("Unrecoverable D3D11 error");
   }
 
-  if (mDevice && hr == DXGI_ERROR_DEVICE_REMOVED) {
+  bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;
+
+  if (deviceRemoved && mDevice) {
     hr = mDevice->GetDeviceRemovedReason();
   }
 
+  // Device reset may not be an error on our side, but can mess things up so
+  // it's useful to see it in the reports.
+  gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved))
+    << (deviceRemoved ? "[CompositorD3D11] device removed with error code: "
+                      : "[CompositorD3D11] error code: ")
+    << hexa(hr);
+
   // Always crash if we are making invalid calls
   if (hr == DXGI_ERROR_INVALID_CALL) {
     MOZ_CRASH("Invalid D3D11 api call");
   }
 
   if (aSeverity == Recoverable) {
     NS_WARNING("Encountered a recoverable D3D11 error");
   }
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -162,29 +162,29 @@ private:
   bool VerifyBufferSize();
   void UpdateRenderTarget();
   bool CreateShaders();
   bool UpdateConstantBuffers();
   void SetSamplerForFilter(gfx::Filter aFilter);
   void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);
   void PaintToTarget();
 
-  virtual gfx::IntSize GetWidgetSize() const override { return gfx::ToIntSize(mSize); }
+  virtual gfx::IntSize GetWidgetSize() const override { return mSize; }
 
   RefPtr<ID3D11DeviceContext> mContext;
   RefPtr<ID3D11Device> mDevice;
   RefPtr<IDXGISwapChain> mSwapChain;
   RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
   RefPtr<CompositingRenderTargetD3D11> mCurrentRT;
 
   DeviceAttachmentsD3D11* mAttachments;
 
   nsIWidget* mWidget;
 
-  nsIntSize mSize;
+  gfx::IntSize mSize;
 
   HWND mHwnd;
 
   D3D_FEATURE_LEVEL mFeatureLevel;
 
   VertexShaderConstants mVSConstants;
   PixelShaderConstants mPSConstants;
   bool mDisableSequenceForNextFrame;
--- a/gfx/layers/d3d9/CompositorD3D9.h
+++ b/gfx/layers/d3d9/CompositorD3D9.h
@@ -147,17 +147,17 @@ private:
   void CheckResetCount();
 
   void FailedToResetDevice();
 
   void ReportFailure(const nsACString &aMsg, HRESULT aCode);
 
   virtual gfx::IntSize GetWidgetSize() const override
   {
-    return gfx::ToIntSize(mSize);
+    return mSize;
   }
 
   /* Device manager instance for this compositor */
   nsRefPtr<DeviceManagerD3D9> mDeviceManager;
 
   /* Swap chain associated with this compositor */
   nsRefPtr<SwapChainD3D9> mSwapChain;
 
--- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp
@@ -235,26 +235,26 @@ DeviceManagerD3D9::Init()
     if (!d3d9Create) {
       gfxCriticalError() << "[D3D9] Failed to load symbols";
       return false;
     }
 
     mD3D9 = dont_AddRef(d3d9Create(D3D_SDK_VERSION));
 
     if (!mD3D9) {
-      gfxCriticalError() << "[D3D9] Failed to create the device";
+      gfxCriticalError() << "[D3D9] Failed to create the IDirect3D9 object";
       return false;
     }
   }
 
   D3DADAPTER_IDENTIFIER9 ident;
   hr = mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &ident);
 
   if (FAILED(hr)) {
-    gfxCriticalError() << "[D3D9] Failed to create the environment";
+    gfxCriticalError() << "[D3D9] Failed to create the environment code: " << gfx::hexa(hr);
     return false;
   }
 
   D3DPRESENT_PARAMETERS pp;
   memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS));
 
   pp.BackBufferWidth = 1;
   pp.BackBufferHeight = 1;
@@ -326,153 +326,153 @@ DeviceManagerD3D9::Init()
   if (mNv3DVUtils) { 
     IUnknown* devUnknown = nullptr; 
     if (mDevice) { 
       mDevice->QueryInterface(IID_IUnknown, (void **)&devUnknown); 
     } 
     mNv3DVUtils->SetDeviceInfo(devUnknown); 
   } 
 
-  auto failCreateShaderMsg = "[D3D9] failed to create a critical resource (shader)";
+  auto failCreateShaderMsg = "[D3D9] failed to create a critical resource (shader) code: ";
 
   hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS,
                                    getter_AddRefs(mLayerVS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS,
                                   getter_AddRefs(mRGBPS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPS,
                                   getter_AddRefs(mRGBAPS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPS,
                                   getter_AddRefs(mComponentPass1PS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPS,
                                   getter_AddRefs(mComponentPass2PS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS,
                                   getter_AddRefs(mYCbCrPS));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS,
                                   getter_AddRefs(mSolidColorPS));
 
   if (FAILED(hr)) {
     gfxCriticalError() << failCreateShaderMsg;
     return false;
   }
 
   hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVSMask,
                                    getter_AddRefs(mLayerVSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
   hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVSMask3D,
                                    getter_AddRefs(mLayerVSMask3D));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPSMask,
                                   getter_AddRefs(mRGBPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPSMask,
                                   getter_AddRefs(mRGBAPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPSMask3D,
                                   getter_AddRefs(mRGBAPSMask3D));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPSMask,
                                   getter_AddRefs(mComponentPass1PSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPSMask,
                                   getter_AddRefs(mComponentPass2PSMask));
 
   if (FAILED(hr)) {
     gfxCriticalError() << failCreateShaderMsg;
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPSMask,
                                   getter_AddRefs(mYCbCrPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPSMask,
                                   getter_AddRefs(mSolidColorPSMask));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << failCreateShaderMsg;
+    gfxCriticalError() << failCreateShaderMsg << gfx::hexa(hr);
     return false;
   }
 
   if (!CreateVertexBuffer()) {
     gfxCriticalError() << "[D3D9] Failed to create a critical resource (vbo)";
     return false;
   }
 
   hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex));
   if (FAILED(hr)) {
-    gfxCriticalError() << "[D3D9] Failed to set the stream source";
+    gfxCriticalError() << "[D3D9] Failed to set the stream source code: " << gfx::hexa(hr);
     return false;
   }
 
   D3DVERTEXELEMENT9 elements[] = {
     { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
       D3DDECLUSAGE_POSITION, 0 },
     D3DDECL_END()
   };
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -442,17 +442,17 @@ DataTextureSourceD3D9::Update(gfx::DataS
 bool
 DataTextureSourceD3D9::Update(gfxWindowsSurface* aSurface)
 {
   MOZ_ASSERT(aSurface);
   if (!mCompositor || !mCompositor->device()) {
     NS_WARNING("No D3D device to update the texture.");
     return false;
   }
-  mSize = ToIntSize(aSurface->GetSize());
+  mSize = aSurface->GetSize();
 
   uint32_t bpp = 0;
 
   _D3DFORMAT format = D3DFMT_A8R8G8B8;
   mFormat = ImageFormatToSurfaceFormat(
     gfxPlatform::GetPlatform()->OptimalFormatForContent(aSurface->GetContentType()));
   switch (mFormat) {
   case SurfaceFormat::B8G8R8X8:
--- a/gfx/layers/ipc/SharedRGBImage.cpp
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -44,18 +44,17 @@ CreateSharedRGBImage(ImageContainer *aIm
   nsRefPtr<Image> image = aImageContainer->CreateImage(ImageFormat::SHARED_RGB);
 
   if (!image) {
     NS_WARNING("Failed to create SharedRGBImage");
     return nullptr;
   }
 
   nsRefPtr<SharedRGBImage> rgbImage = static_cast<SharedRGBImage*>(image.get());
-  if (!rgbImage->Allocate(gfx::ToIntSize(aSize),
-                          gfx::ImageFormatToSurfaceFormat(aImageFormat))) {
+  if (!rgbImage->Allocate(aSize, gfx::ImageFormatToSurfaceFormat(aImageFormat))) {
     NS_WARNING("Failed to allocate a shared image");
     return nullptr;
   }
   return image.forget();
 }
 
 SharedRGBImage::SharedRGBImage(ImageClient* aCompositable)
 : Image(nullptr, ImageFormat::SHARED_RGB)
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -302,31 +302,31 @@ public:
   GLuint GetTemporaryTexture(GLenum aTarget, GLenum aUnit);
 
   const gfx::Matrix4x4& GetProjMatrix() const {
     return mProjMatrix;
   }
 private:
   virtual gfx::IntSize GetWidgetSize() const override
   {
-    return gfx::ToIntSize(mWidgetSize);
+    return mWidgetSize;
   }
 
   bool InitializeVR();
   void DestroyVR(GLContext *gl);
 
   void DrawVRDistortion(const gfx::Rect& aRect,
                         const gfx::Rect& aClipRect,
                         const EffectChain& aEffectChain,
                         gfx::Float aOpacity,
                         const gfx::Matrix4x4& aTransform);
 
   /** Widget associated with this compositor */
   nsIWidget *mWidget;
-  nsIntSize mWidgetSize;
+  gfx::IntSize mWidgetSize;
   nsRefPtr<GLContext> mGLContext;
   UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
   gfx::Matrix4x4 mProjMatrix;
 
   /** The size of the surface we are rendering to */
   nsIntSize mSurfaceSize;
 
   ScreenPoint mRenderOffset;
--- a/gfx/layers/opengl/X11TextureSourceOGL.cpp
+++ b/gfx/layers/opengl/X11TextureSourceOGL.cpp
@@ -55,17 +55,17 @@ X11TextureSourceOGL::BindTexture(GLenum 
   }
 
   ApplyFilterToBoundTexture(gl(), aFilter, LOCAL_GL_TEXTURE_2D);
 }
 
 IntSize
 X11TextureSourceOGL::GetSize() const
 {
-  return ToIntSize(mSurface->GetSize());
+  return mSurface->GetSize();
 }
 
 SurfaceFormat
 X11TextureSourceOGL::GetFormat() const {
   gfxContentType type = mSurface->GetContentType();
   return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
 }
 
--- a/gfx/tests/gtest/TestSkipChars.cpp
+++ b/gfx/tests/gtest/TestSkipChars.cpp
@@ -64,16 +64,19 @@ TestIterator()
 
   gfxSkipCharsIterator iter1(skipChars1);
 
   EXPECT_TRUE(iter1.GetOriginalOffset() == 0) <<
     "[2] Check initial original offset";
   EXPECT_TRUE(iter1.GetSkippedOffset() == 0) <<
     "[3] Check initial skipped offset";
 
+  EXPECT_TRUE(iter1.IsOriginalCharSkipped() == false) <<
+    "[3a] Check IsOriginalCharSkipped for initial position";
+
   uint32_t expectSkipped1[] =
   {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
      9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
     18, 19, 20, 21, 22, 23, 24, 25, 26, 27 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped1); i++) {
     EXPECT_TRUE(iter1.ConvertOriginalToSkipped(i) == expectSkipped1[i]) <<
       "[4] Check mapping of original to skipped for " << i;
@@ -84,16 +87,28 @@ TestIterator()
     10, 11, 12, 13, 14, 15, 16, 17, 18,
     20, 21, 22, 23, 24, 25, 26, 27, 28 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal1); i++) {
     EXPECT_TRUE(iter1.ConvertSkippedToOriginal(i) == expectOriginal1[i]) <<
       "[5] Check mapping of skipped to original for " << i;
   }
 
+  bool expectIsOriginalSkipped1[] =
+  {  false, false, false, false, false, false, false, false, false, true,
+     false, false, false, false, false, false, false, false, false, true,
+     false, false, false, false, false, false, false, false, false
+  };
+
+  for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped1); i++) {
+    iter1.SetOriginalOffset(i);
+    EXPECT_TRUE(iter1.IsOriginalCharSkipped() == expectIsOriginalSkipped1[i]) <<
+      "[5.a] Check IsOriginalCharSkipped for " << i;
+  }
+
   // Test a gfxSkipChars that starts with skipped chars
   gfxSkipChars skipChars2;
 
   skipChars2.SkipChars(9);
   skipChars2.KeepChar();
   skipChars2.SkipChars(9);
   skipChars2.KeepChar();
   skipChars2.SkipChars(9);
@@ -103,16 +118,19 @@ TestIterator()
 
   gfxSkipCharsIterator iter2(skipChars2);
 
   EXPECT_TRUE(iter2.GetOriginalOffset() == 0) <<
     "[7] Check initial original offset";
   EXPECT_TRUE(iter2.GetSkippedOffset() == 0) <<
     "[8] Check initial skipped offset";
 
+  EXPECT_TRUE(iter2.IsOriginalCharSkipped() == true) <<
+    "[8a] Check IsOriginalCharSkipped for initial position";
+
   uint32_t expectSkipped2[] =
   {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped2); i++) {
     EXPECT_TRUE(iter2.ConvertOriginalToSkipped(i) == expectSkipped2[i]) <<
       "[9] Check mapping of original to skipped for " << i;
@@ -120,16 +138,28 @@ TestIterator()
 
   uint32_t expectOriginal2[] = { 9, 19, 29 };
 
   for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) {
     EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i]) <<
       "[10] Check mapping of skipped to original for " << i;
   }
 
+  bool expectIsOriginalSkipped2[] =
+  {  true, true, true, true, true, true, true, true, true, false,
+     true, true, true, true, true, true, true, true, true, false,
+     true, true, true, true, true, true, true, true, true
+  };
+
+  for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped2); i++) {
+    iter2.SetOriginalOffset(i);
+    EXPECT_TRUE(iter2.IsOriginalCharSkipped() == expectIsOriginalSkipped2[i]) <<
+      "[10.a] Check IsOriginalCharSkipped for " << i;
+  }
+
   return true;
 }
 
 TEST(Gfx, gfxSkipChars) {
   TestConstructor();
   TestLength();
   TestIterator();
 }
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -143,17 +143,17 @@ void AssertYCbCrSurfacesEqual(PlanarYCbC
   }
 }
 
 // Run the test for a texture client and a surface
 void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) {
 
   // client allocation
   ASSERT_TRUE(texture->CanExposeDrawTarget());
-  texture->AllocateForSurface(ToIntSize(surface->GetSize()));
+  texture->AllocateForSurface(surface->GetSize());
   ASSERT_TRUE(texture->IsAllocated());
 
   ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE));
   // client painting
   RefPtr<DrawTarget> dt = texture->BorrowDrawTarget();
   RefPtr<SourceSurface> source =
     gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface);
   dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint());
--- a/gfx/thebes/gfx2DGlue.h
+++ b/gfx/thebes/gfx2DGlue.h
@@ -76,21 +76,16 @@ inline IntPoint ToIntPoint(const nsIntPo
   return IntPoint(aPoint.x, aPoint.y);
 }
 
 inline Size ToSize(const gfxSize &aSize)
 {
   return Size(Float(aSize.width), Float(aSize.height));
 }
 
-inline IntSize ToIntSize(const gfxIntSize &aSize)
-{
-  return IntSize(aSize.width, aSize.height);
-}
-
 inline Filter ToFilter(GraphicsFilter aFilter)
 {
   switch (aFilter) {
   case GraphicsFilter::FILTER_NEAREST:
     return Filter::POINT;
   case GraphicsFilter::FILTER_GOOD:
     return Filter::GOOD;
   default:
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -832,17 +832,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
     // DrawTarget and can't pick a better surface type. Doing this also avoids
     // readback of aSurface's surface into memory if, for example, aSurface
     // wraps an xlib cairo surface (which can be important to avoid a major
     // slowdown).
     NativeSurface surf;
     surf.mFormat = format;
     surf.mType = NativeSurfaceType::CAIRO_SURFACE;
     surf.mSurface = aSurface->CairoSurface();
-    surf.mSize = ToIntSize(aSurface->GetSize());
+    surf.mSize = aSurface->GetSize();
     // We return here regardless of whether CreateSourceSurfaceFromNativeSurface
     // succeeds or not since we don't expect to be able to do any better below
     // if it fails.
     //
     // Note that the returned SourceSurfaceCairo holds a strong reference to
     // the cairo_surface_t* that it wraps, which essencially means it holds a
     // strong reference to aSurface since aSurface shares its
     // cairo_surface_t*'s reference count variable. As a result we can't cache
@@ -857,17 +857,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
 
 #ifdef XP_WIN
   if (aSurface->GetType() == gfxSurfaceType::D2D &&
       format != SurfaceFormat::A8) {
     NativeSurface surf;
     surf.mFormat = format;
     surf.mType = NativeSurfaceType::D3D10_TEXTURE;
     surf.mSurface = static_cast<gfxD2DSurface*>(aSurface)->GetTexture();
-    surf.mSize = ToIntSize(aSurface->GetSize());
+    surf.mSize = aSurface->GetSize();
     mozilla::gfx::DrawTarget *dt = static_cast<mozilla::gfx::DrawTarget*>(aSurface->GetData(&kDrawTarget));
     if (dt) {
       dt->Flush();
     }
     srcBuffer = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
   }
 #endif
   // Currently no other DrawTarget types implement CreateSourceSurfaceFromNativeSurface
@@ -905,17 +905,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
     // likely create a DataSourceSurface (possibly involving copying and/or
     // readback), and the OptimizeSourceSurface may well copy again and upload
     // to the GPU. So, while this code path is rarely hit, hitting it may be
     // very slow.
     NativeSurface surf;
     surf.mFormat = format;
     surf.mType = NativeSurfaceType::CAIRO_SURFACE;
     surf.mSurface = aSurface->CairoSurface();
-    surf.mSize = ToIntSize(aSurface->GetSize());
+    surf.mSize = aSurface->GetSize();
     RefPtr<DrawTarget> drawTarget =
       Factory::CreateDrawTarget(BackendType::CAIRO, IntSize(1, 1), format);
     if (!drawTarget) {
       gfxWarning() << "gfxPlatform::GetSourceSurfaceForSurface failed in CreateDrawTarget";
       return nullptr;
     }
     srcBuffer = drawTarget->CreateSourceSurfaceFromNativeSurface(surf);
     if (srcBuffer) {
@@ -951,17 +951,17 @@ gfxPlatform::GetWrappedDataSourceSurface
 {
   nsRefPtr<gfxImageSurface> image = aSurface->GetAsImageSurface();
   if (!image) {
     return nullptr;
   }
   RefPtr<DataSourceSurface> result =
     Factory::CreateWrappingDataSourceSurface(image->Data(),
                                              image->Stride(),
-                                             ToIntSize(image->GetSize()),
+                                             image->GetSize(),
                                              ImageFormatToSurfaceFormat(image->Format()));
 
   if (!result) {
     return nullptr;
   }
 
   // If we wrapped the underlying data of aSurface, then we need to add user data
   // to make sure aSurface stays alive until we are done with the data.
--- a/gfx/thebes/gfxSkipChars.h
+++ b/gfx/thebes/gfxSkipChars.h
@@ -172,27 +172,30 @@ public:
                          int32_t aOriginalStringToSkipCharsOffset,
                          int32_t aOriginalStringOffset)
         : mSkipChars(&aSkipChars),
           mOriginalStringOffset(0),
           mSkippedStringOffset(0),
           mCurrentRangeIndex(-1),
           mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
     {
-          SetOriginalOffset(aOriginalStringOffset);
+        SetOriginalOffset(aOriginalStringOffset);
     }
 
     explicit gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
                                   int32_t aOriginalStringToSkipCharsOffset = 0)
         : mSkipChars(&aSkipChars),
           mOriginalStringOffset(0),
           mSkippedStringOffset(0),
-          mCurrentRangeIndex(-1),
           mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
-    { }
+    {
+        mCurrentRangeIndex =
+            mSkipChars->mRanges.IsEmpty() ||
+            mSkipChars->mRanges[0].Start() > 0 ? -1 : 0;
+    }
 
     gfxSkipCharsIterator(const gfxSkipCharsIterator& aIterator)
         : mSkipChars(aIterator.mSkipChars),
           mOriginalStringOffset(aIterator.mOriginalStringOffset),
           mSkippedStringOffset(aIterator.mSkippedStringOffset),
           mCurrentRangeIndex(aIterator.mCurrentRangeIndex),
           mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset)
     { }
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -436,18 +436,17 @@ CreateSamplingRestrictedDrawable(gfxDraw
     // matter what we do here, but we should avoid trying to
     // create a zero-size surface.
     if (needed.IsEmpty())
         return nullptr;
 
     gfxIntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
 
     RefPtr<DrawTarget> target =
-      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size),
-                                                                   aFormat);
+      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
     if (!target) {
       return nullptr;
     }
 
     nsRefPtr<gfxContext> tmpCtx = new gfxContext(target);
     tmpCtx->SetOperator(OptimalFillOperator());
     aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true,
                     GraphicsFilter::FILTER_FAST, 1.0, gfxMatrix::Translation(needed.TopLeft()));
@@ -864,17 +863,17 @@ gfxUtils::GetYCbCrToRGBDestFormatAndSize
     TypeFromSize(aData.mYSize.width,
                       aData.mYSize.height,
                       aData.mCbCrSize.width,
                       aData.mCbCrSize.height);
 
   // 'prescale' is true if the scaling is to be done as part of the
   // YCbCr to RGB conversion rather than on the RGB data when rendered.
   bool prescale = aSuggestedSize.width > 0 && aSuggestedSize.height > 0 &&
-                    ToIntSize(aSuggestedSize) != aData.mPicSize;
+                    aSuggestedSize != aData.mPicSize;
 
   if (aSuggestedFormat == gfxImageFormat::RGB16_565) {
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (prescale &&
         !IsScaleYCbCrToRGB565Fast(aData.mPicX,
                                        aData.mPicY,
                                        aData.mPicSize.width,
                                        aData.mPicSize.height,
@@ -900,17 +899,17 @@ gfxUtils::GetYCbCrToRGBDestFormatAndSize
   }
   if (aSuggestedFormat == gfxImageFormat::RGB24) {
     /* ScaleYCbCrToRGB32 does not support a picture offset, nor 4:4:4 data.
        See bugs 639415 and 640073. */
     if (aData.mPicX != 0 || aData.mPicY != 0 || yuvtype == YV24)
       prescale = false;
   }
   if (!prescale) {
-    ToIntSize(aSuggestedSize) = aData.mPicSize;
+    aSuggestedSize = aData.mPicSize;
   }
 }
 
 void
 gfxUtils::ConvertYCbCrToRGB(const PlanarYCbCrData& aData,
                             const gfxImageFormat& aDestFormat,
                             const gfxIntSize& aDestSize,
                             unsigned char* aDestBuffer,
@@ -924,17 +923,17 @@ gfxUtils::ConvertYCbCrToRGB(const Planar
               aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1));
   YUVType yuvtype =
     TypeFromSize(aData.mYSize.width,
                       aData.mYSize.height,
                       aData.mCbCrSize.width,
                       aData.mCbCrSize.height);
 
   // Convert from YCbCr to RGB now, scaling the image if needed.
-  if (ToIntSize(aDestSize) != aData.mPicSize) {
+  if (aDestSize != aData.mPicSize) {
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (aDestFormat == gfxImageFormat::RGB16_565) {
       ScaleYCbCrToRGB565(aData.mYChannel,
                               aData.mCbChannel,
                               aData.mCrChannel,
                               aDestBuffer,
                               aData.mPicX,
                               aData.mPicY,
--- a/gfx/thebes/gfxWindowsNativeDrawing.cpp
+++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp
@@ -267,17 +267,17 @@ gfxWindowsNativeDrawing::PaintToContext(
         nsRefPtr<gfxImageSurface> white = mWhiteSurface->GetAsImageSurface();
         if (!gfxAlphaRecovery::RecoverAlpha(black, white)) {
             NS_ERROR("Alpha recovery failure");
             return;
         }
         RefPtr<DataSourceSurface> source =
             Factory::CreateWrappingDataSourceSurface(black->Data(),
                                                      black->Stride(),
-                                                     ToIntSize(black->GetSize()),
+                                                     black->GetSize(),
                                                      SurfaceFormat::B8G8R8A8);
 
         mContext->Save();
         mContext->SetMatrix(
           mContext->CurrentMatrix().Translate(mNativeRect.TopLeft()));
         mContext->NewPath();
         mContext->Rectangle(gfxRect(gfxPoint(0.0, 0.0), mNativeRect.Size()));
 
--- a/gfx/thebes/gfxXlibNativeRenderer.cpp
+++ b/gfx/thebes/gfxXlibNativeRenderer.cpp
@@ -568,17 +568,17 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
         cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ?
             SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
     if (method != eAlphaExtraction) {
         if (drawTarget) {
             NativeSurface native;
             native.mFormat = moz2DFormat;
             native.mType = NativeSurfaceType::CAIRO_SURFACE;
             native.mSurface = tempXlibSurface;
-            native.mSize = ToIntSize(size);
+            native.mSize = size;
             RefPtr<SourceSurface> sourceSurface =
                 drawTarget->CreateSourceSurfaceFromNativeSurface(native);
             if (sourceSurface) {
                 drawTarget->DrawSurface(sourceSurface,
                     Rect(offset.x, offset.y, size.width, size.height),
                     Rect(0, 0, size.width, size.height));
             }
         } else {
@@ -610,17 +610,17 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
         }
 
         gfxASurface* paintSurface = blackImage;
         if (drawTarget) {
             NativeSurface native;
             native.mFormat = moz2DFormat;
             native.mType = NativeSurfaceType::CAIRO_SURFACE;
             native.mSurface = paintSurface->CairoSurface();
-            native.mSize = ToIntSize(size);
+            native.mSize = size;
             RefPtr<SourceSurface> sourceSurface =
                 drawTarget->CreateSourceSurfaceFromNativeSurface(native);
             if (sourceSurface) {
                 drawTarget->DrawSurface(sourceSurface,
                     Rect(offset.x, offset.y, size.width, size.height),
                     Rect(0, 0, size.width, size.height));
             }
         } else {
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -276,17 +276,17 @@ nsPNGDecoder::InitInternal()
   if (mCMSMode == eCMSMode_Off) {
     png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2);
   }
 
   png_set_keep_unknown_chunks(mPNG, 1, unused_chunks,
                               (int)sizeof(unused_chunks)/5);
 #endif
 
-#ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
   if (mCMSMode != eCMSMode_Off) {
     png_set_chunk_malloc_max(mPNG, 4000000L);
   }
 #endif
 
 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
 #ifndef PR_LOGGING
   // Disallow palette-index checking, for speed; we would ignore the warning
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -93,17 +93,17 @@ OrientedImage::GetFrame(uint32_t aWhichF
     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
   }
 
   // Create a surface to draw into.
   RefPtr<DrawTarget> target =
     gfxPlatform::GetPlatform()->
-      CreateOffscreenContentDrawTarget(ToIntSize(size), surfaceFormat);
+      CreateOffscreenContentDrawTarget(size, surfaceFormat);
   if (!target) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
 
   // Create our drawable.
   RefPtr<SourceSurface> innerSurface =
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -2617,16 +2617,19 @@ void imgCacheValidator::AddProxy(imgRequ
   mProxies.AppendObject(aProxy);
 }
 
 /** nsIRequestObserver methods **/
 
 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
 {
+  // We may be holding on to a document, so ensure that it's released.
+  nsCOMPtr<nsISupports> context = mContext.forget();
+
   // If for some reason we don't still have an existing request (probably
   // because OnStartRequest got delivered more than once), just bail.
   if (!mRequest) {
     MOZ_ASSERT_UNREACHABLE("OnStartRequest delivered more than once?");
     aRequest->Cancel(NS_BINDING_ABORTED);
     return NS_ERROR_FAILURE;
   }
 
@@ -2665,17 +2668,17 @@ NS_IMETHODIMP imgCacheValidator::OnStart
         // Notify synchronously, because we're already in OnStartRequest, an
         // asynchronously-called function.
         proxy->SyncNotifyListener();
       }
 
       // We don't need to load this any more.
       aRequest->Cancel(NS_BINDING_ABORTED);
 
-      mRequest->SetLoadId(mContext);
+      mRequest->SetLoadId(context);
       mRequest->SetValidator(nullptr);
 
       mRequest = nullptr;
 
       mNewRequest = nullptr;
       mNewEntry = nullptr;
 
       return NS_OK;
@@ -2706,17 +2709,17 @@ NS_IMETHODIMP imgCacheValidator::OnStart
 
   mRequest->SetValidator(nullptr);
   mRequest = nullptr;
 
   // We use originalURI here to fulfil the imgIRequest contract on GetURI.
   nsCOMPtr<nsIURI> originalURI;
   channel->GetOriginalURI(getter_AddRefs(originalURI));
   mNewRequest->Init(originalURI, uri, mHadInsecureRedirect, aRequest, channel,
-                    mNewEntry, mContext, loadingPrincipal, corsmode, refpol);
+                    mNewEntry, context, loadingPrincipal, corsmode, refpol);
 
   mDestListener = new ProxyListener(mNewRequest);
 
   // Try to add the new request into the cache. Note that the entry must be in
   // the cache before the proxies' ownership changes, because adding a proxy
   // changes the caching behaviour for imgRequests.
   mImgLoader->PutIntoCache(originalURI, mNewEntry);
 
@@ -2735,16 +2738,19 @@ NS_IMETHODIMP imgCacheValidator::OnStart
   mNewEntry = nullptr;
 
   return mDestListener->OnStartRequest(aRequest, ctxt);
 }
 
 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
 NS_IMETHODIMP imgCacheValidator::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
 {
+  // Be sure we've released the document that we may have been holding on to.
+  mContext = nullptr;
+
   if (!mDestListener)
     return NS_OK;
 
   return mDestListener->OnStopRequest(aRequest, ctxt, status);
 }
 
 /** nsIStreamListener methods **/
 
--- a/intl/unicharutil/util/moz.build
+++ b/intl/unicharutil/util/moz.build
@@ -1,15 +1,15 @@
 # -*- 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/.
 
-DIRS += ['internal']
+DIRS += ['internal', 'standalone']
 
 EXPORTS += [
     'GreekCasing.h',
     'ICUUtils.h',
     'IrishCasing.h',
     'nsBidiUtils.h',
     'nsSpecialCasingData.h',
     'nsUnicharUtils.h',
new file mode 100644
--- /dev/null
+++ b/intl/unicharutil/util/standalone/moz.build
@@ -0,0 +1,23 @@
+# -*- 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['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
+    Library('unicharutil_standalone')
+
+intl_unicharutil_util_lcppsrcs = [
+    '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/js/src/asmjs/AsmJSFrameIterator.cpp
+++ b/js/src/asmjs/AsmJSFrameIterator.cpp
@@ -677,16 +677,22 @@ BuiltinToName(AsmJSExit::BuiltinKind bui
     // Note: this label is regexp-matched by
     // browser/devtools/profiler/cleopatra/js/parserWorker.js.
 
     switch (builtin) {
       case AsmJSExit::Builtin_ToInt32:   return "ToInt32 (in asm.js)";
 #if defined(JS_CODEGEN_ARM)
       case AsmJSExit::Builtin_IDivMod:   return "software idivmod (in asm.js)";
       case AsmJSExit::Builtin_UDivMod:   return "software uidivmod (in asm.js)";
+      case AsmJSExit::Builtin_AtomicCmpXchg:  return "Atomics.compareExchange (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchAdd: return "Atomics.add (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchSub: return "Atomics.sub (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchAnd: return "Atomics.and (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchOr:  return "Atomics.or (in asm.js)";
+      case AsmJSExit::Builtin_AtomicFetchXor: return "Atomics.xor (in asm.js)";
 #endif
       case AsmJSExit::Builtin_ModD:      return "fmod (in asm.js)";
       case AsmJSExit::Builtin_SinD:      return "Math.sin (in asm.js)";
       case AsmJSExit::Builtin_CosD:      return "Math.cos (in asm.js)";
       case AsmJSExit::Builtin_TanD:      return "Math.tan (in asm.js)";
       case AsmJSExit::Builtin_ASinD:     return "Math.asin (in asm.js)";
       case AsmJSExit::Builtin_ACosD:     return "Math.acos (in asm.js)";
       case AsmJSExit::Builtin_ATanD:     return "Math.atan (in asm.js)";
--- a/js/src/asmjs/AsmJSFrameIterator.h
+++ b/js/src/asmjs/AsmJSFrameIterator.h
@@ -75,16 +75,22 @@ namespace AsmJSExit
 
     // For Reason_Builtin, the list of builtins, so they can be displayed in the
     // profile call stack.
     enum BuiltinKind {
         Builtin_ToInt32,
 #if defined(JS_CODEGEN_ARM)
         Builtin_IDivMod,
         Builtin_UDivMod,
+        Builtin_AtomicCmpXchg,
+        Builtin_AtomicFetchAdd,
+        Builtin_AtomicFetchSub,
+        Builtin_AtomicFetchAnd,
+        Builtin_AtomicFetchOr,
+        Builtin_AtomicFetchXor,
 #endif
         Builtin_ModD,
         Builtin_SinD,
         Builtin_CosD,
         Builtin_TanD,
         Builtin_ASinD,
         Builtin_ACosD,
         Builtin_ATanD,
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -812,16 +812,26 @@ NewExportedFunction(JSContext* cx, const
 }
 
 static bool
 HandleDynamicLinkFailure(JSContext* cx, CallArgs args, AsmJSModule& module, HandlePropertyName name)
 {
     if (cx->isExceptionPending())
         return false;
 
+    // Source discarding is allowed to affect JS semantics because it is never
+    // enabled for normal JS content.
+    bool haveSource = module.scriptSource()->hasSourceData();
+    if (!haveSource && !JSScript::loadSource(cx, module.scriptSource(), &haveSource))
+        return false;
+    if (!haveSource) {
+        JS_ReportError(cx, "asm.js link failure with source discarding enabled");
+        return false;
+    }
+
     uint32_t begin = module.srcBodyStart();  // starts right after 'use asm'
     uint32_t end = module.srcEndBeforeCurly();
     Rooted<JSFlatString*> src(cx, module.scriptSource()->substringDontDeflate(cx, begin, end));
     if (!src)
         return false;
 
     RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED,
                                                name, JSFunction::FinalizeKind,
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -30,16 +30,17 @@
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsprf.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "prmjtime.h"
 
+#include "builtin/AtomicsObject.h"
 #include "frontend/Parser.h"
 #include "jit/IonCode.h"
 #include "js/Class.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
 
 #include "jsobjinlines.h"
 
@@ -685,16 +686,28 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
         return RedirectCall(FuncCast(CoerceInPlace_ToNumber), Args_General1);
       case AsmJSImm_ToInt32:
         return RedirectCall(FuncCast<int32_t (double)>(JS::ToInt32), Args_Int_Double);
 #if defined(JS_CODEGEN_ARM)
       case AsmJSImm_aeabi_idivmod:
         return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2);
       case AsmJSImm_aeabi_uidivmod:
         return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2);
+      case AsmJSImm_AtomicCmpXchg:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t, int32_t)>(js::atomics_cmpxchg_asm_callout), Args_General4);
+      case AsmJSImm_AtomicFetchAdd:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_add_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchSub:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_sub_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchAnd:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_and_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchOr:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_or_asm_callout), Args_General3);
+      case AsmJSImm_AtomicFetchXor:
+        return RedirectCall(FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_xor_asm_callout), Args_General3);
 #endif
       case AsmJSImm_ModD:
         return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble);
       case AsmJSImm_SinD:
 #ifdef _WIN64
         // Workaround a VS 2013 sin issue, see math_sin_uncached.
         return RedirectCall(FuncCast<double (double)>(js::math_sin_uncached), Args_Double_Double);
 #else
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -8916,29 +8916,44 @@ GenerateFFIExits(ModuleCompiler& m, cons
 //     (coming out) and preserve non-volatile registers.
 static bool
 GenerateBuiltinThunk(ModuleCompiler& m, AsmJSExit::BuiltinKind builtin)
 {
     MacroAssembler& masm = m.masm();
     MOZ_ASSERT(masm.framePushed() == 0);
 
     MIRTypeVector argTypes(m.cx());
-    if (!argTypes.reserve(2))
+    if (!argTypes.reserve(5))
         return false;
 
     switch (builtin) {
       case AsmJSExit::Builtin_ToInt32:
         argTypes.infallibleAppend(MIRType_Int32);
         break;
 #if defined(JS_CODEGEN_ARM)
       case AsmJSExit::Builtin_IDivMod:
       case AsmJSExit::Builtin_UDivMod:
         argTypes.infallibleAppend(MIRType_Int32);
         argTypes.infallibleAppend(MIRType_Int32);
         break;
+      case AsmJSExit::Builtin_AtomicCmpXchg:
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        break;
+      case AsmJSExit::Builtin_AtomicFetchAdd:
+      case AsmJSExit::Builtin_AtomicFetchSub:
+      case AsmJSExit::Builtin_AtomicFetchAnd:
+      case AsmJSExit::Builtin_AtomicFetchOr:
+      case AsmJSExit::Builtin_AtomicFetchXor:
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        argTypes.infallibleAppend(MIRType_Int32);
+        break;
 #endif
       case AsmJSExit::Builtin_SinD:
       case AsmJSExit::Builtin_CosD:
       case AsmJSExit::Builtin_TanD:
       case AsmJSExit::Builtin_ASinD:
       case AsmJSExit::Builtin_ACosD:
       case AsmJSExit::Builtin_ATanD:
       case AsmJSExit::Builtin_CeilD:
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -210,16 +210,88 @@ atomics_fence_impl(JSContext* cx, Mutabl
 
 bool
 js::atomics_fence(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return atomics_fence_impl(cx, args.rval());
 }
 
+static int32_t
+do_cmpxchg(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidate, void* viewData,
+           uint32_t offset, bool* badArrayType)
+{
+    // CAS always sets oldval to the old value of the cell.
+    // addr must be a T*, and oldval and newval should be variables of type T
+
+#if defined(CXX11_ATOMICS)
+# define CAS(T, addr, oldval, newval)                                    \
+    do {                                                                \
+        std::atomic_compare_exchange_strong(reinterpret_cast<std::atomic<T>*>(addr), &oldval, newval); \
+    } while(0)
+#elif defined(GNU_ATOMICS)
+# define CAS(T, addr, oldval, newval)                                    \
+    do {                                                                \
+        oldval = __sync_val_compare_and_swap(addr, (oldval), (newval)); \
+    } while(0)
+#else
+# define CAS(a, b, c, newval)  (void)newval
+#endif
+
+    switch (viewType) {
+      case Scalar::Int8: {
+          int8_t oldval = (int8_t)oldCandidate;
+          int8_t newval = (int8_t)newCandidate;
+          CAS(int8_t, (int8_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint8: {
+          uint8_t oldval = (uint8_t)oldCandidate;
+          uint8_t newval = (uint8_t)newCandidate;
+          CAS(uint8_t, (uint8_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint8Clamped: {
+          uint8_t oldval = ClampIntForUint8Array(oldCandidate);
+          uint8_t newval = ClampIntForUint8Array(newCandidate);
+          CAS(uint8_t, (uint8_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Int16: {
+          int16_t oldval = (int16_t)oldCandidate;
+          int16_t newval = (int16_t)newCandidate;
+          CAS(int16_t, (int16_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint16: {
+          uint16_t oldval = (uint16_t)oldCandidate;
+          uint16_t newval = (uint16_t)newCandidate;
+          CAS(uint16_t, (uint16_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Int32: {
+          int32_t oldval = oldCandidate;
+          int32_t newval = newCandidate;
+          CAS(int32_t, (int32_t*)viewData + offset, oldval, newval);
+          return oldval;
+      }
+      case Scalar::Uint32: {
+          uint32_t oldval = (uint32_t)oldCandidate;
+          uint32_t newval = (uint32_t)newCandidate;
+          CAS(uint32_t, (uint32_t*)viewData + offset, oldval, newval);
+          return (int32_t)oldval;
+      }
+      default:
+        *badArrayType = true;
+        return 0;
+    }
+
+    // Do not undef CAS, it is used later
+}
+
 bool
 js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
     HandleValue oldv = args.get(2);
     HandleValue newv = args.get(3);
@@ -237,88 +309,27 @@ js::atomics_compareExchange(JSContext* c
         return false;
     int32_t newCandidate;
     if (!ToInt32(cx, newv, &newCandidate))
         return false;
 
     if (!inRange)
         return atomics_fence_impl(cx, r);
 
-    // CAS always sets oldval to the old value of the cell.
-    // addr must be a T*, and oldval and newval should be variables of type T
+    bool badType = false;
+    int32_t result = do_cmpxchg(view->type(), oldCandidate, newCandidate, view->viewData(), offset, &badType);
 
-#if defined(CXX11_ATOMICS)
-# define CAS(T, addr, oldval, newval)                                    \
-    do {                                                                \
-        std::atomic_compare_exchange_strong(reinterpret_cast<std::atomic<T>*>(addr), &oldval, newval); \
-    } while(0)
-#elif defined(GNU_ATOMICS)
-# define CAS(T, addr, oldval, newval)                                    \
-    do {                                                                \
-        oldval = __sync_val_compare_and_swap(addr, (oldval), (newval)); \
-    } while(0)
-#else
-# define CAS(a, b, c, newval)  (void)newval
-#endif
+    if (badType)
+        return ReportBadArrayType(cx);
 
-    switch (view->type()) {
-      case Scalar::Int8: {
-          int8_t oldval = (int8_t)oldCandidate;
-          int8_t newval = (int8_t)newCandidate;
-          CAS(int8_t, (int8_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint8: {
-          uint8_t oldval = (uint8_t)oldCandidate;
-          uint8_t newval = (uint8_t)newCandidate;
-          CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint8Clamped: {
-          uint8_t oldval = ClampIntForUint8Array(oldCandidate);
-          uint8_t newval = ClampIntForUint8Array(newCandidate);
-          CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Int16: {
-          int16_t oldval = (int16_t)oldCandidate;
-          int16_t newval = (int16_t)newCandidate;
-          CAS(int16_t, (int16_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint16: {
-          uint16_t oldval = (uint16_t)oldCandidate;
-          uint16_t newval = (uint16_t)newCandidate;
-          CAS(uint16_t, (uint16_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Int32: {
-          int32_t oldval = oldCandidate;
-          int32_t newval = newCandidate;
-          CAS(int32_t, (int32_t*)view->viewData() + offset, oldval, newval);
-          r.setInt32(oldval);
-          return true;
-      }
-      case Scalar::Uint32: {
-          uint32_t oldval = (uint32_t)oldCandidate;
-          uint32_t newval = (uint32_t)newCandidate;
-          CAS(uint32_t, (uint32_t*)view->viewData() + offset, oldval, newval);
-          r.setNumber((double)oldval);
-          return true;
-      }
-      default:
-        return ReportBadArrayType(cx);
-    }
-
-    // Do not undef CAS, it is used later
+    if (view->type() == Scalar::Uint32)
+        r.setNumber((double)(uint32_t)result);
+    else
+        r.setInt32(result);
+    return true;
 }
 
 bool
 js::atomics_load(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
@@ -683,16 +694,162 @@ js::atomics_xor(JSContext* cx, unsigned 
     return atomics_binop_impl<do_xor>(cx, args.get(0), args.get(1), args.get(2), args.rval());
 }
 
 #undef INTEGRAL_TYPES_FOR_EACH
 #undef CAST_ATOMIC
 #undef DO_NOTHING
 #undef ZERO
 
+// asm.js callouts for platforms that do not have non-word-sized
+// atomics where we don't want to inline the logic for the atomics.
+//
+// size is currently -1 (signed byte), 1 (unsigned byte), -2 (signed halfword),
+// or 2 (halfword).
+// ptr is the byte offset within the heap array.  This will have low bit zero
+// for halfword accesses.
+// value (for binops) and oldval/newval (for cmpxchg) are the values
+// to be operated upon.
+
+static void
+GetCurrentAsmJSHeap(void** heap, size_t* length)
+{
+    JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
+    AsmJSModule& mod = rt->asmJSActivationStack()->module();
+    *heap = mod.heapDatum();
+    *length = mod.heapLength();
+}
+
+int32_t
+js::atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_add::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_add::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_add::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_add::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_sub_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_sub::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_sub::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_sub::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_sub::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_and_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_and::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_and::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_and::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_and::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_or_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_or::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_or::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_or::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_or::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_xor_asm_callout(int32_t vt, int32_t offset, int32_t value)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_xor::operate((int8_t*)heap + offset, value);
+      case Scalar::Uint8:
+        return do_xor::operate((uint8_t*)heap + offset, value);
+      case Scalar::Int16:
+        return do_xor::operate((int16_t*)heap + (offset >> 1), value);
+      case Scalar::Uint16:
+        return do_xor::operate((uint16_t*)heap + (offset >> 1), value);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
+int32_t
+js::atomics_cmpxchg_asm_callout(int32_t vt, int32_t offset, int32_t oldval, int32_t newval)
+{
+    void* heap;
+    size_t heapLength;
+    GetCurrentAsmJSHeap(&heap, &heapLength);
+    if ((size_t)offset >= heapLength) return 0;
+    bool badType = false;
+    switch (Scalar::Type(vt)) {
+      case Scalar::Int8:
+        return do_cmpxchg(Scalar::Int8, oldval, newval, heap, offset, &badType);
+      case Scalar::Uint8:
+        return do_cmpxchg(Scalar::Uint8, oldval, newval, heap, offset, &badType);
+      case Scalar::Int16:
+        return do_cmpxchg(Scalar::Int16, oldval, newval, heap, offset>>1, &badType);
+      case Scalar::Uint16:
+        return do_cmpxchg(Scalar::Uint16, oldval, newval, heap, offset>>1, &badType);
+      default:
+        MOZ_CRASH("Invalid size");
+    }
+}
+
 namespace js {
 
 // Represents one waiting worker.
 //
 // The type is declared opaque in SharedArrayObject.h.  Instances of
 // js::FutexWaiter are stack-allocated and linked onto a list across a
 // call to FutexRuntime::wait().
 //
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -39,16 +39,24 @@ bool atomics_add(JSContext* cx, unsigned
 bool atomics_sub(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_and(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_or(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_futexWait(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_futexWake(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp);
 
+/* asm.js callouts */
+int32_t atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_sub_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_and_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_or_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_xor_asm_callout(int32_t vt, int32_t offset, int32_t value);
+int32_t atomics_cmpxchg_asm_callout(int32_t vt, int32_t offset, int32_t oldval, int32_t newval);
+
 class FutexRuntime
 {
 public:
     static bool initialize();
     static void destroy();
 
     static void lock();
     static void unlock();
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2489,27 +2489,34 @@ DumpStringRepresentation(JSContext* cx, 
     str->dumpRepresentation(stderr, 0);
 
     args.rval().setUndefined();
     return true;
 }
 #endif
 
 static bool
-SetLazyParsingEnabled(JSContext* cx, unsigned argc, Value* vp)
+SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (argc < 1) {
-        JS_ReportError(cx, "setLazyParsingEnabled: need an argument");
-        return false;
-    }
-
-    bool arg = ToBoolean(args.get(0));
-    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(!arg);
+    bool disable = !args.hasDefined(0) || ToBoolean(args[0]);
+    JS::CompartmentOptionsRef(cx->compartment()).setDisableLazyParsing(disable);
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+SetDiscardSource(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    bool discard = !args.hasDefined(0) || ToBoolean(args[0]);
+    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(discard);
 
     args.rval().setUndefined();
     return true;
 }
 
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
@@ -2897,19 +2904,25 @@ gc::ZealModeHelpText),
 "  because the object is a revoked proxy)."),
 
 #ifdef DEBUG
     JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
 "dumpStringRepresentation(str)",
 "  Print a human-readable description of how the string |str| is represented.\n"),
 #endif
 
-    JS_FN_HELP("setLazyParsingEnabled", SetLazyParsingEnabled, 1, 0,
-"setLazyParsingEnabled(bool)",
-"  Enable or disable lazy parsing in the current compartment.  The default is enabled."),
+    JS_FN_HELP("setLazyParsingDisabled", SetLazyParsingDisabled, 1, 0,
+"setLazyParsingDisabled(bool)",
+"  Explicitly disable lazy parsing in the current compartment.  The default is that lazy "
+"  parsing is not explicitly disabled."),
+
+    JS_FN_HELP("setDiscardSource", SetDiscardSource, 1, 0,
+"setDiscardSource(bool)",
+"  Explicitly enable source discarding in the current compartment.  The default is that "
+"  source discarding is not explicitly enabled."),
 
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1480,17 +1480,17 @@ OutlineTypedObject::createUnattachedWith
                clasp == &OutlineOpaqueTypedObject::class_);
 
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
                                                              TaggedProto(&descr->typedProto()),
                                                              descr));
     if (!group)
         return nullptr;
 
-    NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
+    NewObjectKind newKind = (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
     OutlineTypedObject* obj = NewObjectWithGroup<OutlineTypedObject>(cx, group,
                                                                      gc::AllocKind::OBJECT0,
                                                                      newKind);
     if (!obj)
         return nullptr;
 
     obj->setOwnerAndData(nullptr, nullptr);
     return obj;
@@ -2114,17 +2114,17 @@ InlineTypedObject::create(JSContext* cx,
                          : &InlineTransparentTypedObject::class_;
 
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
                                                              TaggedProto(&descr->typedProto()),
                                                              descr));
     if (!group)
         return nullptr;
 
-    NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
+    NewObjectKind newKind = (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
     return NewObjectWithGroup<InlineTypedObject>(cx, group, allocKind, newKind);
 }
 
 /* static */ InlineTypedObject*
 InlineTypedObject::createCopy(JSContext* cx, Handle<InlineTypedObject*> templateObject,
                               gc::InitialHeap heap)
 {
     Rooted<TypeDescr*> descr(cx, &templateObject->typeDescr());
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -142,16 +142,17 @@ MaybeCheckEvalFreeVariables(ExclusiveCon
     return true;
 }
 
 static inline bool
 CanLazilyParse(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
 {
     return options.canLazilyParse &&
            !options.hasPollutedGlobalScope &&
+           !cx->compartment()->options().disableLazyParsing() &&
            !cx->compartment()->options().discardSource() &&
            !options.sourceIsLazy;
 }
 
 static void
 MarkFunctionsWithinEvalScript(JSScript* script)
 {
     // Mark top level functions in an eval script as being within an eval.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1227,17 +1227,17 @@ Parser<ParseHandler>::newFunction(Handle
         flags = JSFunction::INTERPRETED;
         break;
     }
     
     gc::AllocKind allocKind = JSFunction::FinalizeKind;
     if (kind == Arrow)
         allocKind = JSFunction::ExtendedFinalizeKind;
     fun = NewFunctionWithProto(context, nullptr, 0, flags, NullPtr(), atom, proto,
-                               allocKind, MaybeSingletonObject);
+                               allocKind, TenuredObject);
     if (!fun)
         return nullptr;
     if (options().selfHostingMode)
         fun->setIsSelfHostedBuiltin();
     return fun;
 }
 
 static bool
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -848,23 +848,16 @@ js::Nursery::collect(JSRuntime* rt, JS::
     TIME_START(markRelocatableCells);
     sb.markRelocatableCells(&trc);
     TIME_END(markRelocatableCells);
 
     TIME_START(markGenericEntries);
     sb.markGenericEntries(&trc);
     TIME_END(markGenericEntries);
 
-    TIME_START(checkHashTables);
-#ifdef JS_GC_ZEAL
-    if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC)
-        CheckHashTablesAfterMovingGC(rt);
-#endif
-    TIME_END(checkHashTables);
-
     TIME_START(markRuntime);
     rt->gc.markRuntime(&trc);
     TIME_END(markRuntime);
 
     TIME_START(markDebugger);
     {
         gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&trc);
@@ -910,16 +903,24 @@ js::Nursery::collect(JSRuntime* rt, JS::
     TIME_START(sweep);
     sweep();
     TIME_END(sweep);
 
     TIME_START(clearStoreBuffer);
     rt->gc.storeBuffer.clear();
     TIME_END(clearStoreBuffer);
 
+    // Make sure hashtables have been updated after the collection.
+    TIME_START(checkHashTables);
+#ifdef JS_GC_ZEAL
+    if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC)
+        CheckHashTablesAfterMovingGC(rt);
+#endif
+    TIME_END(checkHashTables);
+
     // Resize the nursery.
     TIME_START(resize);
     double promotionRate = trc.tenuredSize / double(allocationEnd() - start());
     if (promotionRate > 0.05)
         growAllocableSpace();
     else if (promotionRate < 0.01)
         shrinkAllocableSpace();
     TIME_END(resize);
--- a/js/src/jit-test/tests/SIMD/swizzle.js
+++ b/js/src/jit-test/tests/SIMD/swizzle.js
@@ -55,17 +55,32 @@ function testBailouts(uglyDuckling) {
             caught = true;
             assertEq(i, 149);
             assertEq(e instanceof TypeError, true);
         }
         assertEq(i < 149 || caught, true);
     }
 }
 
+function testInt32x4SwizzleBailout() {
+    // Test out-of-bounds non-constant indices. This is expected to throw.
+    var i4 = SIMD.int32x4(1, 2, 3, 4);
+    for (var i = 0; i < 150; i++) {
+        assertEqX4(SIMD.int32x4.swizzle(i4, i, 3, 2, 0), [i + 1, 4, 3, 1]);
+    }
+}
+
 f();
 testBailouts(-1);
 testBailouts(4);
 testBailouts(2.5);
 testBailouts(undefined);
 testBailouts(null);
 testBailouts({});
 testBailouts('one');
 testBailouts(true);
+
+try {
+    testInt32x4SwizzleBailout();
+    throw 'not caught';
+} catch(e) {
+    assertEq(e instanceof TypeError, true);
+}
--- a/js/src/jit-test/tests/asm.js/testBug1147144.js
+++ b/js/src/jit-test/tests/asm.js/testBug1147144.js
@@ -1,14 +1,20 @@
 load(libdir + 'asm.js');
-setLazyParsingEnabled(false)
+load(libdir + 'asserts.js');
+
+if (!isAsmJSCompilationAvailable())
+    quit();
+
+setDiscardSource(true)
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toString(), "function asmModule() {\n    [sourceless code]\n}");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toSource(), "(function asmModule() {\n    [sourceless code]\n})");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`)().toString(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`)().toSource(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule1() { "use asm"; function asmFun() {} return asmFun }`), asmModule1).toString(), "function asmModule1() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule2() { "use asm"; function asmFun() {} return asmFun }`), asmModule2).toSource(), "function asmModule2() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule3() { "use asm"; function asmFun() {} return asmFun }`), asmModule3)().toString(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq((evaluate(`function asmModule4() { "use asm"; function asmFun() {} return asmFun }`), asmModule4)().toSource(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`).toString(), "function anonymous() {\n    [sourceless code]\n}");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`).toSource(), "(function anonymous() {\n    [sourceless code]\n})");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`)().toString(), "function asmFun() {\n    [sourceless code]\n}");
 assertEq(asmCompile(USE_ASM + `function asmFun() {} return asmFun`)().toSource(), "function asmFun() {\n    [sourceless code]\n}");
+assertThrowsInstanceOf(()=>asmCompile('stdlib',USE_ASM + "var sin=stdlib.Math.sin;return {}")({Math:{}}), Error);
--- a/js/src/jit-test/tests/basic/eval-scopes.js
+++ b/js/src/jit-test/tests/basic/eval-scopes.js
@@ -17,17 +17,17 @@ function hasGname(f, v, hasIt = true) {
     } catch (e) {
 	print(e.stack);
 	throw e;
     }
 }
 
 var x = "outer";
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 {
     let x = "inner";
     eval("function g() { assertEq(x, 'inner');} g()");
     eval("function g2() { (function nest() { assertEq(x, 'inner'); })(); } g2()");
 }
 eval(`
      function g3() {
 	 assertEq(x, 'outer');
@@ -38,17 +38,17 @@ eval(`
 eval(`
      function g4() {
 	 function nest() { assertEq(x, 'outer'); }
 	 nest();
 	 return nest;
      }
      hasGname(g4(), 'x');
      `);
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 {
     let x = "inner";
     eval("function h() { assertEq(x, 'inner');} h()");
     eval("function h2() { (function nest() { assertEq(x, 'inner'); })(); } h2()");
 }
 // It sure would be nice if we could run the h3/h4 tests below, but it turns out
 // that lazy functions and eval don't play together all that well.  See bug
@@ -65,37 +65,37 @@ eval(`
      function h4() {
 	 function nest() { assertEq(x, 'outer'); }
 	 nest();
 	 return nest;
      }
      hasGname(h4(), 'x', false);
      `);
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 with ({}) {
     let x = "inner";
     eval("function i() { assertEq(x, 'inner');} i()");
     eval("function i2() { (function nest() { assertEq(x, 'inner'); })(); } i2()");
 }
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 with ({}) {
     let x = "inner";
     eval("function j() { assertEq(x, 'inner');} j()");
     eval("function j2() { (function nest() { assertEq(x, 'inner'); })(); } j2()");
 }
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 (function () {
     var x = "inner";
     eval("function k() { assertEq(x, 'inner');} k()");
     eval("function k2() { (function nest() { assertEq(x, 'inner'); })(); } k2()");
 })();
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 (function () {
     let x = "inner";
     eval("function l() { assertEq(x, 'inner');} l()");
     eval("function l2() { (function nest() { assertEq(x, 'inner'); })(); } l2()");
 })();
 
 var y1 = 5;
@@ -109,17 +109,17 @@ assertEq(y1, 5);
 
 eval(`
      'use strict';
      var y2 = 6;
      assertEq(y2, 6);
      (function() { assertEq(y2, 6); })()
      `);
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 
 var y3 = 5;
 eval(`
      'use strict';
      var y3 = 6;
      assertEq(y3, 6);
      (function() { assertEq(y3, 6); })()
      `);
@@ -127,9 +127,9 @@ assertEq(y3, 5);
 
 eval(`
      'use strict';
      var y4 = 6;
      assertEq(y4, 6);
      (function() { assertEq(y4, 6); })()
      `);
 
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
--- a/js/src/jit-test/tests/basic/function-gname.js
+++ b/js/src/jit-test/tests/basic/function-gname.js
@@ -21,29 +21,29 @@ function hasGname(f, v) {
 }
 
 var x = "outer";
 
 var f1 = new Function("assertEq(x, 'outer')");
 f1();
 hasGname(f1, 'x');
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 var f2 = new Function("assertEq(x, 'outer')");
 f2();
 hasGname(f2, 'x');
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
 
 {
     let x = "inner";
     var f3 = new Function("assertEq(x, 'outer')");
     f3();
     hasGname(f3, 'x');
 }
 
-setLazyParsingEnabled(false);
+setLazyParsingDisabled(true);
 {
     let x = "inner";
     var f4 = new Function("assertEq(x, 'outer')");
     f4();
     hasGname(f4, 'x');
 }
-setLazyParsingEnabled(true);
+setLazyParsingDisabled(false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1148383.js
@@ -0,0 +1,19 @@
+// This testcase tests setting object metadata for objects created from JIT
+// code.
+if (!("getJitCompilerOptions" in this))
+  quit();
+opts = getJitCompilerOptions();
+if (!opts['ion.enable'] || !opts['baseline.enable'])
+  quit();
+
+function TestCase() {}
+function reportCompare () {
+  var output = "";
+  var testcase = new TestCase();
+  testcase.reason = output;
+}
+reportCompare();
+gczeal(4, 1000);
+setObjectMetadataCallback(true);
+for (var i = 0; i < 10000; ++i)
+  reportCompare();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/bug-1149495.js
@@ -0,0 +1,6 @@
+try {
+  offThreadCompileScript('Error()', { lineNumber: (4294967295)});
+  runOffThreadScript().stack;
+} catch (e) {
+  // Ignore "Error: Can't use offThreadCompileScript with --no-threads"
+}
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1372,17 +1372,17 @@ BaselineCompiler::emit_JSOP_OBJECT()
 {
     if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
         if (!obj)
             return false;
 
         prepareVMCall();
 
-        pushArg(ImmWord(js::MaybeSingletonObject));
+        pushArg(ImmWord(TenuredObject));
         pushArg(ImmGCPtr(obj));
 
         if (!callVM(DeepCloneObjectLiteralInfo))
             return false;
 
         // Box and push return value.
         masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
         frame.push(R0);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -169,16 +169,58 @@ void
 ReceiverGuard::trace(JSTracer* trc)
 {
     if (shape_)
         TraceEdge(trc, &shape_, "receiver_guard_shape");
     else
         TraceEdge(trc, &group_, "receiver_guard_group");
 }
 
+ReceiverGuard::StackGuard::StackGuard(JSObject* obj)
+  : group(nullptr), shape(nullptr)
+{
+    if (obj) {
+        if (obj->is<UnboxedPlainObject>()) {
+            group = obj->group();
+            if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+                shape = expando->lastProperty();
+        } else if (obj->is<TypedObject>()) {
+            group = obj->group();
+        } else {
+            shape = obj->maybeShape();
+        }
+    }
+}
+
+ReceiverGuard::StackGuard::StackGuard(ObjectGroup* group, Shape* shape)
+  : group(group), shape(shape)
+{
+    if (group) {
+        if (IsTypedObjectClass(group->clasp()))
+            this->shape = nullptr;
+        else if (group->clasp() != &UnboxedPlainObject::class_)
+            this->group = nullptr;
+    }
+}
+
+/* static */ int32_t
+ReceiverGuard::keyBits(JSObject* obj)
+{
+    if (obj->is<UnboxedPlainObject>()) {
+        // Both the group and shape need to be guarded for unboxed plain objects.
+        return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
+    }
+    if (obj->is<TypedObject>()) {
+        // Only the group needs to be guarded for typed objects.
+        return 2;
+    }
+    // Other objects only need the shape to be guarded.
+    return 3;
+}
+
 /* static */ void
 ICStub::trace(JSTracer* trc)
 {
     markCode(trc, "baseline-stub-jitcode");
 
     // If the stub is a monitored fallback stub, then mark the monitor ICs hanging
     // off of that stub.  We don't need to worry about the regular monitored stubs,
     // because the regular monitored stubs will always have a monitored fallback stub
@@ -3161,24 +3203,23 @@ ICUnaryArith_Double::Compiler::generateS
     EmitStubGuardFailure(masm);
     return true;
 }
 
 //
 // GetElem_Fallback
 //
 
-static void GetFixedOrDynamicSlotOffset(NativeObject* obj, uint32_t slot,
-                                        bool* isFixed, uint32_t* offset)
+static void GetFixedOrDynamicSlotOffset(Shape* shape, bool* isFixed, uint32_t* offset)
 {
     MOZ_ASSERT(isFixed);
     MOZ_ASSERT(offset);
-    *isFixed = obj->isFixedSlot(slot);
-    *offset = *isFixed ? NativeObject::getFixedSlotOffset(slot)
-                       : obj->dynamicSlotIndex(slot) * sizeof(Value);
+    *isFixed = shape->slot() < shape->numFixedSlots();
+    *offset = *isFixed ? NativeObject::getFixedSlotOffset(shape->slot())
+                       : (shape->slot() - shape->numFixedSlots()) * sizeof(Value);
 }
 
 static JSObject*
 GetDOMProxyProto(JSObject* obj)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     return obj->getTaggedProto().toObjectOrNull();
 }
@@ -3347,17 +3388,19 @@ CheckHasNoSuchProperty(JSContext* cx, Ha
 }
 
 static bool
 IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false)
 {
     MOZ_ASSERT_IF(isDOMProxy, IsCacheableDOMProxy(obj));
 
     if (!isDOMProxy && !obj->isNative()) {
-        if (obj == holder || !obj->is<UnboxedPlainObject>() || !obj->is<TypedObject>())
+        if (obj == holder)
+            return false;
+        if (!obj->is<UnboxedPlainObject>() && !obj->is<TypedObject>())
             return false;
     }
 
     // Don't handle objects which require a prototype guard. This should
     // be uncommon so handling it is likely not worth the complexity.
     if (obj->hasUncacheableProto())
         return false;
 
@@ -3423,66 +3466,75 @@ IsCacheableGetPropCall(JSContext* cx, JS
         *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     *isScripted = true;
     return true;
 }
 
-static bool
-IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, JSObject* holder, Shape* shape)
-{
-    if (!shape)
-        return false;
-
+static Shape*
+LastPropertyForSetProp(JSObject* obj)
+{
+    if (obj->isNative())
+        return obj->as<NativeObject>().lastProperty();
+
+    if (obj->is<UnboxedPlainObject>()) {
+        UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
+        return expando ? expando->lastProperty() : nullptr;
+    }
+
+    return nullptr;
+}
+
+static bool
+IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, Shape* propertyShape)
+{
     // Object shape must not have changed during the property set.
-    if (!obj->isNative() || obj->as<NativeObject>().lastProperty() != oldShape)
-        return false;
-
-    // Currently we only optimize direct writes.
-    if (obj != holder)
-        return false;
-
-    if (!shape->hasSlot() || !shape->hasDefaultSetter() || !shape->writable())
-        return false;
+    if (LastPropertyForSetProp(obj) != oldShape)
+        return false;
+
+    if (!propertyShape->hasSlot() ||
+        !propertyShape->hasDefaultSetter() ||
+        !propertyShape->writable())
+    {
+        return false;
+    }
 
     return true;
 }
 
 static bool
 IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape,
-                          jsid id, JSObject* holder, Shape* shape,
-                          size_t* protoChainDepth)
-{
-    if (!shape)
-        return false;
-
-    // Property must be set directly on object, and be last added property of object.
-    if (!obj->isNative() || obj != holder || shape != obj->as<NativeObject>().lastProperty())
-        return false;
-
-    // Object must be extensible, oldShape must be immediate parent of curShape.
-    if (!obj->nonProxyIsExtensible() || shape->previous() != oldShape)
+                          jsid id, Shape* propertyShape, size_t* protoChainDepth)
+{
+    // The property must be the last added property of the object.
+    if (LastPropertyForSetProp(obj) != propertyShape)
+        return false;
+
+    // Object must be extensible, oldShape must be immediate parent of current shape.
+    if (!obj->nonProxyIsExtensible() || propertyShape->previous() != oldShape)
         return false;
 
     // Basic shape checks.
-    if (shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter() ||
-        !shape->writable())
+    if (propertyShape->inDictionary() ||
+        !propertyShape->hasSlot() ||
+        !propertyShape->hasDefaultSetter() ||
+        !propertyShape->writable())
     {
         return false;
     }
 
     // If object has a resolve hook, don't inline
     if (obj->getClass()->resolve)
         return false;
 
     size_t chainDepth = 0;
-    // walk up the object prototype chain and ensure that all prototypes
-    // are native, and that all prototypes have setter defined on the property
+    // Walk up the object prototype chain and ensure that all prototypes are
+    // native, and that all prototypes have no setter defined on the property.
     for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) {
         chainDepth++;
         // if prototype is non-native, don't optimize
         if (!proto->isNative())
             return false;
 
         // if prototype defines this property in a non-plain way, don't optimize
         Shape* protoShape = proto->as<NativeObject>().lookup(cx, id);
@@ -3493,17 +3545,17 @@ IsCacheableSetPropAddSlot(JSContext* cx,
         // to be invoked and thus prevent inlining of property addition.
         if (proto->getClass()->resolve)
              return false;
     }
 
     // Only add a IC entry if the dynamic slots didn't change when the shapes
     // changed.  Need to ensure that a shape change for a subsequent object
     // won't involve reallocating the slot array.
-    if (NativeObject::dynamicSlotsCount(shape) != NativeObject::dynamicSlotsCount(oldShape))
+    if (NativeObject::dynamicSlotsCount(propertyShape) != NativeObject::dynamicSlotsCount(oldShape))
         return false;
 
     *protoChainDepth = chainDepth;
     return true;
 }
 
 static bool
 IsCacheableSetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape,
@@ -3753,18 +3805,17 @@ TryAttachNativeGetValueElemStub(JSContex
         if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
             return true;
 
         // Remove any existing stubs that may interfere with the new stub being added.
         RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&holder->as<NativeObject>(),
-                                    shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
         ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlot
                                             : ICStub::GetElem_NativePrototypeSlot;
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(Native %s%s slot) stub "
                                     "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
                     (obj == holder) ? "direct" : "prototype",
@@ -5935,17 +5986,17 @@ TryAttachGlobalNameValueStub(JSContext* 
             uint32_t slot = shape->slot() - current->numFixedSlots();
 
             JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName) stub");
             ICGetName_Global::Compiler compiler(cx, monitorStub, current->lastProperty(), slot);
             newStub = compiler.getStub(compiler.getStubSpace(script));
         } else {
             bool isFixedSlot;
             uint32_t offset;
-            GetFixedOrDynamicSlotOffset(current, shape->slot(), &isFixedSlot, &offset);
+            GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
             if (!IsCacheableGetPropReadSlot(global, current, shape))
                 return true;
 
             JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName prototype) stub");
             ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_NativePrototype, false, monitorStub,
                                              global, current, name, isFixedSlot, offset,
                                              /* inputDefinitelyObject = */ true);
             newStub = compiler.getStub(compiler.getStubSpace(script));
@@ -6058,18 +6109,17 @@ TryAttachScopeNameStub(JSContext* cx, Ha
     // IonBuilder::getPropTryCommonGetter (which requires a Baseline stub to
     // work) handles non-outerized this objects correctly.
 
     if (!IsCacheableGetPropReadSlot(scopeChain, scopeChain, shape))
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
-    GetFixedOrDynamicSlotOffset(&scopeChain->as<NativeObject>(),
-                                shape->slot(), &isFixedSlot, &offset);
+    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     ICStub* newStub;
 
     switch (shapes.length()) {
       case 1: {
         ICGetName_Scope<0>::Compiler compiler(cx, monitorStub, &shapes, isFixedSlot, offset);
         newStub = compiler.getStub(compiler.getStubSpace(script));
@@ -6516,26 +6566,31 @@ UpdateExistingGenerationalDOMProxyStub(I
                 updateStub->setGeneration(generation);
                 return true;
             }
         }
     }
     return false;
 }
 
-static bool
-HasUnanalyzedNewScript(JSObject* obj)
+// Return whether obj is in some PreliminaryObjectArray and has a structure
+// that might change in the future.
+static bool
+IsPreliminaryObject(JSObject* obj)
 {
     if (obj->isSingleton())
         return false;
 
     TypeNewScript* newScript = obj->group()->newScript();
     if (newScript && !newScript->analyzed())
         return true;
 
+    if (obj->group()->maybePreliminaryObjects())
+        return true;
+
     return false;
 }
 
 static void
 StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub)
 {
     // Before the new script properties analysis has been performed on a type,
     // all instances of that type have the maximum number of fixed slots.
@@ -6578,34 +6633,34 @@ TryAttachNativeGetValuePropStub(JSContex
         return false;
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&holder->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         // Instantiate this property for singleton holders, for use during Ion compilation.
         if (IsIonEnabled(cx))
             EnsureTrackPropertyTypes(cx, holder, NameToId(name));
 
         ICStub::Kind kind =
             (obj == holder) ? ICStub::GetProp_Native : ICStub::GetProp_NativePrototype;
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetProp(Native %s) stub",
                     (obj == holder) ? "direct" : "prototype");
         ICGetPropNativeCompiler compiler(cx, kind, isCallProp, monitorStub, obj, holder,
                                          name, isFixedSlot, offset);
         ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
-        if (HasUnanalyzedNewScript(obj))
+        if (IsPreliminaryObject(obj))
             newStub->notePreliminaryObject();
         else
             StripPreliminaryObjectStubs(cx, stub);
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
@@ -6833,17 +6888,17 @@ TryAttachUnboxedExpandoGetPropStub(JSCon
         return true;
 
     Shape* shape = expando->lookup(cx, name);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
-    GetFixedOrDynamicSlotOffset(expando, shape->slot(), &isFixedSlot, &offset);
+    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
     ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_Native, isCallProp, monitorStub, obj, obj,
                                      name, isFixedSlot, offset);
     ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
@@ -6930,17 +6985,17 @@ TryAttachPrimitiveGetPropStub(JSContext*
 
     // For now, only look for properties directly set on the prototype.
     RootedShape shape(cx, proto->lookup(cx, id));
     if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
-    GetFixedOrDynamicSlotOffset(proto, shape->slot(), &isFixedSlot, &offset);
+    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
 
     JitSpew(JitSpew_BaselineIC, "  Generating GetProp_Primitive stub");
     ICGetProp_Primitive::Compiler compiler(cx, monitorStub, primitiveType, proto,
                                            isFixedSlot, offset);
     ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
@@ -8166,83 +8221,100 @@ BaselineScript::noteAccessedGetter(uint3
 // value property.
 static bool
 TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICSetProp_Fallback* stub,
                           HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
                           HandlePropertyName name, HandleId id, HandleValue rhs, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
-    if (!obj->isNative() || obj->watched())
+    if (obj->watched())
         return true;
 
     RootedShape shape(cx);
     RootedObject holder(cx);
     if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
         return false;
+    if (obj != holder)
+        return true;
+
+    if (!obj->isNative()) {
+        if (obj->is<UnboxedPlainObject>()) {
+            UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
+            if (expando) {
+                shape = expando->lookup(cx, name);
+                if (!shape)
+                    return true;
+            } else {
+                return true;
+            }
+        } else {
+            return true;
+        }
+    }
 
     size_t chainDepth;
-    if (IsCacheableSetPropAddSlot(cx, obj, oldShape, id, holder, shape, &chainDepth)) {
+    if (IsCacheableSetPropAddSlot(cx, obj, oldShape, id, shape, &chainDepth)) {
         // Don't attach if proto chain depth is too high.
         if (chainDepth > ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH)
             return true;
 
         // Don't attach if we are adding a property to an object which the new
         // script properties analysis hasn't been performed for yet, as there
         // may be a shape change required here afterwards. Pretend we attached
         // a stub, though, so the access is not marked as unoptimizable.
         if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
             *attached = true;
             return true;
         }
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.ADD) stub");
         ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldGroup,
                                             chainDepth, isFixedSlot, offset);
         ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
-    if (IsCacheableSetPropWriteSlot(obj, oldShape, holder, shape)) {
+    if (IsCacheableSetPropWriteSlot(obj, oldShape, shape)) {
         // For some property writes, such as the initial overwrite of global
         // properties, TI will not mark the property as having been
         // overwritten. Don't attach a stub in this case, so that we don't
         // execute another write to the property without TI seeing that write.
         EnsureTrackPropertyTypes(cx, obj, id);
         if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
             *attached = true;
             return true;
         }
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.PROP) stub");
-        MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldShape,
+        MOZ_ASSERT(LastPropertyForSetProp(obj) == oldShape,
                    "Should this really be a SetPropWriteSlot?");
         ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
         ICSetProp_Native* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
             return false;
 
-        if (HasUnanalyzedNewScript(obj))
+        if (IsPreliminaryObject(obj))
             newStub->notePreliminaryObject();
         else
             StripPreliminaryObjectStubs(cx, stub);
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
@@ -8439,16 +8511,21 @@ DoSetPropFallback(JSContext* cx, Baselin
     if (!obj)
         return false;
     RootedShape oldShape(cx, obj->maybeShape());
     RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
     if (!oldGroup)
         return false;
     ReceiverGuard::RootedStackGuard oldGuard(cx, ReceiverGuard::StackGuard(obj));
 
+    if (obj->is<UnboxedPlainObject>()) {
+        if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+            oldShape = expando->lastProperty();
+    }
+
     bool attached = false;
     // There are some reasons we can fail to attach a stub that are temporary.
     // We want to avoid calling noteUnoptimizableAccess() if the reason we
     // failed to attach a stub is one of those temporary reasons, since we might
     // end up attaching a stub for the exact same access later.
     bool isTemporarilyUnoptimizable = false;
     if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
         lhs.isObject() &&
@@ -8576,36 +8653,60 @@ bool
 ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> code)
 {
     CodeOffsetLabel offset(returnOffset_);
     offset.fixup(&masm);
     cx->compartment()->jitCompartment()->initBaselineSetPropReturnAddr(code->raw() + offset.offset());
     return true;
 }
 
+static void
+GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj,
+                                      Register object, Register scratch,
+                                      size_t offsetOfGroup, size_t offsetOfShape, Label* failure)
+{
+    // Guard against object group.
+    masm.loadPtr(Address(BaselineStubReg, offsetOfGroup), scratch);
+    masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
+                   failure);
+
+    // Guard against shape or expando shape.
+    masm.loadPtr(Address(BaselineStubReg, offsetOfShape), scratch);
+    if (obj->is<UnboxedPlainObject>()) {
+        Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
+        masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
+        Label done;
+        masm.push(object);
+        masm.loadPtr(expandoAddress, object);
+        masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
+        masm.pop(object);
+        masm.jump(failure);
+        masm.bind(&done);
+        masm.pop(object);
+    } else {
+        masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
+    }
+}
+
 bool
 ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure;
 
     // Guard input is an object.
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+    Register objReg = masm.extractObject(R0, ExtractTemp0);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratch = regs.takeAny();
 
-    // Unbox and shape guard.
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfShape()), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
-
-    // Guard that the object group matches.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfGroup()), scratch);
-    masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch,
-                   &failure);
+    GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch,
+                                          ICSetProp_Native::offsetOfGroup(),
+                                          ICSetProp_Native::offsetOfShape(),
+                                          &failure);
 
     // Stow both R0 and R1 (object and value).
     EmitStowICValues(masm, 2);
 
     // Type update stub expects the value to check in R0.
     masm.moveValue(R1, R0);
 
     // Call the type-update stub.
@@ -8614,17 +8715,23 @@ ICSetProp_Native::Compiler::generateStub
 
     // Unstow R0 and R1 (object and key)
     EmitUnstowICValues(masm, 2);
 
     regs.add(R0);
     regs.takeUnchecked(objReg);
 
     Register holderReg;
-    if (isFixedSlot_) {
+    if (obj_->is<UnboxedPlainObject>()) {
+        // We are loading off the expando object, so use that for the holder.
+        holderReg = regs.takeAny();
+        masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
+        if (!isFixedSlot_)
+            masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
+    } else if (isFixedSlot_) {
         holderReg = objReg;
     } else {
         holderReg = regs.takeAny();
         masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
     }
 
     // Perform the store.
     masm.load32(Address(BaselineStubReg, ICSetProp_Native::offsetOfOffset()), scratch);
@@ -8679,29 +8786,25 @@ ICSetPropNativeAddCompiler::getStub(ICSt
 bool
 ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure;
     Label failureUnstow;
 
     // Guard input is an object.
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+    Register objReg = masm.extractObject(R0, ExtractTemp0);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratch = regs.takeAny();
 
-    // Unbox and guard against old shape.
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(0)), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
-
-    // Guard that the object group matches.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfGroup()), scratch);
-    masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch,
-                   &failure);
+    GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch,
+                                          ICSetProp_NativeAdd::offsetOfGroup(),
+                                          ICSetProp_NativeAddImpl<0>::offsetOfShape(0),
+                                          &failure);
 
     // Stow both R0 and R1 (object and value).
     EmitStowICValues(masm, 2);
 
     regs = availableGeneralRegs(1);
     scratch = regs.takeAny();
     Register protoReg = regs.takeAny();
     // Check the proto chain.
@@ -8723,54 +8826,71 @@ ICSetPropNativeAddCompiler::generateStub
     if (!callTypeUpdateIC(masm, sizeof(Value)))
         return false;
 
     // Unstow R0 and R1 (object and key)
     EmitUnstowICValues(masm, 2);
     regs = availableGeneralRegs(2);
     scratch = regs.takeAny();
 
-    // Changing object shape.  Write the object's new shape.
-    Address shapeAddr(objReg, JSObject::offsetOfShape());
-    EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
-    masm.storePtr(scratch, shapeAddr);
-
-    // Try to change the object's group.
-    Label noGroupChange;
-
-    // Check if the cache has a new group to change to.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
-    masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
-
-    // Check if the old group still has a newScript.
-    masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
-    masm.branchPtr(Assembler::Equal,
-                   Address(scratch, ObjectGroup::offsetOfAddendum()),
-                   ImmWord(0),
-                   &noGroupChange);
-
-    // Reload the new group from the cache.
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
-
-    // Change the object's group.
-    Address groupAddr(objReg, JSObject::offsetOfGroup());
-    EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup);
-    masm.storePtr(scratch, groupAddr);
-
-    masm.bind(&noGroupChange);
+    if (obj_->is<PlainObject>()) {
+        // Try to change the object's group.
+        Label noGroupChange;
+
+        // Check if the cache has a new group to change to.
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
+        masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
+
+        // Check if the old group still has a newScript.
+        masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
+        masm.branchPtr(Assembler::Equal,
+                       Address(scratch, ObjectGroup::offsetOfAddendum()),
+                       ImmWord(0),
+                       &noGroupChange);
+
+        // Reload the new group from the cache.
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
+
+        // Change the object's group.
+        Address groupAddr(objReg, JSObject::offsetOfGroup());
+        EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup);
+        masm.storePtr(scratch, groupAddr);
+
+        masm.bind(&noGroupChange);
+    }
 
     Register holderReg;
     regs.add(R0);
     regs.takeUnchecked(objReg);
-    if (isFixedSlot_) {
-        holderReg = objReg;
+
+    if (obj_->is<UnboxedPlainObject>()) {
+        holderReg = regs.takeAny();
+        masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
+
+        // Write the expando object's new shape.
+        Address shapeAddr(holderReg, JSObject::offsetOfShape());
+        EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
+        masm.storePtr(scratch, shapeAddr);
+
+        if (!isFixedSlot_)
+            masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
     } else {
-        holderReg = regs.takeAny();
-        masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
+        // Write the object's new shape.
+        Address shapeAddr(objReg, JSObject::offsetOfShape());
+        EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
+        masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
+        masm.storePtr(scratch, shapeAddr);
+
+        if (isFixedSlot_) {
+            holderReg = objReg;
+        } else {
+            holderReg = regs.takeAny();
+            masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
+        }
     }
 
     // Perform the store.  No write barrier required since this is a new
     // initialization.
     masm.load32(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfOffset()), scratch);
     masm.storeValue(R1, BaseIndex(holderReg, scratch, TimesOne));
 
     if (holderReg != objReg)
@@ -9536,17 +9656,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
 
                     // This is temporary until the analysis is perfomed, so
                     // don't treat this as unoptimizable.
                     *handled = true;
                     return true;
                 }
             }
 
-            JSObject* thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
+            JSObject* thisObject = CreateThisForFunction(cx, fun, TenuredObject);
             if (!thisObject)
                 return false;
 
             if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
                 templateObject = thisObject;
         }
 
         JitSpew(JitSpew_BaselineIC,
@@ -12077,17 +12197,17 @@ ICSetProp_Native::ICSetProp_Native(JitCo
 
 ICSetProp_Native*
 ICSetProp_Native::Compiler::getStub(ICStubSpace* space)
 {
     RootedObjectGroup group(cx, obj_->getGroup(cx));
     if (!group)
         return nullptr;
 
-    RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
+    RootedShape shape(cx, LastPropertyForSetProp(obj_));
     ICSetProp_Native* stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
     if (!stub || !stub->initUpdatingChain(cx, space))
         return nullptr;
     return stub;
 }
 
 ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode* stubCode, ObjectGroup* group,
                                          size_t protoChainDepth,
@@ -12263,17 +12383,17 @@ ICCall_ScriptedFunCall::Clone(ICStubSpac
 ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitCode* stubCode,
                                                                  ICStub* firstMonitorStub,
                                                                  Shape* shape,
                                                                  Shape* expandoShape,
                                                                  JSObject* holder,
                                                                  Shape* holderShape,
                                                                  JSFunction* getter,
                                                                  uint32_t pcOffset)
-  : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(shape),
+  : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(nullptr, shape),
                         holder, holderShape, getter, pcOffset),
     expandoShape_(expandoShape)
 { }
 
 ICGetPropCallDOMProxyNativeCompiler::ICGetPropCallDOMProxyNativeCompiler(JSContext* cx,
                                                                          ICStub::Kind kind,
                                                                          ICStub* firstMonitorStub,
                                                                          Handle<ProxyObject*> proxy,
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -3868,49 +3868,37 @@ class ReceiverGuard
         {}
     };
 
     struct StackGuard
     {
         ObjectGroup* group;
         Shape* shape;
 
+        StackGuard()
+          : group(nullptr), shape(nullptr)
+        {}
+
         MOZ_IMPLICIT StackGuard(const ReceiverGuard& guard)
           : group(guard.group_), shape(guard.shape_)
         {}
 
         MOZ_IMPLICIT StackGuard(const RootedStackGuard& guard)
           : group(guard.group), shape(guard.shape)
         {}
 
-        explicit StackGuard(JSObject* obj)
-          : group(nullptr), shape(nullptr)
-        {
-            if (obj) {
-                if (obj->is<UnboxedPlainObject>()) {
-                    group = obj->group();
-                    if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
-                        shape = expando->lastProperty();
-                } else if (obj->is<TypedObject>()) {
-                    group = obj->group();
-                } else {
-                    shape = obj->maybeShape();
-                }
-            }
+        explicit StackGuard(JSObject* obj);
+        StackGuard(ObjectGroup* group, Shape* shape);
+
+        bool operator ==(const StackGuard& other) const {
+            return group == other.group && shape == other.shape;
         }
 
-        explicit StackGuard(Shape* shape)
-          : group(nullptr), shape(shape)
-        {}
-
-        Shape* ownShape() const {
-            // Get a shape belonging to the object itself, rather than an unboxed expando.
-            if (!group || !group->maybeUnboxedLayout())
-                return shape;
-            return nullptr;
+        bool operator !=(const StackGuard& other) const {
+            return !(*this == other);
         }
     };
 
     explicit ReceiverGuard(const StackGuard& guard)
       : group_(guard.group), shape_(guard.shape)
     {}
 
     bool matches(const StackGuard& guard) {
@@ -3926,42 +3914,26 @@ class ReceiverGuard
 
     Shape* shape() const {
         return shape_;
     }
     ObjectGroup* group() const {
         return group_;
     }
 
-    Shape* ownShape() const {
-        return StackGuard(*this).ownShape();
-    }
-
     static size_t offsetOfShape() {
         return offsetof(ReceiverGuard, shape_);
     }
     static size_t offsetOfGroup() {
         return offsetof(ReceiverGuard, group_);
     }
 
     // Bits to munge into IC compiler keys when that IC has a ReceiverGuard.
-    // This uses at two bits for data.
-    static int32_t keyBits(JSObject* obj) {
-        if (obj->is<UnboxedPlainObject>()) {
-            // Both the group and shape need to be guarded for unboxed objects.
-            return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
-        }
-        if (obj->is<TypedObject>()) {
-            // Only the group needs to be guarded for typed objects.
-            return 2;
-        }
-        // Other objects only need the shape to be guarded, except for ICs
-        // which always guard the group.
-        return 3;
-    }
+    // This uses at most two bits for data.
+    static int32_t keyBits(JSObject* obj);
 };
 
 // Base class for native GetProp stubs.
 class ICGetPropNativeStub : public ICMonitoredStub
 {
     // Object shape/group.
     ReceiverGuard receiverGuard_;
 
@@ -4802,17 +4774,19 @@ class ICSetProp_Native : public ICUpdate
 
     class Compiler : public ICStubCompiler {
         RootedObject obj_;
         bool isFixedSlot_;
         uint32_t offset_;
 
       protected:
         virtual int32_t getKey() const {
-            return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16);
+            return static_cast<int32_t>(kind) |
+                   (static_cast<int32_t>(isFixedSlot_) << 16) |
+                   (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 17);
         }
 
         bool generateStubCode(MacroAssembler& masm);
 
       public:
         Compiler(JSContext* cx, HandleObject obj, bool isFixedSlot, uint32_t offset)
           : ICStubCompiler(cx, ICStub::SetProp_Native),
             obj_(cx, obj),
@@ -4904,18 +4878,20 @@ class ICSetPropNativeAddCompiler : publi
     RootedShape oldShape_;
     RootedObjectGroup oldGroup_;
     size_t protoChainDepth_;
     bool isFixedSlot_;
     uint32_t offset_;
 
   protected:
     virtual int32_t getKey() const {
-        return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16) |
-               (static_cast<int32_t>(protoChainDepth_) << 20);
+        return static_cast<int32_t>(kind) |
+               (static_cast<int32_t>(isFixedSlot_) << 16) |
+               (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 17) |
+               (static_cast<int32_t>(protoChainDepth_) << 18);
     }
 
     bool generateStubCode(MacroAssembler& masm);
 
   public:
     ICSetPropNativeAddCompiler(JSContext* cx, HandleObject obj,
                                HandleShape oldShape, HandleObjectGroup oldGroup,
                                size_t protoChainDepth, bool isFixedSlot, uint32_t offset);
@@ -4928,17 +4904,21 @@ class ICSetPropNativeAddCompiler : publi
             return nullptr;
 
         // Only specify newGroup when the object's group changes due to the
         // object becoming fully initialized per the acquired properties
         // analysis.
         if (newGroup == oldGroup_)
             newGroup = nullptr;
 
-        RootedShape newShape(cx, obj_->as<NativeObject>().lastProperty());
+        RootedShape newShape(cx);
+        if (obj_->isNative())
+            newShape = obj_->as<NativeObject>().lastProperty();
+        else
+            newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
 
         return ICStub::New<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
                     space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
     }
 
     ICUpdatedStub* getStub(ICStubSpace* space);
 };
 
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -85,92 +85,80 @@ VectorAppendNoDuplicate(S& list, T value
 {
     for (size_t i = 0; i < list.length(); i++) {
         if (list[i] == value)
             return true;
     }
     return list.append(value);
 }
 
+static bool
+AddReceiver(const ReceiverGuard::StackGuard& receiver,
+            BaselineInspector::ReceiverVector& receivers,
+            BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
+{
+    if (receiver.group && receiver.group->maybeUnboxedLayout()) {
+        if (receiver.group->unboxedLayout().nativeGroup())
+            return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group);
+    }
+    return VectorAppendNoDuplicate(receivers, receiver);
+}
+
 bool
-BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc,
-                                          ShapeVector& nativeShapes,
-                                          ObjectGroupVector& unboxedGroups,
+BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
                                           ObjectGroupVector& convertUnboxedGroups)
 {
-    // Return lists of native shapes and unboxed objects seen by the baseline
-    // IC for the current op. Empty lists indicate no shapes/types are known,
-    // or there was an uncacheable access. convertUnboxedGroups is used for
-    // unboxed object groups which have been seen, but have had instances
-    // converted to native objects and should be eagerly converted by Ion.
-    MOZ_ASSERT(nativeShapes.empty());
-    MOZ_ASSERT(unboxedGroups.empty());
+    // Return a list of the receivers seen by the baseline IC for the current
+    // op. Empty lists indicate no receivers are known, or there was an
+    // uncacheable access. convertUnboxedGroups is used for unboxed object
+    // groups which have been seen, but have had instances converted to native
+    // objects and should be eagerly converted by Ion.
+    MOZ_ASSERT(receivers.empty());
     MOZ_ASSERT(convertUnboxedGroups.empty());
 
     if (!hasBaselineScript())
         return true;
 
     MOZ_ASSERT(isValidPC(pc));
     const ICEntry& entry = icEntryFromPC(pc);
 
     ICStub* stub = entry.firstStub();
     while (stub->next()) {
-        Shape* shape = nullptr;
-        ObjectGroup* group = nullptr;
+        ReceiverGuard::StackGuard receiver;
         if (stub->isGetProp_Native()) {
-            shape = stub->toGetProp_Native()->receiverGuard().ownShape();
+            receiver = stub->toGetProp_Native()->receiverGuard();
         } else if (stub->isSetProp_Native()) {
-            shape = stub->toSetProp_Native()->shape();
+            receiver = ReceiverGuard::StackGuard(stub->toSetProp_Native()->group(),
+                                                 stub->toSetProp_Native()->shape());
         } else if (stub->isGetProp_Unboxed()) {
-            group = stub->toGetProp_Unboxed()->group();
+            receiver = ReceiverGuard::StackGuard(stub->toGetProp_Unboxed()->group(), nullptr);
         } else if (stub->isSetProp_Unboxed()) {
-            group = stub->toSetProp_Unboxed()->group();
-        }
-
-        if (!shape && !group) {
-            nativeShapes.clear();
-            unboxedGroups.clear();
+            receiver = ReceiverGuard::StackGuard(stub->toSetProp_Unboxed()->group(), nullptr);
+        } else {
+            receivers.clear();
             return true;
         }
 
-        if (group && group->unboxedLayout().nativeGroup()) {
-            if (!VectorAppendNoDuplicate(convertUnboxedGroups, group))
-                return false;
-            shape = group->unboxedLayout().nativeShape();
-            group = nullptr;
-        }
-
-        if (shape) {
-            if (!VectorAppendNoDuplicate(nativeShapes, shape))
-                return false;
-        } else {
-            if (!VectorAppendNoDuplicate(unboxedGroups, group))
-                return false;
-        }
+        if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
+            return false;
 
         stub = stub->next();
     }
 
     if (stub->isGetProp_Fallback()) {
-        if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
-            nativeShapes.clear();
-            unboxedGroups.clear();
-        }
+        if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
+            receivers.clear();
     } else {
-        if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) {
-            nativeShapes.clear();
-            unboxedGroups.clear();
-        }
+        if (stub->toSetProp_Fallback()->hadUnoptimizableAccess())
+            receivers.clear();
     }
 
-    // Don't inline if there are more than 5 shapes/groups.
-    if (nativeShapes.length() + unboxedGroups.length() > 5) {
-        nativeShapes.clear();
-        unboxedGroups.clear();
-    }
+    // Don't inline if there are more than 5 receivers.
+    if (receivers.length() > 5)
+        receivers.clear();
 
     return true;
 }
 
 ICStub*
 BaselineInspector::monomorphicStub(jsbytecode* pc)
 {
     if (!hasBaselineScript())
@@ -584,66 +572,39 @@ GlobalShapeForGetPropFunction(ICStub* st
         if (Shape* shape = guard.shape()) {
             if (shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL)
                 return shape;
         }
     }
     return nullptr;
 }
 
-static bool
-AddReceiver(BaselineInspector::ShapeVector& nativeShapes,
-            BaselineInspector::ObjectGroupVector& unboxedGroups,
-            ReceiverGuard::StackGuard receiver)
-{
-    if (Shape* shape = receiver.ownShape())
-        return VectorAppendNoDuplicate(nativeShapes, shape);
-
-    // Only unboxed objects with no expandos are handled by the common
-    // getprop/setprop optimizations.
-    if (!receiver.shape)
-        return VectorAppendNoDuplicate(unboxedGroups, receiver.group);
-
-    return false;
-}
-
-static bool
-AddReceiverForGetPropFunction(BaselineInspector::ShapeVector& nativeShapes,
-                              BaselineInspector::ObjectGroupVector& unboxedGroups,
-                              ICGetPropCallGetter* stub)
-{
-    if (stub->isOwnGetter())
-        return true;
-
-    return AddReceiver(nativeShapes, unboxedGroups, stub->receiverGuard());
-}
-
 bool
 BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                          JSFunction** commonGetter, Shape** globalShape,
                                          bool* isOwnProperty,
-                                         ShapeVector& nativeShapes,
-                                         ObjectGroupVector& unboxedGroups)
+                                         ReceiverVector& receivers,
+                                         ObjectGroupVector& convertUnboxedGroups)
 {
     if (!hasBaselineScript())
         return false;
 
-    MOZ_ASSERT(nativeShapes.empty());
-    MOZ_ASSERT(unboxedGroups.empty());
+    MOZ_ASSERT(receivers.empty());
+    MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *holder = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isGetProp_CallScripted() ||
             stub->isGetProp_CallNative())
         {
             ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub);
             bool isOwn = nstub->isOwnGetter();
-            if (!AddReceiverForGetPropFunction(nativeShapes, unboxedGroups, nstub))
+            if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
                 return false;
 
             if (!*holder) {
                 *holder = nstub->holder();
                 *holderShape = nstub->holderShape();
                 *commonGetter = nstub->getter();
                 *globalShape = GlobalShapeForGetPropFunction(nstub);
                 *isOwnProperty = isOwn;
@@ -665,39 +626,39 @@ BaselineInspector::commonGetPropFunction
         } else {
             return false;
         }
     }
 
     if (!*holder)
         return false;
 
-    MOZ_ASSERT(*isOwnProperty == (nativeShapes.empty() && unboxedGroups.empty()));
+    MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty()));
     return true;
 }
 
 bool
 BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                          JSFunction** commonSetter, bool* isOwnProperty,
-                                         ShapeVector& nativeShapes,
-                                         ObjectGroupVector& unboxedGroups)
+                                         ReceiverVector& receivers,
+                                         ObjectGroupVector& convertUnboxedGroups)
 {
     if (!hasBaselineScript())
         return false;
 
-    MOZ_ASSERT(nativeShapes.empty());
-    MOZ_ASSERT(unboxedGroups.empty());
+    MOZ_ASSERT(receivers.empty());
+    MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *holder = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
             ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub);
-            if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard()))
+            if (!AddReceiver(nstub->guard(), receivers, convertUnboxedGroups))
                 return false;
 
             if (!*holder) {
                 *holder = nstub->holder();
                 *holderShape = nstub->holderShape();
                 *commonSetter = nstub->setter();
                 *isOwnProperty = false;
             } else if (nstub->holderShape() != *holderShape) {
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -87,21 +87,19 @@ class BaselineInspector
         }
         return ICInspectorType(this, pc, ent);
     }
 
     ICStub* monomorphicStub(jsbytecode* pc);
     bool dimorphicStub(jsbytecode* pc, ICStub** pfirst, ICStub** psecond);
 
   public:
-    typedef Vector<Shape*, 4, JitAllocPolicy> ShapeVector;
+    typedef Vector<ReceiverGuard::StackGuard, 4, JitAllocPolicy> ReceiverVector;
     typedef Vector<ObjectGroup*, 4, JitAllocPolicy> ObjectGroupVector;
-    bool maybeInfoForPropertyOp(jsbytecode* pc,
-                                ShapeVector& nativeShapes,
-                                ObjectGroupVector& unboxedGroups,
+    bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
                                 ObjectGroupVector& convertUnboxedGroups);
 
     SetElemICInspector setElemICInspector(jsbytecode* pc) {
         return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
     }
 
     MIRType expectedResultType(jsbytecode* pc);
     MCompare::CompareType expectedCompareType(jsbytecode* pc);
@@ -121,20 +119,20 @@ class BaselineInspector
 
     JSFunction* getSingleCallee(jsbytecode* pc);
 
     DeclEnvObject* templateDeclEnvObject();
     CallObject* templateCallObject();
 
     bool commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty,
-                               ShapeVector& nativeShapes, ObjectGroupVector& unboxedGroups);
+                               ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups);
     bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                JSFunction** commonSetter, bool* isOwnProperty,
-                               ShapeVector& nativeShapes, ObjectGroupVector& unboxedGroups);
+                               ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups);
 
     bool instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot, JSObject** prototypeObject);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BaselineInspector_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1945,17 +1945,17 @@ CodeGenerator::visitTableSwitchV(LTableS
 
 typedef JSObject* (*DeepCloneObjectLiteralFn)(JSContext*, HandleObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
 
 void
 CodeGenerator::visitCloneLiteral(LCloneLiteral* lir)
 {
-    pushArg(ImmWord(js::MaybeSingletonObject));
+    pushArg(ImmWord(TenuredObject));
     pushArg(ToRegister(lir->getObjectLiteral()));
     callVM(DeepCloneObjectLiteralInfo, lir);
 }
 
 void
 CodeGenerator::visitParameter(LParameter* lir)
 {
 }
@@ -2256,79 +2256,80 @@ CodeGenerator::visitStoreSlotV(LStoreSlo
     const ValueOperand value = ToValue(lir, LStoreSlotV::Value);
 
     if (lir->mir()->needsBarrier())
        emitPreBarrier(Address(base, offset));
 
     masm.storeValue(value, Address(base, offset));
 }
 
+static void
+GuardReceiver(MacroAssembler& masm, const ReceiverGuard::StackGuard& guard,
+              Register obj, Register scratch, Label* miss, bool checkNullExpando)
+{
+    if (guard.group) {
+        masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss);
+
+        Address expandoAddress(obj, UnboxedPlainObject::offsetOfExpando());
+        if (guard.shape) {
+            masm.loadPtr(expandoAddress, scratch);
+            masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), miss);
+            masm.branchTestObjShape(Assembler::NotEqual, scratch, guard.shape, miss);
+        } else if (checkNullExpando) {
+            masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), miss);
+        }
+    } else {
+        masm.branchTestObjShape(Assembler::NotEqual, obj, guard.shape, miss);
+    }
+}
+
 void
 CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Register scratch,
                                           const TypedOrValueRegister& output)
 {
     MGetPropertyPolymorphic* mir = ins->mirRaw()->toGetPropertyPolymorphic();
 
-    size_t total = mir->numUnboxedGroups() + mir->numShapes();
-    MOZ_ASSERT(total > 1);
-
-    bool groupInScratch = mir->numUnboxedGroups() > 1;
-    bool shapeInScratch = mir->numShapes() > 1;
-
     Label done;
 
-    for (size_t i = 0; i < total; i++) {
-        bool unboxedGroup = i < mir->numUnboxedGroups();
-
-        ImmGCPtr comparePtr = unboxedGroup
-                              ? ImmGCPtr(mir->unboxedGroup(i))
-                              : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups()));
-        Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape());
-
-        if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch))
-            masm.loadPtr(addr, scratch);
-
-        bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch;
+    for (size_t i = 0; i < mir->numReceivers(); i++) {
+        ReceiverGuard::StackGuard receiver = mir->receiver(i);
 
         Label next;
-        if (i == total - 1) {
-            if (inScratch)
-                bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot());
-            else
-                bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot());
-        } else {
-            if (inScratch)
-                masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next);
-            else
-                masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next);
-        }
-
-        if (unboxedGroup) {
-            const UnboxedLayout::Property* property =
-                mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name());
-            Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
-
-            masm.loadUnboxedProperty(propertyAddr, property->type, output);
-        } else {
-            Shape* shape = mir->shape(i - mir->numUnboxedGroups());
+        GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
+
+        if (receiver.shape) {
+            // If this is an unboxed expando access, GuardReceiver loaded the
+            // expando object into scratch.
+            Register target = receiver.group ? scratch : obj;
+
+            Shape* shape = mir->shape(i);
             if (shape->slot() < shape->numFixedSlots()) {
                 // Fixed slot.
-                masm.loadTypedOrValue(Address(obj, NativeObject::getFixedSlotOffset(shape->slot())),
+                masm.loadTypedOrValue(Address(target, NativeObject::getFixedSlotOffset(shape->slot())),
                                       output);
             } else {
                 // Dynamic slot.
                 uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value);
-                masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
+                masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch);
                 masm.loadTypedOrValue(Address(scratch, offset), output);
             }
+        } else {
+            const UnboxedLayout::Property* property =
+                receiver.group->unboxedLayout().lookup(mir->name());
+            Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
+
+            masm.loadUnboxedProperty(propertyAddr, property->type, output);
         }
 
-        if (i != total - 1)
+        if (i == mir->numReceivers() - 1) {
+            bailoutFrom(&next, ins->snapshot());
+        } else {
             masm.jump(&done);
-        masm.bind(&next);
+            masm.bind(&next);
+        }
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitGetPropertyPolymorphicV(LGetPropertyPolymorphicV* ins)
 {
@@ -2349,83 +2350,64 @@ CodeGenerator::visitGetPropertyPolymorph
 }
 
 void
 CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Register scratch,
                                           const ConstantOrRegister& value)
 {
     MSetPropertyPolymorphic* mir = ins->mirRaw()->toSetPropertyPolymorphic();
 
-    size_t total = mir->numUnboxedGroups() + mir->numShapes();
-    MOZ_ASSERT(total > 1);
-
-    bool groupInScratch = mir->numUnboxedGroups() > 1;
-    bool shapeInScratch = mir->numShapes() > 1;
-
     Label done;
-    for (size_t i = 0; i < total; i++) {
-        bool unboxedGroup = i < mir->numUnboxedGroups();
-
-        ImmGCPtr comparePtr = unboxedGroup
-                              ? ImmGCPtr(mir->unboxedGroup(i))
-                              : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups()));
-        Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape());
-
-        if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch))
-            masm.loadPtr(addr, scratch);
-
-        bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch;
+    for (size_t i = 0; i < mir->numReceivers(); i++) {
+        ReceiverGuard::StackGuard receiver = mir->receiver(i);
 
         Label next;
-        if (i == total - 1) {
-            if (inScratch)
-                bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot());
-            else
-                bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot());
+        GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
+
+        if (receiver.shape) {
+            // If this is an unboxed expando access, GuardReceiver loaded the
+            // expando object into scratch.
+            Register target = receiver.group ? scratch : obj;
+
+            Shape* shape = mir->shape(i);
+            if (shape->slot() < shape->numFixedSlots()) {
+                // Fixed slot.
+                Address addr(target, NativeObject::getFixedSlotOffset(shape->slot()));
+                if (mir->needsBarrier())
+                    emitPreBarrier(addr);
+                masm.storeConstantOrRegister(value, addr);
+            } else {
+                // Dynamic slot.
+                masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch);
+                Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value));
+                if (mir->needsBarrier())
+                    emitPreBarrier(addr);
+                masm.storeConstantOrRegister(value, addr);
+            }
         } else {
-            if (inScratch)
-                masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next);
-            else
-                masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next);
-        }
-
-        if (unboxedGroup) {
             const UnboxedLayout::Property* property =
-                mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name());
+                receiver.group->unboxedLayout().lookup(mir->name());
             Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
 
             if (property->type == JSVAL_TYPE_OBJECT)
                 masm.patchableCallPreBarrier(propertyAddr, MIRType_Object);
             else if (property->type == JSVAL_TYPE_STRING)
                 masm.patchableCallPreBarrier(propertyAddr, MIRType_String);
             else
                 MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(property->type));
 
             masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr);
+        }
+
+        if (i == mir->numReceivers() - 1) {
+            bailoutFrom(&next, ins->snapshot());
         } else {
-            Shape* shape = mir->shape(i - mir->numUnboxedGroups());
-            if (shape->slot() < shape->numFixedSlots()) {
-                // Fixed slot.
-                Address addr(obj, NativeObject::getFixedSlotOffset(shape->slot()));
-                if (mir->needsBarrier())
-                    emitPreBarrier(addr);
-                masm.storeConstantOrRegister(value, addr);
-            } else {
-                // Dynamic slot.
-                masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
-                Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value));
-                if (mir->needsBarrier())
-                    emitPreBarrier(addr);
-                masm.storeConstantOrRegister(value, addr);
-            }
+            masm.jump(&done);
+            masm.bind(&next);
         }
-
-        if (i != total - 1)
-            masm.jump(&done);
-        masm.bind(&next);
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins)
 {
@@ -2543,52 +2525,57 @@ CodeGenerator::visitGuardObjectIdentity(
 
 void
 CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir)
 {
     const MGuardReceiverPolymorphic* mir = lir->mir();
     Register obj = ToRegister(lir->object());
     Register temp = ToRegister(lir->temp());
 
-    MOZ_ASSERT(mir->numShapes() + mir->numUnboxedGroups() > 1);
-
     Label done;
 
-    if (mir->numShapes()) {
-        masm.loadObjShape(obj, temp);
-
-        for (size_t i = 0; i < mir->numShapes(); i++) {
-            Shape* shape = mir->getShape(i);
-            if (i == mir->numShapes() - 1 && !mir->numUnboxedGroups())
-                bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), lir->snapshot());
-            else
-                masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done);
-        }
-    }
-
-    if (mir->numUnboxedGroups()) {
-        // The guard requires that unboxed objects not have expandos.
-        bailoutCmpPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfShape()),
-                      ImmWord(0), lir->snapshot());
-
-        masm.loadObjGroup(obj, temp);
-
-        for (size_t i = 0; i < mir->numUnboxedGroups(); i++) {
-            ObjectGroup* group = mir->getUnboxedGroup(i);
-            if (i == mir->numUnboxedGroups() - 1)
-                bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(group), lir->snapshot());
-            else
-                masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(group), &done);
+    for (size_t i = 0; i < mir->numReceivers(); i++) {
+        const ReceiverGuard::StackGuard& receiver = mir->receiver(i);
+
+        Label next;
+        GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true);
+
+        if (i == mir->numReceivers() - 1) {
+            bailoutFrom(&next, lir->snapshot());
+        } else {
+            masm.jump(&done);
+            masm.bind(&next);
         }
     }
 
     masm.bind(&done);
 }
 
 void
+CodeGenerator::visitGuardUnboxedExpando(LGuardUnboxedExpando* lir)
+{
+    Label miss;
+
+    Register obj = ToRegister(lir->object());
+    masm.branchPtr(lir->mir()->requireExpando() ? Assembler::Equal : Assembler::NotEqual,
+                   Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0), &miss);
+
+    bailoutFrom(&miss, lir->snapshot());
+}
+
+void
+CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir)
+{
+    Register obj = ToRegister(lir->object());
+    Register result = ToRegister(lir->getDef(0));
+
+    masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result);
+}
+
+void
 CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
 {
     ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
     Register scratch = ToTempRegisterOrInvalid(lir->temp());
 
     Label miss;
     masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), lir->mir()->barrierKind(), scratch, &miss);
     bailoutFrom(&miss, lir->snapshot());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -118,16 +118,18 @@ class CodeGenerator : public CodeGenerat
     void visitStoreSlotT(LStoreSlotT* lir);
     void visitStoreSlotV(LStoreSlotV* lir);
     void visitElements(LElements* lir);
     void visitConvertElementsToDoubles(LConvertElementsToDoubles* lir);
     void visitMaybeToDoubleElement(LMaybeToDoubleElement* lir);
     void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir);
     void visitGuardObjectIdentity(LGuardObjectIdentity* guard);
     void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
+    void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
+    void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
     void visitTypeBarrierV(LTypeBarrierV* lir);
     void visitTypeBarrierO(LTypeBarrierO* lir);
     void visitMonitorTypes(LMonitorTypes* lir);
     void visitPostWriteBarrierO(LPostWriteBarrierO* lir);
     void visitPostWriteBarrierV(LPostWriteBarrierV* lir);
     void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool);
     void visitCallNative(LCallNative* call);
     void emitCallInvokeFunction(LInstruction* call, Register callereg,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10588,66 +10588,68 @@ IonBuilder::getPropTryUnboxed(bool* emit
 
     trackOptimizationSuccess();
     *emitted = true;
     return true;
 }
 
 MDefinition*
 IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
-                const BaselineInspector::ShapeVector& receiverShapes,
-                const BaselineInspector::ObjectGroupVector& receiverUnboxedGroups,
+                const BaselineInspector::ReceiverVector& receivers,
+                const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
                 bool isOwnProperty)
 {
     MOZ_ASSERT(holder);
     MOZ_ASSERT(holderShape);
 
+    obj = convertUnboxedObjects(obj, convertUnboxedGroups);
+
     if (isOwnProperty) {
-        MOZ_ASSERT(receiverShapes.empty());
+        MOZ_ASSERT(receivers.empty());
         return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
     }
 
     MDefinition* holderDef = constantMaybeNursery(holder);
     addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
 
-    return addGuardReceiverPolymorphic(obj, receiverShapes, receiverUnboxedGroups);
+    return addGuardReceiverPolymorphic(obj, receivers);
 }
 
 bool
 IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
                                    TemporaryTypeSet* types)
 {
     MOZ_ASSERT(*emitted == false);
 
     Shape* lastProperty = nullptr;
     JSFunction* commonGetter = nullptr;
     Shape* globalShape = nullptr;
     JSObject* foundProto = nullptr;
     bool isOwnProperty = false;
-    BaselineInspector::ShapeVector receiverShapes(alloc());
-    BaselineInspector::ObjectGroupVector receiverUnboxedGroups(alloc());
+    BaselineInspector::ReceiverVector receivers(alloc());
+    BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
     if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter,
                                           &globalShape, &isOwnProperty,
-                                          receiverShapes, receiverUnboxedGroups))
+                                          receivers, convertUnboxedGroups))
     {
         return true;
     }
 
     TemporaryTypeSet* objTypes = obj->resultTypeSet();
     MDefinition* guard = nullptr;
     MDefinition* globalGuard = nullptr;
     bool canUseTIForGetter =
         testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
                                foundProto, lastProperty, commonGetter, &guard,
                                globalShape, &globalGuard);
     if (!canUseTIForGetter) {
         // If type information is bad, we can still optimize the getter if we
         // shape guard.
         obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
-                                            receiverShapes, receiverUnboxedGroups,
+                                            receivers, convertUnboxedGroups,
                                             isOwnProperty);
         if (!obj)
             return false;
     }
 
     bool isDOM = objTypes && objTypes->isDOMClass(constraints());
 
     if (isDOM && testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter)) {
@@ -10749,159 +10751,168 @@ IonBuilder::getPropTryCommonGetter(bool*
     if (!commonGetter->isInterpreted())
         trackOptimizationSuccess();
 
     *emitted = true;
     return true;
 }
 
 bool
-IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ShapeVector& nativeShapes,
-                                      const BaselineInspector::ObjectGroupVector& unboxedGroups)
-{
-    if (nativeShapes.empty() && unboxedGroups.empty()) {
+IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers)
+{
+    if (receivers.empty()) {
         trackOptimizationOutcome(TrackedOutcome::NoShapeInfo);
         return false;
     }
 
-    for (size_t i = 0; i < nativeShapes.length(); i++) {
+    for (size_t i = 0; i < receivers.length(); i++) {
         // We inline the property access as long as the shape is not in
         // dictionary mode. We cannot be sure that the shape is still a
         // lastProperty, and calling Shape::search() on dictionary mode
         // shapes that aren't lastProperty is invalid.
-        if (nativeShapes[i]->inDictionary()) {
+        if (receivers[i].shape && receivers[i].shape->inDictionary()) {
             trackOptimizationOutcome(TrackedOutcome::InDictionaryMode);
             return false;
         }
     }
 
     return true;
 }
 
-static bool
-GetPropertyShapes(jsid id, const BaselineInspector::ShapeVector& shapes,
-                  BaselineInspector::ShapeVector& propShapes, bool* sameSlot)
-{
-    MOZ_ASSERT(propShapes.empty());
-
-    if (!propShapes.reserve(shapes.length()))
-        return false;
-
-    *sameSlot = true;
-    for (size_t i = 0; i < shapes.length(); i++) {
-        Shape* objShape = shapes[i];
-        Shape* shape = objShape->searchLinear(id);
+static Shape*
+PropertyShapesHaveSameSlot(const BaselineInspector::ReceiverVector& receivers, jsid id)
+{
+    Shape* firstShape = nullptr;
+    for (size_t i = 0; i < receivers.length(); i++) {
+        if (receivers[i].group)
+            return nullptr;
+
+        Shape* shape = receivers[i].shape->searchLinear(id);
         MOZ_ASSERT(shape);
-        propShapes.infallibleAppend(shape);
-
-        if (i > 0) {
-            if (shape->slot() != propShapes[0]->slot() ||
-                shape->numFixedSlots() != propShapes[0]->numFixedSlots())
-            {
-                *sameSlot = false;
-            }
-        }
-    }
-
-    return true;
+
+        if (i == 0) {
+            firstShape = shape;
+        } else if (shape->slot() != firstShape->slot() ||
+                   shape->numFixedSlots() != firstShape->numFixedSlots())
+        {
+            return nullptr;
+        }
+    }
+
+    return firstShape;
 }
 
 bool
 IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
                                    BarrierKind barrier, TemporaryTypeSet* types)
 {
     MOZ_ASSERT(*emitted == false);
 
     if (obj->type() != MIRType_Object) {
         trackOptimizationOutcome(TrackedOutcome::NotObject);
         return true;
     }
 
-    BaselineInspector::ShapeVector nativeShapes(alloc());
-    BaselineInspector::ObjectGroupVector unboxedGroups(alloc()), convertUnboxedGroups(alloc());
-    if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedGroups, convertUnboxedGroups))
-        return false;
-
-    if (!canInlinePropertyOpShapes(nativeShapes, unboxedGroups))
+    BaselineInspector::ReceiverVector receivers(alloc());
+    BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
+    if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
+        return false;
+
+    if (!canInlinePropertyOpShapes(receivers))
         return true;
 
     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
 
     MIRType rvalType = types->getKnownMIRType();
     if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
         rvalType = MIRType_Value;
 
-    if (nativeShapes.length() == 1 && unboxedGroups.empty()) {
-        // In the monomorphic case, use separate ShapeGuard and LoadSlot
-        // instructions.
-        spew("Inlining monomorphic GETPROP");
-
-        Shape* objShape = nativeShapes[0];
-        obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard);
-
-        Shape* shape = objShape->searchLinear(NameToId(name));
-        MOZ_ASSERT(shape);
-
-        if (!loadSlot(obj, shape, rvalType, barrier, types))
-            return false;
-
-        trackOptimizationOutcome(TrackedOutcome::Monomorphic);
-        *emitted = true;
-        return true;
-    }
-
-    if (nativeShapes.empty() && unboxedGroups.length() == 1) {
-        spew("Inlining monomorphic unboxed GETPROP");
-
-        ObjectGroup* group = unboxedGroups[0];
-        obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
-
-        const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
+    if (receivers.length() == 1) {
+        if (!receivers[0].group) {
+            // Monomorphic load from a native object.
+            spew("Inlining monomorphic native GETPROP");
+
+            obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
+
+            Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
+            MOZ_ASSERT(shape);
+
+            if (!loadSlot(obj, shape, rvalType, barrier, types))
+                return false;
+
+            trackOptimizationOutcome(TrackedOutcome::Monomorphic);
+            *emitted = true;
+            return true;
+        }
+
+        if (receivers[0].shape) {
+            // Monomorphic load from an unboxed object expando.
+            spew("Inlining monomorphic unboxed expando GETPROP");
+
+            obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
+            obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
+
+            MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
+            current->add(expando);
+
+            expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
+
+            Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
+            MOZ_ASSERT(shape);
+
+            if (!loadSlot(expando, shape, rvalType, barrier, types))
+                return false;
+
+            trackOptimizationOutcome(TrackedOutcome::Monomorphic);
+            *emitted = true;
+            return true;
+        }
+
+        // Monomorphic load from an unboxed object.
+        obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
+
+        const UnboxedLayout::Property* property = receivers[0].group->unboxedLayout().lookup(name);
         MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
         current->push(load);
 
         if (!pushTypeBarrier(load, types, barrier))
             return false;
 
+        trackOptimizationOutcome(TrackedOutcome::Monomorphic);
         *emitted = true;
         return true;
     }
 
-    MOZ_ASSERT(nativeShapes.length() + unboxedGroups.length() > 1);
+    MOZ_ASSERT(receivers.length() > 1);
     spew("Inlining polymorphic GETPROP");
 
-    BaselineInspector::ShapeVector propShapes(alloc());
-    bool sameSlot;
-    if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot))
-        return false;
-
-    if (sameSlot && unboxedGroups.empty()) {
-        obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups);
+    if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
+        obj = addGuardReceiverPolymorphic(obj, receivers);
         if (!obj)
             return false;
 
-        if (!loadSlot(obj, propShapes[0], rvalType, barrier, types))
+        if (!loadSlot(obj, propShape, rvalType, barrier, types))
             return false;
 
         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
         *emitted = true;
         return true;
     }
 
     MGetPropertyPolymorphic* load = MGetPropertyPolymorphic::New(alloc(), obj, name);
     current->add(load);
     current->push(load);
 
-    for (size_t i = 0; i < nativeShapes.length(); i++) {
-        if (!load->addShape(nativeShapes[i], propShapes[i]))
-            return false;
-    }
-
-    for (size_t i = 0; i < unboxedGroups.length(); i++) {
-        if (!load->addUnboxedGroup(unboxedGroups[i]))
+    for (size_t i = 0; i < receivers.length(); i++) {
+        Shape* propShape = nullptr;
+        if (receivers[i].shape) {
+            propShape = receivers[i].shape->searchLinear(NameToId(name));
+            MOZ_ASSERT(propShape);
+        }
+        if (!load->addReceiver(receivers[i], propShape))
             return false;
     }
 
     if (failedShapeGuard_)
         load->setNotMovable();
 
     load->setResultType(rvalType);
     if (!pushTypeBarrier(load, types, barrier))
@@ -11125,36 +11136,36 @@ IonBuilder::setPropTryCommonSetter(bool*
                                    PropertyName* name, MDefinition* value)
 {
     MOZ_ASSERT(*emitted == false);
 
     Shape* lastProperty = nullptr;
     JSFunction* commonSetter = nullptr;
     JSObject* foundProto = nullptr;
     bool isOwnProperty;
-    BaselineInspector::ShapeVector receiverShapes(alloc());
-    BaselineInspector::ObjectGroupVector receiverUnboxedGroups(alloc());
+    BaselineInspector::ReceiverVector receivers(alloc());
+    BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
     if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter,
                                           &isOwnProperty,
-                                          receiverShapes, receiverUnboxedGroups))
+                                          receivers, convertUnboxedGroups))
     {
         trackOptimizationOutcome(TrackedOutcome::NoProtoFound);
         return true;
     }
 
     TemporaryTypeSet* objTypes = obj->resultTypeSet();
     MDefinition* guard = nullptr;
     bool canUseTIForSetter =
         testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
                                foundProto, lastProperty, commonSetter, &guard);
     if (!canUseTIForSetter) {
         // If type information is bad, we can still optimize the setter if we
         // shape guard.
         obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
-                                            receiverShapes, receiverUnboxedGroups,
+                                            receivers, convertUnboxedGroups,
                                             isOwnProperty);
         if (!obj)
             return false;
     }
 
     // Emit common setter.
 
     // Setters can be called even if the property write needs a type
@@ -11493,99 +11504,113 @@ IonBuilder::setPropTryInlineAccess(bool*
 {
     MOZ_ASSERT(*emitted == false);
 
     if (barrier) {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return true;
     }
 
-    BaselineInspector::ShapeVector nativeShapes(alloc());
-    BaselineInspector::ObjectGroupVector unboxedGroups(alloc()), convertUnboxedGroups(alloc());
-    if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedGroups, convertUnboxedGroups))
-        return false;
-
-    if (!canInlinePropertyOpShapes(nativeShapes, unboxedGroups))
+    BaselineInspector::ReceiverVector receivers(alloc());
+    BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
+    if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
+        return false;
+
+    if (!canInlinePropertyOpShapes(receivers))
         return true;
 
     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
 
-    if (nativeShapes.length() == 1 && unboxedGroups.empty()) {
-        spew("Inlining monomorphic SETPROP");
-
-        // The Baseline IC was monomorphic, so we inline the property access as
-        // long as the shape is not in dictionary mode. We cannot be sure
-        // that the shape is still a lastProperty, and calling Shape::search
-        // on dictionary mode shapes that aren't lastProperty is invalid.
-        Shape* objShape = nativeShapes[0];
-        obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard);
-
-        Shape* shape = objShape->searchLinear(NameToId(name));
-        MOZ_ASSERT(shape);
-
-        bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
-        if (!storeSlot(obj, shape, value, needsBarrier))
-            return false;
-
-        trackOptimizationOutcome(TrackedOutcome::Monomorphic);
-        *emitted = true;
-        return true;
-    }
-
-    if (nativeShapes.empty() && unboxedGroups.length() == 1) {
+    if (receivers.length() == 1) {
+        if (!receivers[0].group) {
+            // Monomorphic store to a native object.
+            spew("Inlining monomorphic native SETPROP");
+
+            obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
+
+            Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
+            MOZ_ASSERT(shape);
+
+            bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
+            if (!storeSlot(obj, shape, value, needsBarrier))
+                return false;
+
+            trackOptimizationOutcome(TrackedOutcome::Monomorphic);
+            *emitted = true;
+            return true;
+        }
+
+        if (receivers[0].shape) {
+            // Monomorphic store to an unboxed object expando.
+            spew("Inlining monomorphic unboxed expando SETPROP");
+
+            obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
+            obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
+
+            MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
+            current->add(expando);
+
+            expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
+
+            Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
+            MOZ_ASSERT(shape);
+
+            bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
+            if (!storeSlot(expando, shape, value, needsBarrier))
+                return false;
+
+            trackOptimizationOutcome(TrackedOutcome::Monomorphic);
+            *emitted = true;
+            return true;
+        }
+
+        // Monomorphic store to an unboxed object.
         spew("Inlining monomorphic unboxed SETPROP");
 
-        ObjectGroup* group = unboxedGroups[0];
+        ObjectGroup* group = receivers[0].group;
         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
 
         const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
         storeUnboxedProperty(obj, property->offset, property->type, value);
 
         current->push(value);
 
+        trackOptimizationOutcome(TrackedOutcome::Monomorphic);
         *emitted = true;
         return true;
     }
 
-    MOZ_ASSERT(nativeShapes.length() + unboxedGroups.length() > 1);
+    MOZ_ASSERT(receivers.length() > 1);
     spew("Inlining polymorphic SETPROP");
 
-    BaselineInspector::ShapeVector propShapes(alloc());
-    bool sameSlot;
-    if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot))
-        return false;
-
-    if (sameSlot && unboxedGroups.empty()) {
-        obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups);
+    if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
+        obj = addGuardReceiverPolymorphic(obj, receivers);
         if (!obj)
             return false;
 
         bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
-        if (!storeSlot(obj, propShapes[0], value, needsBarrier))
+        if (!storeSlot(obj, propShape, value, needsBarrier))
             return false;
 
         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
         *emitted = true;
         return true;
     }
 
     MSetPropertyPolymorphic* ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name);
     current->add(ins);
     current->push(value);
 
-    for (size_t i = 0; i < nativeShapes.length(); i++) {
-        Shape* objShape = nativeShapes[i];
-        Shape* shape =  objShape->searchLinear(NameToId(name));
-        MOZ_ASSERT(shape);
-        if (!ins->addShape(objShape, shape))
-            return false;
-    }
-
-    for (size_t i = 0; i < unboxedGroups.length(); i++) {
-        if (!ins->addUnboxedGroup(unboxedGroups[i]))
+    for (size_t i = 0; i < receivers.length(); i++) {
+        Shape* propShape = nullptr;
+        if (receivers[i].shape) {
+            propShape = receivers[i].shape->searchLinear(NameToId(name));
+            MOZ_ASSERT(propShape);
+        }
+        if (!ins->addReceiver(receivers[i], propShape))
             return false;
     }
 
     if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
         ins->setNeedsBarrier();
 
     if (!resumeAfter(ins))
         return false;
@@ -12461,65 +12486,74 @@ IonBuilder::addShapeGuard(MDefinition* o
     // If a shape guard failed in the past, don't optimize shape guard.
     if (failedShapeGuard_)
         guard->setNotMovable();
 
     return guard;
 }
 
 MInstruction*
-IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind,
-                          bool checkUnboxedExpando)
+IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind)
 {
     MGuardObjectGroup* guard = MGuardObjectGroup::New(alloc(), obj, group,
-                                                      /* bailOnEquality = */ false,
-                                                      bailoutKind, checkUnboxedExpando);
+                                                      /* bailOnEquality = */ false, bailoutKind);
     current->add(guard);
 
     // If a shape guard failed in the past, don't optimize group guards.
     if (failedShapeGuard_)
         guard->setNotMovable();
 
     LifoAlloc* lifoAlloc = alloc().lifoAlloc();
     guard->setResultTypeSet(lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc,
                                                             TypeSet::ObjectType(group)));
 
     return guard;
 }
 
 MInstruction*
+IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind)
+{
+    MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind);
+    current->add(guard);
+
+    // If a shape guard failed in the past, don't optimize group guards.
+    if (failedShapeGuard_)
+        guard->setNotMovable();
+
+    return guard;
+}
+
+MInstruction*
 IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj,
-                                        const BaselineInspector::ShapeVector& shapes,
-                                        const BaselineInspector::ObjectGroupVector& unboxedGroups)
-{
-    if (shapes.length() == 1 && unboxedGroups.empty())
-        return addShapeGuard(obj, shapes[0], Bailout_ShapeGuard);
-
-    if (shapes.empty() && unboxedGroups.length() == 1) {
-        // The guard requires that unboxed objects not have expando objects.
-        // An inline cache will be used in these cases.
-        return addGroupGuard(obj, unboxedGroups[0], Bailout_ShapeGuard,
-                             /* checkUnboxedExpando = */ true);
-    }
-
-    MOZ_ASSERT(shapes.length() + unboxedGroups.length() > 1);
+                                        const BaselineInspector::Receiv