Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 18 Jul 2014 16:08:36 -0400
changeset 216923 85d5040f1ad2bccff75bfeaf6912791bfca527f0
parent 216922 a1778d8e2e382f970154fe4bb81ffaf6c3ed7cf9 (current diff)
parent 216855 50a8424cf1b8a579ba486eecc109459377a59ab3 (diff)
child 216924 ed168e7e8b0c9b0ece0cc3e36c11d04f85f33e73
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.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
--- a/b2g/components/test/mochitest/mochitest.ini
+++ b/b2g/components/test/mochitest/mochitest.ini
@@ -1,14 +1,14 @@
 [DEFAULT]
 run-if = toolkit == "gonk"
 support-files =
   permission_handler_chrome.js
   SandboxPromptTest.html
   filepicker_path_handler_chrome.js
   systemapp_helper.js
 
-[test_sandbox_permission.html]
 [test_filepicker_path.html]
 [test_permission_deny.html]
 [test_permission_gum_remember.html]
-skip-if = (toolkit == 'gonk' && debug) # Bug 1019572 - debug-only timeout
+skip-if = true # Bug 1019572 - frequent timeouts
+[test_sandbox_permission.html]
 [test_systemapp.html]
--- 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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <!-- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <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"/>
--- 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="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
--- 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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <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"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "72a7f5cea568a34d5aa9e221a4cc546de856cace", 
+    "revision": "ced3062ac363c95da1cd3a55f8c6e172490e42d5", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <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"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="48fe31ffcd3b9eca4eeb13cd0a73c1a28b45b295"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7104d2996b9ad65d9fff57ada454699d3348f0a5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1521,16 +1521,18 @@ pref("loop.enabled", true);
 #else
 pref("loop.enabled", false);
 #endif
 
 pref("loop.server", "https://loop.services.mozilla.com");
 pref("loop.seenToS", "unseen");
 pref("loop.do_not_disturb", false);
 pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg");
+pref("loop.retry_delay.start", 60000);
+pref("loop.retry_delay.limit", 300000);
 
 // serverURL to be assigned by services team
 pref("services.push.serverURL", "wss://push.services.mozilla.com/");
 
 pref("social.sidebar.unload_timeout_ms", 10000);
 
 pref("dom.identity.enabled", false);
 
--- a/browser/base/content/browser-webrtcUI.js
+++ b/browser/base/content/browser-webrtcUI.js
@@ -54,12 +54,17 @@ let WebrtcIndicator = {
 
     let browserWindow = streamData.browser.ownerDocument.defaultView;
     if (streamData.tab) {
       browserWindow.gBrowser.selectedTab = streamData.tab;
     } else {
       streamData.browser.focus();
     }
     browserWindow.focus();
-    PopupNotifications.getNotification("webRTC-sharingDevices",
-                                       streamData.browser).reshow();
+    let notif = PopupNotifications.getNotification("webRTC-sharingDevices",
+                                                   streamData.browser);
+    if (!notif) {
+      notif = PopupNotifications.getNotification("webRTC-sharingScreen",
+                                                 streamData.browser);
+    }
+    notif.reshow();
   }
 }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -724,16 +724,18 @@
                 <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="plugin-install-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="mixed-content-blocked-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon" role="button"/>
+                <image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon" role="button"/>
+                <image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="translate-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="translated-notification-icon" class="notification-anchor-icon" role="button"/>
               </box>
               <!-- Use onclick instead of normal popup= syntax since the popup
                    code fires onmousedown, and hence eats our favicon drag events.
                    We only add the identity-box button to the tab order when the location bar
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -13,16 +13,29 @@
         <separator class="thin"/>
         <label value="&getUserMedia.selectCamera.label;"
                accesskey="&getUserMedia.selectCamera.accesskey;"
                control="webRTC-selectCamera-menulist"/>
         <menulist id="webRTC-selectCamera-menulist">
           <menupopup id="webRTC-selectCamera-menupopup"/>
         </menulist>
       </popupnotificationcontent>
+
+      <popupnotificationcontent id="webRTC-selectWindowOrScreen" orient="vertical">
+        <separator class="thin"/>
+        <label value="&getUserMedia.selectWindowOrScreen.label;"
+               accesskey="&getUserMedia.selectWindowOrScreen.accesskey;"
+               control="webRTC-selectWindow-menulist"/>
+        <menulist id="webRTC-selectWindow-menulist"
+                  oncommand="WebrtcIndicator.UIModule.updateMainActionLabel(this);">
+          <menupopup id="webRTC-selectWindow-menupopup"/>
+        </menulist>
+        <description id="webRTC-all-windows-shared" hidden="true">&getUserMedia.allWindowsShared.message;</description>
+      </popupnotificationcontent>
+
       <popupnotificationcontent id="webRTC-selectMicrophone" orient="vertical">
         <separator class="thin"/>
         <label value="&getUserMedia.selectMicrophone.label;"
                accesskey="&getUserMedia.selectMicrophone.accesskey;"
                control="webRTC-selectMicrophone-menulist"/>
         <menulist id="webRTC-selectMicrophone-menulist">
           <menupopup id="webRTC-selectMicrophone-menupopup"/>
         </menulist>
--- a/browser/components/loop/MozLoopPushHandler.jsm
+++ b/browser/components/loop/MozLoopPushHandler.jsm
@@ -3,36 +3,56 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Timer.jsm");
 
 this.EXPORTED_SYMBOLS = ["MozLoopPushHandler"];
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 /**
  * We don't have push notifications on desktop currently, so this is a
  * workaround to get them going for us.
- *
- * XXX Handle auto-reconnections if connection fails for whatever reason
- * (bug 1013248).
  */
 let MozLoopPushHandler = {
   // This is the uri of the push server.
   pushServerUri: Services.prefs.getCharPref("services.push.serverURL"),
   // This is the channel id we're using for notifications
   channelID: "8b1081ce-9b35-42b5-b8f5-3ff8cb813a50",
+  // This is the UserAgent UUID assigned by the PushServer
+  uaID: undefined,
   // Stores the push url if we're registered and we have one.
   pushUrl: undefined,
+  // Set to true once the channelID has been registered with the PushServer.
+  registered: false,
+
+  _minRetryDelay_ms: (() => {
+    try {
+      return Services.prefs.getIntPref("loop.retry_delay.start")
+    }
+    catch (e) {
+      return 60000 // 1 minute
+    }
+  })(),
+
+  _maxRetryDelay_ms: (() => {
+    try {
+      return Services.prefs.getIntPref("loop.retry_delay.limit")
+    }
+    catch (e) {
+      return 300000 // 5 minutes
+    }
+  })(),
 
    /**
     * Starts a connection to the push socket server. On
     * connection, it will automatically say hello and register the channel
     * id with the server.
     *
     * Register callback parameters:
     * - {String|null} err: Encountered error, if any
@@ -46,109 +66,196 @@ let MozLoopPushHandler = {
     *                     registered.
     * @param {Function} notificationCallback Callback to be called when a
     *                     push notification is received (may be called multiple
     *                     times).
     * @param {Object} mockPushHandler Optional, test-only object, to allow
     *                                 the websocket to be mocked for tests.
     */
   initialize: function(registerCallback, notificationCallback, mockPushHandler) {
-    if (Services.io.offline) {
-      registerCallback("offline");
-      return;
+    if (mockPushHandler) {
+      this._mockPushHandler = mockPushHandler;
     }
 
     this._registerCallback = registerCallback;
     this._notificationCallback = notificationCallback;
-
-    if (mockPushHandler) {
-      // For tests, use the mock instance.
-      this._websocket = mockPushHandler;
-    } else {
-      this._websocket = Cc["@mozilla.org/network/protocol;1?name=wss"]
-        .createInstance(Ci.nsIWebSocketChannel);
-    }
-    this._websocket.protocol = "push-notification";
-
-    let pushURI = Services.io.newURI(this.pushServerUri, null, null);
-    this._websocket.asyncOpen(pushURI, this.pushServerUri, this, null);
+    this._openSocket();
   },
 
   /**
    * Listener method, handles the start of the websocket stream.
    * Sends a hello message to the server.
    *
    * @param {nsISupports} aContext Not used
    */
   onStart: function() {
-    let helloMsg = { messageType: "hello", uaid: "", channelIDs: [] };
-    this._websocket.sendMsg(JSON.stringify(helloMsg));
+    this._retryEnd();
+    // If a uaID has already been assigned, assume this is a re-connect
+    // and send the uaID in order to re-synch with the
+    // PushServer. If a registration has been completed, send the channelID.
+    let helloMsg = { messageType: "hello",
+		     uaid: this.uaID,
+		     channelIDs: this.registered ? [this.channelID] :[] };
+    this._retryOperation(() => this.onStart(), this._maxRetryDelay_ms);
+    try { // in case websocket has closed before this handler is run
+      this._websocket.sendMsg(JSON.stringify(helloMsg));
+    }
+    catch (e) {console.warn("MozLoopPushHandler::onStart websocket.sendMsg() failure");}
   },
 
   /**
    * Listener method, called when the websocket is closed.
    *
    * @param {nsISupports} aContext Not used
    * @param {nsresult} aStatusCode Reason for stopping (NS_OK = successful)
    */
   onStop: function(aContext, aStatusCode) {
-    // XXX We really should be handling auto-reconnect here, this will be
-    // implemented in bug 994151. For now, just log a warning, so that a
-    // developer can find out it has happened and not get too confused.
     Cu.reportError("Loop Push server web socket closed! Code: " + aStatusCode);
-    this.pushUrl = undefined;
+    this._retryOperation(() => this._openSocket());
   },
 
   /**
    * Listener method, called when the websocket is closed by the server.
    * If there are errors, onStop may be called without ever calling this
    * method.
    *
    * @param {nsISupports} aContext Not used
    * @param {integer} aCode the websocket closing handshake close code
    * @param {String} aReason the websocket closing handshake close reason
    */
   onServerClose: function(aContext, aCode) {
-    // XXX We really should be handling auto-reconnect here, this will be
-    // implemented in bug 994151. For now, just log a warning, so that a
-    // developer can find out it has happened and not get too confused.
     Cu.reportError("Loop Push server web socket closed (server)! Code: " + aCode);
-    this.pushUrl = undefined;
+    this._retryOperation(() => this._openSocket());
   },
 
   /**
    * Listener method, called when the websocket receives a message.
    *
    * @param {nsISupports} aContext Not used
    * @param {String} aMsg The message data
    */
   onMessageAvailable: function(aContext, aMsg) {
     let msg = JSON.parse(aMsg);
 
     switch(msg.messageType) {
       case "hello":
-        this._registerChannel();
+        this._retryEnd();
+	if (this.uaID !== msg.uaid) {
+	  this.uaID = msg.uaid;
+          this._registerChannel();
+	}
         break;
+
       case "register":
-        this.pushUrl = msg.pushEndpoint;
-        this._registerCallback(null, this.pushUrl);
+        this._onRegister(msg);
         break;
+
       case "notification":
-        msg.updates.forEach(function(update) {
+        msg.updates.forEach((update) => {
           if (update.channelID === this.channelID) {
             this._notificationCallback(update.version);
           }
-        }.bind(this));
+        });
         break;
     }
   },
 
   /**
+   * Handles the PushServer registration response.
+   *
+   * @param {} msg PushServer to UserAgent registration response (parsed from JSON).
+   */
+  _onRegister: function(msg) {
+    switch (msg.status) {
+      case 200:
+        this._retryEnd(); // reset retry mechanism
+	this.registered = true;
+        if (this.pushUrl !== msg.pushEndpoint) {
+          this.pushUrl = msg.pushEndpoint;
+          this._registerCallback(null, this.pushUrl);
+        }
+        break;
+
+      case 500:
+        // retry the registration request after a suitable delay
+        this._retryOperation(() => this._registerChannel());
+        break;
+
+      case 409:
+        this._registerCallback("error: PushServer ChannelID already in use");
+	break;
+
+      default:
+        this._registerCallback("error: PushServer registration failure, status = " + msg.status);
+	break;
+    }
+  },
+
+  /**
+   * Attempts to open a websocket.
+   *
+   * A new websocket interface is used each time. If an onStop callback
+   * was received, calling asyncOpen() on the same interface will
+   * trigger a "alreay open socket" exception even though the channel
+   * is logically closed.
+   */
+  _openSocket: function() {
+    if (this._mockPushHandler) {
+      // For tests, use the mock instance.
+      this._websocket = this._mockPushHandler;
+    } else if (!Services.io.offline) {
+      this._websocket = Cc["@mozilla.org/network/protocol;1?name=wss"]
+                        .createInstance(Ci.nsIWebSocketChannel);
+    } else {
+      this._registerCallback("offline");
+      console.warn("MozLoopPushHandler - IO offline");
+      return;
+    }
+
+    this._websocket.protocol = "push-notification";
+    let uri = Services.io.newURI(this.pushServerUri, null, null);
+    this._websocket.asyncOpen(uri, this.pushServerUri, this, null);
+  },
+
+  /**
    * Handles registering a service
    */
   _registerChannel: function() {
-    this._websocket.sendMsg(JSON.stringify({
-      messageType: "register",
-      channelID: this.channelID
-    }));
+    this.registered = false;
+    try { // in case websocket has closed
+      this._websocket.sendMsg(JSON.stringify({messageType: "register",
+                                              channelID: this.channelID}));
+    }
+    catch (e) {console.warn("MozLoopPushHandler::_registerChannel websocket.sendMsg() failure");}
+  },
+
+  /**
+   * Method to handle retrying UserAgent to PushServer request following
+   * a retry back-off scheme managed by this function.
+   *
+   * @param {function} delayedOp Function to call after current delay is satisfied
+   *
+   * @param {number} [optional] retryDelay This parameter will be used as the initial delay
+   */
+  _retryOperation: function(delayedOp, retryDelay) {
+    if (!this._retryCount) {
+      this._retryDelay = retryDelay || this._minRetryDelay_ms;
+      this._retryCount = 1;
+    } else {
+      let nextDelay = this._retryDelay * 2;
+      this._retryDelay = nextDelay > this._maxRetryDelay_ms ? this._maxRetryDelay_ms : nextDelay;
+      this._retryCount += 1;
+    }
+    this._timeoutID = setTimeout(delayedOp, this._retryDelay);
+  },
+
+  /**
+   * Method used to reset the retry delay back-off logic.
+   *
+   */
+  _retryEnd: function() {
+    if (this._retryCount) {
+      clearTimeout(this._timeoutID);
+      this._retryCount = 0;
+    }
   }
 };
 
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/components/loop/test/xpcshell/head.js
@@ -13,16 +13,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
                                   "resource:///modules/loop/MozLoopPushHandler.jsm");
 
 const kMockWebSocketChannelName = "Mock WebSocket Channel";
 const kWebSocketChannelContractID = "@mozilla.org/network/protocol;1?name=wss";
 
 const kServerPushUrl = "http://localhost:3456";
+const kEndPointUrl = "http://example.com/fake";
+const kUAID = "f47ac11b-58ca-4372-9567-0e02b2c3d479";
 
 // Fake loop server
 var loopServer;
 
 function setupFakeLoopServer() {
   loopServer = new HttpServer();
   loopServer.start(-1);
 
@@ -62,17 +64,18 @@ let mockPushHandler = {
   }
 };
 
 /**
  * Mock nsIWebSocketChannel for tests. This mocks the WebSocketChannel, and
  * enables us to check parameters and return messages similar to the push
  * server.
  */
-let MockWebSocketChannel = function() {
+let MockWebSocketChannel = function(initRegStatus) {
+  this.initRegStatus = initRegStatus;
 };
 
 MockWebSocketChannel.prototype = {
   QueryInterface: XPCOMUtils.generateQI(Ci.nsIWebSocketChannel),
 
   /**
    * nsIWebSocketChannel implementations.
    * See nsIWebSocketChannel.idl for API details.
@@ -81,33 +84,51 @@ MockWebSocketChannel.prototype = {
     this.uri = aURI;
     this.origin = aOrigin;
     this.listener = aListener;
     this.context = aContext;
 
     this.listener.onStart(this.context);
   },
 
-  notify: function(version) {
-    this.listener.onMessageAvailable(this.context,
-      JSON.stringify({
-        messageType: "notification", updates: [{
-          channelID: "8b1081ce-9b35-42b5-b8f5-3ff8cb813a50",
-          version: version
-        }]
-    }));
-  },
-
   sendMsg: function(aMsg) {
     var message = JSON.parse(aMsg);
 
     switch(message.messageType) {
       case "hello":
         this.listener.onMessageAvailable(this.context,
-          JSON.stringify({messageType: "hello"}));
+          JSON.stringify({messageType: "hello",
+                          uaid: kUAID}));
         break;
       case "register":
+        this.channelID = message.channelID;
+        let statusCode = 200;
+        if (this.initRegStatus) {
+          statusCode = this.initRegStatus;
+          this.initRegStatus = 0;
+        }
         this.listener.onMessageAvailable(this.context,
-          JSON.stringify({messageType: "register", pushEndpoint: "http://example.com/fake"}));
+          JSON.stringify({messageType: "register",
+                          status: statusCode,
+                          channelID: this.channelID,
+                          pushEndpoint: kEndPointUrl}));
         break;
     }
-  }
+  },
+
+  notify: function(version) {
+    this.listener.onMessageAvailable(this.context,
+      JSON.stringify({
+        messageType: "notification", updates: [{
+          channelID: this.channelID,
+          version: version
+        }]
+    }));
+  },
+
+  stop: function (err) {
+    this.listener.onStop(this.context, err || -1);
+  },
+
+  serverClose: function (err) {
+    this.listener.onServerClose(this.context, err || -1);
+  },
 };
--- a/browser/components/loop/test/xpcshell/test_looppush_initialize.js
+++ b/browser/components/loop/test/xpcshell/test_looppush_initialize.js
@@ -1,36 +1,63 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
-
-add_test(function test_initalize_offline() {
-  Services.io.offline = true;
-
-  MozLoopPushHandler.initialize(function(err) {
-    Assert.equal(err, "offline", "Should error with 'offline' when offline");
+{
+  add_test(function test_initalize_offline() {
+    Services.io.offline = true;
 
-    Services.io.offline = false;
-    run_next_test();
+    MozLoopPushHandler.initialize(function(err) {
+      Assert.equal(err, "offline", "Should error with 'offline' when offline");
+
+      Services.io.offline = false;
+      run_next_test();
+    });
   });
-});
 
-add_test(function test_initalize_websocket() {
   let mockWebSocket = new MockWebSocketChannel();
 
-  MozLoopPushHandler.initialize(function(err) {
-    Assert.equal(err, null, "Should return null for success");
+  add_test(function test_initalize_websocket() {
+    MozLoopPushHandler.initialize(
+      function(err, url) {
+        Assert.equal(err, null, "Should return null for success");
+        Assert.equal(url, kEndPointUrl, "Should return push server application URL");
+        Assert.equal(mockWebSocket.uri.prePath, kServerPushUrl,
+                     "Should have the url from preferences");
+        Assert.equal(mockWebSocket.origin, kServerPushUrl,
+                     "Should have the origin url from preferences");
+        Assert.equal(mockWebSocket.protocol, "push-notification",
+                     "Should have the protocol set to push-notifications");
+        mockWebSocket.notify(15);
+      },
+      function(version) {
+        Assert.equal(version, 15, "Should have version number 15");
+        run_next_test();
+      }, 
+      mockWebSocket);
+  });
 
-    Assert.equal(mockWebSocket.uri.prePath, kServerPushUrl,
-                 "Should have the url from preferences");
-    Assert.equal(mockWebSocket.origin, kServerPushUrl,
-                 "Should have the origin url from preferences");
-    Assert.equal(mockWebSocket.protocol, "push-notification",
-                 "Should have the protocol set to push-notifications");
+  add_test(function test_reconnect_websocket() {
+    MozLoopPushHandler.uaID = undefined;
+    MozLoopPushHandler.pushUrl = undefined; //Do this to force a new registration callback.
+    mockWebSocket.stop();
+  });
+
+  add_test(function test_reopen_websocket() {
+    MozLoopPushHandler.uaID = undefined;
+    MozLoopPushHandler.pushUrl = undefined; //Do this to force a new registration callback.
+    mockWebSocket.serverClose();
+  });
+
+  add_test(function test_retry_registration() {
+    MozLoopPushHandler.uaID = undefined;
+    MozLoopPushHandler.pushUrl = undefined; //Do this to force a new registration callback.
+    mockWebSocket.initRegStatus = 500;
+    mockWebSocket.stop();
+  });
+
+  function run_test() {
+    Services.prefs.setCharPref("services.push.serverURL", kServerPushUrl);
+    Services.prefs.setIntPref("loop.retry_delay.start", 10); // 10 ms
+    Services.prefs.setIntPref("loop.retry_delay.limit", 20); // 20 ms
 
     run_next_test();
-  }, function() {}, mockWebSocket);
-});
-
-function run_test() {
-  Services.prefs.setCharPref("services.push.serverURL", kServerPushUrl);
-
-  run_next_test();
-};
+  };
+}
--- a/browser/components/shell/src/nsWindowsShellService.h
+++ b/browser/components/shell/src/nsWindowsShellService.h
@@ -11,19 +11,20 @@
 #include "nsIWindowsShellService.h"
 #include "nsITimer.h"
 
 #include <windows.h>
 #include <ole2.h>
 
 class nsWindowsShellService : public nsIWindowsShellService
 {
+  virtual ~nsWindowsShellService();
+
 public:
   nsWindowsShellService();
-  virtual ~nsWindowsShellService();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHELLSERVICE
   NS_DECL_NSIWINDOWSSHELLSERVICE
 
 protected:
   bool IsDefaultBrowserVista(bool aCheckAllTypes, bool* aIsDefaultBrowser);
   nsresult LaunchControlPanelDefaultPrograms();
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -704,18 +704,21 @@ just addresses the organization to follo
 
 <!ENTITY social.markpageMenu.accesskey "P">
 <!ENTITY social.markpageMenu.label "Save Page To…">
 <!ENTITY social.marklinkMenu.accesskey "L">
 <!ENTITY social.marklinkMenu.label "Save Link To…">
 
 <!ENTITY getUserMedia.selectCamera.label "Camera to share:">
 <!ENTITY getUserMedia.selectCamera.accesskey "C">
+<!ENTITY getUserMedia.selectWindowOrScreen.label "Window or screen to share:">
+<!ENTITY getUserMedia.selectWindowOrScreen.accesskey "W">
 <!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
 <!ENTITY getUserMedia.selectMicrophone.accesskey "M">
+<!ENTITY getUserMedia.allWindowsShared.message "All visible windows on your screen will be shared.">
 
 <!ENTITY webrtcIndicatorButton.label "Camera / Microphone Access">
 <!ENTITY webrtcIndicatorButton.tooltip "Display sites you are currently sharing your camera or microphone with">
 
 <!ENTITY loopCallButton.tooltip "Invite someone to talk">
 
 <!ENTITY mixedContentBlocked.moreinfo "Most websites will still work properly even when this content is blocked.">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -490,37 +490,49 @@ identity.newIdentity.description = Enter
 identity.next.label = Next
 identity.next.accessKey = n
 # LOCALIZATION NOTE: shown in the popup notification when a user successfully logs into a website
 # LOCALIZATION NOTE (identity.loggedIn.description): %S is the user's identity (e.g. user@example.com)
 identity.loggedIn.description = Signed in as: %S
 identity.loggedIn.signOut.label = Sign Out
 identity.loggedIn.signOut.accessKey = O
 
-# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
+# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message,
+#                    getUserMedia.shareScreen.message, getUserMedia.shareCameraAndMicrophone.message,
+#                    getUserMedia.shareScreenAndMicrophone.message):
+#  %S is the website origin (e.g. www.mozilla.org)
+getUserMedia.shareCamera.message = Would you like to share your camera with %S?
+getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
+getUserMedia.shareScreen.message = Would you like to share your screen with %S?
+getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
+getUserMedia.shareScreenAndMicrophone.message = Would you like to share your microphone and screen with %S?
+getUserMedia.noVideo.label = No Video
+getUserMedia.noWindowOrScreen.label = No Window or Screen
+getUserMedia.noAudio.label = No Audio
+getUserMedia.shareEntireScreen.label = Entire screen
 # LOCALIZATION NOTE (getUserMedia.shareSelectedDevices.label):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The number of devices can be either one or two.
-getUserMedia.shareCamera.message = Would you like to share your camera with %S?
-getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
-getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
-getUserMedia.noVideo.label = No Video
-getUserMedia.noAudio.label = No Audio
 getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected Devices
 getUserMedia.shareSelectedDevices.accesskey = S
+getUserMedia.shareScreen.label = Share Screen
+getUserMedia.shareWindow.label = Share Selected Window
+getUserMedia.shareSelectedItems.label = Share Selected Items
 getUserMedia.always.label = Always Share
 getUserMedia.always.accesskey = A
 getUserMedia.denyRequest.label = Don't Share
 getUserMedia.denyRequest.accesskey = D
 getUserMedia.never.label = Never Share
 getUserMedia.never.accesskey = N
 getUserMedia.sharingCamera.message2 = You are currently sharing your camera with this page.
 getUserMedia.sharingMicrophone.message2 = You are currently sharing your microphone with this page.
 getUserMedia.sharingCameraAndMicrophone.message2 = You are currently sharing your camera and microphone with this page.
+getUserMedia.sharingScreen.message = You are currently sharing your screen with this page.
+getUserMedia.sharingWindow.message = You are currently sharing a window with this page.
 getUserMedia.continueSharing.label = Continue Sharing
 getUserMedia.continueSharing.accesskey = C
 getUserMedia.stopSharing.label = Stop Sharing
 getUserMedia.stopSharing.accesskey = S
 
 # Mixed Content Blocker Doorhanger Notification
 # LOCALIZATION NOTE - %S is brandShortName
 mixedContentBlocked.message = %S has blocked content that isn't secure.
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -47,16 +47,32 @@ this.webrtcUI = {
                 browserWindow.gBrowser._getTabForContentWindow(contentWindow.top);
       activeStreams.push({
         uri: contentWindow.location.href,
         tab: tab,
         browser: browser
       });
     }
     return activeStreams;
+  },
+
+  updateMainActionLabel: function(aMenuList) {
+    let type = aMenuList.selectedItem.getAttribute("devicetype");
+    let document = aMenuList.ownerDocument;
+    document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
+
+    // If we are also requesting audio in addition to screen sharing,
+    // always use a generic label.
+    if (!document.getElementById("webRTC-selectMicrophone").hidden)
+      type = "";
+
+    let bundle = document.defaultView.gNavigatorBundle;
+    let stringId = "getUserMedia.share" + (type || "SelectedItems") + ".label";
+    let popupnotification = aMenuList.parentNode.parentNode;
+    popupnotification.setAttribute("buttonlabel", bundle.getString(stringId));
   }
 }
 
 function getBrowserForWindowId(aWindowID) {
   return getBrowserForWindow(Services.wm.getOuterWindowWithId(aWindowID));
 }
 
 function getBrowserForWindow(aContentWindow) {
@@ -89,89 +105,106 @@ function denyRequest(aCallID, aError) {
   let msg = null;
   if (aError) {
     msg = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
     msg.data = aError;
   }
   Services.obs.notifyObservers(msg, "getUserMedia:response:deny", aCallID);
 }
 
-function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevices, aSecure) {
+function prompt(aContentWindow, aCallID, aAudio, aVideo, aDevices, aSecure) {
   let audioDevices = [];
   let videoDevices = [];
+
+  // MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
+  let sharingScreen = aVideo && typeof(aVideo) != "boolean" &&
+                      aVideo.mediaSource != "camera";
   for (let device of aDevices) {
     device = device.QueryInterface(Ci.nsIMediaDevice);
     switch (device.type) {
       case "audio":
-        if (aAudioRequested)
+        if (aAudio)
           audioDevices.push(device);
         break;
       case "video":
-        if (aVideoRequested)
+        // Verify that if we got a camera, we haven't requested a screen share,
+        // or that if we requested a screen share we aren't getting a camera.
+        if (aVideo && (device.mediaSource == "camera") != sharingScreen)
           videoDevices.push(device);
         break;
     }
   }
 
-  let requestType;
-  if (audioDevices.length && videoDevices.length)
-    requestType = "CameraAndMicrophone";
-  else if (audioDevices.length)
-    requestType = "Microphone";
-  else if (videoDevices.length)
-    requestType = "Camera";
-  else {
+  let requestTypes = [];
+  if (videoDevices.length)
+    requestTypes.push(sharingScreen ? "Screen" : "Camera");
+  if (audioDevices.length)
+    requestTypes.push("Microphone");
+
+  if (!requestTypes.length) {
     denyRequest(aCallID, "NO_DEVICES_FOUND");
     return;
   }
 
   let uri = aContentWindow.document.documentURIObject;
   let browser = getBrowserForWindow(aContentWindow);
   let chromeDoc = browser.ownerDocument;
   let chromeWin = chromeDoc.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
-  let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message",
-                                                [ uri.host ]);
+  let stringId = "getUserMedia.share" + requestTypes.join("And") + ".message";
+  let message = stringBundle.getFormattedString(stringId, [uri.host]);
 
+  let mainLabel;
+  if (sharingScreen) {
+    mainLabel = stringBundle.getString("getUserMedia.shareSelectedItems.label");
+  }
+  else {
+    let string = stringBundle.getString("getUserMedia.shareSelectedDevices.label");
+    mainLabel = PluralForm.get(requestTypes.length, string);
+  }
   let mainAction = {
-    label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1,
-                          stringBundle.getString("getUserMedia.shareSelectedDevices.label")),
+    label: mainLabel,
     accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"),
     // The real callback will be set during the "showing" event. The
     // empty function here is so that PopupNotifications.show doesn't
     // reject the action.
     callback: function() {}
   };
 
   let secondaryActions = [
     {
       label: stringBundle.getString("getUserMedia.denyRequest.label"),
       accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
       callback: function () {
         denyRequest(aCallID);
       }
-    },
-    {
+    }
+  ];
+
+  if (!sharingScreen) { // Bug 1037438: implement 'never' for screen sharing.
+    secondaryActions.push({
       label: stringBundle.getString("getUserMedia.never.label"),
       accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
       callback: function () {
         denyRequest(aCallID);
         // Let someone save "Never" for http sites so that they can be stopped from
         // bothering you with doorhangers.
         let perms = Services.perms;
         if (audioDevices.length)
           perms.add(uri, "microphone", perms.DENY_ACTION);
         if (videoDevices.length)
           perms.add(uri, "camera", perms.DENY_ACTION);
       }
-    }
-  ];
+    });
+  }
 
-  if (aSecure) {
-    // Don't show the 'Always' action if the connection isn't secure.
+  if (aSecure && !sharingScreen) {
+    // Don't show the 'Always' action if the connection isn't secure, or for
+    // screen sharing (because we can't guess which window the user wants to
+    // share without prompting).
     secondaryActions.unshift({
       label: stringBundle.getString("getUserMedia.always.label"),
       accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
       callback: function () {
         mainAction.callback(true);
       }
     });
   }
@@ -180,17 +213,21 @@ function prompt(aContentWindow, aCallID,
     eventCallback: function(aTopic, aNewBrowser) {
       if (aTopic == "swapping")
         return true;
 
       let chromeDoc = this.browser.ownerDocument;
 
       if (aTopic == "shown") {
         let PopupNotifications = chromeDoc.defaultView.PopupNotifications;
-        let popupId = requestType == "Microphone" ? "Microphone" : "Devices";
+        let popupId = "Devices";
+        if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
+          popupId = "Microphone";
+        if (requestTypes.indexOf("Screen") != -1)
+          popupId = "Screen";
         PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-share" + popupId);
       }
 
       if (aTopic != "showing")
         return false;
 
       // DENY_ACTION is handled immediately by MediaManager, but handling
       // of ALLOW_ACTION is delayed until the popupshowing event
@@ -201,16 +238,20 @@ function prompt(aContentWindow, aCallID,
         let micPerm = perms.testExactPermission(uri, "microphone");
         if (micPerm == perms.PROMPT_ACTION)
           micPerm = perms.UNKNOWN_ACTION;
 
         let camPerm = perms.testExactPermission(uri, "camera");
         if (camPerm == perms.PROMPT_ACTION)
           camPerm = perms.UNKNOWN_ACTION;
 
+        // Screen sharing shouldn't follow the camera permissions.
+        if (videoDevices.length && sharingScreen)
+          camPerm = perms.UNKNOWN_ACTION;
+
         // We don't check that permissions are set to ALLOW_ACTION in this
         // test; only that they are set. This is because if audio is allowed
         // and video is denied persistently, we don't want to show the prompt,
         // and will grant audio access immediately.
         if ((!audioDevices.length || micPerm) && (!videoDevices.length || camPerm)) {
           // All permissions we were about to request are already persistently set.
           let allowedDevices = Cc["@mozilla.org/supports-array;1"]
                                  .createInstance(Ci.nsISupportsArray);
@@ -230,43 +271,90 @@ function prompt(aContentWindow, aCallID,
 
         let deviceIndex = 0;
         for (let device of devices) {
           addDeviceToList(menupopup, device.name, deviceIndex);
           deviceIndex++;
         }
       }
 
-      function addDeviceToList(menupopup, deviceName, deviceIndex) {
+      function listScreenShareDevices(menupopup, devices) {
+        while (menupopup.lastChild)
+          menupopup.removeChild(menupopup.lastChild);
+
+        // "No Window or Screen" is the default because we can't pick a
+        // 'default' window to share.
+        addDeviceToList(menupopup,
+                        stringBundle.getString("getUserMedia.noWindowOrScreen.label"),
+                        "-1");
+
+        // Then add the 'Entire screen' item if mozGetUserMediaDevices returned it.
+        for (let i = 0; i < devices.length; ++i) {
+          if (devices[i].mediaSource == "screen") {
+            menupopup.appendChild(chromeDoc.createElement("menuseparator"));
+            addDeviceToList(menupopup,
+                            stringBundle.getString("getUserMedia.shareEntireScreen.label"),
+                            i, "Screen");
+            break;
+          }
+        }
+
+        // Finally add all the window names.
+        let separatorNeeded = true;
+        for (let i = 0; i < devices.length; ++i) {
+          if (devices[i].mediaSource == "window") {
+            if (separatorNeeded) {
+              menupopup.appendChild(chromeDoc.createElement("menuseparator"));
+              separatorNeeded = false;
+            }
+            addDeviceToList(menupopup, devices[i].name, i, "Window");
+          }
+        }
+
+        // Always re-select the "No Window or Screen" item.
+        chromeDoc.getElementById("webRTC-selectWindow-menulist").removeAttribute("value");
+        chromeDoc.getElementById("webRTC-all-windows-shared").hidden = true;
+      }
+
+      function addDeviceToList(menupopup, deviceName, deviceIndex, type) {
         let menuitem = chromeDoc.createElement("menuitem");
         menuitem.setAttribute("value", deviceIndex);
         menuitem.setAttribute("label", deviceName);
         menuitem.setAttribute("tooltiptext", deviceName);
+        if (type)
+          menuitem.setAttribute("devicetype", type);
         menupopup.appendChild(menuitem);
       }
 
-      chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length;
+      chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
+      chromeDoc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
       chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length;
 
       let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup");
+      let windowMenupopup = chromeDoc.getElementById("webRTC-selectWindow-menupopup");
       let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup");
-      listDevices(camMenupopup, videoDevices);
+      if (sharingScreen)
+        listScreenShareDevices(windowMenupopup, videoDevices);
+      else
+        listDevices(camMenupopup, videoDevices);
       listDevices(micMenupopup, audioDevices);
-      if (requestType == "CameraAndMicrophone") {
+      if (requestTypes.length == 2) {
         let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
-        addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
+        if (!sharingScreen)
+          addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
         addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
       }
 
       this.mainAction.callback = function(aRemember) {
         let allowedDevices = Cc["@mozilla.org/supports-array;1"]
                                .createInstance(Ci.nsISupportsArray);
         let perms = Services.perms;
         if (videoDevices.length) {
-          let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value;
+          let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
+          let videoDeviceIndex = chromeDoc.getElementById(listId).value;
           let allowCamera = videoDeviceIndex != "-1";
           if (allowCamera)
             allowedDevices.AppendElement(videoDevices[videoDeviceIndex]);
           if (aRemember) {
             perms.add(uri, "camera",
                       allowCamera ? perms.ALLOW_ACTION : perms.DENY_ACTION);
           }
         }
@@ -287,18 +375,21 @@ function prompt(aContentWindow, aCallID,
         }
 
         Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID);
       };
       return false;
     }
   };
 
-  let anchorId = requestType == "Microphone" ? "webRTC-shareMicrophone-notification-icon"
-                                             : "webRTC-shareDevices-notification-icon";
+  let anchorId = "webRTC-shareDevices-notification-icon";
+  if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
+    anchorId = "webRTC-shareMicrophone-notification-icon";
+  if (requestTypes.indexOf("Screen") != -1)
+    anchorId = "webRTC-shareScreen-notification-icon";
   chromeWin.PopupNotifications.show(browser, "webRTC-shareDevices", message,
                                     anchorId, mainAction, secondaryActions, options);
 }
 
 function updateIndicators() {
   webrtcUI.showGlobalIndicator =
     MediaManagerService.activeMediaCaptureWindows.Count() > 0;
 
@@ -306,82 +397,115 @@ function updateIndicators() {
   while (e.hasMoreElements())
     e.getNext().WebrtcIndicator.updateButton();
 
   for (let {browser: browser} of webrtcUI.activeStreams)
     showBrowserSpecificIndicator(browser);
 }
 
 function showBrowserSpecificIndicator(aBrowser) {
-  let hasVideo = {};
-  let hasAudio = {};
+  let camera = {}, microphone = {}, screen = {}, window = {};
   MediaManagerService.mediaCaptureWindowState(aBrowser.contentWindow,
-                                              hasVideo, hasAudio);
+                                              camera, microphone, screen, window);
   let captureState;
-  if (hasVideo.value && hasAudio.value) {
+  if (camera.value && microphone.value) {
     captureState = "CameraAndMicrophone";
-  } else if (hasVideo.value) {
+  } else if (camera.value) {
     captureState = "Camera";
-  } else if (hasAudio.value) {
+  } else if (microphone.value) {
     captureState = "Microphone";
-  } else {
+  } else if (!screen.value && !window.value) {
     Cu.reportError("showBrowserSpecificIndicator: got neither video nor audio access");
     return;
   }
 
   let chromeWin = aBrowser.ownerDocument.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
 
-  let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
-
   let uri = aBrowser.contentWindow.document.documentURIObject;
   let windowId = aBrowser.contentWindow
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils)
                          .currentInnerWindowID;
   let mainAction = {
     label: stringBundle.getString("getUserMedia.continueSharing.label"),
     accessKey: stringBundle.getString("getUserMedia.continueSharing.accesskey"),
     callback: function () {},
     dismiss: true
   };
   let secondaryActions = [{
     label: stringBundle.getString("getUserMedia.stopSharing.label"),
     accessKey: stringBundle.getString("getUserMedia.stopSharing.accesskey"),
     callback: function () {
       let perms = Services.perms;
-      if (hasVideo.value &&
+      if (camera.value &&
           perms.testExactPermission(uri, "camera") == perms.ALLOW_ACTION)
         perms.remove(uri.host, "camera");
-      if (hasAudio.value &&
+      if (microphone.value &&
           perms.testExactPermission(uri, "microphone") == perms.ALLOW_ACTION)
         perms.remove(uri.host, "microphone");
 
       Services.obs.notifyObservers(null, "getUserMedia:revoke", windowId);
+
+      // Performing an action from a notification removes it, but if the page
+      // uses screensharing and a device, we may have another notification to remove.
+      let outerWindowID = Services.wm.getCurrentInnerWindowWithId(windowId)
+                                     .QueryInterface(Ci.nsIInterfaceRequestor)
+                                     .getInterface(Ci.nsIDOMWindowUtils)
+                                     .outerWindowID;
+      removeBrowserSpecificIndicator(null, null, outerWindowID);
     }
   }];
   let options = {
     hideNotNow: true,
     dismissed: true,
     eventCallback: function(aTopic) {
       if (aTopic == "shown") {
         let PopupNotifications = this.browser.ownerDocument.defaultView.PopupNotifications;
         let popupId = captureState == "Microphone" ? "Microphone" : "Devices";
         PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-sharing" + popupId);
       }
       return aTopic == "swapping";
     }
   };
-  let anchorId = captureState == "Microphone" ? "webRTC-sharingMicrophone-notification-icon"
-                                              : "webRTC-sharingDevices-notification-icon";
-  chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
-                                    anchorId, mainAction, secondaryActions, options);
+  if (captureState) {
+    let anchorId = captureState == "Microphone" ? "webRTC-sharingMicrophone-notification-icon"
+                                                : "webRTC-sharingDevices-notification-icon";
+    let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
+    chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
+                                      anchorId, mainAction, secondaryActions, options);
+  }
+
+  // Now handle the screen sharing indicator.
+  if (!screen.value && !window.value)
+    return;
+
+  options = {
+    hideNotNow: true,
+    dismissed: true,
+    eventCallback: function(aTopic) {
+      if (aTopic == "shown") {
+        let PopupNotifications = this.browser.ownerDocument.defaultView.PopupNotifications;
+        PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-sharingScreen");
+      }
+      return aTopic == "swapping";
+    }
+  };
+  // If we are sharing both a window and the screen, show 'Screen'.
+  let stringId = "getUserMedia.sharing" + (screen.value ? "Screen" : "Window") + ".message";
+  chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingScreen",
+                                    stringBundle.getString(stringId),
+                                    "webRTC-sharingScreen-notification-icon",
+                                    mainAction, secondaryActions, options);
 }
 
 function removeBrowserSpecificIndicator(aSubject, aTopic, aData) {
   let browser = getBrowserForWindowId(aData);
   let PopupNotifications = browser.ownerDocument.defaultView.PopupNotifications;
-  let notification = PopupNotifications &&
-                     PopupNotifications.getNotification("webRTC-sharingDevices",
-                                                        browser);
-  if (notification)
-    PopupNotifications.remove(notification);
+  if (!PopupNotifications)
+    return;
+
+  for (let notifId of ["webRTC-sharingDevices", "webRTC-sharingScreen"]) {
+    let notification = PopupNotifications.getNotification(notifId, browser);
+    if (notification)
+      PopupNotifications.remove(notification);
+  }
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1226,16 +1226,21 @@ toolbarbutton[sdk-button="true"][cui-are
   list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
 }
 
 .popup-notification-icon[popupid="webRTC-sharingMicrophone"],
 .popup-notification-icon[popupid="webRTC-shareMicrophone"] {
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-64.png);
 }
 
+.popup-notification-icon[popupid="webRTC-sharingScreen"],
+.popup-notification-icon[popupid="webRTC-shareScreen"] {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64.png);
+}
+
 .popup-notification-icon[popupid="pointerLock"] {
   list-style-image: url(chrome://browser/skin/pointerLock-64.png);
 }
 
 /* Notification icon box */
 #notification-popup-box {
   position: relative;
   background-color: #fff;
@@ -1372,16 +1377,26 @@ toolbarbutton[sdk-button="true"][cui-are
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-16.png);
 }
 
 .webRTC-sharingMicrophone-notification-icon,
 #webRTC-sharingMicrophone-notification-icon {
   list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16.png);
 }
 
+.webRTC-shareScreen-notification-icon,
+#webRTC-shareScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
+}
+
+.webRTC-sharingScreen-notification-icon,
+#webRTC-sharingScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16.png);
+}
+
 .web-notifications-notification-icon,
 #web-notifications-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-16.png);
 }
 
 #pointerLock-notification-icon {
   list-style-image: url(chrome://browser/skin/pointerLock-16.png);
 }
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -69,16 +69,19 @@ browser.jar:
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/webRTC-shareDevice-16.png
   skin/classic/browser/webRTC-shareDevice-64.png
   skin/classic/browser/webRTC-sharingDevice-16.png
   skin/classic/browser/webRTC-shareMicrophone-16.png
   skin/classic/browser/webRTC-shareMicrophone-64.png
   skin/classic/browser/webRTC-sharingMicrophone-16.png
+  skin/classic/browser/webRTC-shareScreen-16.png      (../shared/webrtc/webRTC-shareScreen-16.png)
+  skin/classic/browser/webRTC-shareScreen-64.png      (../shared/webrtc/webRTC-shareScreen-64.png)
+  skin/classic/browser/webRTC-sharingScreen-16.png    (../shared/webrtc/webRTC-sharingScreen-16.png)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
   skin/classic/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
   skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
   skin/classic/browser/customizableui/info-icon-customizeTip.png  (../shared/customizableui/info-icon-customizeTip.png)
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3565,16 +3565,38 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 @media (min-resolution: 2dppx) {
   .webRTC-sharingMicrophone-notification-icon,
   #webRTC-sharingMicrophone-notification-icon {
     list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16@2x.png);
   }
 }
 
+.webRTC-shareScreen-notification-icon,
+#webRTC-shareScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
+}
+@media (min-resolution: 2dppx) {
+  .webRTC-shareScreen-notification-icon,
+  #webRTC-shareScreen-notification-icon {
+    list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16@2x.png);
+  }
+}
+
+.webRTC-sharingScreen-notification-icon,
+#webRTC-sharingScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16.png);
+}
+@media (min-resolution: 2dppx) {
+  .webRTC-sharingScreen-notification-icon,
+  #webRTC-sharingScreen-notification-icon {
+    list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16@2x.png);
+  }
+}
+
 .web-notifications-notification-icon,
 #web-notifications-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-16.png);
 }
 @media (min-resolution: 2dppx) {
   .web-notifications-notification-icon,
   #web-notifications-notification-icon {
     list-style-image: url(chrome://browser/skin/notification-16@2x.png);
@@ -3887,16 +3909,27 @@ menulist.translate-infobar-element > .me
 }
 @media (min-resolution: 2dppx) {
   .popup-notification-icon[popupid="webRTC-sharingMicrophone"],
   .popup-notification-icon[popupid="webRTC-shareMicrophone"] {
     list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-64@2x.png);
   }
 }
 
+.popup-notification-icon[popupid="webRTC-sharingScreen"],
+.popup-notification-icon[popupid="webRTC-shareScreen"] {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64.png);
+}
+@media (min-resolution: 2dppx) {
+  .popup-notification-icon[popupid="webRTC-sharingScreen"],
+  .popup-notification-icon[popupid="webRTC-shareScreen"] {
+    list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64@2x.png);
+  }
+}
+
 /* Popup Buttons */
 #identity-popup-more-info-button {
   @hudButton@
   margin: 10px 0 0;
   min-height: 0px;
 }
 
 #identity-popup-more-info-button:focus {
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -120,16 +120,22 @@ browser.jar:
   skin/classic/browser/webRTC-sharingDevice-16.png
   skin/classic/browser/webRTC-sharingDevice-16@2x.png
   skin/classic/browser/webRTC-shareMicrophone-16.png
   skin/classic/browser/webRTC-shareMicrophone-16@2x.png
   skin/classic/browser/webRTC-shareMicrophone-64.png
   skin/classic/browser/webRTC-shareMicrophone-64@2x.png
   skin/classic/browser/webRTC-sharingMicrophone-16.png
   skin/classic/browser/webRTC-sharingMicrophone-16@2x.png
+  skin/classic/browser/webRTC-shareScreen-16.png      (../shared/webrtc/webRTC-shareScreen-16.png)
+  skin/classic/browser/webRTC-shareScreen-16@2x.png   (../shared/webrtc/webRTC-shareScreen-16@2x.png)
+  skin/classic/browser/webRTC-shareScreen-64.png      (../shared/webrtc/webRTC-shareScreen-64.png)
+  skin/classic/browser/webRTC-shareScreen-64@2x.png   (../shared/webrtc/webRTC-shareScreen-64@2x.png)
+  skin/classic/browser/webRTC-sharingScreen-16.png    (../shared/webrtc/webRTC-sharingScreen-16.png)
+  skin/classic/browser/webRTC-sharingScreen-16@2x.png (../shared/webrtc/webRTC-sharingScreen-16@2x.png)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
   skin/classic/browser/customizableui/customize-illustration@2x.png  (../shared/customizableui/customize-illustration@2x.png)
   skin/classic/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
   skin/classic/browser/customizableui/customize-illustration-rtl@2x.png  (../shared/customizableui/customize-illustration-rtl@2x.png)
   skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5779047366753cf26b867526e5ee4cd760af19b6
GIT binary patch
literal 15506
zc%1E<%a7Yc9LI+q5Tq=v1X3%6gzHj7B^-|(KVmn@HchhZQc_mi1+waa`Y}$dCU$B&
zO?K&FRk&0_6)Y`+3W**-;!r_bA=HWkr798!)IWd=a4Cfg;!&wR&}E#*ZZ>gNMf8Bs
zSh5}eX1?E#=Qj_}CC8`tJv$bAECv7=o0`ne;QQwAeBge3ed3R04c{KDPtN%OY<wg<
z?*pgb-wMF4mOWbvO2y|@!>z?N)74SDRjY&WNR7AZnsEpPl#a@_lcs<D;!B#c%{2Y8
zScJuT4pr>Q1rP0C*f(n|95NJ>9?!&5EfpKoP@qw*TGjE@R+?`6s`z&pW@)NjB{-C(
zvta>MDo#^5*F%&T7a0Tc93?ApP84OCe}>{AC$NxXxdg*0svxKkQr&~j#PBucnU*?}
zpXlQ7Q<|;>L0x6pX0sV@CgQGFW;sPsSje+H&)^!2Kko!ui*fwz9h06<9{Gl6*8|&i
zsPI|S-A0h6=@`{_blX*{7mF(>$M0qj=aFq`b(V`mwpL@KlzuRSto4=t?0g-uGst%v
zo`G=n?a@YsBf&p0DjY#h5NU;5v%XQ3$TU_o^@dk%C)zYvR7Ew+@v)AJ)YU6);QAGJ
z1ahT#Bsj3G5eW=SM#@mj9w7@$I<m~FF4gX0^##wy!(XfBU851UYbu|?1##0hRm+kj
z0VXU)5-o)h6f42#x?(a(L4q7FLzzz`Q|w5aAyfoHRwlBNlH{SFC>&Q1vdTmuDNFpG
z0?ctqKHIZj&vwf31I;l|*Ve|iJSQeKn1oD1kxfPrk;EvX1sR0IL{d|d2}$RB)_wBf
zH%6X)1ep__TWe34y5{*P9CK;9H|Y9P8VcLPU$uwd30V>pp~*9*&RdLNK#oaTCS(N5
zg0dj;oFU?D4#}h3&DCsl79SkIgq4w{CS@5V3{Jxs6R#|sEHfIEAfwBcjznG)Bwp*W
z%*fQTLkSqp&ZO-Jt~WpMieaAiqr+>Xy@INJmw8-iFJ1#w%?OvQjIZHW317P}Hd++X
z1%_^zQWDAx2l0X{@VdYxb<torix(wJlN8HDJ^xW%u!hmBASV;9;Y?^M&T^8<i)w<y
zk9Z~TS+#48Ht}66XlfqHmT@m+Iv<VcURP4N9R5h)kH*tQ&9I#?xU=WGRyZ19R5YiI
z%naKT_5wp)#CH1Vzu0$oH>Lkw-zAc2`|<zNcb(|k>DZgw!_zmM*J{o~8jjDpZZ*0=
zoUpb*gQ6%ul*i{lwxObmVlUeRH3vM3u4!BI5gu!LY@p_#2i~2TTA<yPOMl%yAJb4R
z*Q;qk#;$2)#MYg1id_x%)m&YRb8gl3@N1z=BE?39`&8lgJ^Ym7F`>1m2$y1qVuSTd
z;WVr^+GR2i@xgN8wJYr}9+b|lFP&RQx?1$2V+R?&D;Ws)slrW2r`7xM#v5fMQA~&_
zb}&4sn6_J}TFTq_6XmxvB6T%`1(CwJQNp<)VP~rJZIL_sZ??Mu$9sQvW%D03VekDx
zAOu1n1VSJLLLdY}AOu1n1VSJLLLdY}AOu1n1VSJLLLdY}AOu1n1VSJLLLdY}AOu1n
z1VSJLLLdY}AOu1n1VSJLa*rXg)xXms$4t{rdOUOel=>L}fHEd$iU2I^0ATSo0B$ei
z`_BM0AOOD{1VDWkfGzIQD_>9Gx+kad+1b{2-!7dWyR!SC!uLBq)VDnK&7&vJE}pw_
zVMFosr*C}qDD~V+S$*+F<>K|ly{~V)b*gdUgKNvXu5SJ9<10JmgKx#A%4e8APwXyR
ze;v5&-vW<seDlDa%}d9Z=yMz1x%_Y?cINH(#%^y~`uwt`-xT3d{_u(OH<z!L5O)c@
w`oo=@+de8ic`1GITIr|Xy~**tKXTyc__nim8pk$;{~Deu?8|?$=isrw0jROsv;Y7A
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6c8bd361383a573b54dd4ea783484c46533b91c4
GIT binary patch
literal 15584
zc%1E<duSX*9LKi_sJSNfQJ<~YWlJGS_x62zyWU-!-d#+vmxeUbL@0J{cQ&`=?)Kc>
zB$xalX)96%6-q^|r4$R&{!^v?QEUTk1q;?H_)pa$wF-)l;;Til2I}lRlFQv*sMtS5
zW?^q{e>30jXXZCMv-8hB-{1FGM_@&OAc&6Mp2PsUlHPgSE$G*on!1WEZH1m;hai?N
z_s%86-hKBI#G^%XaL64>ZB#WY7X-SMhQVU4KzK)FU9kYPQRtFsIAZ3b)GsGaQlzOz
zsV!29O%>uWWA;qhaPw5(pf)wCDLS<-7Kjv8WRQa{Ad9(d-cgHDs^qJp|6Z7($Wj$|
zG)i@O1>{hwpNv~JB&DE4Yb?)^vJ&JZNtXGwB+qgp!*UEK(43-*qRO&l<)C5#^c%5t
zLmfzTS8(VqN@ZNPpfb$F#6)mH2wL_C!zqfwusp-_G^#;6lX({u>AbVLY*O_}Ku5F9
zf@@lN(z^#~Ys`&ORDi5GD*eh8QmHvq-l=2{<&i0Z0>cGaCYNLUl#V+9jm4GD;A8<Z
z1JJR?Yz?C7tNo3V<L*B(N{++4>uZI2v$&C;NY~~xg)uu@O0=#qFbi{t;~*X9t1DzI
z*K#se6XaZRQ?P3qO%mvpG?l@k*+k}*lx4AbU8FR`>L%Mnk3YyJEN#r|S8pPQ3WB<+
ztA-H@iL7AIA<0l^Q85HMomO-@EQVN)msyz?!V#vaO#{jYA$AMlq`)hzs3;tl6uXq}
zWLOUI8<K3C3-eu7`^9W~^NtJh8m!ow$QH0bm&CM2iz4t^4p5^7U8jX~niaz`1eye^
z);03R7DL+{hkCbd<w_H#0Bi?(&s>zMK6JGy^@OFzUo{)s1dR|Yu{uxdY2KhkjpgXD
zp|iAT7_2NxJf}%0n+<Y5cYQt^<;4dD5Ml1gQp2(g1&sp;qob9DlVuvPA(l?dMjA>y
z5JNnuvUK0nG7Sl6%ubK#xRyOx*F-N*o8i+7qt$|}d4qYJ>n~aZRG@iFR?GpYDPFT{
zV$DT9Lm+dq06<!#MPTT(sB3^$0H3D$w8D#8T2WY;tNJ$^f(49;49v&8HJoN;nv({(
zkjhIcA40ciC9hhQYBe`;EW@1uHtZTfqYx{<H2SMUNyg*oC4pWVohhK1c`x{I)pwz=
zIY7&R{0P)zOjTG7Gz<|_9;5%_*xlHa{&!<1$f_9rUyWTkx^OyH=XT@t_2#u2w;@3B
zLCebecZd@gcBo$D=LZw$tjjj5sDe_>c3sUnkCdgG#-xwOf)T5$S?_^%XF71f4Y}0T
zt?|({)v)XwaARf;j6kN4ABiyY!J3-$YjNDlS~hAHj0q9OFI=PY-tXS46g?B5G)2&I
z)DWw$Kjclr>{zKx=2>(wocCLi))v=GhZmO)FCv{UdLnPSF}@<H3)iT;O-Q-do6*J_
z#zK-LND-z!T(9Uii!eKsFwrZ@@!BEq6-|ACuW-1TaJWHOo+>q4<nsQTscgW}-k+J<
z{QFJVn}1*khF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_
z2!>z?hF}PWU<ig_2!>z?hF}PWU<iiXWJqBCb6S|!qtpboE_S7J=g;0(x?0abiXf)e
z5XAIOg81_~y8b{AV=O`ZxQ!sx*9hW1Yxh$hbfdc8^d`Cnix)1x%DvFibw~26T?e|)
zTs)m<|2TBz!80p9eD0aEpRaFkzc=zCmC-M?-F<rLTB=jy-)FzrczfuhKVIG&xOVc$
zkw=an48Qc-;lTcTh?e!zO7`8@J1tKx`|INNRlNtP2MW@yhnB>JpT7NM{n^>JwcB$?
zgKs)_P9OVj_nB31Z{KtN@bN>fTMvAFY3vpET<fnr=T@G2D05=}`EzgW-gB(u<XLvp
z)vfw6@$s48r(Sz@2}#VhO}}AHzrKa&U4EeT^32SxyLQFD-?67}&9$vgYtIE@>7~y;
moo#79db)GA<9hVeC^5c+`*QVr`)0gvkb9GTi6a}f?f4r@>i)z4
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3a2daa3eacbf5fb6f86c8a5355eb162d5f564103
GIT binary patch
literal 15965
zc%1E<Yitx%6vuC&yjDxvsNvz4$u`I%%+6zHc6KJaLrb^V61LP*VCxr{ow?hN-QC&l
zOxtaZ3B?ym#00_ejuI2~gAx-%P*jiv6a9dM5TZ6lh>50Xf+56!3E-W5wA<a;N`%Ci
z+(~A3_n!MdzdPsLduHy3eWkta$r{&U7XYB9wI$SnJ{#<FZZ*1st)FZ}*LkUyE)#%<
z9<k3VaNy8F00QUq&WIHWuai_G;ZZar3O$)b$`*mZs!U2zd!a={VYi+Py04u)<tB73
z=-$kS>2RtE_UJ9cX}Dpyty3NDRYlFcN_GV@5^|7$mO^9_@uVqbg6^ze34OPlDL0Yr
zV)X{yjkbY^gxiTGBMk}O!;>n_GK3&{7@ijdb|t~m3`fxn#dt|ZlsHbJX(E5PWf!^z
z(ppUF2(8KE(66Ao$FfoqMGX!PdIr57Bi&6gq9{@{OR+48dXVN&(o!;H(yY%pDfkIN
zQ%&nBOE;2){ac9|{Z`QJb`eEK{=O2a2~g6^#|}l2$|xy{@z9i$NK+>?ss404J2XwD
zU>qi3(lU{banz-H49hTkj0(sJb44&3Z8$vrcoPXnrD-(}Sko)b&Y={fI-qIvr&ZWI
zfNrJU$(5zYV$PmyOU1Fnj9x)zTXHgaQWrq?DmA5{Vj1a9!-&hdS=L^N5z*9?omIrL
zu%hZoyK#AdDit~TgCTUvNb4nOhNQinv|r+V5-pJE3r!a}$@MJp5jHeEHdOGzkx0kz
zos7?()hzNSO!z43S?U9=AWgB9d-77;x5$sCN--myP%K$bDBX}sCA$OEWMfg!iFqY8
z8F3?xh6QCNKshx#szQd^Z%^#TqzX+GSyP^p-t3ZFt#nUqEg0guoGTofq3YV?5|I3Y
z0KF=skesTas=x>Wsn9-}j0&+R<XMIDu}Xl-yQt(E1<0czw?1#v8rH}9IWNmHOf&2E
z`&qx}=Y?j@=Wl9U<LoHcI(6v9ahdMM1KG7GwI)qVLFU#_b}Tv?3(sk>XjJu)kckN-
z$NIda7}YpZ<V8VL7*5ngm`&|ed70}19od{C=!*%0=+!h7!I)Yxf@RLiK<m>wdNL^S
zqCSPVdZT_(<u!Xk@==nbHJ((^ZxX^N3Um~*G1gZ=IJhZ8wdi&nha4u*2H00A%h?*9
zQqm^0D^$>3C{gY*fvw3_I!RIO>MyI=Wvm6LDXo=ddU-nUEd*uq%8bDtn4GTs6!og?
z#57{oppu4--6+z|wGlmmr_@F#VOhlgVZq%;DokbI{u04!kXE8-NeM9#tt+qSC6yS*
zk}RaXwBN6Cd@TB}5>cu8{+EfE1gJeqvKwkLRS*^${#UuXr<#a!=k$D<S{Vz~w!D%C
z?AI^53ZpS8*%YCNd|6v*|A;*e;{92hz|!cT82g%+7Mn|@UDHdurjbsXHYRmTX7iGg
z<|38-j+lE-*?_h)Q1<b>mk&^-&83QVJp<#BkdB^5racZHm)DdU9EDw#gk5FAT&jvT
z9=Tl&mEYE&9ThdPrE;1u5B|Uq48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M3=
z5DdW(48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M32z!2BupOj%z3%Up0tK>KS
z9RCaeK&UMpVE~4g0I=<O0B+qypW^`Z(*RuE3V^f^fCa|xEoavNP&KbL)YzH%;rH(w
zUEAAhXJ5X%w=UHGg75Ir`q0ixADkRnx?_I(&gA){^&cn37Cn9O(`KS&<o)lSxG@rW
z{W`P#?uohu=Uz4r{rc8g;_;f=hK3)j+6JzAm=(A0EIl#z`sn8I{onkxt@qs}5BJ@v
zK0kU{pLL!&HgeiU?Dm{_v#;)%zO!}nFaGrP!5vp_&)9c`7@Om{@Y;J@zHN)tetyCH
zDzk6qFOkvtb3gj>%%k-2KdQIsvu|#id1UX4r}i&+`*6*QD|6;mRo6Cv+~u)Z%k+lL
z2Y+h@&)&RNzh_a~((#1@qX%|9_r*)qwe7)%v9*_ui>KSxuX}azQ}V#VJxAm-o5rec
i{k$8@c&F-E6<E1zdE&;RlZ)*?fVVccg+5%pb^G6eYGF_S
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..caeedeb80e774936d05d08e035e55f1a2c522559
GIT binary patch
literal 15148
zc%1E<eQXnD9LJvt2o6!hP*FlS4oF<W^<LWRwP)Ko+Kp{koTJ7rny~BL)9qY)SMF|W
zHw{kFM8h8@Xw)dm9~vVDi7(MeL<A8NgGQs6_yS>nxF8Cm{DCo`_*`GMzFZqc{(PR~
z?t0Jj`+h&qJuknzfBJau`n3z1Z*B$vEa>iv^r7p7eaxGU&Z@BUHgvfv)75VRFu%n<
zn!t107X#q$QDbo{9=%<Z^|VV;^aOO}(ivcvKwB;&$-~eh5^zw}g5;U^KO_lN36gj7
zQ6`!R!y&b6+<+U#*T>}XVcD;cZK38sPDBQ2Xh}paozhG(7bNq(BKmDt(<G4(v4(?W
zyWK#<qrF5}Hz2{gcuHnCmJs|dmgfb5TTO5b>!BHzX5AF)7d;-4VTfWOL(S+MFqEX&
z7wIhG&|Q!mvaF0q)7fm+m36yxV~}S3em~7{G{;dWf-=W6OUhB2xuRfF@`*rGHq?xz
z>Kb9+O9_3%3X)_qQC5o2mCj6qG_yE%XcXz3l%ZJ{LpzBSd0LYhF;e-iDKZUHFby@!
zL^{?{ml@J6-5k>EAg7z_f>Cdy(Z>BvryZ51)iG){R+_P~45a&@sgD>k>=;Flvck#A
zQj<w%C%dI?-(gOzBePoyve1k!fSy$h8&I-zBc|)AP+^tzmd1z(hx4n7xFssds%BTV
z2Iy*$lOK$rBZRbWieV|n9b<eV?-dz=LO&R$%%l)m<q_2tH91!D@K8v{@-fzHuWFt{
zx6>YFk<}h(2Pu*z{VSL9*fJkQ7L&S>maLGPmIfi6(FOzbOl4W*^tuwmdP+A?w_u13
z&`#Bks)#O+*bBQ|lc9-5)(lllZ+^?Ikp?SkOARSCR7f0(F00DS77%@c0NpYxQ65=A
zS%DP<N@BbWl@O8%$a9j%%Si#cXi?7^4UkJgwqAF*lk;*ukDKFIwuAHee4Nkk<An~7
z*B5T@bT%r~R(8F7TpB%iAio#IZq2kL)Z87(_eDj0VObCBV-r4#bwh^oaJ~fP6F7n5
z880jF9>%YD-TA4ll-F37=tXPgKU$Z`Ypm}uRP-*8QvVa-U{izYQtgo&b67GP;HG->
zn$Ox9$uOavlY(R^DUme-Ta(Xiq9oh-J|yQit`eXtYU|6?OP#<Pd3{E92x^TIPFYla
z6H`xGS;>IygJ`6kvmtutR%AmbVa<p)*cp{GRGRhL84cKp-(C#}Amzs!rI(tz>iD=l
ztErKEo4_%s(5!tfO3Rz8rTvYi{SBluO?PRk72=ALs_HV8{dOv3$PMU23WmHq@8$z^
zb#=9(SIxmxJffm~Vdh7K^A$zaEso0my2}0<Wnr?)K6(mYRCMu^3VqGd)1Pxr3+(b2
z48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M3=5DdW(48af#
z!4M3=5DdW(48af#!4M3=kjo5dp810$)RZ8ZCEG#^_qs;`0EFDt7X@H^6#x@k0QmDF
zx}F4JgaP2^0RY5j0k~1$w((FW08RIHN7`e#?@t|Ldp_=6H0R4DZ@#_gk@nTG4DAZf
zo_}~il3K|dzS}>!@YD<Zd$YD4=v=h!z(+mvH@!BryyepqJAPQXvi0mWXWBNmeD%P~
zzwVk1D$0>(zS+0+^yb~}*2&YyjTa9eUv~2EBTewB9lNH;`@^dr>SmW7RSsTVjDC7g
z%Z25_o+Z+pYcI}WKELzWy2*FX-SnF_?}|eE_4=ddjcs4=dScK1bH6ZOY!dF-JU4aX
zzN1t0TTh<6Zry-8%6_m9UI{L(yt;UD=jg<i&xV_R`b&G`dHBcqz5Fbv!1#}=rY^jG
yW^CTX`R!BhJa%@O`R#)*{eG~cGOBlDXfF8V)}@Em#$P#ZS9Eu*kL+GE@bEu=j$Lj5
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6011caf90748782775e410d2241f9c1f3226017c
GIT binary patch
literal 15506
zc%1E<Ply{;9LL{UsqMN|(MqES9jCTd=w#lT%%9AdWV=mv-G!zu+ZA_D?)-U^3{7Un
znb~f3PgOx%6bj;@h(b}1QW1I(JXo<xFA9Qq5JUwjs0WdHQN;C4lG)8Blci!0B5z<a
z$?v`I_w(NG{h4>k+1cZd?-{*+6acViX1X#*o_EFfuASub!RzZfdF=M57efGUzbC#&
zz-zB(0C=qB%r8U>)nl6JHM6?q893W&`XIgv<1Jq|PvVF%aLsXx>@OdD$TE&qWS^3%
zu<Dm_-I-ns@QJnK^XA$~Q?=Of(rBTj5rZa<bf(p8xS`f6vTa|De2>FC%e1RRCyVSv
zT)-?;XPL4WU`EPHoCyWQC~6i-lA;KQ837`Zhlocx4yl?bY7jEri!F_k&q83?+FWI-
zizB~^Y(0v6jptWZR<bL(tQXXHq^c?p1zr$1QiBUu-AHe7Zn(c=lK50`Xa<fSIiAbJ
zzjeb~j*2Wh%Jf{_el`7Sbpz#w-RzM(@-5xxQ5N#eCZDDZqd9DEuMFo`eaz3{&|3~n
zOselsH<~;Z{S%|fQ`n7Ctw?XSH%b#(=7z>!4jS!5TPBYixJfu6(V<kGU-u#}tb0R{
z8^uGxkz)@@U|cd(hFi`MSzOYQl{R&S_7H1N1P+=0dZXf*%W=PEDkV~owH!;cZCMs!
z&gNvvRyk3%bDUwQ7MB-ghy(>HLM~t6huRFFQV@zdH6g2c0g9@M(4;t_PEF<&SvWcg
z%P22QB=+0c&bVQuyC&}1I>c7Ap_Nmx$w{V><3z)RoC?Vr%iDsa8U_-O3=``f`CyB2
z;GDwNRNyt+3&z)j5XW<_$R?AnH>I(#J^eLjuua&Op#&{~vkbxJL=z&?91C)yZ9_$r
z1Y}Ain*;JRcWW~noy~^?2w`JpX?aD#ITPswW09Q&DGH}U8FGeV8(0!_Q5N)sWu>N;
zA4tGpcBY*$@`BaACdPR>fuGqLO$r*$4d!v9zhn>4bTi(vN}*1g5;waiHe8f41g2qH
zavmxiiYJ387$TQ9B$GpATFAC8tG0y`|6xP0g|Sk{ZYkcwx!6>iN3teJS`Lw)WG7Fo
z+O>w8gq|I(=mDOnkx?jhUK+E>P%`B*c}b9$#-XZiI&K_%H1XXk91bw+x?96miBE({
zU|@*&&KUg{$L_|K^uHUsocezoyH0fLa!l6t;PQ>vwN?(WPU7>P*GTUW61R4!UzFyD
zE9CCWHdHl9O0wNov(KaIS&qG$;<07K`fB!jkkgr^NBRx9^w#b1u}sbOf~FpooTgsG
zyzkZu{ARGH=H^~p_8MM5nuSZb0-qM{QN{1~_*F{ggx+2vsK5`z`s**m%doNBE>i?Z
zE*{07U1@J|zjSeX>Ebrh&7vn=Cn^bDNnf}}6(2%6y*@z>-nb-7Qcf!H{o#Jatkc4c
zg^EL7QDHkHa#zz|kSbgpCR`j4c9u%d5xH~z=DP=Qa`xvp4*zKj_U2y{LLn4FArwL(
z6ha{sLLn4FArwL(6ha{sLLn4FArwL(6ha{sLLn4FArwL(6ha{sLLn4FArwL(6ha{s
zLLn4FArwL(HyJXz`FC3ET19q+9WR~BT(|}RV9e>cDgbK-066_T0Kc!3=Z^p^LjZnS
z0zf+tz&`Jlr$3n@bzhySOw6~wHvU{+AK!7{^x0>R9(ix%u>Y0#>BnPZV{+@uzwpx1
zq1x3yzJ2K%aOT<_J9oYywZ>Lo%wFDg=N0GW_4_iJ-!hrZ`{Wsrd*RnibmpO-*Y`&I
zE+3J1>?rRYdG|I1;jxeIJ#*#O!`Ht%J9Y3<{mrYVZy`qKet6{J`XzYGd^<n(`Q6{Y
i@z(j>&IMxxg4Kf$yz>RK_*wj~;hD+fm5WE0p8FeGi_Osh
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bac3900bcddf2e83570eadf454a6cda4f4f8fe58
GIT binary patch
literal 15585
zc%1E<TWl0n7{|u~Dy3W!Ov>ef%>q`Vvvc3s8Fv?#-L0*%ZPONQBSy~7oas*3omppQ
zyW7T)Moq*A3;~1&Z%qV4d?3USeNeCv#i%bNK1on38WNR=CPu}C=$XB=+r2axc`)Wo
zGP^t9IsgCfobO!byzC2oJr6B!UDrww#PaU0SU>vqxc8m6qfdK${8#j~G}|?76U4IB
z?tL3^<dro9@jyWz9CC&d+hoPe_<(9Ap|6n167Ch;Qpf^j7j!&HIHDUN@3r?n@OpGL
z<b6y?(1~mmru43H3l5C;3@YQh6iM}N3AY9dGBU_O2Y3pZv|-DIkhkb7qu*|r@_LF@
zoLwPr#4Yd)CHg#3(}EtsCy)xwG9FR#F@hk9?EM~=W_XHbD25{$N#=Q(rak4$8*W9P
zK}*%-{#a)jhn_;-l;dP&ipuBnzC7nMtr3cmB#EL~ie*VugS5vC2NX!d-dHlJ_{5;C
zSbEmcO~d0pgQS^rLSApHr|K&AE0awmW>JP+&K}AmRRCFv@zGQ!Lp4&`PCwKZR@#GO
zSxEIm+ss)CMAbJoHj0lr|HLRh3Js^B73$5xMvX+OGONkvtaLHaszSju%pi`9bWB5C
zHf1`doidvsXN#MH9bIdZK)0l+3>NezGPk583(x6-#UYk^EgemNkdB#3&h1xsEQ|_#
zs;<hK=J)e7r;&a^lSp3DI5L@(R5HN(X@(VPk>vtGs;NyKssV!U<N|Sym1tg)7$(j~
zq|SIi^s`&zbd(9Ok&697w%vy907HRgTOHYQEG?*_Dv_L@Q%Igq@}v}CAt`A*kR(my
z7*45JSIO&J3@v>Ws-2dZDK3~Sux#kgxsbOq>8exe3X9WU*6Z5@H9sxTDod(KRwH?Z
zX2^i1(j>2Gw8#rAqX;OQb@E2;>RdKTn-2;g!tBhF1EL5yg#ieoqMd~iMH0|{noNpX
z5(+Hf{Vb@k)P||0>Jm_&oi5#WOlz#BiEf?-;8XLXm4dW>lX;x&FWLiSptxIB*aoO6
zZnLXm%|#7{Kx9M?05lnRps6IUDu9##n<Uw!#PUi~l4y~s_%|DZd5nAt8ew-2Cuxyn
zgh9qHvx3a}(IeW)D^|r?%}s1mbMnA~kr6Zs;nGW^uQHULXcWC9&`aZ<1W<It4c=Sv
zoiA(-P*T7cfohnl2rGfQA)-oS^j{pin_JTVZtOToX8vDeSBlPGj+M1tzkJ<wEk`W~
zP<+rd(~UdC@bf!VD{ABiW9Y8QHZ7rqSF&AGv&JJ~s=7ATz+>Ks)zqx@K&LYmIN+vS
zs_RzysEVwaRt7j>Jp)D{l{H3!)LgKt=G<N!HPfbrngzpLkZKgJQn~MU_f?AK1Sl>M
zv>nyOYU>ZV%P^fQmWeElE{btK%hKxNTIuk@(%}W9b4A+?-3ha0Nlmy)<sL#xy&gaZ
zZy5Fq0w)Bi+HkF+PcOjqP)tX!DBEoZ$Cfp<1r3G6&4j~s!qQTyIwF_O-&FYkj?Vtn
z?BT!Bg1z+@hF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_
z2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_$SsDn&i$Ph8fwU!_ihRAJxa8=|I$^u`V#~(
zzL_8<o+XIuf1&U12_i=m#1A_OLY^duc60xeA9bR--*(3$gN2J%51np3N8i=i`(dPG
z<m<EX9j{(`dd0hKJ(rKqT=lG2yZH6<8!miuwxjRhnTb79JMLIUI{PM*tL!)XA3k&7
zt4og@+%t2a<+Y_Pk*UcsWpMrEqV3P;%;`4r%lwA7wyj^68*S_TJoeeAKds#K&B|%w
z$3L#Ge(|@>U6WJCe!s!(JK6QpvBmtv+tEj#J9K*QbZ7;&=<%s5??3TIXKL-mcffsz
ze{R3gd*#8+AMZbWZc|%JWTs>Bi8D*i?_P4MZ9+czi@kBY`|8&Aqk-=x+rL<r8g3bw
oxx8j~i+qhb23Fm8dD<rK?qE)NFC72V{S$I`yeD>I>&|EX1|q)tQ~&?~
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2208,16 +2208,21 @@ toolbarbutton.bookmark-item[dragover="tr
   list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
 }
 
 .popup-notification-icon[popupid="webRTC-sharingMicrophone"],
 .popup-notification-icon[popupid="webRTC-shareMicrophone"] {
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-64.png);
 }
 
+.popup-notification-icon[popupid="webRTC-sharingScreen"],
+.popup-notification-icon[popupid="webRTC-shareScreen"] {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64.png);
+}
+
 .popup-notification-icon[popupid="pointerLock"] {
   list-style-image: url(chrome://browser/skin/pointerLock-64.png);
 }
 
 /* Notification icon box */
 #notification-popup-box {
   position: relative;
   background-color: #fff;
@@ -2348,16 +2353,26 @@ toolbarbutton.bookmark-item[dragover="tr
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-16.png);
 }
 
 .webRTC-sharingMicrophone-notification-icon,
 #webRTC-sharingMicrophone-notification-icon {
   list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16.png);
 }
 
+.webRTC-shareScreen-notification-icon,
+#webRTC-shareScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
+}
+
+.webRTC-sharingScreen-notification-icon,
+#webRTC-sharingScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16.png);
+}
+
 .web-notifications-notification-icon,
 #web-notifications-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-16.png);
 }
 
 #pointerLock-notification-icon {
   list-style-image: url(chrome://browser/skin/pointerLock-16.png);
 }
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -90,16 +90,19 @@ browser.jar:
         skin/classic/browser/notification-pluginAlert.png            (../shared/plugins/notification-pluginAlert.png)
         skin/classic/browser/notification-pluginBlocked.png          (../shared/plugins/notification-pluginBlocked.png)
         skin/classic/browser/webRTC-shareDevice-16.png
         skin/classic/browser/webRTC-shareDevice-64.png
         skin/classic/browser/webRTC-sharingDevice-16.png
         skin/classic/browser/webRTC-shareMicrophone-16.png
         skin/classic/browser/webRTC-shareMicrophone-64.png
         skin/classic/browser/webRTC-sharingMicrophone-16.png
+        skin/classic/browser/webRTC-shareScreen-16.png               (../shared/webrtc/webRTC-shareScreen-16.png)
+        skin/classic/browser/webRTC-shareScreen-64.png               (../shared/webrtc/webRTC-shareScreen-64.png)
+        skin/classic/browser/webRTC-sharingScreen-16.png             (../shared/webrtc/webRTC-sharingScreen-16.png)
         skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
         skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
         skin/classic/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
         skin/classic/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
         skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
         skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
         skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
         skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
@@ -499,16 +502,19 @@ browser.jar:
         skin/classic/aero/browser/notification-pluginAlert.png      (../shared/plugins/notification-pluginAlert.png)
         skin/classic/aero/browser/notification-pluginBlocked.png    (../shared/plugins/notification-pluginBlocked.png)
         skin/classic/aero/browser/webRTC-shareDevice-16.png
         skin/classic/aero/browser/webRTC-shareDevice-64.png
         skin/classic/aero/browser/webRTC-sharingDevice-16.png
         skin/classic/aero/browser/webRTC-shareMicrophone-16.png
         skin/classic/aero/browser/webRTC-shareMicrophone-64.png
         skin/classic/aero/browser/webRTC-sharingMicrophone-16.png
+        skin/classic/aero/browser/webRTC-shareScreen-16.png               (../shared/webrtc/webRTC-shareScreen-16.png)
+        skin/classic/aero/browser/webRTC-shareScreen-64.png               (../shared/webrtc/webRTC-shareScreen-64.png)
+        skin/classic/aero/browser/webRTC-sharingScreen-16.png             (../shared/webrtc/webRTC-sharingScreen-16.png)
         skin/classic/aero/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
         skin/classic/aero/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
         skin/classic/aero/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
         skin/classic/aero/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
         skin/classic/aero/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
         skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
         skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
         skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -73,17 +73,17 @@ DEBUGGER_INFO = {
     "requiresEscapedArgs": True
   },
 
   # valgrind doesn't explain much about leaks unless you set the
   # '--leak-check=full' flag. But there are a lot of objects that are
   # semi-deliberately leaked, so we set '--show-possibly-lost=no' to avoid
   # uninteresting output from those objects. We set '--smc-check==all-non-file'
   # and '--vex-iropt-register-updates=allregs-at-mem-access' so that valgrind
-  # deals properly with JIT'd JavaScript code.  
+  # deals properly with JIT'd JavaScript code.
   "valgrind": {
     "interactive": False,
     "args": " ".join(["--leak-check=full",
                       "--show-possibly-lost=no",
                       "--smc-check=all-non-file",
                       "--vex-iropt-register-updates=allregs-at-mem-access"])
   }
 }
@@ -597,48 +597,50 @@ class ShutdownLeaks(object):
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
 
-  def log(self, line):
-    if line[2:11] == "DOMWINDOW":
-      self._logWindow(line)
-    elif line[2:10] == "DOCSHELL":
-      self._logDocShell(line)
-    elif line.startswith("TEST-START"):
-      fileName = line.split(" ")[-1].strip().replace("chrome://mochitests/content/browser/", "")
+  def log(self, message):
+    if message['action'] == 'log':
+        line = message['message']
+        if line[2:11] == "DOMWINDOW":
+          self._logWindow(line)
+        elif line[2:10] == "DOCSHELL":
+          self._logDocShell(line)
+    elif message['action'] == 'test_start':
+      fileName = message['test'].replace("chrome://mochitests/content/browser/", "")
       self.currentTest = {"fileName": fileName, "windows": set(), "docShells": set()}
-    elif line.startswith("INFO TEST-END"):
+    elif message['action'] == 'test_end':
       # don't track a test if no windows or docShells leaked
       if self.currentTest and (self.currentTest["windows"] or self.currentTest["docShells"]):
         self.tests.append(self.currentTest)
       self.currentTest = None
-    elif line.startswith("INFO TEST-START | Shutdown"):
+    elif message['action'] == 'suite_end':
       self.seenShutdown = True
 
   def process(self):
     for test in self._parseLeakingTests():
       for url, count in self._zipLeakedWindows(test["leakedWindows"]):
-        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d window(s) until shutdown [url = %s]", test["fileName"], count, url)
+        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d window(s) until shutdown [url = %s]" % (test["fileName"], count, url))
 
       if test["leakedDocShells"]:
-        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d docShell(s) until shutdown", test["fileName"], len(test["leakedDocShells"]))
+        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d docShell(s) until shutdown" % (test["fileName"], len(test["leakedDocShells"])))
 
   def _logWindow(self, line):
     created = line[:2] == "++"
     pid = self._parseValue(line, "pid")
     serial = self._parseValue(line, "serial")
 
     # log line has invalid format
     if not pid or not serial:
-      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>", line)
+      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line)
       return
 
     key = pid + "." + serial
 
     if self.currentTest:
       windows = self.currentTest["windows"]
       if created:
         windows.add(key)
@@ -649,17 +651,17 @@ class ShutdownLeaks(object):
 
   def _logDocShell(self, line):
     created = line[:2] == "++"
     pid = self._parseValue(line, "pid")
     id = self._parseValue(line, "id")
 
     # log line has invalid format
     if not pid or not id:
-      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>", line)
+      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line)
       return
 
     key = pid + "." + id
 
     if self.currentTest:
       docShells = self.currentTest["docShells"]
       if created:
         docShells.add(key)
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -16,21 +16,23 @@ import mozcrash
 # signatures for logcat messages that we don't care about much
 fennecLogcatFilters = [ "The character encoding of the HTML document was not declared",
                         "Use of Mutation Events is deprecated. Use MutationObserver instead.",
                         "Unexpected value from nativeGetEnabledTags: 0" ]
 
 class RemoteAutomation(Automation):
     _devicemanager = None
 
-    def __init__(self, deviceManager, appName = '', remoteLog = None):
+    def __init__(self, deviceManager, appName = '', remoteLog = None,
+                 processArgs=None):
         self._devicemanager = deviceManager
         self._appName = appName
         self._remoteProfile = None
         self._remoteLog = remoteLog
+        self._processArgs = processArgs or {};
 
         # Default our product to fennec
         self._product = "fennec"
         self.lastTestSeen = "remoteautomation.py"
         Automation.__init__(self)
 
     def setDeviceManager(self, deviceManager):
         self._devicemanager = deviceManager
@@ -179,116 +181,142 @@ class RemoteAutomation(Automation):
 #TODO: figure out which platform require NO_EM_RESTART
 #        return app, ['--environ:NO_EM_RESTART=1'] + args
         return app, args
 
     def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = None):
         if stdout == None or stdout == -1 or stdout == subprocess.PIPE:
             stdout = self._remoteLog
 
-        return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd, self._appName)
+        return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd, self._appName,
+                             **self._processArgs)
 
     # be careful here as this inner class doesn't have access to outer class members
     class RProcess(object):
         # device manager process
         dm = None
-        def __init__(self, dm, cmd, stdout = None, stderr = None, env = None, cwd = None, app = None):
+        def __init__(self, dm, cmd, stdout=None, stderr=None, env=None, cwd=None, app=None,
+                     messageLogger=None):
             self.dm = dm
             self.stdoutlen = 0
             self.lastTestSeen = "remoteautomation.py"
             self.proc = dm.launchProcess(cmd, stdout, cwd, env, True)
+            self.messageLogger = messageLogger
+
             if (self.proc is None):
                 if cmd[0] == 'am':
                     self.proc = stdout
                 else:
                     raise Exception("unable to launch process")
             self.procName = cmd[0].split('/')[-1]
             if cmd[0] == 'am' and cmd[1] == "instrument":
                 self.procName = app
                 print "Robocop process name: "+self.procName
 
             # Setting timeout at 1 hour since on a remote device this takes much longer
             self.timeout = 3600
             # The benefit of the following sleep is unclear; it was formerly 15 seconds
             time.sleep(1)
 
+            # Used to buffer log messages until we meet a line break
+            self.logBuffer = ""
+
         @property
         def pid(self):
             pid = self.dm.processExist(self.procName)
             # HACK: we should probably be more sophisticated about monitoring
             # running processes for the remote case, but for now we'll assume
             # that this method can be called when nothing exists and it is not
             # an error
             if pid is None:
                 return 0
             return pid
 
-        @property
-        def stdout(self):
+        def read_stdout(self):
             """ Fetch the full remote log file using devicemanager and return just
-                the new log entries since the last call (as a multi-line string).
+                the new log entries since the last call (as a list of messages or lines).
             """
-            if self.dm.fileExists(self.proc):
-                try:
-                    newLogContent = self.dm.pullFile(self.proc, self.stdoutlen)
-                except DMError:
-                    # we currently don't retry properly in the pullFile
-                    # function in dmSUT, so an error here is not necessarily
-                    # the end of the world
-                    return ''
-                self.stdoutlen += len(newLogContent)
-                # Match the test filepath from the last TEST-START line found in the new
-                # log content. These lines are in the form:
-                # 1234 INFO TEST-START | /filepath/we/wish/to/capture.html\n
+            if not self.dm.fileExists(self.proc):
+                return []
+            try:
+                newLogContent = self.dm.pullFile(self.proc, self.stdoutlen)
+            except DMError:
+                # we currently don't retry properly in the pullFile
+                # function in dmSUT, so an error here is not necessarily
+                # the end of the world
+                return []
+            if not newLogContent:
+                return []
+
+            self.stdoutlen += len(newLogContent)
+
+            if self.messageLogger is None:
                 testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", newLogContent)
                 if testStartFilenames:
                     self.lastTestSeen = testStartFilenames[-1]
-                return newLogContent.strip('\n').strip()
-            else:
-                return ''
+                print newLogContent
+                return [newLogContent]
+
+            self.logBuffer += newLogContent
+            lines = self.logBuffer.split('\n')
+            if not lines:
+                return
+
+            # We only keep the last (unfinished) line in the buffer
+            self.logBuffer = lines[-1]
+            del lines[-1]
+            messages = []
+            for line in lines:
+                # This passes the line to the logger (to be logged or buffered)
+                # and returns a list of structured messages (dict) or None, depending on the log
+                parsed_messages = self.messageLogger.write(line)
+                for message in parsed_messages:
+                    if message['action'] == 'test_start':
+                        self.lastTestSeen = message['test']
+                messages += parsed_messages
+            return messages
 
         @property
         def getLastTestSeen(self):
             return self.lastTestSeen
 
         # Wait for the remote process to end (or for its activity to go to background).
         # While waiting, periodically retrieve the process output and print it.
         # If the process is still running after *timeout* seconds, return 1;
         # If the process is still running but no output is received in *noOutputTimeout*
         # seconds, return 2;
         # Else, once the process exits/goes to background, return 0.
         def wait(self, timeout = None, noOutputTimeout = None):
             timer = 0
             noOutputTimer = 0
-            interval = 20 
+            interval = 20
 
             if timeout == None:
                 timeout = self.timeout
 
             status = 0
             while (self.dm.getTopActivity() == self.procName):
                 # retrieve log updates every 60 seconds
-                if timer % 60 == 0: 
-                    t = self.stdout
-                    if t != '':
-                        print t
+                if timer % 60 == 0:
+                    messages = self.read_stdout()
+                    if messages:
                         noOutputTimer = 0
 
                 time.sleep(interval)
                 timer += interval
                 noOutputTimer += interval
                 if (timer > timeout):
                     status = 1
                     break
                 if (noOutputTimeout and noOutputTimer > noOutputTimeout):
                     status = 2
                     break
 
             # Flush anything added to stdout during the sleep
-            print self.stdout
+            self.read_stdout()
 
             return status
 
         def kill(self, stagedShutdown = False):
             if stagedShutdown:
                 # Trigger an ANR report with "kill -3" (SIGQUIT)
                 self.dm.killProcess(self.procName, 3)
                 time.sleep(3)
--- a/build/mobile/robocop/FennecMochitestAssert.java
+++ b/build/mobile/robocop/FennecMochitestAssert.java
@@ -7,17 +7,16 @@ package org.mozilla.gecko;
 import java.util.LinkedList;
 
 import android.os.SystemClock;
 
 public class FennecMochitestAssert implements Assert {
     private LinkedList<testInfo> mTestList = new LinkedList<testInfo>();
 
     // Internal state variables to make logging match up with existing mochitests
-    private int mLineNumber = 0;
     private int mPassed = 0;
     private int mFailed = 0;
     private int mTodo = 0;
 
     // Used to write the first line of the test file
     private boolean mLogStarted = false;
 
     // Used to write the test-start/test-end log lines
@@ -40,35 +39,35 @@ public class FennecMochitestAssert imple
     }
 
     /** Set the filename used for dumpLog. */
     public void setLogFile(String filename) {
         FennecNativeDriver.setLogFile(filename);
 
         String message;
         if (!mLogStarted) {
-            dumpLog(Integer.toString(mLineNumber++) + " INFO SimpleTest START");
+            dumpLog("SimpleTest START");
             mLogStarted = true;
         }
 
         if (mLogTestName != "") {
             long diff = SystemClock.uptimeMillis() - mStartTime;
-            message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+            message = "TEST-END | " + mLogTestName;
             message += " | finished in " + diff + "ms";
             dumpLog(message);
             mLogTestName = "";
         }
     }
 
     public void setTestName(String testName) {
         String[] nameParts = testName.split("\\.");
         mLogTestName = nameParts[nameParts.length - 1];
         mStartTime = SystemClock.uptimeMillis();
 
-        dumpLog(Integer.toString(mLineNumber++) + " INFO TEST-START | " + mLogTestName);
+        dumpLog("TEST-START | " + mLogTestName);
     }
 
     class testInfo {
         public boolean mResult;
         public String mName;
         public String mDiag;
         public boolean mTodo;
         public boolean mInfo;
@@ -90,17 +89,17 @@ public class FennecMochitestAssert imple
         }
         if (test.mResult)
         {
             resultString = passString;
         }
         String diag = test.mName;
         if (test.mDiag != null) diag += " - " + test.mDiag;
 
-        String message = Integer.toString(mLineNumber++) + " INFO " + resultString + " | " + mLogTestName + " | " + diag;
+        String message = resultString + " | " + mLogTestName + " | " + diag;
         dumpLog(message);
 
         if (test.mInfo) {
             // do not count TEST-INFO messages
         } else if (test.mTodo) {
             mTodo++;
         } else if (isError) {
             mFailed++;
@@ -112,31 +111,31 @@ public class FennecMochitestAssert imple
         }
     }
 
     public void endTest() {
         String message;
 
         if (mLogTestName != "") {
             long diff = SystemClock.uptimeMillis() - mStartTime;
-            message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+            message = "TEST-END | " + mLogTestName;
             message += " | finished in " + diff + "ms";
             dumpLog(message);
             mLogTestName = "";
         }
 
-        message = Integer.toString(mLineNumber++) + " INFO TEST-START | Shutdown";
+        message = "TEST-START | Shutdown";
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO Passed: " + Integer.toString(mPassed);
+        message = "Passed: " + Integer.toString(mPassed);
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO Failed: " + Integer.toString(mFailed);
+        message = "Failed: " + Integer.toString(mFailed);
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO Todo: " + Integer.toString(mTodo);
+        message = "Todo: " + Integer.toString(mTodo);
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO SimpleTest FINISHED";
+        message = "SimpleTest FINISHED";
         dumpLog(message);
     }
 
     public void ok(boolean condition, String name, String diag) {
         testInfo test = new testInfo(condition, name, diag, false, false);
         _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
         mTestList.add(test);
     }
--- a/configure.in
+++ b/configure.in
@@ -499,16 +499,18 @@ case "$target" in
         fi
         AC_SUBST(MSVS_VERSION)
         AC_SUBST(MSVC_C_RUNTIME_DLL)
         AC_SUBST(MSVC_CXX_RUNTIME_DLL)
 
         # Disable SEH on clang-cl because it doesn't implement them yet.
         if test -z "$CLANG_CL"; then
             AC_DEFINE(HAVE_SEH_EXCEPTIONS)
+        else
+            AC_DEFINE_UNQUOTED(GTEST_HAS_SEH, 0)
         fi
 
         if test -n "$WIN32_REDIST_DIR"; then
           if test ! -d "$WIN32_REDIST_DIR"; then
             AC_MSG_ERROR([Invalid Win32 Redist directory: ${WIN32_REDIST_DIR}])
           fi
           WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd`
         fi
@@ -3871,17 +3873,16 @@ MOZ_LOCALE_SWITCHER=
 MOZ_ANDROID_SEARCH_ACTIVITY=
 MOZ_ANDROID_MLS_STUMBLER=
 ACCESSIBILITY=1
 MOZ_TIME_MANAGER=
 MOZ_PAY=
 MOZ_AUDIO_CHANNEL_MANAGER=
 NSS_NO_LIBPKIX=
 MOZ_CONTENT_SANDBOX=
-MOZ_CONTENT_SANDBOX_REPORTER=1
 JSGC_USE_EXACT_ROOTING=
 JSGC_GENERATIONAL=
 
 case "$target_os" in
     mingw*)
         NS_ENABLE_TSF=1
         AC_DEFINE(NS_ENABLE_TSF)
         ;;
@@ -6437,27 +6438,16 @@ MOZ_ARG_ENABLE_BOOL(content-sandbox,
     MOZ_CONTENT_SANDBOX=)
 
 if test -n "$MOZ_CONTENT_SANDBOX"; then
     AC_DEFINE(MOZ_CONTENT_SANDBOX)
 fi
 
 AC_SUBST(MOZ_CONTENT_SANDBOX)
 
-MOZ_ARG_ENABLE_BOOL(content-sandbox-reporter,
-[ --enable-content-sandbox-reporter        Enable syscall reporter to troubleshoot syscalls denied by the content-processes sandbox],
-    MOZ_CONTENT_SANDBOX_REPORTER=1,
-    MOZ_CONTENT_SANDBOX_REPORTER=)
-
-if test -n "$MOZ_CONTENT_SANDBOX_REPORTER"; then
-    AC_DEFINE(MOZ_CONTENT_SANDBOX_REPORTER)
-fi
-
-AC_SUBST(MOZ_CONTENT_SANDBOX_REPORTER)
-
 dnl ========================================================
 dnl =
 dnl = Module specific options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Individual module options)
 
 dnl ========================================================
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2986,16 +2986,24 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
       nsAutoCString scheme;
 
       rv = mChannel->GetURI(getter_AddRefs(uri));
       if (NS_SUCCEEDED(rv)) {
         uri->GetScheme(scheme);
         if (scheme.LowerCaseEqualsLiteral("app") ||
             scheme.LowerCaseEqualsLiteral("jar")) {
           mIsMappedArrayBuffer = true;
+          if (XRE_GetProcessType() != GeckoProcessType_Default) {
+            nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(mChannel);
+            // For memory mapping from child process, we need to get file
+            // descriptor of the JAR file opened remotely on the parent proess.
+            // Set this to make sure that file descriptor can be obtained by
+            // child process.
+            jarChannel->EnsureChildFd();
+          }
         }
       }
     }
     // Start reading from the channel
     rv = mChannel->AsyncOpen(listener, nullptr);
   }
 
   if (NS_FAILED(rv)) {
--- a/content/base/test/test_fileapi.html
+++ b/content/base/test/test_fileapi.html
@@ -144,17 +144,16 @@ is(r.readyState, FileReader.EMPTY,
   "readyState in test reader get result without reading");
 is(r.error, null,
   "no error in test reader get result without reading");
 is(r.result, null,
   "result in test reader get result without reading");
 
 // Test loading an empty file works (and doesn't crash!)
 var emptyFile = createFileWithData("");
-dump("hello nurse");
 r = new FileReader();
 r.onload = getLoadHandler("", 0, "empty no encoding reading");
 r.readAsText(emptyFile, "");
 expectedTestCount++;
 
 r = new FileReader();
 r.onload = getLoadHandler("", 0, "empty utf8 reading");
 r.readAsText(emptyFile, "utf8");
@@ -422,18 +421,18 @@ function getLoadHandlerForArrayBuffer(ex
     testHasRun();
   }
 }
 
 function testHasRun() {
  //alert(testRanCounter);
  ++testRanCounter;
  if (testRanCounter == expectedTestCount) {
-    is(onloadHasRunText, true, "onload text should have fired by now"); 
-    is(onloadHasRunBinary, true, "onload binary should have fired by now"); 
+    is(onloadHasRunText, true, "onload text should have fired by now");
+    is(onloadHasRunBinary, true, "onload binary should have fired by now");
     SimpleTest.finish();
   }
 }
 
 function createFileWithData(fileData) {
   var dirSvc = SpecialPowers.Cc["@mozilla.org/file/directory_service;1"].getService(SpecialPowers.Ci.nsIProperties);
   var testFile = dirSvc.get("ProfD", SpecialPowers.Ci.nsIFile);
   testFile.append("fileAPItestfile" + fileNum);
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -307,29 +307,16 @@ AudioNodeStream::ObtainInputBlock(AudioC
     }
     MediaStream* s = mInputs[i]->GetSource();
     AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
     MOZ_ASSERT(a == s->AsAudioNodeStream());
     if (a->IsAudioParamStream()) {
       continue;
     }
 
-    // It is possible for mLastChunks to be empty here, because `a` might be a
-    // AudioNodeStream that has not been scheduled yet, because it is further
-    // down the graph _but_ as a connection to this node. Because we enforce the
-    // presence of at least one DelayNode, with at least one block of delay, and
-    // because the output of a DelayNode when it has been fed less that
-    // `delayTime` amount of audio is silence, we can simply continue here,
-    // because this input would not influence the output of this node. Next
-    // iteration, a->mLastChunks.IsEmpty() will be false, and everthing will
-    // work as usual.
-    if (a->mLastChunks.IsEmpty()) {
-      continue;
-    }
-
     AudioChunk* chunk = &a->mLastChunks[mInputs[i]->OutputNumber()];
     MOZ_ASSERT(chunk);
     if (chunk->IsNull() || chunk->mChannelData.IsEmpty()) {
       continue;
     }
 
     inputChunks.AppendElement(chunk);
     outputChannelCount =
@@ -448,17 +435,17 @@ AudioNodeStream::ProcessInput(GraphTime 
   uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
   mLastChunks.SetLength(outputCount);
 
   // Consider this stream blocked if it has already finished output. Normally
   // mBlocked would reflect this, but due to rounding errors our audio track may
   // appear to extend slightly beyond aFrom, so we might not be blocked yet.
   bool blocked = mFinished || mBlocked.GetAt(aFrom);
   // If the stream has finished at this time, it will be blocked.
-  if (mMuted || blocked) {
+  if (blocked || InMutedCycle()) {
     for (uint16_t i = 0; i < outputCount; ++i) {
       mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
   } else {
     // We need to generate at least one input
     uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
     OutputChunks inputChunks;
     inputChunks.SetLength(maxInputs);
@@ -494,16 +481,42 @@ AudioNodeStream::ProcessInput(GraphTime 
       // of the depending streams have finished their output as well, so now
       // it's time to mark this stream as finished.
       FinishOutput();
     }
   }
 }
 
 void
+AudioNodeStream::ProduceOutputBeforeInput(GraphTime aFrom)
+{
+  MOZ_ASSERT(mEngine->AsDelayNodeEngine());
+  MOZ_ASSERT(mEngine->OutputCount() == 1,
+             "DelayNodeEngine output count should be 1");
+  MOZ_ASSERT(!InMutedCycle(), "DelayNodes should break cycles");
+  mLastChunks.SetLength(1);
+
+  // Consider this stream blocked if it has already finished output. Normally
+  // mBlocked would reflect this, but due to rounding errors our audio track may
+  // appear to extend slightly beyond aFrom, so we might not be blocked yet.
+  bool blocked = mFinished || mBlocked.GetAt(aFrom);
+  // If the stream has finished at this time, it will be blocked.
+  if (blocked) {
+    mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+  } else {
+    mEngine->ProduceBlockBeforeInput(&mLastChunks[0]);
+    NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE,
+                 "Invalid WebAudio chunk size");
+    if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
+      mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+    }
+  }
+}
+
+void
 AudioNodeStream::AdvanceOutputSegment()
 {
   StreamBuffer::Track* track = EnsureTrack(AUDIO_TRACK, mSampleRate);
   AudioSegment* segment = track->Get<AudioSegment>();
 
   if (mKind == MediaStreamGraph::EXTERNAL_STREAM) {
     segment->AppendAndConsumeChunk(&mLastChunks[0]);
   } else {
--- a/content/media/AudioNodeStream.h
+++ b/content/media/AudioNodeStream.h
@@ -10,17 +10,16 @@
 #include "mozilla/dom/AudioNodeBinding.h"
 #include "AudioSegment.h"
 
 namespace mozilla {
 
 namespace dom {
 struct ThreeDPoint;
 class AudioParamTimeline;
-class DelayNodeEngine;
 class AudioContext;
 }
 
 class ThreadSharedFloatArrayBufferList;
 class AudioNodeEngine;
 
 /**
  * An AudioNodeStream produces one audio track with ID AUDIO_TRACK.
@@ -50,18 +49,17 @@ public:
                   MediaStreamGraph::AudioNodeStreamKind aKind,
                   TrackRate aSampleRate)
     : ProcessedMediaStream(nullptr),
       mEngine(aEngine),
       mSampleRate(aSampleRate),
       mKind(aKind),
       mNumberOfInputChannels(2),
       mMarkAsFinishedAfterThisBlock(false),
-      mAudioParamStream(false),
-      mMuted(false)
+      mAudioParamStream(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
     mChannelCountMode = ChannelCountMode::Max;
     mChannelInterpretation = ChannelInterpretation::Speakers;
     // AudioNodes are always producing data
     mHasCurrentData = true;
     MOZ_COUNT_CTOR(AudioNodeStream);
   }
@@ -102,28 +100,27 @@ public:
 
   // Graph thread only
   void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
                                   double aStreamTime);
   void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
                                       ChannelCountMode aChannelCountMoe,
                                       ChannelInterpretation aChannelInterpretation);
   virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
+  /**
+   * Produce the next block of output, before input is provided.
+   * ProcessInput() will be called later, and it then should not change
+   * the output.  This is used only for DelayNodeEngine in a feedback loop.
+   */
+  void ProduceOutputBeforeInput(GraphTime aFrom);
   TrackTicks GetCurrentPosition();
   bool IsAudioParamStream() const
   {
     return mAudioParamStream;
   }
-  void Mute() {
-    mMuted = true;
-  }
-
-  void Unmute() {
-    mMuted = false;
-  }
 
   const OutputChunks& LastChunks() const
   {
     return mLastChunks;
   }
   virtual bool MainThreadNeedsUpdates() const MOZ_OVERRIDE
   {
     // Only source and external streams need updates on the main thread.
@@ -190,15 +187,13 @@ protected:
   // The mixing modes
   ChannelCountMode mChannelCountMode;
   ChannelInterpretation mChannelInterpretation;
   // Whether the stream should be marked as finished as soon
   // as the current time range has been computed block by block.
   bool mMarkAsFinishedAfterThisBlock;
   // Whether the stream is an AudioParamHelper stream.
   bool mAudioParamStream;
-  // Whether the stream is muted. Access only on the MediaStreamGraph thread.
-  bool mMuted;
 };
 
 }
 
 #endif /* MOZILLA_AUDIONODESTREAM_H_ */
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -253,17 +253,18 @@ AudioStream::AudioStream()
 {
   // keep a ref in case we shut down later than nsLayoutStatics
   mLatencyLog = AsyncLatencyLogger::Get(true);
 }
 
 AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
-  Shutdown();
+  MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
+             "Should've called Shutdown() before deleting an AudioStream");
   if (mDumpFile) {
     fclose(mDumpFile);
   }
 }
 
 size_t
 AudioStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
@@ -312,27 +313,28 @@ nsresult AudioStream::EnsureTimeStretche
     mTimeStretcher->setChannels(mOutChannels);
     mTimeStretcher->setPitch(1.0);
   }
   return NS_OK;
 }
 
 nsresult AudioStream::SetPlaybackRate(double aPlaybackRate)
 {
+  // MUST lock since the rate transposer is used from the cubeb callback,
+  // and rate changes can cause the buffer to be reallocated
+  MonitorAutoLock mon(mMonitor);
+
   NS_ASSERTION(aPlaybackRate > 0.0,
                "Can't handle negative or null playbackrate in the AudioStream.");
   // Avoid instantiating the resampler if we are not changing the playback rate.
   // GetPreservesPitch/SetPreservesPitch don't need locking before calling
   if (aPlaybackRate == mAudioClock.GetPlaybackRate()) {
     return NS_OK;
   }
 
-  // MUST lock since the rate transposer is used from the cubeb callback,
-  // and rate changes can cause the buffer to be reallocated
-  MonitorAutoLock mon(mMonitor);
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return NS_ERROR_FAILURE;
   }
 
   mAudioClock.SetPlaybackRateUnlocked(aPlaybackRate);
   mOutRate = mInRate / aPlaybackRate;
 
   if (mAudioClock.GetPreservesPitch()) {
@@ -342,24 +344,25 @@ nsresult AudioStream::SetPlaybackRate(do
     mTimeStretcher->setTempo(1.0f);
     mTimeStretcher->setRate(aPlaybackRate);
   }
   return NS_OK;
 }
 
 nsresult AudioStream::SetPreservesPitch(bool aPreservesPitch)
 {
+  // MUST lock since the rate transposer is used from the cubeb callback,
+  // and rate changes can cause the buffer to be reallocated
+  MonitorAutoLock mon(mMonitor);
+
   // Avoid instantiating the timestretcher instance if not needed.
   if (aPreservesPitch == mAudioClock.GetPreservesPitch()) {
     return NS_OK;
   }
 
-  // MUST lock since the rate transposer is used from the cubeb callback,
-  // and rate changes can cause the buffer to be reallocated
-  MonitorAutoLock mon(mMonitor);
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return NS_ERROR_FAILURE;
   }
 
   if (aPreservesPitch == true) {
     mTimeStretcher->setTempo(mAudioClock.GetPlaybackRate());
     mTimeStretcher->setRate(1.0f);
   } else {
@@ -369,16 +372,17 @@ nsresult AudioStream::SetPreservesPitch(
 
   mAudioClock.SetPreservesPitch(aPreservesPitch);
 
   return NS_OK;
 }
 
 int64_t AudioStream::GetWritten()
 {
+  MonitorAutoLock mon(mMonitor);
   return mWritten;
 }
 
 /*static*/ int AudioStream::MaxNumberOfChannels()
 {
   cubeb* cubebContext = GetCubebContext();
   uint32_t maxNumberOfChannels;
   if (cubebContext &&
@@ -528,17 +532,20 @@ AudioStream::Init(int32_t aNumChannels, 
     RefPtr<AudioInitTask> init = new AudioInitTask(this, aLatencyRequest, params);
     init->Dispatch();
     return NS_OK;
   }
   // High latency - open synchronously
   nsresult rv = OpenCubeb(params, aLatencyRequest);
   // See if we need to start() the stream, since we must do that from this
   // thread for now (cubeb API issue)
-  CheckForStart();
+  {
+    MonitorAutoLock mon(mMonitor);
+    CheckForStart();
+  }
   return rv;
 }
 
 // This code used to live inside AudioStream::Init(), but on Mac (others?)
 // it has been known to take 300-800 (or even 8500) ms to execute(!)
 nsresult
 AudioStream::OpenCubeb(cubeb_stream_params &aParams,
                        LatencyRequest aLatencyRequest)
@@ -595,16 +602,17 @@ AudioStream::OpenCubeb(cubeb_stream_para
   }
 
   return NS_OK;
 }
 
 void
 AudioStream::CheckForStart()
 {
+  mMonitor.AssertCurrentThreadOwns();
   if (mState == INITIALIZED) {
     // Start the stream right away when low latency has been requested. This means
     // that the DataCallback will feed silence to cubeb, until the first frames
     // are written to this AudioStream.  Also start if a start has been queued.
     if (mLatencyRequest == LowLatency || mNeedsStart) {
       StartUnlocked(); // mState = STARTED or ERRORED
       mNeedsStart = false;
       PR_LOG(gAudioStreamLog, PR_LOG_WARNING,
@@ -774,19 +782,22 @@ AudioStream::Start()
 void
 AudioStream::StartUnlocked()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (!mCubebStream) {
     mNeedsStart = true;
     return;
   }
-  MonitorAutoUnlock mon(mMonitor);
   if (mState == INITIALIZED) {
-    int r = cubeb_stream_start(mCubebStream);
+    int r;
+    {
+      MonitorAutoUnlock mon(mMonitor);
+      r = cubeb_stream_start(mCubebStream);
+    }
     mState = r == CUBEB_OK ? STARTED : ERRORED;
     LOG(("AudioStream: started %p, state %s", this, mState == STARTED ? "STARTED" : "ERRORED"));
   }
 }
 
 void
 AudioStream::Pause()
 {
@@ -823,31 +834,29 @@ AudioStream::Resume()
   if (mState != ERRORED && r == CUBEB_OK) {
     mState = STARTED;
   }
 }
 
 void
 AudioStream::Shutdown()
 {
+  MonitorAutoLock mon(mMonitor);
   LOG(("AudioStream: Shutdown %p, state %d", this, mState));
-  {
-    MonitorAutoLock mon(mMonitor);
-    if (mState == STARTED || mState == RUNNING) {
-      MonitorAutoUnlock mon(mMonitor);
-      Pause();
-    }
-    MOZ_ASSERT(mState != STARTED && mState != RUNNING); // paranoia
-    mState = SHUTDOWN;
-  }
-  // Must not try to shut down cubeb from within the lock!  wasapi may still
-  // call our callback after Pause()/stop()!?! Bug 996162
+
   if (mCubebStream) {
+    MonitorAutoUnlock mon(mMonitor);
+    // Force stop to put the cubeb stream in a stable state before deletion.
+    cubeb_stream_stop(mCubebStream);
+    // Must not try to shut down cubeb from within the lock!  wasapi may still
+    // call our callback after Pause()/stop()!?! Bug 996162
     mCubebStream.reset();
   }
+
+  mState = SHUTDOWN;
 }
 
 int64_t
 AudioStream::GetPosition()
 {
   MonitorAutoLock mon(mMonitor);
   return mAudioClock.GetPositionUnlocked();
 }
@@ -902,16 +911,17 @@ AudioStream::IsPaused()
 {
   MonitorAutoLock mon(mMonitor);
   return mState == STOPPED;
 }
 
 void
 AudioStream::GetBufferInsertTime(int64_t &aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   if (mInserts.Length() > 0) {
     // Find the right block, but don't leave the array empty
     while (mInserts.Length() > 1 && mReadPoint >= mInserts[0].mFrames) {
       mReadPoint -= mInserts[0].mFrames;
       mInserts.RemoveElementAt(0);
     }
     // offset for amount already read
     // XXX Note: could misreport if we couldn't find a block in the right timeframe
@@ -919,16 +929,17 @@ AudioStream::GetBufferInsertTime(int64_t
   } else {
     aTimeMs = INT64_MAX;
   }
 }
 
 long
 AudioStream::GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
 
   // Flush the timestretcher pipeline, if we were playing using a playback rate
   // other than 1.0.
   uint32_t flushedFrames = 0;
   if (mTimeStretcher && mTimeStretcher->numSamples()) {
     flushedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames);
     wpos += FramesToBytes(flushedFrames);
@@ -950,16 +961,17 @@ AudioStream::GetUnprocessed(void* aBuffe
   return BytesToFrames(available) + flushedFrames;
 }
 
 // Get unprocessed samples, and pad the beginning of the buffer with silence if
 // there is not enough data.
 long
 AudioStream::GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t& aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   uint32_t toPopBytes = FramesToBytes(aFrames);
   uint32_t available = std::min(toPopBytes, mBuffer.Length());
   uint32_t silenceOffset = toPopBytes - available;
 
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
 
   memset(wpos, 0, silenceOffset);
   wpos += silenceOffset;
@@ -974,16 +986,17 @@ AudioStream::GetUnprocessedWithSilencePa
   GetBufferInsertTime(aTimeMs);
 
   return aFrames;
 }
 
 long
 AudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   long processedFrames = 0;
 
   // We need to call the non-locking version, because we already have the lock.
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return 0;
   }
 
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
@@ -1016,16 +1029,17 @@ AudioStream::GetTimeStretched(void* aBuf
 
   return processedFrames;
 }
 
 long
 AudioStream::DataCallback(void* aBuffer, long aFrames)
 {
   MonitorAutoLock mon(mMonitor);
+  MOZ_ASSERT(mState != SHUTDOWN, "No data callback after shutdown");
   uint32_t available = std::min(static_cast<uint32_t>(FramesToBytes(aFrames)), mBuffer.Length());
   NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames");
   AudioDataValue* output = reinterpret_cast<AudioDataValue*>(aBuffer);
   uint32_t underrunFrames = 0;
   uint32_t servicedFrames = 0;
   int64_t insertTime;
 
   // NOTE: wasapi (others?) can call us back *after* stop()/Shutdown() (mState == SHUTDOWN)
@@ -1126,16 +1140,18 @@ AudioStream::DataCallback(void* aBuffer,
 
   return servicedFrames;
 }
 
 void
 AudioStream::StateCallback(cubeb_state aState)
 {
   MonitorAutoLock mon(mMonitor);
+  MOZ_ASSERT(mState != SHUTDOWN, "No state callback after shutdown");
+  LOG(("AudioStream: StateCallback %p, mState=%d cubeb_state=%d", this, mState, aState));
   if (aState == CUBEB_STATE_DRAINED) {
     mState = DRAINED;
   } else if (aState == CUBEB_STATE_ERROR) {
     LOG(("AudioStream::StateCallback() state %d cubeb error", mState));
     mState = ERRORED;
   }
   mon.NotifyAll();
 }
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/dom/RecordErrorEvent.h"
 #include "nsTArray.h"
 #include "DOMMediaStream.h"
 #include "EncodedBufferCache.h"
 #include "nsIDOMFile.h"
 #include "mozilla/dom/BlobEvent.h"
 #include "nsIPrincipal.h"
 #include "nsMimeTypes.h"
+#include "nsProxyRelease.h"
 
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaRecorderLog;
 #define LOG(type, msg) PR_LOG(gMediaRecorderLog, type, msg)
 #else
@@ -144,32 +145,48 @@ class MediaRecorder::Session: public nsI
     nsString mEventName;
   };
 
   // Record thread task and it run in Media Encoder thread.
   // Fetch encoded Audio/Video data from MediaEncoder.
   class ExtractRunnable : public nsRunnable
   {
   public:
-    ExtractRunnable(Session *aSession)
+    ExtractRunnable(already_AddRefed<Session>&& aSession)
       : mSession(aSession) {}
 
+    ~ExtractRunnable()
+    {
+      if (mSession) {
+        NS_WARNING("~ExtractRunnable something wrong the mSession should null");
+        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+        NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
+        NS_ProxyRelease(mainThread, mSession);
+      }
+    }
+
     NS_IMETHODIMP Run()
     {
       MOZ_ASSERT(NS_GetCurrentThread() == mSession->mReadThread);
 
       LOG(PR_LOG_DEBUG, ("Session.ExtractRunnable shutdown = %d", mSession->mEncoder->IsShutdown()));
       if (!mSession->mEncoder->IsShutdown()) {
         mSession->Extract(false);
-        NS_DispatchToCurrentThread(new ExtractRunnable(mSession));
+        nsRefPtr<nsIRunnable> event = new ExtractRunnable(mSession.forget());
+        if (NS_FAILED(NS_DispatchToCurrentThread(event))) {
+          NS_WARNING("Failed to dispatch ExtractRunnable to encoder thread");
+        }
       } else {
         // Flush out remaining encoded data.
         mSession->Extract(true);
         // Destroy this Session object in main thread.
-        NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(mSession)));
+        if (NS_FAILED(NS_DispatchToMainThread(
+                        new DestroyRunnable(mSession.forget())))) {
+          MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+        }
       }
       return NS_OK;
     }
 
   private:
     nsRefPtr<Session> mSession;
   };
 
@@ -224,17 +241,20 @@ class MediaRecorder::Session: public nsI
       // We need to switch MediaRecorder to "Stop" state first to make sure
       // MediaRecorder is not associated with this Session anymore, then, it's
       // safe to delete this Session.
       // Also avoid to run if this session already call stop before
       if (!mSession->mStopIssued) {
         ErrorResult result;
         mSession->mStopIssued = true;
         recorder->Stop(result);
-        NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
+        if (NS_FAILED(NS_DispatchToMainThread(
+                        new DestroyRunnable(mSession.forget())))) {
+          MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
+        }
         return NS_OK;
       }
 
       // Dispatch stop event and clear MIME type.
       mSession->mMimeType = NS_LITERAL_STRING("");
       recorder->SetMimeType(mSession->mMimeType);
       recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
       recorder->RemoveSession(mSession);
@@ -256,17 +276,16 @@ public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
     : mRecorder(aRecorder),
       mTimeSlice(aTimeSlice),
       mStopIssued(false),
       mCanRetrieveData(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    AddRef();
     mEncodedBufferCache = new EncodedBufferCache(MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
 
   void Start()
   {
     LOG(PR_LOG_DEBUG, ("Session.Start %p", this));
     MOZ_ASSERT(NS_IsMainThread());
@@ -355,18 +374,21 @@ private:
     // Whether push encoded data back to onDataAvailable automatically or we
     // need a flush.
     bool pushBlob = false;
     if ((mTimeSlice > 0) &&
         ((TimeStamp::Now()-mLastBlobTimeStamp).ToMilliseconds() > mTimeSlice)) {
       pushBlob = true;
     }
     if (pushBlob || aForceFlush) {
-      NS_DispatchToMainThread(new PushBlobRunnable(this));
-      mLastBlobTimeStamp = TimeStamp::Now();
+      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
+      } else {
+        mLastBlobTimeStamp = TimeStamp::Now();
+      }
     }
   }
 
   // Bind media source with MediaEncoder to receive raw media data.
   void SetupStreams()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -427,30 +449,37 @@ private:
         return;
       }
     }
 
     // In case source media stream does not notify track end, recieve
     // shutdown notification and stop Read Thread.
     nsContentUtils::RegisterShutdownObserver(this);
 
-    mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
+    nsRefPtr<nsIRunnable> event = new ExtractRunnable(this);
+    if (NS_FAILED(mReadThread->Dispatch(event, NS_DISPATCH_NORMAL))) {
+      NS_WARNING("Failed to dispatch ExtractRunnable at beginning");
+    }
   }
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
     if (NS_FAILED(rv)) {
       mRecorder->NotifyError(rv);
     }
 
     CleanupStreams();
+    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+      MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
+    }
     // Destroy this session object in main thread.
-    NS_DispatchToMainThread(new PushBlobRunnable(this));
-    NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(this)));
+    if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
+      MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+    }
   }
   void CleanupStreams()
   {
     if (mInputPort.get()) {
       mInputPort->Destroy();
       mInputPort = nullptr;
     }
 
@@ -603,20 +632,27 @@ MediaRecorder::Start(const Optional<int3
       aResult.Throw(NS_ERROR_INVALID_ARG);
       return;
     }
 
     timeSlice = aTimeSlice.Value();
   }
 
   mState = RecordingState::Recording;
-  // Start a session
-
+  // Start a session.
+  // Add session's reference here and pass to ExtractRunnable since the
+  // MediaRecorder doesn't hold any reference to Session. Also
+  // the DoSessionEndTask need this reference for DestroyRunnable.
+  // Note that the reference count is not balance here due to the
+  // DestroyRunnable will destroyed the last reference.
+  nsRefPtr<Session> session = new Session(this, timeSlice);
+  Session* rawPtr;
+  session.forget(&rawPtr);
   mSessions.AppendElement();
-  mSessions.LastElement() = new Session(this, timeSlice);
+  mSessions.LastElement() = rawPtr;
   mSessions.LastElement()->Start();
 }
 
 void
 MediaRecorder::Stop(ErrorResult& aResult)
 {
   LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
   if (mState == RecordingState::Inactive) {
@@ -686,19 +722,21 @@ CreateAndDispatchBlobEventRunnable::Run(
 void
 MediaRecorder::RequestData(ErrorResult& aResult)
 {
   if (mState != RecordingState::Recording) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   MOZ_ASSERT(mSessions.Length() > 0);
-  NS_DispatchToMainThread(
-    new CreateAndDispatchBlobEventRunnable(mSessions.LastElement()->GetEncodedData(),
-                                           this));
+  if (NS_FAILED(NS_DispatchToMainThread(
+                  new CreateAndDispatchBlobEventRunnable(
+                    mSessions.LastElement()->GetEncodedData(), this)))) {
+    MOZ_ASSERT(false, "NS_DispatchToMainThread CreateAndDispatchBlobEventRunnable failed");
+  }
 }
 
 JSObject*
 MediaRecorder::WrapObject(JSContext* aCx)
 {
   return MediaRecorderBinding::Wrap(aCx, this);
 }
 
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1,15 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaStreamGraphImpl.h"
-#include "mozilla/LinkedList.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
@@ -85,17 +84,17 @@ MediaStreamGraphImpl::FinishStream(Media
 
   SetStreamOrderDirty();
 }
 
 void
 MediaStreamGraphImpl::AddStream(MediaStream* aStream)
 {
   aStream->mBufferStartTime = mCurrentTime;
-  *mStreams.AppendElement() = already_AddRefed<MediaStream>(aStream);
+  mStreams.AppendElement(aStream);
   STREAM_LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph", aStream));
 
   SetStreamOrderDirty();
 }
 
 void
 MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
 {
@@ -108,18 +107,18 @@ MediaStreamGraphImpl::RemoveStream(Media
       if (mStreamUpdates[i].mStream == aStream) {
         mStreamUpdates[i].mStream = nullptr;
       }
     }
   }
 
   SetStreamOrderDirty();
 
-  // This unrefs the stream, probably destroying it
   mStreams.RemoveElement(aStream);
+  NS_RELEASE(aStream); // probably destroying it
 
   STREAM_LOG(PR_LOG_DEBUG, ("Removing media stream %p from the graph", aStream));
 }
 
 void
 MediaStreamGraphImpl::UpdateConsumptionState(SourceMediaStream* aStream)
 {
   MediaStreamListener::Consumption state =
@@ -525,80 +524,16 @@ MediaStreamGraphImpl::MarkConsumed(Media
     return;
   }
   // Mark all the inputs to this stream as consumed
   for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
     MarkConsumed(ps->mInputs[i]->mSource);
   }
 }
 
-void
-MediaStreamGraphImpl::UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream>* aStack,
-                                                 already_AddRefed<MediaStream> aStream)
-{
-  nsRefPtr<MediaStream> stream = aStream;
-  NS_ASSERTION(!stream->mHasBeenOrdered, "stream should not have already been ordered");
-  if (stream->mIsOnOrderingStack) {
-    MediaStream* iter = aStack->getLast();
-    AudioNodeStream* ns = stream->AsAudioNodeStream();
-    bool delayNodePresent = ns ? ns->Engine()->AsDelayNodeEngine() != nullptr : false;
-    bool cycleFound = false;
-    if (iter) {
-      do {
-        cycleFound = true;
-        iter->AsProcessedStream()->mInCycle = true;
-        AudioNodeStream* ns = iter->AsAudioNodeStream();
-        if (ns && ns->Engine()->AsDelayNodeEngine()) {
-          delayNodePresent = true;
-        }
-        iter = iter->getPrevious();
-      } while (iter && iter != stream);
-    }
-    if (cycleFound && !delayNodePresent) {
-      // If we have detected a cycle, the previous loop should exit with stream
-      // == iter, or the node is connected to itself. Go back in the cycle and
-      // mute all nodes we find, or just mute the node itself.
-      if (!iter) {
-        // The node is connected to itself.
-        // There can't be a non-AudioNodeStream here, because only AudioNodes
-        // can be self-connected.
-        iter = aStack->getLast();
-        MOZ_ASSERT(iter->AsAudioNodeStream());
-        iter->AsAudioNodeStream()->Mute();
-      } else {
-        MOZ_ASSERT(iter);
-        do {
-          AudioNodeStream* nodeStream = iter->AsAudioNodeStream();
-          if (nodeStream) {
-            nodeStream->Mute();
-          }
-        } while((iter = iter->getNext()));
-      }
-    }
-    return;
-  }
-  ProcessedMediaStream* ps = stream->AsProcessedStream();
-  if (ps) {
-    aStack->insertBack(stream);
-    stream->mIsOnOrderingStack = true;
-    for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
-      MediaStream* source = ps->mInputs[i]->mSource;
-      if (!source->mHasBeenOrdered) {
-        nsRefPtr<MediaStream> s = source;
-        UpdateStreamOrderForStream(aStack, s.forget());
-      }
-    }
-    aStack->popLast();
-    stream->mIsOnOrderingStack = false;
-  }
-
-  stream->mHasBeenOrdered = true;
-  *mStreams.AppendElement() = stream.forget();
-}
-
 static void AudioMixerCallback(AudioDataValue* aMixedBuffer,
                                AudioSampleFormat aFormat,
                                uint32_t aChannels,
                                uint32_t aFrames,
                                uint32_t aSampleRate)
 {
   // Need an api to register mixer callbacks, bug 989921
 #ifdef MOZ_WEBRTC
@@ -610,55 +545,206 @@ static void AudioMixerCallback(AudioData
     }
   }
 #endif
 }
 
 void
 MediaStreamGraphImpl::UpdateStreamOrder()
 {
-  mOldStreams.SwapElements(mStreams);
-  mStreams.ClearAndRetainStorage();
   bool shouldMix = false;
-  for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
-    MediaStream* stream = mOldStreams[i];
-    stream->mHasBeenOrdered = false;
+  // Value of mCycleMarker for unvisited streams in cycle detection.
+  const uint32_t NOT_VISITED = UINT32_MAX;
+  // Value of mCycleMarker for ordered streams in muted cycles.
+  const uint32_t IN_MUTED_CYCLE = 1;
+
+  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+    MediaStream* stream = mStreams[i];
     stream->mIsConsumed = false;
-    stream->mIsOnOrderingStack = false;
     stream->mInBlockingSet = false;
     if (stream->AsSourceStream() &&
         stream->AsSourceStream()->NeedsMixing()) {
       shouldMix = true;
     }
-    ProcessedMediaStream* ps = stream->AsProcessedStream();
-    if (ps) {
-      ps->mInCycle = false;
-      AudioNodeStream* ns = ps->AsAudioNodeStream();
-      if (ns) {
-        ns->Unmute();
-      }
-    }
   }
 
   if (!mMixer && shouldMix) {
     mMixer = new AudioMixer(AudioMixerCallback);
   } else if (mMixer && !shouldMix) {
     mMixer = nullptr;
   }
 
-  mozilla::LinkedList<MediaStream> stack;
-  for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
-    nsRefPtr<MediaStream>& s = mOldStreams[i];
+  // The algorithm for finding cycles is based on Tim Leslie's iterative
+  // implementation [1][2] of Pearce's variant [3] of Tarjan's strongly
+  // connected components (SCC) algorithm.  There are variations (a) to
+  // distinguish whether streams in SCCs of size 1 are in a cycle and (b) to
+  // re-run the algorithm over SCCs with breaks at DelayNodes.
+  //
+  // [1] http://www.timl.id.au/?p=327
+  // [2] https://github.com/scipy/scipy/blob/e2c502fca/scipy/sparse/csgraph/_traversal.pyx#L582
+  // [3] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.102.1707
+  //
+  // There are two stacks.  One for the depth-first search (DFS),
+  mozilla::LinkedList<MediaStream> dfsStack;
+  // and another for streams popped from the DFS stack, but still being
+  // considered as part of SCCs involving streams on the stack.
+  mozilla::LinkedList<MediaStream> sccStack;
+
+  // An index into mStreams for the next stream found with no unsatisfied
+  // upstream dependencies.
+  uint32_t orderedStreamCount = 0;
+
+  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+    MediaStream* s = mStreams[i];
     if (s->IsIntrinsicallyConsumed()) {
       MarkConsumed(s);
     }
-    if (!s->mHasBeenOrdered) {
-      UpdateStreamOrderForStream(&stack, s.forget());
+    ProcessedMediaStream* ps = s->AsProcessedStream();
+    if (ps) {
+      // The dfsStack initially contains a list of all processed streams in
+      // unchanged order.
+      dfsStack.insertBack(s);
+      ps->mCycleMarker = NOT_VISITED;
+    } else {
+      // SourceMediaStreams have no inputs and so can be ordered now.
+      mStreams[orderedStreamCount] = s;
+      ++orderedStreamCount;
     }
   }
+
+  // mNextStackMarker corresponds to "index" in Tarjan's algorithm.  It is a
+  // counter to label mCycleMarker on the next visited stream in the DFS
+  // uniquely in the set of visited streams that are still being considered.
+  //
+  // In this implementation, the counter descends so that the values are
+  // strictly greater than the values that mCycleMarker takes when the stream
+  // has been ordered (0 or IN_MUTED_CYCLE).
+  //
+  // Each new stream labelled, as the DFS searches upstream, receives a value
+  // less than those used for all other streams being considered.
+  uint32_t nextStackMarker = NOT_VISITED - 1;
+  // Reset list of DelayNodes in cycles stored at the tail of mStreams.
+  mFirstCycleBreaker = mStreams.Length();
+
+  // Rearrange dfsStack order as required to DFS upstream and pop streams
+  // in processing order to place in mStreams.
+  while (auto ps = static_cast<ProcessedMediaStream*>(dfsStack.getFirst())) {
+    const auto& inputs = ps->mInputs;
+    MOZ_ASSERT(ps->AsProcessedStream());
+    if (ps->mCycleMarker == NOT_VISITED) {
+      // Record the position on the visited stack, so that any searches
+      // finding this stream again know how much of the stack is in the cycle.
+      ps->mCycleMarker = nextStackMarker;
+      --nextStackMarker;
+      // Not-visited input streams should be processed first.
+      // SourceMediaStreams have already been ordered.
+      for (uint32_t i = inputs.Length(); i--; ) {
+        auto input = inputs[i]->mSource->AsProcessedStream();
+        if (input && input->mCycleMarker == NOT_VISITED) {
+          input->remove();
+          dfsStack.insertFront(input);
+        }
+      }
+      continue;
+    }
+
+    // Returning from DFS.  Pop from dfsStack.
+    ps->remove();
+
+    // cycleStackMarker keeps track of the highest marker value on any
+    // upstream stream, if any, found receiving input, directly or indirectly,
+    // from the visited stack (and so from |ps|, making a cycle).  In a
+    // variation from Tarjan's SCC algorithm, this does not include |ps|
+    // unless it is part of the cycle.
+    uint32_t cycleStackMarker = 0;
+    for (uint32_t i = inputs.Length(); i--; ) {
+      auto input = inputs[i]->mSource->AsProcessedStream();
+      if (input) {
+        cycleStackMarker = std::max(cycleStackMarker, input->mCycleMarker);
+      }
+    }
+
+    if (cycleStackMarker <= IN_MUTED_CYCLE) {
+      // All inputs have been ordered and their stack markers have been removed.
+      // This stream is not part of a cycle.  It can be processed next.
+      ps->mCycleMarker = 0;
+      mStreams[orderedStreamCount] = ps;
+      ++orderedStreamCount;
+      continue;
+    }
+
+    // A cycle has been found.  Record this stream for ordering when all
+    // streams in this SCC have been popped from the DFS stack.
+    sccStack.insertFront(ps);
+
+    if (cycleStackMarker > ps->mCycleMarker) {
+      // Cycles have been found that involve streams that remain on the stack.
+      // Leave mCycleMarker indicating the most downstream (last) stream on
+      // the stack known to be part of this SCC.  In this way, any searches on
+      // other paths that find |ps| will know (without having to traverse from
+      // this stream again) that they are part of this SCC (i.e. part of an
+      // intersecting cycle).
+      ps->mCycleMarker = cycleStackMarker;
+      continue;
+    }
+
+    // |ps| is the root of an SCC involving no other streams on dfsStack, the
+    // complete SCC has been recorded, and streams in this SCC are part of at
+    // least one cycle.
+    MOZ_ASSERT(cycleStackMarker == ps->mCycleMarker);
+    // If there are DelayNodes in this SCC, then they may break the cycles.
+    bool haveDelayNode = false;
+    auto next = static_cast<ProcessedMediaStream*>(sccStack.getFirst());
+    // Streams in this SCC are identified by mCycleMarker <= cycleStackMarker.
+    // (There may be other streams later in sccStack from other incompletely
+    // searched SCCs, involving streams still on dfsStack.)
+    //
+    // DelayNodes in cycles must behave differently from those not in cycles,
+    // so all DelayNodes in the SCC must be identified.
+    while (next && next->mCycleMarker <= cycleStackMarker) {
+      auto ns = next->AsAudioNodeStream();
+      // Get next before perhaps removing from list below.
+      next = static_cast<ProcessedMediaStream*>(next->getNext());
+      if (ns && ns->Engine()->AsDelayNodeEngine()) {
+        haveDelayNode = true;
+        // DelayNodes break cycles by producing their output in a
+        // preprocessing phase; they do not need to be ordered before their
+        // consumers.  Order them at the tail of mStreams so that they can be
+        // handled specially.  Do so now, so that DFS ignores them.
+        ns->remove();
+        ns->mCycleMarker = 0;
+        --mFirstCycleBreaker;
+        mStreams[mFirstCycleBreaker] = ns;
+      }
+    }
+    auto after_scc = next;
+    while ((next = static_cast<ProcessedMediaStream*>(sccStack.popFirst()))
+           != after_scc) {
+      if (haveDelayNode) {
+        // Return streams to the DFS stack again (to order and detect cycles
+        // without delayNodes).  Any of these streams that are still inputs
+        // for streams on the visited stack must be returned to the front of
+        // the stack to be ordered before their dependents.  We know that none
+        // of these streams need input from streams on the visited stack, so
+        // they can all be searched and ordered before the current stack head
+        // is popped.
+        next->mCycleMarker = NOT_VISITED;
+        dfsStack.insertFront(next);
+      } else {
+        // Streams in cycles without any DelayNodes must be muted, and so do
+        // not need input and can be ordered now.  They must be ordered before
+        // their consumers so that their muted output is available.
+        next->mCycleMarker = IN_MUTED_CYCLE;
+        mStreams[orderedStreamCount] = next;
+        ++orderedStreamCount;
+      }
+    }
+  }
+
+  MOZ_ASSERT(orderedStreamCount == mFirstCycleBreaker);
 }
 
 void
 MediaStreamGraphImpl::RecomputeBlocking(GraphTime aEndBlockingDecisions)
 {
   bool blockingDecisionsWillChange = false;
 
   STREAM_LOG(PR_LOG_DEBUG+1, ("Media graph %p computing blocking for time %f",
@@ -1162,19 +1248,26 @@ RoundUpToNextAudioBlock(TrackRate aSampl
 }
 
 void
 MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
                                                         TrackRate aSampleRate,
                                                         GraphTime aFrom,
                                                         GraphTime aTo)
 {
+  MOZ_ASSERT(aStreamIndex <= mFirstCycleBreaker,
+             "Cycle breaker is not AudioNodeStream?");
   GraphTime t = aFrom;
   while (t < aTo) {
     GraphTime next = RoundUpToNextAudioBlock(aSampleRate, t);
+    for (uint32_t i = mFirstCycleBreaker; i < mStreams.Length(); ++i) {
+      auto ns = static_cast<AudioNodeStream*>(mStreams[i]);
+      MOZ_ASSERT(ns->AsAudioNodeStream());
+      ns->ProduceOutputBeforeInput(t);
+    }
     for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
       ProcessedMediaStream* ps = mStreams[i]->AsProcessedStream();
       if (ps) {
         ps->ProcessInput(t, next, (next == aTo) ? ProcessedMediaStream::ALLOW_FINISH : 0);
       }
     }
     t = next;
   }
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -655,19 +655,16 @@ protected:
    * unblock it until there's more data.
    */
   bool mHasCurrentData;
   /**
    * True if mHasCurrentData is true and we've notified listeners.
    */
   bool mNotifiedHasCurrentData;
 
-  // Temporary data for ordering streams by dependency graph
-  bool mHasBeenOrdered;
-  bool mIsOnOrderingStack;
   // True if the stream is being consumed (i.e. has track data being played,
   // or is feeding into some stream that is being consumed).
   bool mIsConsumed;
   // Temporary data for computing blocking status of streams
   // True if we've added this stream to the set of streams we're computing
   // blocking for.
   bool mInBlockingSet;
   // True if this stream should be blocked in this phase.
@@ -1014,17 +1011,17 @@ private:
 /**
  * This stream processes zero or more input streams in parallel to produce
  * its output. The details of how the output is produced are handled by
  * subclasses overriding the ProcessInput method.
  */
 class ProcessedMediaStream : public MediaStream {
 public:
   ProcessedMediaStream(DOMMediaStream* aWrapper)
-    : MediaStream(aWrapper), mAutofinish(false), mInCycle(false)
+    : MediaStream(aWrapper), mAutofinish(false)
   {}
 
   // Control API.
   /**
    * Allocates a new input port attached to source aStream.
    * This stream can be removed by calling MediaInputPort::Remove().
    */
   already_AddRefed<MediaInputPort> AllocateInputPort(MediaStream* aStream,
@@ -1083,17 +1080,20 @@ public:
   virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
   void SetAutofinishImpl(bool aAutofinish) { mAutofinish = aAutofinish; }
 
   /**
    * Forward SetTrackEnabled() to the input MediaStream(s) and translate the ID
    */
   virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) {};
 
-  bool InCycle() const { return mInCycle; }
+  // Only valid after MediaStreamGraphImpl::UpdateStreamOrder() has run.
+  // A DelayNode is considered to break a cycle and so this will not return
+  // true for echo loops, only for muted cycles.
+  bool InMutedCycle() const { return mCycleMarker; }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     size_t amount = MediaStream::SizeOfExcludingThis(aMallocSizeOf);
     // Not owned:
     // - mInputs elements
     amount += mInputs.SizeOfExcludingThis(aMallocSizeOf);
     return amount;
@@ -1105,19 +1105,20 @@ public:
   }
 
 protected:
   // This state is all accessed only on the media graph thread.
 
   // The list of all inputs that are currently enabled or waiting to be enabled.
   nsTArray<MediaInputPort*> mInputs;
   bool mAutofinish;
-  // True if and only if this stream is in a cycle.
-  // Updated by MediaStreamGraphImpl::UpdateStreamOrder.
-  bool mInCycle;
+  // After UpdateStreamOrder(), mCycleMarker is either 0 or 1 to indicate
+  // whether this stream is in a muted cycle.  During ordering it can contain
+  // other marker values - see MediaStreamGraphImpl::UpdateStreamOrder().
+  uint32_t mCycleMarker;
 };
 
 /**
  * Initially, at least, we will have a singleton MediaStreamGraph per
  * process.  Each OfflineAudioContext object creates its own MediaStreamGraph
  * object too.
  */
 class MediaStreamGraph {
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -227,22 +227,16 @@ public:
    */
   void ExtractPendingInput(SourceMediaStream* aStream,
                            GraphTime aDesiredUpToTime,
                            bool* aEnsureNextIteration);
   /**
    * Update "have enough data" flags in aStream.
    */
   void UpdateBufferSufficiencyState(SourceMediaStream* aStream);
-  /*
-   * If aStream hasn't already been ordered, push it onto aStack and order
-   * its children.
-   */
-  void UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream>* aStack,
-                                  already_AddRefed<MediaStream> aStream);
   /**
    * Mark aStream and all its inputs (recursively) as consumed.
    */
   static void MarkConsumed(MediaStream* aStream);
   /**
    * Sort mStreams so that every stream not in a cycle is after any streams
    * it depends on, and every stream in a cycle is marked as being in a cycle.
    * Also sets mIsConsumed on every stream.
@@ -421,22 +415,28 @@ public:
    * Readonly after initialization on the main thread.
    */
   nsCOMPtr<nsIThread> mThread;
 
   // The following state is managed on the graph thread only, unless
   // mLifecycleState > LIFECYCLE_RUNNING in which case the graph thread
   // is not running and this state can be used from the main thread.
 
-  nsTArray<nsRefPtr<MediaStream> > mStreams;
   /**
-   * mOldStreams is used as temporary storage for streams when computing the
-   * order in which we compute them.
+   * The graph keeps a reference to each stream.
+   * References are maintained manually to simplify reordering without
+   * unnecessary thread-safe refcount changes.
    */
-  nsTArray<nsRefPtr<MediaStream> > mOldStreams;
+  nsTArray<MediaStream*> mStreams;
+  /**
+   * Streams from mFirstCycleBreaker to the end of mStreams produce output
+   * before they receive input.  They correspond to DelayNodes that are in
+   * cycles.
+   */
+  uint32_t mFirstCycleBreaker;
   /**
    * The current graph time for the current iteration of the RunThread control
    * loop.
    */
   GraphTime mCurrentTime;
   /**
    * Blocking decisions and all stream contents have been computed up to this
    * time. The next batch of updates from the main thread will be processed
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -63,72 +63,75 @@ public:
   bool IsWaitingMediaResources() MOZ_OVERRIDE
   {
     return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
   }
 
   void RequestAudioData() MOZ_OVERRIDE
   {
     if (!GetAudioReader()) {
-      MSE_DEBUG("%p DecodeAudioFrame called with no audio reader", this);
+      MSE_DEBUG("%p MSR::RequestAudioData called with no audio reader", this);
       MOZ_ASSERT(mPendingDecoders.IsEmpty());
       GetCallback()->OnDecodeError();
       return;
     }
     GetAudioReader()->RequestAudioData();
   }
 
   void OnAudioDecoded(AudioData* aSample)
   {
     GetCallback()->OnAudioDecoded(aSample);
   }
 
   void OnAudioEOS()
   {
+    MSE_DEBUG("%p OnAudioEOS %d (%p) EOS (readers=%u)",
+              this, mActiveAudioDecoder, mDecoders[mActiveAudioDecoder].get(), mDecoders.Length());
     GetCallback()->OnAudioEOS();
   }
 
   void RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE
   {
     if (!GetVideoReader()) {
-      MSE_DEBUG("%p DecodeVideoFrame called with no video reader", this);
+      MSE_DEBUG("%p MSR::RequestVideoData called with no video reader", this);
       MOZ_ASSERT(mPendingDecoders.IsEmpty());
       GetCallback()->OnDecodeError();
       return;
     }
     mTimeThreshold = aTimeThreshold;
+    SwitchVideoReaders(SWITCH_OPTIONAL);
     GetVideoReader()->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
   }
 
   void OnVideoDecoded(VideoData* aSample)
   {
     if (mDropVideoBeforeThreshold) {
       if (aSample->mTime < mTimeThreshold) {
+        MSE_DEBUG("%p MSR::OnVideoDecoded VideoData mTime %lld below mTimeThreshold %lld",
+                  this, aSample->mTime, mTimeThreshold);
         delete aSample;
         GetVideoReader()->RequestVideoData(false, mTimeThreshold);
-      } else {
-        mDropVideoBeforeThreshold = false;
-        GetCallback()->OnVideoDecoded(aSample);
+        return;
       }
-    } else {
-      GetCallback()->OnVideoDecoded(aSample);
+      mDropVideoBeforeThreshold = false;
     }
+    GetCallback()->OnVideoDecoded(aSample);
   }
 
   void OnVideoEOS()
   {
     // End of stream. See if we can switch to another video decoder.
-    MSE_DEBUG("%p MSR::DecodeVF %d (%p) returned false (readers=%u)",
+    MSE_DEBUG("%p MSR::OnVideoEOS %d (%p) (readers=%u)",
               this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
-    if (MaybeSwitchVideoReaders()) {
+    if (SwitchVideoReaders(SWITCH_FORCED)) {
       // Success! Resume decoding with next video decoder.
       RequestVideoData(false, mTimeThreshold);
     } else {
       // End of stream.
-      MSE_DEBUG("%p MSR::DecodeVF %d (%p) EOS (readers=%u)",
+      MSE_DEBUG("%p MSR::OnVideoEOS %d (%p) EOS (readers=%u)",
                 this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
       GetCallback()->OnVideoEOS();
     }
   }
 
   void OnDecodeError() {
     GetCallback()->OnDecodeError();
   }
@@ -170,43 +173,43 @@ public:
   void InitializePendingDecoders();
 
   bool IsShutdown() {
     ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
     return mDecoder->IsShutdown();
   }
 
 private:
-
   // These are read and written on the decode task queue threads.
   int64_t mTimeThreshold;
   bool mDropVideoBeforeThreshold;
 
-  enum SwitchState {
-    SWITCHSTATE_SEEKING,
-    SWITCHSTATE_PLAYING
+  enum SwitchType {
+    SWITCH_OPTIONAL,
+    SWITCH_FORCED
   };
 
-  bool MaybeSwitchVideoReaders(SwitchState aState = SWITCHSTATE_PLAYING) {
+  bool SwitchVideoReaders(SwitchType aType) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     MOZ_ASSERT(mActiveVideoDecoder != -1);
 
     InitializePendingDecoders();
 
     for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
       SubBufferDecoder* decoder = mDecoders[i];
       if (decoder->IsDiscarded() || !decoder->GetReader()->GetMediaInfo().HasVideo()) {
         continue;
       }
 
-      if (aState == SWITCHSTATE_SEEKING || mTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
+      if (aType == SWITCH_FORCED || mTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
         GetVideoReader()->SetIdle();
 
         mActiveVideoDecoder = i;
-        MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
+        mDropVideoBeforeThreshold = true;
+        MSE_DEBUG("%p MSR::SwitchVideoReaders(%d) switching to %d", this, aType, mActiveVideoDecoder);
         return true;
       }
     }
 
     return false;
   }
 
   MediaDecoderReader* GetAudioReader() {
@@ -479,17 +482,17 @@ MediaSourceReader::Seek(int64_t aTime, i
 
   // Loop until we have the requested time range in the source buffers.
   // This is a workaround for our lack of async functionality in the
   // MediaDecoderStateMachine. Bug 979104 implements what we need and
   // we'll remove this for an async approach based on that in bug XXXXXXX.
   while (!mMediaSource->ActiveSourceBuffers()->AllContainsTime(target)
          && !IsShutdown()) {
     mMediaSource->WaitForData();
-    MaybeSwitchVideoReaders(SWITCHSTATE_SEEKING);
+    SwitchVideoReaders(SWITCH_FORCED);
   }
 
   if (IsShutdown()) {
     return NS_OK;
   }
 
   ResetDecode();
   if (GetAudioReader()) {
--- a/content/media/webaudio/AudioNode.h
+++ b/content/media/webaudio/AudioNode.h
@@ -102,20 +102,16 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioNode,
                                            DOMEventTargetHelper)
 
   virtual AudioBufferSourceNode* AsAudioBufferSourceNode() {
     return nullptr;
   }
 
-  virtual const DelayNode* AsDelayNode() const {
-    return nullptr;
-  }
-
   AudioContext* GetParentObject() const
   {
     return mContext;
   }
 
   AudioContext* Context() const
   {
     return mContext;
--- a/content/media/webaudio/DelayNode.cpp
+++ b/content/media/webaudio/DelayNode.cpp
@@ -37,17 +37,17 @@ public:
     // Keep the default value in sync with the default value in DelayNode::DelayNode.
     , mDelay(0.f)
     // Use a smoothing range of 20ms
     , mBuffer(std::max(aMaxDelayTicks,
                        static_cast<double>(WEBAUDIO_BLOCK_SIZE)),
               WebAudioUtils::ComputeSmoothingRate(0.02,
                                                   mDestination->SampleRate()))
     , mMaxDelay(aMaxDelayTicks)
-    , mLastOutputPosition(-1)
+    , mHaveProducedBeforeInput(false)
     , mLeftOverData(INT32_MIN)
   {
   }
 
   virtual DelayNodeEngine* AsDelayNodeEngine()
   {
     return this;
   }
@@ -105,63 +105,62 @@ public:
           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
       }
       *aOutput = aInput;
       return;
     }
 
     mBuffer.Write(aInput);
 
-    UpdateOutputBlock(aOutput);
+    // Skip output update if mLastChunks has already been set by
+    // ProduceBlockBeforeInput() when in a cycle.
+    if (!mHaveProducedBeforeInput) {
+      UpdateOutputBlock(aOutput, 0.0);
+    }
+    mHaveProducedBeforeInput = false;
     mBuffer.NextBlock();
   }
 
-  void UpdateOutputBlock(AudioChunk* aOutput)
+  void UpdateOutputBlock(AudioChunk* aOutput, double minDelay)
   {
-    TrackTicks tick = mSource->GetCurrentPosition();
-    if (tick == mLastOutputPosition) {
-      return; // mLastChunks is already set on the stream
-    }
-
-    mLastOutputPosition = tick;
-    bool inCycle = mSource->AsProcessedStream()->InCycle();
-    double minDelay = inCycle ? static_cast<double>(WEBAUDIO_BLOCK_SIZE) : 0.0;
     double maxDelay = mMaxDelay;
     double sampleRate = mSource->SampleRate();
     ChannelInterpretation channelInterpretation =
       mSource->GetChannelInterpretation();
     if (mDelay.HasSimpleValue()) {
       // If this DelayNode is in a cycle, make sure the delay value is at least
       // one block, even if that is greater than maxDelay.
       double delayFrames = mDelay.GetValue() * sampleRate;
       double delayFramesClamped =
         std::max(minDelay, std::min(delayFrames, maxDelay));
       mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
     } else {
       // Compute the delay values for the duration of the input AudioChunk
       // If this DelayNode is in a cycle, make sure the delay value is at least
       // one block.
+      TrackTicks tick = mSource->GetCurrentPosition();
       double computedDelay[WEBAUDIO_BLOCK_SIZE];
       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
         double delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate;
         double delayAtTickClamped =
           std::max(minDelay, std::min(delayAtTick, maxDelay));
         computedDelay[counter] = delayAtTickClamped;
       }
       mBuffer.Read(computedDelay, aOutput, channelInterpretation);
     }
   }
 
   virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) MOZ_OVERRIDE
   {
     if (mLeftOverData <= 0) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     } else {
-      UpdateOutputBlock(aOutput);
+      UpdateOutputBlock(aOutput, WEBAUDIO_BLOCK_SIZE);
     }
+    mHaveProducedBeforeInput = true;
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     // Not owned:
     // - mSource - probably not owned
     // - mDestination - probably not owned
@@ -175,17 +174,17 @@ public:
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   AudioParamTimeline mDelay;
   DelayBuffer mBuffer;
   double mMaxDelay;
-  TrackTicks mLastOutputPosition;
+  bool mHaveProducedBeforeInput;
   // How much data we have in our buffer which needs to be flushed out when our inputs
   // finish.
   int32_t mLeftOverData;
 };
 
 DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
   : AudioNode(aContext,
               2,
--- a/content/media/webaudio/DelayNode.h
+++ b/content/media/webaudio/DelayNode.h
@@ -25,21 +25,16 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   AudioParam* DelayTime() const
   {
     return mDelay;
   }
 
-  virtual const DelayNode* AsDelayNode() const MOZ_OVERRIDE
-  {
-    return this;
-  }
-
   virtual const char* NodeType() const
   {
     return "DelayNode";
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
 
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -35,37 +35,47 @@ enum MediaEngineState {
 };
 
 // We only support 1 audio and 1 video track for now.
 enum {
   kVideoTrack = 1,
   kAudioTrack = 2
 };
 
+// includes everything from dom::MediaSourceEnum (really video sources), plus audio sources
+enum MediaSourceType {
+  Camera = (int) dom::MediaSourceEnum::Camera,
+  Screen = (int) dom::MediaSourceEnum::Screen,
+  Application = (int) dom::MediaSourceEnum::Application,
+  Window, // = (int) dom::MediaSourceEnum::Window, // XXX bug 1038926
+  //Browser = (int) dom::MediaSourceEnum::Browser, // proposed in WG, unclear if it's useful
+  Microphone
+};
+
 class MediaEngine
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
 
   static const int DEFAULT_VIDEO_FPS = 30;
   static const int DEFAULT_VIDEO_MIN_FPS = 10;
   static const int DEFAULT_43_VIDEO_WIDTH = 640;
   static const int DEFAULT_43_VIDEO_HEIGHT = 480;
   static const int DEFAULT_169_VIDEO_WIDTH = 1280;
   static const int DEFAULT_169_VIDEO_HEIGHT = 720;
   static const int DEFAULT_AUDIO_TIMER_MS = 10;
 
   /* Populate an array of video sources in the nsTArray. Also include devices
    * that are currently unavailable. */
-  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+  virtual void EnumerateVideoDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineVideoSource> >*) = 0;
 
   /* Populate an array of audio sources in the nsTArray. Also include devices
    * that are currently unavailable. */
-  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+  virtual void EnumerateAudioDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineAudioSource> >*) = 0;
 
 protected:
   virtual ~MediaEngine() {}
 };
 
 /**
  * Common abstract base class for audio and video sources.
@@ -114,16 +124,19 @@ public:
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) = 0;
 
   /* Returns true if a source represents a fake capture device and
    * false otherwise
    */
   virtual bool IsFake() = 0;
 
+  /* Returns the type of media source (camera, microphone, screen, window, etc) */
+  virtual const MediaSourceType GetMediaSource() = 0;
+
   /* Return false if device is currently allocated or started */
   bool IsAvailable() {
     if (mState == kAllocated || mState == kStarted) {
       return false;
     } else {
       return true;
     }
   }
@@ -180,18 +193,18 @@ private:
   }
 };
 
 class MediaEngineVideoSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineVideoSource() {}
 
-  virtual const dom::MediaSourceEnum GetMediaSource() {
-      return dom::MediaSourceEnum::Camera;
+  virtual const MediaSourceType GetMediaSource() {
+      return MediaSourceType::Camera;
   }
   /* This call reserves but does not start the device. */
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs) = 0;
 };
 
 /**
  * Audio source and friends.
--- a/content/media/webrtc/MediaEngineDefault.cpp
+++ b/content/media/webrtc/MediaEngineDefault.cpp
@@ -473,37 +473,37 @@ MediaEngineDefaultAudioSource::Notify(ns
   channels.AppendElement(dest);
   segment.AppendFrames(buffer.forget(), channels, AUDIO_FRAME_LENGTH);
   mSource->AppendToTrack(mTrackID, &segment);
 
   return NS_OK;
 }
 
 void
-MediaEngineDefault::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineDefault::EnumerateVideoDevices(MediaSourceType aMediaSource,
                                           nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
   MutexAutoLock lock(mMutex);
 
   // only supports camera sources (for now).  See Bug 1038241
-  if (aMediaSource != dom::MediaSourceEnum::Camera) {
+  if (aMediaSource != MediaSourceType::Camera) {
     return;
   }
 
   // We once had code here to find a VideoSource with the same settings and re-use that.
   // This no longer is possible since the resolution is being set in Allocate().
 
   nsRefPtr<MediaEngineVideoSource> newSource = new MediaEngineDefaultVideoSource();
   mVSources.AppendElement(newSource);
   aVSources->AppendElement(newSource);
 
   return;
 }
 
 void
-MediaEngineDefault::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineDefault::EnumerateAudioDevices(MediaSourceType aMediaSource,
                                           nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
   MutexAutoLock lock(mMutex);
   int32_t len = mASources.Length();
 
   // aMediaSource is ignored for audio devices (for now).
 
   for (int32_t i = 0; i < len; i++) {
     nsRefPtr<MediaEngineAudioSource> source = mASources.ElementAt(i);
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -56,16 +56,20 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return true;
   }
 
+  virtual const MediaSourceType GetMediaSource() {
+    return MediaSourceType::Camera;
+  }
+
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
 protected:
   ~MediaEngineDefaultVideoSource();
 
   friend class MediaEngineDefault;
 
@@ -112,16 +116,20 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime) {}
 
   virtual bool IsFake() {
     return true;
   }
 
+  virtual const MediaSourceType GetMediaSource() {
+    return MediaSourceType::Microphone;
+  }
+
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
 protected:
   ~MediaEngineDefaultAudioSource();
 
   TrackID mTrackID;
   nsCOMPtr<nsITimer> mTimer;
@@ -133,19 +141,19 @@ protected:
 
 class MediaEngineDefault : public MediaEngine
 {
 public:
   MediaEngineDefault()
   : mMutex("mozilla::MediaEngineDefault")
   {}
 
-  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+  virtual void EnumerateVideoDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
-  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+  virtual void EnumerateAudioDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 
 private:
   ~MediaEngineDefault() {}
 
   Mutex mMutex;
   // protected with mMutex:
 
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -42,16 +42,17 @@ GetUserMediaLog()
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
 
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
     : mMutex("mozilla::MediaEngineWebRTC")
     , mScreenEngine(nullptr)
+    , mWinEngine(nullptr)
     , mAppEngine(nullptr)
     , mVideoEngine(nullptr)
     , mVoiceEngine(nullptr)
     , mVideoEngineInit(false)
     , mAudioEngineInit(false)
     , mScreenEngineInit(false)
     , mAppEngineInit(false)
 {
@@ -67,24 +68,24 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
   // XXX
   gFarendObserver = new AudioOutputObserver();
 
   NS_NewNamedThread("AudioGUM", getter_AddRefs(mThread));
   MOZ_ASSERT(mThread);
 }
 
 void
-MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineWebRTC::EnumerateVideoDevices(MediaSourceType aMediaSource,
                                          nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
  #ifdef MOZ_B2G_CAMERA
-  if (aMediaSource != dom::MediaSourceEnum::Camera) {
+  if (aMediaSource != MediaSourceType::Camera) {
     // only supports camera sources
     return;
   }
 
   /**
    * We still enumerate every time, in case a new device was plugged in since
    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
    * new devices (with or without new engine creation) and accordingly adjust.
@@ -132,39 +133,50 @@ MediaEngineWebRTC::EnumerateVideoDevices
 
   if (webrtc::VideoEngine::SetAndroidObjects(jvm) != 0) {
     LOG(("VieCapture:SetAndroidObjects Failed"));
     return;
   }
 #endif
 
   switch (aMediaSource) {
-    case dom::MediaSourceEnum::Application:
+    case MediaSourceType::Window:
+      mWinEngineConfig.Set<webrtc::CaptureDeviceInfo>(
+          new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Window));
+      if (!mWinEngine) {
+        if (!(mWinEngine = webrtc::VideoEngine::Create(mWinEngineConfig))) {
+          return;
+        }
+      }
+      videoEngine = mWinEngine;
+      videoEngineInit = &mWinEngineInit;
+      break;
+    case MediaSourceType::Application:
       mAppEngineConfig.Set<webrtc::CaptureDeviceInfo>(
           new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Application));
       if (!mAppEngine) {
         if (!(mAppEngine = webrtc::VideoEngine::Create(mAppEngineConfig))) {
           return;
         }
       }
       videoEngine = mAppEngine;
       videoEngineInit = &mAppEngineInit;
       break;
-    case dom::MediaSourceEnum::Screen:
+    case MediaSourceType::Screen:
       mScreenEngineConfig.Set<webrtc::CaptureDeviceInfo>(
           new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Screen));
       if (!mScreenEngine) {
         if (!(mScreenEngine = webrtc::VideoEngine::Create(mScreenEngineConfig))) {
           return;
         }
       }
       videoEngine = mScreenEngine;
       videoEngineInit = &mScreenEngineInit;
       break;
-    case dom::MediaSourceEnum::Camera:
+    case MediaSourceType::Camera:
       // fall through
     default:
       if (!mVideoEngine) {
         if (!(mVideoEngine = webrtc::VideoEngine::Create())) {
           return;
         }
       }
       videoEngine = mVideoEngine;
@@ -254,17 +266,17 @@ MediaEngineWebRTC::EnumerateVideoDevices
   if (mHasTabVideoSource)
     aVSources->AppendElement(new MediaEngineTabVideoSource());
 
   return;
 #endif
 }
 
 void
-MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineWebRTC::EnumerateAudioDevices(MediaSourceType aMediaSource,
                                          nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources)
 {
   ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
   ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw;
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
 #ifdef MOZ_WIDGET_ANDROID
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -91,17 +91,17 @@ class MediaEngineWebRTCVideoSource : pub
                                    , public mozilla::hal::ScreenConfigurationObserver
 #else
                                    , public webrtc::ExternalRenderer
 #endif
 {
 public:
 #ifdef MOZ_B2G_CAMERA
   MediaEngineWebRTCVideoSource(int aIndex,
-                               dom::MediaSourceEnum aMediaSource = dom::MediaSourceEnum::Camera)
+                               MediaSourceType aMediaSource = MediaSourceType::Camera)
     : mCameraControl(nullptr)
     , mCallbackMonitor("WebRTCCamera.CallbackMonitor")
     , mRotation(0)
     , mBackCamera(false)
     , mCaptureIndex(aIndex)
     , mMediaSource(aMediaSource)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
@@ -122,17 +122,17 @@ public:
   /**
    * Does DeliverFrame() support a null buffer and non-null handle
    * (video texture)?
    * XXX Investigate!  Especially for Android/B2G
    */
   virtual bool IsTextureSupported() { return false; }
 
   MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr, int aIndex,
-                               dom::MediaSourceEnum aMediaSource = dom::MediaSourceEnum::Camera)
+                               MediaSourceType aMediaSource = MediaSourceType::Camera)
     : mVideoEngine(aVideoEnginePtr)
     , mCaptureIndex(aIndex)
     , mFps(-1)
     , mMinFps(-1)
     , mMediaSource(aMediaSource)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
@@ -164,17 +164,17 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return false;
   }
 
-  virtual const dom::MediaSourceEnum GetMediaSource() {
+  virtual const MediaSourceType GetMediaSource() {
     return mMediaSource;
   }
 
 #ifndef MOZ_B2G_CAMERA
   NS_DECL_THREADSAFE_ISUPPORTS
 #else
   // We are subclassed from CameraControlListener, which implements a
   // threadsafe reference-count for us.
@@ -242,17 +242,17 @@ private:
   webrtc::ViECapture* mViECapture;
   webrtc::ViERender* mViERender;
 #endif
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
 
   int mCaptureIndex;
   int mFps; // Track rate (30 fps by default)
   int mMinFps; // Min rate we want to accept
-  dom::MediaSourceEnum mMediaSource; // source of media (camera | application | screen)
+  MediaSourceType mMediaSource; // source of media (camera | application | screen)
 
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
   // image changes).  Note that mSources is not accessed from other threads
   // for video and is not protected.
   Monitor mMonitor; // Monitor for processing WebRTC frames.
   int mWidth, mHeight;
   nsRefPtr<layers::Image> mImage;
@@ -322,16 +322,20 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return false;
   }
 
+  virtual const MediaSourceType GetMediaSource() {
+    return MediaSourceType::Microphone;
+  }
+
   // VoEMediaProcess.
   void Process(int channel, webrtc::ProcessingTypes type,
                int16_t audio10ms[], int length,
                int samplingFreq, bool isStereo);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
 protected:
@@ -385,19 +389,19 @@ class MediaEngineWebRTC : public MediaEn
 {
 public:
   MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
 
   // Clients should ensure to clean-up sources video/audio sources
   // before invoking Shutdown on this class.
   void Shutdown();
 
-  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+  virtual void EnumerateVideoDevices(MediaSourceType,
                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
-  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+  virtual void EnumerateAudioDevices(MediaSourceType,
                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 private:
   ~MediaEngineWebRTC() {
     Shutdown();
 #ifdef MOZ_B2G_CAMERA
     AsyncLatencyLogger::Get()->Release();
 #endif
     // XXX
@@ -405,28 +409,31 @@ private:
   }
 
   nsCOMPtr<nsIThread> mThread;
 
   Mutex mMutex;
 
   // protected with mMutex:
   webrtc::VideoEngine* mScreenEngine;
+  webrtc::VideoEngine* mWinEngine;
   webrtc::VideoEngine* mAppEngine;
   webrtc::VideoEngine* mVideoEngine;
   webrtc::VoiceEngine* mVoiceEngine;
 
   // specialized configurations
   webrtc::Config mAppEngineConfig;
+  webrtc::Config mWinEngineConfig;
   webrtc::Config mScreenEngineConfig;
 
   // Need this to avoid unneccesary WebRTC calls while enumerating.
   bool mVideoEngineInit;
   bool mAudioEngineInit;
   bool mScreenEngineInit;
+  bool mWinEngineInit;
   bool mAppEngineInit;
   bool mHasTabVideoSource;
 
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 };
--- a/content/media/webrtc/MediaTrackConstraints.h
+++ b/content/media/webrtc/MediaTrackConstraints.h
@@ -41,16 +41,18 @@ public:
           mUnsupportedRequirement = true;
         }
       }
     }
 
     // treat MediaSource special because it's always required
     mRequired.mMediaSource = mMediaSource;
 
+    // we guarantee (int) equivalence from MediaSourceEnum ->MediaSourceType
+    // (but not the other way)
     if (mMediaSource != dom::MediaSourceEnum::Camera && mAdvanced.WasPassed()) {
       // iterate through advanced, forcing mediaSource to match "root"
       auto& array = mAdvanced.Value();
       for (uint32_t i = 0; i < array.Length(); i++) {
         if (array[i].mMediaSource == dom::MediaSourceEnum::Camera) {
           array[i].mMediaSource = mMediaSource;
         }
       }
--- a/content/media/webspeech/synth/ipc/test/file_ipc.html
+++ b/content/media/webspeech/synth/ipc/test/file_ipc.html
@@ -32,44 +32,40 @@
         sendAsyncMessage("test:SpeechSynthesis:ipcTestComplete", {
           result: JSON.stringify(TestRunner._failedTests)
         });
 
         if (oldComplete) {
           oldComplete();
         }
       };
-
-      let oldLog = TestRunner.log;
-      TestRunner.log = function(msg) {
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:SpeechSynthesis:ipcTestMessage", { msg: msg });
       }
     }
 
-    let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
-
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
-      let message = SpecialPowers.wrap(data).json.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
+      let message = SpecialPowers.wrap(data).data.msg;
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
-            break;
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+            return;
+        }
 
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
+        let details = message.test + " | " + message.message;
+
+        switch(message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-DEBUG-INFO":
           default:
             info(details);
         }
       }
     }
 
     function onTestComplete() {
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
--- a/dom/apps/tests/file_bug_945152.html
+++ b/dom/apps/tests/file_bug_945152.html
@@ -18,16 +18,23 @@
 
   function ok(p, msg) {
     if (p)
       sendMessage("OK: " + msg);
     else
       sendMessage("KO: " + msg);
   }
 
+  function is(a, b, msg) {
+    if (a == b)
+      sendMessage("OK: " + a + " == " + b + " - " + msg);
+    else
+      sendMessage("KO: " + a + " != " + b + " - " + msg);
+  }
+
   function testXHR(file, data_head, mapped, cb) {
     var xhr = new XMLHttpRequest();
     xhr.open('GET', file);
     xhr.responseType = 'arraybuffer';
     xhr.onreadystatechange = function xhrReadystatechange() {
       if (xhr.readyState !== xhr.DONE) {
         return;
       }
@@ -36,18 +43,18 @@
         if (mapped) {
           ok(ct.indexOf("mem-mapped") != -1, "Data is memory-mapped");
         } else {
           ok(ct.indexOf("mem-mapped") == -1, "Data is not memory-mapped");
         }
         var data = xhr.response;
         ok(data, "Data is non-null");
         var str = String.fromCharCode.apply(null, Uint8Array(data));
-        ok(str.length == data_head.length + gPaddingSize, "Data size is correct");
-        ok(str.slice(0, data_head.length) == data_head, "Data head is correct");
+        is(str.length, data_head.length + gPaddingSize, "Data size is correct");
+        is(str.slice(0, data_head.length), data_head, "Data head is correct");
         ok(str.slice(data_head.length) == gPadding, "Data padding is correct");
         cb();
       } else {
         ok(false, "XHR error: " + xhr.status + " - " + xhr.statusText + "\n");
         cb();
       }
     }
     xhr.send();
--- a/dom/apps/tests/test_bug_945152.html
+++ b/dom/apps/tests/test_bug_945152.html
@@ -86,18 +86,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     var req = navigator.mozApps.installPackage(gSJS + '?getManifest=1');
     req.onerror = mozAppsError;
     req.onsuccess = function() {
       ok(true, "Application installed");
     };
     yield undefined;
 
-    // Launch app.
-    launchApp(app, continueTest);
+    // Launch app non-OOP.
+    info("Launch app with non-OOP");
+    launchApp(app, continueTest, false);
+    yield undefined;
+
+    // Launch app OOP.
+    info("Launch app with OOP");
+    launchApp(app, continueTest, true);
     yield undefined;
 
     // Uninstall app.
     var req = navigator.mozApps.mgmt.uninstall(app);
     req.onerror = mozAppsError;
     req.onsuccess = continueTest;
     yield undefined;
 
@@ -111,19 +117,20 @@ https://bugzilla.mozilla.org/show_bug.cg
     var url = gSJS + '?createApp=1';
     xhr.addEventListener("load", function() { is(xhr.responseText, "OK", "createApp OK"); cb(); });
     xhr.addEventListener("error", event => xhrError(event, url));
     xhr.addEventListener("abort", event => xhrAbort(url));
     xhr.open('GET', url, true);
     xhr.send();
   }
 
-  function launchApp(app, cb) {
+  function launchApp(app, cb, oop) {
     // Set up the app.
     var ifr = document.createElement('iframe');
+    ifr.setAttribute('remote', oop ? 'true' : 'false');
     ifr.setAttribute('mozbrowser', 'true');
     ifr.setAttribute('mozapp', app.manifestURL);
     ifr.setAttribute('src', app.origin + app.manifest.launch_path);
     var domParent = document.getElementById('container');
 
     // Set us up to listen for messages from the app.
     var listener = function(e) {
       var message = e.detail.message;
--- a/dom/devicestorage/ipc/test_ipc.html
+++ b/dom/devicestorage/ipc/test_ipc.html
@@ -31,44 +31,40 @@
           result: JSON.stringify(TestRunner._failedTests)
         });
 
         if (oldComplete) {
           oldComplete();
         }
       };
 
-      let oldLog = TestRunner.log;
-      TestRunner.log = function(msg) {
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:DeviceStorage:ipcTestMessage", { msg: msg });
       }
     }
 
-    let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
-
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
-      let message = SpecialPowers.wrap(data).json.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
+      let message = SpecialPowers.wrap(data).data.msg;
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
-            break;
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+          return;
+        }
 
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
+        switch (message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-DEBUG-INFO":
           default:
+            let details = message.test + " | " + message.message;
             info(details);
         }
       }
     }
 
     function onTestComplete() {
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
       let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/PageTransitionEvent.h"
 #include "mozilla/dom/PopStateEvent.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/InternalMutationEvent.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
 
 namespace mozilla {
 
@@ -395,16 +396,20 @@ EventDispatcher::Dispatch(nsISupports* a
   PROFILER_LABEL("EventDispatcher", "Dispatch",
     js::ProfileEntry::Category::EVENTS);
 
   NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
   NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
   NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
 
+#ifdef NIGHTLY_BUILD
+  MOZ_RELEASE_ASSERT(!mozilla::ipc::ProcessingUrgentMessages());
+#endif
+
   // If we're dispatching an already created DOMEvent object, make
   // sure it is initialized!
   // If aTargets is non-null, the event isn't going to be dispatched.
   NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
 
   nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
 
--- a/dom/events/test/test_bug603008.html
+++ b/dom/events/test/test_bug603008.html
@@ -422,17 +422,17 @@ function testPreventDefault() {
      { name: "touchend", prevent: true }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false, doPrevent: true },
      { name: "touchend", prevent: false }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false },
-     { name: "touchend", prevent: false, doPrevent: true }]
+     { name: "touchend", prevent: true, doPrevent: true }]
   ];
 
   var dotest = function(aTest) {
     if (aTest.doPrevent) {
       target.addEventListener(aTest.name, preventFunction, false);
     }
 
     if (aTest.name == "touchmove") {
--- a/dom/imptests/testharnessreport.js
+++ b/dom/imptests/testharnessreport.js
@@ -37,21 +37,27 @@ var W3CTest = {
 
   /**
    * Reference to the TestRunner object in the parent frame.
    */
   "runner": parent === this ? null : parent.TestRunner || parent.wrappedJSObject.TestRunner,
 
   /**
    * Prefixes for the error logging. Indexed first by int(todo) and second by
-   * int(result).
+   * int(result). Also contains the test's status, and expected status.
    */
   "prefixes": [
-    ["TEST-UNEXPECTED-FAIL", "TEST-PASS"],
-    ["TEST-KNOWN-FAIL", "TEST-UNEXPECTED-PASS"]
+    [
+      {status: 'FAIL', expected: 'PASS', message: "TEST-UNEXPECTED-FAIL"},
+      {status: 'PASS', expected: 'PASS', message: "TEST-PASS"}
+    ],
+    [
+      {status: 'FAIL', expected: 'FAIL', message: "TEST-KNOWN-FAIL"},
+      {status: 'PASS', expected: 'FAIL', message: "TEST-UNEXPECTED-PASS"}
+    ]
   ],
 
   /**
    * Prefix of the path to parent of the the failures directory.
    */
   "pathprefix": "/tests/dom/imptests/",
 
   /**
@@ -128,24 +134,31 @@ var W3CTest = {
     return aTest.name + (aTest.message ? ": " + aTest.message : "");
   },
 
   /**
    * Lets the test runner know about a test result.
    */
   "_log": function(test) {
     var url = this.getURL();
-    var msg = this.prefixes[+test.todo][+test.result] + " | ";
-    if (url) {
-      msg += url;
-    }
-    msg += " | " + this.formatTestMessage(test);
+    var message = this.formatTestMessage(test);
+    var result = this.prefixes[+test.todo][+test.result];
+
     if (this.runner) {
-      this.runner[(test.result === !test.todo) ? "log" : "error"](msg);
+      this.runner.structuredLogger.testStatus(url,
+                                              test.name,
+                                              result.status,
+                                              result.expected,
+                                              message);
     } else {
+      var msg = result.message + " | ";
+      if (url) {
+        msg += url;
+      }
+      msg += " | " + this.formatTestMessage(test);
       dump(msg + "\n");
     }
   },
 
   /**
    * Logs a message about collapsed messages (if any), and resets the counter.
    */
   "_logCollapsedMessages": function() {
--- a/dom/indexedDB/ipc/test_ipc.html
+++ b/dom/indexedDB/ipc/test_ipc.html
@@ -44,54 +44,43 @@
 
         sendAsyncMessage("test:indexedDB:ipcTestComplete");
 
         if (oldComplete) {
           oldComplete();
         }
       };
 
-      function sendTestMessage(msg) {
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:indexedDB:ipcTestMessage", { msg: msg });
       }
-
-      TestRunner.log = sendTestMessage;
-      TestRunner.error = sendTestMessage;
     }
 
-    let regexString =
-      "^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL" +
-      "|TEST-DEBUG-INFO|TEST-INFO) \\| ([^\\|]+) \\|(.*)";
-
-    let regex = new RegExp(regexString);
-
     let seenTestMessage = false;
 
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
       seenTestMessage = true;
       let message = SpecialPowers.wrap(data).data.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+          return;
+        }
+
+        switch (message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
-            break;
-
-          case "TEST-INFO":
-          case "TEST-DEBUG-INFO":
           default:
+            let details = message.test + " | " + message.message;
             info(details);
         }
       }
     }
 
     let usingChildProcess = false;
 
     function onProcessType(data) {
--- a/dom/inputmethod/mochitest/test_bug960946.html
+++ b/dom/inputmethod/mochitest/test_bug960946.html
@@ -25,17 +25,17 @@ var result = ["keydown", "keypress", "ke
 inputmethod_setup(function() {
   runTest();
 });
 
 // The frame script running in file_test_backspace_event.html.
 function appFrameScript() {
   let input = content.document.getElementById('test-input');
   input.onkeydown = input.onkeypress = input.onkeyup = function(event) {
-    dump('key event was fired in file_test_backspace_event.html.');
+    dump('key event was fired in file_test_backspace_event.html.\n');
     sendAsyncMessage('test:KeyBoard:keyEvent', {'type':event.type});
   };
 }
 
 function runTest() {
   let im = navigator.mozInputMethod;
 
   im.oninputcontextchange = function() {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -724,16 +724,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mPendingTouchPreventedResponse(false)
+  , mTouchEndIsClick(Unknown)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
   , mHasValidInnerSize(false)
   , mUniqueId(0)
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
@@ -1799,16 +1800,20 @@ TabChild::RecvHandleDoubleTap(const CSSP
 
 bool
 TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
+  if (mTouchEndIsClick == IsNotClick) {
+    return true;
+  }
+
   LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();;
 
   MessageLoop::current()->PostDelayedTask(
     FROM_HERE,
     NewRunnableMethod(this, &TabChild::FireSingleTapEvent, currentPoint),
     sActiveDurationMs);
   return true;
 }
@@ -1889,17 +1894,17 @@ TabChild::RecvNotifyAPZStateChange(const
   }
   case APZStateChange::StartPanning:
   {
     mActiveElementManager->HandlePanStart();
     break;
   }
   case APZStateChange::EndTouch:
   {
-    mActiveElementManager->HandleTouchEnd(aArg);
+    mTouchEndIsClick = (aArg ? IsClick : IsNotClick);
     break;
   }
   default:
     // APZStateChange has a 'sentinel' value, and the compiler complains
     // if an enumerator is not handled and there is no 'default' case.
     break;
   }
   return true;
@@ -2108,34 +2113,43 @@ TabChild::RecvRealTouchEvent(const Widge
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
+    mTouchEndIsClick = Unknown;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
       SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
       SendContentReceivedTouch(aGuid, isTouchPrevented);
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
     }
     break;
   }
 
-  case NS_TOUCH_MOVE:
   case NS_TOUCH_END:
-  case NS_TOUCH_CANCEL: {
+    if (isTouchPrevented && mTouchEndIsClick == IsClick) {
+      mTouchEndIsClick = IsNotClick;
+    }
+    // fall through
+  case NS_TOUCH_CANCEL:
+    if (mTouchEndIsClick != Unknown) {
+      mActiveElementManager->HandleTouchEnd(mTouchEndIsClick == IsClick);
+    }
+    // fall through
+  case NS_TOUCH_MOVE: {
     if (mPendingTouchPreventedResponse) {
       MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
       SendContentReceivedTouch(mPendingTouchPreventedGuid, isTouchPrevented);
       mPendingTouchPreventedResponse = false;
     }
     break;
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -586,16 +586,23 @@ private:
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mPendingTouchPreventedResponse;
     ScrollableLayerGuid mPendingTouchPreventedGuid;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
+    enum ClickState {
+      Unknown,
+      IsClick,
+      IsNotClick
+    };
+    ClickState mTouchEndIsClick;
+
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
     bool mHasValidInnerSize;
     uint64_t mUniqueId;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -306,17 +306,16 @@ VideoDevice::VideoDevice(MediaEngineVide
 #endif // MOZ_B2G_CAMERA
 
   // Kludge to test user-facing cameras on OSX.
   if (mName.Find(NS_LITERAL_STRING("Face")) != -1) {
     mHasFacingMode = true;
     mFacingMode = dom::VideoFacingModeEnum::User;
   }
 
-  // dom::MediaSourceEnum::Camera;
   mMediaSource = aSource->GetMediaSource();
 }
 
 AudioDevice::AudioDevice(MediaEngineAudioSource* aSource)
   : MediaDevice(aSource) {}
 
 NS_IMETHODIMP
 MediaDevice::GetName(nsAString& aName)
@@ -362,19 +361,24 @@ MediaDevice::GetFacingMode(nsAString& aF
     aFacingMode.Truncate(0);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaDevice::GetMediaSource(nsAString& aMediaSource)
 {
-
-  aMediaSource.Assign(NS_ConvertUTF8toUTF16(
-    dom::MediaSourceEnumValues::strings[uint32_t(mMediaSource)].value));
+  if (mMediaSource == MediaSourceType::Microphone) {
+    aMediaSource.Assign(NS_LITERAL_STRING("microphone"));
+  } else if (mMediaSource == MediaSourceType::Window) { // this will go away
+    aMediaSource.Assign(NS_LITERAL_STRING("window"));
+  } else { // all the rest are shared
+    aMediaSource.Assign(NS_ConvertUTF8toUTF16(
+      dom::MediaSourceEnumValues::strings[uint32_t(mMediaSource)].value));
+  }
   return NS_OK;
 }
 
 MediaEngineVideoSource*
 VideoDevice::GetSource()
 {
   return static_cast<MediaEngineVideoSource*>(&*mSource);
 }
@@ -754,28 +758,29 @@ static bool SatisfyConstraintSet(const M
 typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
 
 // Source getter that constrains list returned
 
 template<class SourceType, class ConstraintsType>
 static SourceSet *
   GetSources(MediaEngine *engine,
              ConstraintsType &aConstraints,
-             void (MediaEngine::* aEnumerate)(dom::MediaSourceEnum, nsTArray<nsRefPtr<SourceType> >*),
+             void (MediaEngine::* aEnumerate)(MediaSourceType, nsTArray<nsRefPtr<SourceType> >*),
              const char* media_device_name = nullptr)
 {
   ScopedDeletePtr<SourceSet> result(new SourceSet);
 
   const SourceType * const type = nullptr;
   nsString deviceName;
   // First collect sources
   SourceSet candidateSet;
   {
     nsTArray<nsRefPtr<SourceType> > sources;
-    (engine->*aEnumerate)(aConstraints.mMediaSource, &sources);
+    // all MediaSourceEnums are contained in MediaSourceType
+    (engine->*aEnumerate)((MediaSourceType)((int)aConstraints.mMediaSource), &sources);
     /**
       * We're allowing multiple tabs to access the same camera for parity
       * with Chrome.  See bug 811757 for some of the issues surrounding
       * this decision.  To disallow, we'd filter by IsAvailable() as we used
       * to.
       */
     for (uint32_t len = sources.Length(), i = 0; i < len; i++) {
       sources[i]->GetName(deviceName);
@@ -1909,17 +1914,18 @@ WindowsHashToArrayFunc (const uint64_t& 
     // access and windows that are currently capturing media. We want
     // to return only the latter. See bug 975177.
     bool capturing = false;
     if (aData) {
       uint32_t length = aData->Length();
       for (uint32_t i = 0; i < length; ++i) {
         nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
           aData->ElementAt(i);
-        if (listener->CapturingVideo() || listener->CapturingAudio()) {
+        if (listener->CapturingVideo() || listener->CapturingAudio() ||
+            listener->CapturingScreen() || listener->CapturingWindow()) {
           capturing = true;
           break;
         }
       }
     }
 
     if (capturing)
       array->AppendElement(window);
@@ -1940,34 +1946,39 @@ MediaManager::GetActiveMediaCaptureWindo
   mActiveWindows.EnumerateRead(WindowsHashToArrayFunc, array);
 
   *aArray = array;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow, bool* aVideo,
-                                      bool* aAudio)
+                                      bool* aAudio, bool *aScreenShare,
+                                      bool* aWindowShare)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   *aVideo = false;
   *aAudio = false;
+  *aScreenShare = false;
+  *aWindowShare = false;
 
-  nsresult rv = MediaCaptureWindowStateInternal(aWindow, aVideo, aAudio);
+  nsresult rv = MediaCaptureWindowStateInternal(aWindow, aVideo, aAudio, aScreenShare, aWindowShare);
 #ifdef DEBUG
   nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
-  LOG(("%s: window %lld capturing %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
-       *aVideo ? "video" : "", *aAudio ? "audio" : ""));
+  LOG(("%s: window %lld capturing %s %s %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
+       *aVideo ? "video" : "", *aAudio ? "audio" : "",
+       *aScreenShare ? "screenshare" : "",  *aWindowShare ? "windowshare" : ""));
 #endif
   return rv;
 }
 
 nsresult
 MediaManager::MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
-                                              bool* aAudio)
+                                              bool* aAudio, bool *aScreenShare,
+                                              bool* aWindowShare)
 {
   // We need to return the union of all streams in all innerwindows that
   // correspond to that outerwindow.
 
   // Iterate the docshell tree to find all the child windows, find
   // all the listeners for each one, get the booleans, and merge the
   // results.
   nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
@@ -1986,37 +1997,37 @@ MediaManager::MediaCaptureWindowStateInt
           nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
             listeners->ElementAt(i);
           if (listener->CapturingVideo()) {
             *aVideo = true;
           }
           if (listener->CapturingAudio()) {
             *aAudio = true;
           }
-          if (*aAudio && *aVideo) {
-            return NS_OK; // no need to continue iterating
+          if (listener->CapturingScreen()) {
+            *aScreenShare = true;
+          }
+          if (listener->CapturingWindow()) {
+            *aWindowShare = true;
           }
         }
       }
     }
 
     // iterate any children of *this* window (iframes, etc)
     nsCOMPtr<nsIDocShell> docShell = piWin->GetDocShell();
     if (docShell) {
       int32_t i, count;
       docShell->GetChildCount(&count);
       for (i = 0; i < count; ++i) {
         nsCOMPtr<nsIDocShellTreeItem> item;
         docShell->GetChildAt(i, getter_AddRefs(item));
         nsCOMPtr<nsPIDOMWindow> win = item ? item->GetWindow() : nullptr;
 
-        MediaCaptureWindowStateInternal(win, aVideo, aAudio);
-        if (*aAudio && *aVideo) {
-          return NS_OK; // no need to continue iterating
-        }
+        MediaCaptureWindowStateInternal(win, aVideo, aAudio, aScreenShare, aWindowShare);
       }
     }
   }
   return NS_OK;
 }
 
 void
 MediaManager::StopMediaStreams()
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -100,26 +100,39 @@ public:
   }
 
   // mVideo/AudioSource are set by Activate(), so we assume they're capturing
   // if set and represent a real capture device.
   bool CapturingVideo()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     return mVideoSource && !mStopped &&
+           mVideoSource->GetMediaSource() == MediaSourceType::Camera &&
            (!mVideoSource->IsFake() ||
             Preferences::GetBool("media.navigator.permission.fake"));
   }
   bool CapturingAudio()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     return mAudioSource && !mStopped &&
            (!mAudioSource->IsFake() ||
             Preferences::GetBool("media.navigator.permission.fake"));
   }
+  bool CapturingScreen()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+    return mVideoSource && !mStopped &&
+           mVideoSource->GetMediaSource() == MediaSourceType::Screen;
+  }
+  bool CapturingWindow()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+    return mVideoSource && !mStopped &&
+           mVideoSource->GetMediaSource() == MediaSourceType::Window;
+  }
 
   void SetStopped()
   {
     mStopped = true;
   }
 
   // implement in .cpp to avoid circular dependency with MediaOperationRunnable
   // Can be invoked from EITHER MainThread or MSG thread
@@ -481,17 +494,17 @@ public:
 
 protected:
   virtual ~MediaDevice() {}
   MediaDevice(MediaEngineSource* aSource);
   nsString mName;
   nsString mID;
   bool mHasFacingMode;
   dom::VideoFacingModeEnum mFacingMode;
-  dom::MediaSourceEnum mMediaSource;
+  MediaSourceType mMediaSource;
   nsRefPtr<MediaEngineSource> mSource;
 };
 
 class VideoDevice : public MediaDevice
 {
 public:
   VideoDevice(MediaEngineVideoSource* aSource);
   NS_IMETHOD GetType(nsAString& aType);
@@ -574,17 +587,18 @@ private:
   void GetPrefs(nsIPrefBranch *aBranch, const char *aData);
 
   // Make private because we want only one instance of this class
   MediaManager();
 
   ~MediaManager() {}
 
   nsresult MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
-                                           bool* aAudio);
+                                           bool* aAudio, bool *aScreenShare,
+                                           bool* aWindowShare);
 
   void StopMediaStreams();
 
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
   nsRefPtrHashtable<nsStringHashKey, GetUserMediaRunnable> mActiveCallbacks;
   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
   // Always exists
--- a/dom/media/nsIMediaManager.idl
+++ b/dom/media/nsIMediaManager.idl
@@ -7,17 +7,18 @@
 interface nsISupportsArray;
 interface nsIDOMWindow;
 
 %{C++
 #define NS_MEDIAMANAGERSERVICE_CID {0xabc622ea, 0x9655, 0x4123, {0x80, 0xd9, 0x22, 0x62, 0x1b, 0xdd, 0x54, 0x65}}
 #define MEDIAMANAGERSERVICE_CONTRACTID "@mozilla.org/mediaManagerService;1"
 %}
 
-[scriptable, builtinclass, uuid(2efff6ab-0e3e-4cc4-8f9b-4aaca59a1140)]
+[scriptable, builtinclass, uuid(f431b523-4536-4ba7-a2c1-7e1bf670d32a)]
 interface nsIMediaManagerService : nsISupports
 {
   /* return a array of inner windows that have active captures */
   readonly attribute nsISupportsArray activeMediaCaptureWindows;
 
   /* Get the capture state for the given window and all descendant windows (iframes, etc) */
-  void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio);
+  void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio,
+                               [optional] out boolean aScreenShare, [optional] out boolean aWindowShare);
 };
--- a/dom/media/tests/ipc/test_ipc.html
+++ b/dom/media/tests/ipc/test_ipc.html
@@ -19,18 +19,18 @@
     SimpleTest.requestLongerTimeout(100);
 
     // Disable crash observers as it breaks later tests.
     function iframeScriptFirst() {
       SpecialPowers.prototype.registerProcessCrashObservers = function() { };
       SpecialPowers.prototype.unregisterProcessCrashObservers = function() { };
 
       content.wrappedJSObject.RunSet.reloadAndRunAll({
-          preventDefault: function() { },
-          __exposedProps__: { preventDefault: 'r' }
+        preventDefault: function() { },
+        __exposedProps__: { preventDefault: 'r' }
       });
     }
 
     function iframeScriptSecond() {
       let TestRunner = content.wrappedJSObject.TestRunner;
       let oldComplete = TestRunner.onComplete;
 
       TestRunner.onComplete = function() {
@@ -39,47 +39,41 @@
         sendAsyncMessage("test:PeerConnection:ipcTestComplete", {
           result: JSON.stringify(TestRunner._failedTests)
         });
 
         if (oldComplete) {
           oldComplete();
         }
       };
-      let oldLog = TestRunner.log;
-      TestRunner.log = function(msg) {
+
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:PeerConnection:ipcTestMessage", { msg: msg });
-      };
-      TestRunner.error = function(msg) {
-        sendAsyncMessage("test:PeerConnection:ipcTestMessage", { msg: msg });
-      };
+      }
     }
 
-    let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
-
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
-      let message = SpecialPowers.wrap(data).json.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
+      let message = SpecialPowers.wrap(data).data.msg;
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
-            break;
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+          return;
+        }
 
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
+        switch (message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-DEBUG-INFO":
           default:
+            let details = message.test + " | " + message.message;
             info(details);
         }
       }
     }
 
     function onTestComplete() {
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
       let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -20,16 +20,20 @@ skip-if = toolkit == 'gonk' # b2g(Bug 96
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_bug1013809.html]
 skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_dataChannel_noOffer.html]
 [test_getUserMedia_basicAudio.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure
 [test_getUserMedia_basicVideo.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure
+[test_getUserMedia_basicScreenshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no screenshare on b2g/android
+[test_getUserMedia_basicWindowshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no windowshare on b2g/android
 [test_getUserMedia_basicVideoAudio.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
 [test_getUserMedia_constraints.html]
 skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 907352, backwards-compatible behavior on mobile only
 [test_getUserMedia_constraints_mobile.html]
 skip-if = toolkit != 'gonk' && toolkit != 'android' # Bug 907352, backwards-compatible behavior on mobile only
 [test_getUserMedia_exceptions.html]
 [test_getUserMedia_gumWithinGum.html]
@@ -50,16 +54,20 @@ skip-if = toolkit == 'gonk' # b2g(Bug 10
 [test_peerConnection_basicAudio.html]
 skip-if = (toolkit == 'gonk' && debug) # Bug 962984, test fail on b2g debug build
 [test_peerConnection_basicAudioVideo.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicAudioVideoCombined.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicVideo.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+[test_peerConnection_basicScreenshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no screenshare on b2g/android
+[test_peerConnection_basicWindowshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no windowshare on b2g/android
 [test_peerConnection_bug822674.html]
 [test_peerConnection_bug825703.html]
 [test_peerConnection_bug827843.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 [test_peerConnection_bug835370.html]
 [test_peerConnection_bug1013809.html]
 skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=983504
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Screenshare Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=983504">mozGetUserMedia Basic Screenshare Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  /**
+   * Run a test to verify that we can complete a start and stop media playback
+   * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement.
+   */
+  runTest(function () {
+    var testVideo = document.getElementById('testVideo');
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'screen',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+
+    getUserMedia(constraints, function (aStream) {
+      checkMediaStreamTracks(constraints, aStream);
+
+      var playback = new LocalMediaStreamPlayback(testVideo, aStream);
+      playback.playMediaWithStreamStop(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
+
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=983504
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Windowshare Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038926">mozGetUserMedia Basic Windowshare Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  /**
+   * Run a test to verify that we can complete a start and stop media playback
+   * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement.
+   */
+  runTest(function () {
+    var testVideo = document.getElementById('testVideo');
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'window',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+
+    getUserMedia(constraints, function (aStream) {
+      checkMediaStreamTracks(constraints, aStream);
+
+      var playback = new LocalMediaStreamPlayback(testVideo, aStream);
+      playback.playMediaWithStreamStop(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
+
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1039666",
+    title: "Basic screenshare-only peer connection"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'screen',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+    test.setMediaConstraints([constraints], [constraints]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1038926",
+    title: "Basic windowshare-only peer connection"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'window',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+    test.setMediaConstraints([constraints], [constraints]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/test/mochitest/test_npobject_getters.html
+++ b/dom/plugins/test/mochitest/test_npobject_getters.html
@@ -1,16 +1,16 @@
 <head>
   <title>NPNV*NPObject accessibility tests</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="utils.js"></script>
 
 <body onload="runTests()">
   <script class="testbody" type="application/javascript">
-  dump('lastScript');
+  dump('lastScript\n');
 
   SimpleTest.waitForExplicitFinish();
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   function runTests() {
     ok(document.getElementById('plugin1').pluginFoundElement, "plugin1.pluginFoundElement (NPNVPluginElementNPObject)", document.getElementById('plugin1').pluginFoundElement);
     ok(window.pluginFoundWindow, "window.pluginFoundWindow (NPNVWindowNPObject)", window.pluginFoundWindow);
 
--- a/dom/webidl/Constraints.webidl
+++ b/dom/webidl/Constraints.webidl
@@ -12,17 +12,18 @@ enum VideoFacingModeEnum {
     "environment",
     "left",
     "right"
 };
 
 enum MediaSourceEnum {
     "camera",
     "screen",
-    "application"
+    "application",
+    "window"
 };
 
 dictionary ConstrainLongRange {
     long min = -2147483647; // +1 works around windows compiler bug
     long max = 2147483647;
 };
 
 dictionary ConstrainDoubleRange {
--- a/dom/workers/test/rvals_worker.js
+++ b/dom/workers/test/rvals_worker.js
@@ -2,12 +2,12 @@ onmessage = function(evt) {
   postMessage(postMessage('ignore') == undefined);
 
   var id = setInterval(function() {}, 200);
   postMessage(clearInterval(id) == undefined);
 
   id = setTimeout(function() {}, 200);
   postMessage(clearTimeout(id) == undefined);
 
-  postMessage(dump(42) == undefined);
+  postMessage(dump(42 + '\n') == undefined);
 
   postMessage('finished');
 }
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -150,16 +150,22 @@ LayerManager::CreateImageContainer()
 
 already_AddRefed<ImageContainer>
 LayerManager::CreateAsynchronousImageContainer()
 {
   nsRefPtr<ImageContainer> container = new ImageContainer(ImageContainer::ENABLE_ASYNC);
   return container.forget();
 }
 
+bool
+LayerManager::AreComponentAlphaLayersEnabled()
+{
+  return gfxPrefs::ComponentAlphaEnabled();
+}
+
 //--------------------------------------------------
 // Layer
 
 Layer::Layer(LayerManager* aManager, void* aImplData) :
   mManager(aManager),
   mParent(nullptr),
   mNextSibling(nullptr),
   mPrevSibling(nullptr),
@@ -1018,42 +1024,49 @@ ContainerLayer::DefaultComputeEffectiveT
   } else {
     ComputeEffectiveTransformForMaskLayer(Matrix4x4());
   }
 }
 
 void
 ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy)
 {
-  bool supportsComponentAlphaChildren = false;
+  if (!(GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT) ||
+      !Manager()->AreComponentAlphaLayersEnabled()) {
+    mSupportsComponentAlphaChildren = false;
+    if (aNeedsSurfaceCopy) {
+      *aNeedsSurfaceCopy = false;
+    }
+    return;
+  }
+
+  mSupportsComponentAlphaChildren = false;
   bool needsSurfaceCopy = false;
   CompositionOp blendMode = GetEffectiveMixBlendMode();
   if (UseIntermediateSurface()) {
     if (GetEffectiveVisibleRegion().GetNumRects() == 1 &&
         (GetContentFlags() & Layer::CONTENT_OPAQUE))
     {
-      supportsComponentAlphaChildren = true;
+      mSupportsComponentAlphaChildren = true;
     } else {
       gfx::Matrix transform;
       if (HasOpaqueAncestorLayer(this) &&
           GetEffectiveTransform().Is2D(&transform) &&
           !gfx::ThebesMatrix(transform).HasNonIntegerTranslation() &&
           blendMode == gfx::CompositionOp::OP_OVER) {
-        supportsComponentAlphaChildren = true;
+        mSupportsComponentAlphaChildren = true;
         needsSurfaceCopy = true;
       }
     }
   } else if (blendMode == gfx::CompositionOp::OP_OVER) {
-    supportsComponentAlphaChildren =
+    mSupportsComponentAlphaChildren =
       (GetContentFlags() & Layer::CONTENT_OPAQUE) ||
       (GetParent() && GetParent()->SupportsComponentAlphaChildren());
   }
 
-  mSupportsComponentAlphaChildren = supportsComponentAlphaChildren &&
-                                    gfxPrefs::ComponentAlphaEnabled();
   if (aNeedsSurfaceCopy) {
     *aNeedsSurfaceCopy = mSupportsComponentAlphaChildren && needsSurfaceCopy;
   }
 }
 
 void
 ContainerLayer::ComputeEffectiveTransformsForChildren(const Matrix4x4& aTransformToSurface)
 {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -303,22 +303,30 @@ public:
    */
   virtual void Composite() {}
 
   virtual bool HasShadowManagerInternal() const { return false; }
   bool HasShadowManager() const { return HasShadowManagerInternal(); }
 
   bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; }
 
+
+  /**
+   * Returns true if the layer manager can't render component alpha
+   * layers, and layer building should do it's best to avoid
+   * creating them.
+   */
+  virtual bool ShouldAvoidComponentAlphaLayers() { return false; }
+
   /**
    * Returns true if this LayerManager can properly support layers with
-   * SurfaceMode::SURFACE_COMPONENT_ALPHA. This can include disabling component
-   * alpha if required.
+   * SurfaceMode::SURFACE_COMPONENT_ALPHA. LayerManagers that can't will use
+   * transparent surfaces (and lose subpixel-AA for text).
    */
-  virtual bool AreComponentAlphaLayersEnabled() { return true; }
+  virtual bool AreComponentAlphaLayersEnabled();
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the root layer. The root layer is initially null. If there is
    * no root layer, EndTransaction won't draw anything.
    */
   virtual void SetRoot(Layer* aLayer) = 0;
   /**
@@ -743,32 +751,38 @@ public:
      * require per-component alpha for optimal fidelity. However, there is no
      * guarantee that component alpha will be supported for this layer at
      * paint time.
      * This should never be set at the same time as CONTENT_OPAQUE.
      */
     CONTENT_COMPONENT_ALPHA = 0x02,
 
     /**
+     * If this is set then one of the descendant layers of this one has
+     * CONTENT_COMPONENT_ALPHA set.
+     */
+    CONTENT_COMPONENT_ALPHA_DESCENDANT = 0x04,
+
+    /**
      * If this is set then this layer is part of a preserve-3d group, and should
      * be sorted with sibling layers that are also part of the same group.
      */
-    CONTENT_PRESERVE_3D = 0x04,
+    CONTENT_PRESERVE_3D = 0x08,
     /**
      * This indicates that the transform may be changed on during an empty
      * transaction where there is no possibility of redrawing the content, so the
      * implementation should be ready for that.
      */
-    CONTENT_MAY_CHANGE_TRANSFORM = 0x08,
+    CONTENT_MAY_CHANGE_TRANSFORM = 0x10,
 
     /**
      * Disable subpixel AA for this layer. This is used if the display isn't suited
      * for subpixel AA like hidpi or rotated content.
      */
-    CONTENT_DISABLE_SUBPIXEL_AA = 0x10
+    CONTENT_DISABLE_SUBPIXEL_AA = 0x20
   };
   /**
    * CONSTRUCTION PHASE ONLY
    * This lets layout make some promises about what will be drawn into the
    * visible region of the ThebesLayer. This enables internal quality
    * and performance optimizations.
    */
   void SetContentFlags(uint32_t aFlags)
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -460,20 +460,18 @@ RotatedContentBuffer::BeginPaint(ThebesL
     }
 
     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
 #if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK)
       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
 #else
       if (!aLayer->GetParent() ||
           !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
-          !aLayer->Manager()->IsCompositingCheap() ||
           !aLayer->AsShadowableLayer() ||
-          !aLayer->AsShadowableLayer()->HasShadow() ||
-          !gfxPrefs::ComponentAlphaEnabled()) {
+          !aLayer->AsShadowableLayer()->HasShadow()) {
         mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
       } else {
         result.mContentType = gfxContentType::COLOR;
       }
 #endif
     }
 
     if ((aFlags & PAINT_WILL_RESAMPLE) &&
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -883,26 +883,36 @@ APZCTreeManager::HandOffFling(AsyncPanZo
   // Tell |next| to start a fling with the transformed velocity.
   return next->TakeOverFling(transformedVelocity);
 }
 
 bool
 APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
 {
   MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
-  if (mOverscrollHandoffChain.length() == 0) {
-    return false;
-  }
   for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
     nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
     if (item) {
       item->FlushRepaintForOverscrollHandoff();
     }
   }
-  return true;
+  return mOverscrollHandoffChain.length() > 0;
+}
+
+bool
+APZCTreeManager::CancelAnimationsForOverscrollHandoffChain()
+{
+  MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
+  for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
+    nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
+    if (item) {
+      item->CancelAnimation();
+    }
+  }
+  return mOverscrollHandoffChain.length() > 0;
 }
 
 bool
 APZCTreeManager::CanBePanned(AsyncPanZoomController* aApzc)
 {
   MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
 
   // Find |aApzc| in the handoff chain.
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -288,16 +288,17 @@ public:
    *                  pixels per millisecond
    * Returns true iff. another APZC accepted the handed-off fling. The caller
    * (|aApzc|) uses this return value to determine whether it should consume
    * the excess fling itself by going into an overscroll fling.
    */
   bool HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity);
 
   bool FlushRepaintsForOverscrollHandoffChain();
+  bool CancelAnimationsForOverscrollHandoffChain();
 
   /**
    * Determine whether |aApzc|, or any APZC along its overscroll handoff chain,
    * has room to be panned.
    * Expects the overscroll handoff chain to already be built.
    */
   bool CanBePanned(AsyncPanZoomController* aApzc);
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -808,27 +808,55 @@ AsyncPanZoomController::GetTouchStartTol
   return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
 }
 
 /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
 {
   return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
 }
 
+void
+AsyncPanZoomController::CancelAnimationForHandoffChain()
+{
+  APZCTreeManager* treeManagerLocal = mTreeManager;
+  if (treeManagerLocal && treeManagerLocal->CancelAnimationsForOverscrollHandoffChain()) {
+    return;
+  }
+  NS_WARNING("Overscroll handoff chain was empty in CancelAnimationForHandoffChain! This should not be the case.");
+  // Graceful handling of error condition
+  CancelAnimation();
+}
+
 nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
   AssertOnControllerThread();
 
   if (aEvent.mInputType != MULTITOUCH_INPUT) {
     return HandleInputEvent(aEvent);
   }
 
   TouchBlockState* block = nullptr;
   if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
     block = StartNewTouchBlock(false);
     APZC_LOG("%p started new touch block %p\n", this, block);
+
+    // We want to cancel animations here as soon as possible (i.e. without waiting for
+    // content responses) because a finger has gone down and we don't want to keep moving
+    // the content under the finger. However, to prevent "future" touchstart events from
+    // interfering with "past" animations (i.e. from a previous touch block that is still
+    // being processed) we only do this animation-cancellation if there are no older
+    // touch blocks still in the queue.
+    if (block == CurrentTouchBlock()) {
+      if (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
+        // If we're already in a fast fling, then we want the touch event to stop the fling
+        // and to disallow the touch event from being used as part of a fling.
+        block->DisallowSingleTap();
+      }
+      CancelAnimationForHandoffChain();
+    }
+
     if (mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) {
       // Content may intercept the touch events and prevent-default them. So we schedule
       // a timeout to give content time to do that.
       ScheduleContentResponseTimeout();
     } else {
       // Content won't prevent-default this, so we can just pretend like we scheduled
       // a timeout and it expired. Note that we will still receive a ContentReceivedTouch
       // callback for this block, and so we need to make sure we adjust the touch balance.
@@ -947,29 +975,18 @@ nsEventStatus AsyncPanZoomController::Ha
 
 nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-start in state %d\n", this, mState);
   mPanDirRestricted = false;
   ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
 
   switch (mState) {
     case FLING:
-      if (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
-        // This is ugly. Hopefully bug 1009733 can reorganize how events
-        // flow through APZC and change it so that events are handed to the
-        // gesture listener *after* we deal with them here. This should allow
-        // removal of this ugly code.
-        nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
-        if (listener) {
-          listener->CancelSingleTouchDown();
-        }
-      }
-      // Fall through.
     case ANIMATING_ZOOM:
-      CancelAnimation();
+      CancelAnimationForHandoffChain();
       // Fall through.
     case NOTHING: {
       mX.StartTouch(point.x, aEvent.mTime);
       mY.StartTouch(point.y, aEvent.mTime);
       APZCTreeManager* treeManagerLocal = mTreeManager;
       nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
       if (treeManagerLocal && controller) {
         bool touchCanBePan = treeManagerLocal->CanBePanned(this);
@@ -1285,17 +1302,17 @@ AsyncPanZoomController::ConvertToGecko(c
   return false;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
 
   mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
   mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
-  CancelAnimation();
+  CancelAnimationForHandoffChain();
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
 
   mX.CancelTouch();
@@ -1402,27 +1419,29 @@ nsEventStatus AsyncPanZoomController::On
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
-      int32_t modifiers = WidgetModifiersToDOMModifiers(aModifiers);
+      if (!CurrentTouchBlock()->SetSingleTapOccurred()) {
+        return nsEventStatus_eIgnore;
+      }
       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
       // calling controller->HandleSingleTap directly might mean that content receives
       // the single tap message before the corresponding touch-up. To avoid that we
       // schedule the singletap message to run on the next spin of the event loop.
       // See bug 965381 for the issue this was causing.
       controller->PostDelayedTask(
         NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
-                          geckoScreenPoint, modifiers, GetGuid()),
+                          geckoScreenPoint, WidgetModifiersToDOMModifiers(aModifiers),
+                          GetGuid()),
         0);
-      CurrentTouchBlock()->SetSingleTapOccurred();
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 void AsyncPanZoomController::OnTouchEndOrCancel() {
   if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
@@ -1775,16 +1794,20 @@ void AsyncPanZoomController::StartAnimat
   ScheduleComposite();
 }
 
 void AsyncPanZoomController::CancelAnimation() {
   ReentrantMonitorAutoEnter lock(mMonitor);
   APZC_LOG("%p running CancelAnimation in state %d\n", this, mState);
   SetState(NOTHING);
   mAnimation = nullptr;
+  // Since there is no animation in progress now the axes should
+  // have no velocity either.
+  mX.SetVelocity(0);
+  mY.SetVelocity(0);
   // Setting the state to nothing and cancelling the animation can
   // preempt normal mechanisms for relieving overscroll, so we need to clear
   // overscroll here.
   if (mX.IsOverscrolled() || mY.IsOverscrolled()) {
     mX.ClearOverscroll();
     mY.ClearOverscroll();
     RequestContentRepaint();
     ScheduleComposite();
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -532,16 +532,22 @@ protected:
    * mozbrowserasyncscroll events in some conditions, this function ensures
    * that the last mozbrowserasyncscroll event will be fired after a period of
    * time.
    */
   void FireAsyncScrollOnTimeout();
 
 private:
   /**
+   * Cancel animations all the way up the overscroll handoff chain if possible,
+   * or just the local APZC if not.
+   */
+  void CancelAnimationForHandoffChain();
+
+  /**
    * Helper to set the current state. Holds the monitor before actually setting
    * it and fires content controller events based on state changes. Always set
    * the state using this call, do not set it directly.
    */
   void SetState(PanZoomState aState);
 
   /**
    * Convert ScreenPoint relative to this APZC to CSSPoint relative
--- a/gfx/layers/apz/src/GestureEventListener.cpp
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -104,33 +104,16 @@ nsEventStatus GestureEventListener::Hand
     mTouches.Clear();
     rv = HandleInputTouchCancel();
     break;
   }
 
   return rv;
 }
 
-void GestureEventListener::CancelSingleTouchDown()
-{
-  GEL_LOG("Cancelling touch-down while in state %d\n", mState);
-
-  switch (mState) {
-  case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
-    CancelLongTapTimeoutTask();
-    CancelMaxTapTimeoutTask();
-    SetState(GESTURE_NONE);
-    break;
-  default:
-    NS_WARNING("IgnoreLastTouchStart() called while in unexpected state");
-    SetState(GESTURE_NONE);
-    break;
-  }
-}
-
 int32_t GestureEventListener::GetLastTouchIdentifier() const
 {
   if (mTouches.Length() != 1) {
     NS_WARNING("GetLastTouchIdentifier() called when last touch event "
                "did not have one touch");
   }
   return mTouches.IsEmpty() ? -1 : mTouches[0].mIdentifier;
 }
--- a/gfx/layers/apz/src/GestureEventListener.h
+++ b/gfx/layers/apz/src/GestureEventListener.h
@@ -49,22 +49,16 @@ public:
   /**
    * General input handler for a touch event. If the touch event is not a part
    * of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
    * it gets consumed here and never forwarded along.
    */
   nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
 
   /**
-   * Cancels any tap-related timeouts and clears any state that was set because
-   * we recently processed a touch-start.
-   */
-  void CancelSingleTouchDown();
-
-  /**
    * Returns the identifier of the touch in the last touch event processed by
    * this GestureEventListener. This should only be called when the last touch
    * event contained only one touch.
    */
   int32_t GetLastTouchIdentifier() const;
 
 private:
   // Private destructor, to discourage deletion outside of Release():
--- a/gfx/layers/apz/src/TouchBlockState.cpp
+++ b/gfx/layers/apz/src/TouchBlockState.cpp
@@ -23,16 +23,17 @@ static const TouchBlockState::TouchBehav
     AllowedTouchBehavior::PINCH_ZOOM |
     AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
 
 TouchBlockState::TouchBlockState()
   : mAllowedTouchBehaviorSet(false)
   , mPreventDefault(false)
   , mContentResponded(false)
   , mContentResponseTimerExpired(false)
+  , mSingleTapDisallowed(false)
   , mSingleTapOccurred(false)
 {
   TBS_LOG("Creating %p\n", this);
 }
 
 bool
 TouchBlockState::SetContentResponse(bool aPreventDefault)
 {
@@ -99,20 +100,31 @@ TouchBlockState::IsReadyForHandling() co
 bool
 TouchBlockState::IsDefaultPrevented() const
 {
   MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
   return mPreventDefault;
 }
 
 void
+TouchBlockState::DisallowSingleTap()
+{
+  TBS_LOG("%p disallowing single-tap\n", this);
+  mSingleTapDisallowed = true;
+}
+
+bool
 TouchBlockState::SetSingleTapOccurred()
 {
-  TBS_LOG("%p setting single-tap occurred\n", this);
-  mSingleTapOccurred = true;
+  TBS_LOG("%p attempting to set single-tap occurred; disallowed=%d\n", this, mSingleTapDisallowed);
+  if (!mSingleTapDisallowed) {
+    mSingleTapOccurred = true;
+    return true;
+  }
+  return false;
 }
 
 bool
 TouchBlockState::SingleTapOccurred() const
 {
   return mSingleTapOccurred;
 }
 
--- a/gfx/layers/apz/src/TouchBlockState.h
+++ b/gfx/layers/apz/src/TouchBlockState.h
@@ -80,19 +80,24 @@ public:
    */
   bool IsReadyForHandling() const;
   /**
    * @return true iff web content cancelled this block of events.
    */
   bool IsDefaultPrevented() const;
 
   /**
-   * Set a flag that indicates that this touch block triggered a single tap event.
+   * Set a flag that disables setting the single-tap flag on this block.
    */
-  void SetSingleTapOccurred();
+  void DisallowSingleTap();
+  /**
+   * Set a flag that indicates that this touch block triggered a single tap event.
+   * @return true iff DisallowSingleTap was not previously called.
+   */
+  bool SetSingleTapOccurred();
   /**
    * @return true iff SetSingleTapOccurred was previously called on this block.
    */
   bool SingleTapOccurred() const;
 
   /**
    * @return true iff there are pending events in this touch block.
    */
@@ -130,16 +135,17 @@ public:
   bool TouchActionAllowsPanningXY() const;
 
 private:
   nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
   bool mAllowedTouchBehaviorSet;
   bool mPreventDefault;
   bool mContentResponded;
   bool mContentResponseTimerExpired;
+  bool mSingleTapDisallowed;
   bool mSingleTapOccurred;
   nsTArray<MultiTouchInput> mEvents;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_TouchBlockState_h
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -92,33 +92,18 @@ BasicCompositor::CreateRenderTarget(cons
   return rt.forget();
 }
 
 TemporaryRef<CompositingRenderTarget>
 BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect,
                                               const CompositingRenderTarget *aSource,
                                               const IntPoint &aSourcePoint)
 {
-  RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
-  RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect);
-
-  DrawTarget *source;
-  if (aSource) {
-    const BasicCompositingRenderTarget* sourceSurface =
-      static_cast<const BasicCompositingRenderTarget*>(aSource);
-    source = sourceSurface->mDrawTarget;
-  } else {
-    source = mDrawTarget;
-  }
-
-  RefPtr<SourceSurface> snapshot = source->Snapshot();
-
-  IntRect sourceRect(aSourcePoint, aRect.Size());
-  rt->mDrawTarget->CopySurface(snapshot, sourceRect, IntPoint(0, 0));
-  return rt.forget();
+  MOZ_CRASH("Shouldn't be called!");
+  return nullptr;
 }
 
 TemporaryRef<DataTextureSource>
 BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
 {
   RefPtr<DataTextureSource> result = new DataTextureSourceBasic();
   return result.forget();
 }
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -100,17 +100,17 @@ public:
   virtual bool IsInactiveLayerManager() { return mType == BLM_INACTIVE; }
 
   virtual void BeginTransaction();
   virtual void BeginTransactionWithTarget(gfxContext* aTarget);
   virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT);
   virtual void EndTransaction(DrawThebesLayerCallback aCallback,
                               void* aCallbackData,
                               EndTransactionFlags aFlags = END_DEFAULT);
-  virtual bool AreComponentAlphaLayersEnabled() { return !IsWidgetLayerManager(); }
+  virtual bool ShouldAvoidComponentAlphaLayers() { return IsWidgetLayerManager(); }
 
   void AbortTransaction();
 
   virtual void SetRoot(Layer* aLayer);
 
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -532,16 +532,23 @@ ClientLayerManager::Hold(Layer* aLayer)
 bool
 ClientLayerManager::IsCompositingCheap()
 {
   // Whether compositing is cheap depends on the parent backend.
   return mForwarder->mShadowManager &&
          LayerManager::IsCompositingCheap(mForwarder->GetCompositorBackendType());
 }
 
+bool
+ClientLayerManager::AreComponentAlphaLayersEnabled()
+{
+  return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC &&
+         LayerManager::AreComponentAlphaLayersEnabled();
+}
+
 void
 ClientLayerManager::SetIsFirstPaint()
 {
   mForwarder->SetIsFirstPaint();
 }
 
 TextureClientPool*
 ClientLayerManager::GetTexturePool(SurfaceFormat aFormat)
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -140,16 +140,19 @@ public:
 
   void* GetThebesLayerCallbackData() const
   { return mThebesLayerCallbackData; }
 
   CompositorChild* GetRemoteRenderer();
 
   CompositorChild* GetCompositorChild();
 
+  // Disable component alpha layers with the software compositor.
+  virtual bool ShouldAvoidComponentAlphaLayers() { return !IsCompositingCheap(); }
+
   /**
    * Called for each iteration of a progressive tile update. Updates
    * aMetrics with the current scroll offset and scale being used to composite
    * the primary scrollable layer in this manager, to determine what area
    * intersects with the target composition bounds.
    * aDrawingCritical will be true if the current drawing operation is using
    * the critical displayport.
    * Returns true if the update should continue, or false if it should be
@@ -180,16 +183,18 @@ public:
 
   virtual void DidComposite(uint64_t aTransactionId);
 
   virtual bool SupportsMixBlendModes(EnumSet<gfx::CompositionOp>& aMixBlendModes) MOZ_OVERRIDE
   {
    return (GetTextureFactoryIdentifier().mSupportedBlendModes & aMixBlendModes) == aMixBlendModes;
   }
 
+  virtual bool AreComponentAlphaLayersEnabled() MOZ_OVERRIDE;
+
   // Log APZ test data for the current paint. We supply the paint sequence
   // number ourselves, and take care of calling APZTestData::StartNewPaint()
   // when a new paint is started.
   void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
                                   const std::string& aKey,
                                   const std::string& aValue)
   {
     mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -139,16 +139,23 @@ LayerManagerComposite::Destroy()
 }
 
 void
 LayerManagerComposite::UpdateRenderBounds(const nsIntRect& aRect)
 {
   mRenderBounds = aRect;
 }
 
+bool
+LayerManagerComposite::AreComponentAlphaLayersEnabled()
+{
+  return Compositor::GetBackend() != LayersBackend::LAYERS_BASIC &&
+         LayerManager::AreComponentAlphaLayersEnabled();
+}
+
 void
 LayerManagerComposite::BeginTransaction()
 {
   mInTransaction = true;
   
   if (!mCompositor->Ready()) {
     return;
   }
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -147,16 +147,18 @@ public:
   {
     MOZ_CRASH("Shouldn't be called for composited layer manager");
   }
   virtual void GetBackendName(nsAString& name) MOZ_OVERRIDE
   {
     MOZ_CRASH("Shouldn't be called for composited layer manager");
   }
 
+  virtual bool AreComponentAlphaLayersEnabled() MOZ_OVERRIDE;
+
   virtual TemporaryRef<DrawTarget>
     CreateOptimalMaskDrawTarget(const IntSize &aSize) MOZ_OVERRIDE;
 
   virtual const char* Name() const MOZ_OVERRIDE { return ""; }
 
   enum WorldTransforPolicy {
     ApplyWorldTransform,
     DontApplyWorldTransform
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -193,20 +193,26 @@ protected:
 
     testStartTime = TimeStamp::Now();
     AsyncPanZoomController::SetFrameTime(testStartTime);
 
     mcc = new NiceMock<MockContentControllerDelayed>();
     tm = new TestAPZCTreeManager();
     apzc = new TestAsyncPanZoomController(0, mcc, tm, mGestureBehavior);
     apzc->SetFrameMetrics(TestFrameMetrics());
+
+    // Since most tests pass inputs directly to the APZC instead of going through
+    // the tree manager, we need to build the overscroll handoff chain explicitly
+    // for panning and animation-cancelling to work correctly.
+    tm->BuildOverscrollHandoffChain(apzc);
   }
 
   virtual void TearDown()
   {
+    tm->ClearOverscrollHandoffChain();
     apzc->Destroy();
   }
 
   void SetMayHaveTouchListeners()
   {
     apzc->GetFrameMetrics().mMayHaveTouchListeners = true;
   }
 
@@ -246,52 +252,52 @@ ApzcDown(AsyncPanZoomController* apzc, i
 static nsEventStatus
 ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int& aTime)
 {
   MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
   return apzc->ReceiveInputEvent(mti);
 }
 
-static nsEventStatus
-ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime,
-        int aTapLength, MockContentControllerDelayed* mcc = nullptr)
+static void
+ApzcTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength,
+        nsEventStatus (*aOutEventStatuses)[2] = nullptr)
 {
-  nsEventStatus status = ApzcDown(apzc, aX, aY, aTime);
-  if (mcc != nullptr) {
-    // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
-    // if we were provided a non-null mcc we want to clear them.
-    mcc->CheckHasDelayedTask();
-    mcc->ClearDelayedTask();
-    mcc->CheckHasDelayedTask();
-    mcc->ClearDelayedTask();
+  nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[0] = status;
+  }
+  aTime += aTapLength;
+  status = ApzcUp(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[1] = status;
   }
-  EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
-  aTime += aTapLength;
-  return ApzcUp(apzc, aX, aY, aTime);
+}
+
+static void
+ApzcTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength)
+{
+  nsEventStatus statuses[2];
+  ApzcTap(aApzc, aX, aY, aTime, aTapLength, &statuses);
+  EXPECT_EQ(nsEventStatus_eConsumeNoDefault, statuses[0]);
+  EXPECT_EQ(nsEventStatus_eIgnore, statuses[1]);
 }
 
 static void
 ApzcPan(AsyncPanZoomController* aApzc,
-        TestAPZCTreeManager* aTreeManager,
         int& aTime,
         int aTouchStartY,
         int aTouchEndY,
         bool aKeepFingerDown = false,
         nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
         nsEventStatus (*aOutEventStatuses)[4] = nullptr)
 {
   const int TIME_BETWEEN_TOUCH_EVENT = 100;
   const int OVERCOME_TOUCH_TOLERANCE = 100;
 
-  // Since we're passing inputs directly to the APZC instead of going through
-  // the tree manager, we need to build the overscroll handoff chain explicitly
-  // for panning to work correctly.
-  aTreeManager->BuildOverscrollHandoffChain(aApzc);
-
   // Make sure the move is large enough to not be handled as a tap
   nsEventStatus status = ApzcDown(aApzc, 10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE, aTime);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
@@ -323,38 +329,33 @@ ApzcPan(AsyncPanZoomController* aApzc,
   } else {
     status = (nsEventStatus)-1;
   }
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[3] = status;
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
-
-  // Since we've explicitly built the overscroll handoff chain before
-  // touch-start, we need to explicitly clear it after touch-end.
-  aTreeManager->ClearOverscrollHandoffChain();
 }
 
 /*
  * Dispatches mock touch events to the apzc and checks whether apzc properly
  * consumed them and triggered scrolling behavior.
  */
 static void
 ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
-                      TestAPZCTreeManager* aTreeManager,
                       int& aTime,
                       int aTouchStartY,
                       int aTouchEndY,
                       bool expectIgnoredPan,
                       bool hasTouchListeners,
                       nsTArray<uint32_t>* aAllowedTouchBehaviors)
 {
   nsEventStatus statuses[4]; // down, move, move, up
-  ApzcPan(aApzc, aTreeManager, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
+  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
 
   nsEventStatus touchStartStatus;
   if (hasTouchListeners || gfxPrefs::TouchActionEnabled()) {
     // APZC shouldn't consume the start event now, instead queueing it up
     // waiting for content's response and/or allowed behavior.
     touchStartStatus = nsEventStatus_eConsumeDoDefault;
   } else {
     // APZC should go into the touching state and therefore consume the event.
@@ -374,16 +375,26 @@ ApzcPanAndCheckStatus(AsyncPanZoomContro
     // APZC should go into the panning state and therefore consume the event.
     touchMoveStatus = nsEventStatus_eConsumeNoDefault;
   }
   EXPECT_EQ(touchMoveStatus, statuses[1]);
   EXPECT_EQ(touchMoveStatus, statuses[2]);
 }
 
 static void
+ApzcPanNoFling(AsyncPanZoomController* aApzc,
+               int& aTime,
+               int aTouchStartY,
+               int aTouchEndY)
+{
+  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY);
+  aApzc->CancelAnimation();
+}
+
+static void
 ApzcPinchWithPinchInput(AsyncPanZoomController* aApzc,
                         int aFocusX, int aFocusY, float aScale,
                         nsEventStatus (*aOutEventStatuses)[3] = nullptr)
 {
   nsEventStatus actualStatus = aApzc->HandleGestureEvent(
     PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
                       0, TimeStamp(), ScreenPoint(aFocusX, aFocusY),
                       10.0, 10.0, 0));
@@ -791,29 +802,33 @@ protected:
     int touchEnd = 10;
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
 
     nsTArray<uint32_t> allowedTouchBehaviors;
     allowedTouchBehaviors.AppendElement(aBehavior);
 
     // Pan down
-    ApzcPanAndCheckStatus(apzc, tm, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
+    ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
     apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
     if (aShouldTriggerScroll) {
       EXPECT_EQ(ScreenPoint(0, -(touchEnd-touchStart)), pointOut);
       EXPECT_NE(ViewTransform(), viewTransformOut);
     } else {
       EXPECT_EQ(ScreenPoint(), pointOut);
       EXPECT_EQ(ViewTransform(), viewTransformOut);
     }
 
+    // Clear the fling from the previous pan, or stopping it will
+    // consume the next touchstart
+    apzc->CancelAnimation();
+
     // Pan back
-    ApzcPanAndCheckStatus(apzc, tm, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
+    ApzcPanAndCheckStatus(apzc, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
     apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
     EXPECT_EQ(ScreenPoint(), pointOut);
     EXPECT_EQ(ViewTransform(), viewTransformOut);
   }
 
   void DoPanWithPreventDefaultTest()
   {
@@ -823,17 +838,17 @@ protected:
     int touchStart = 50;
     int touchEnd = 10;
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
 
     // Pan down
     nsTArray<uint32_t> allowedTouchBehaviors;
     allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
-    ApzcPanAndCheckStatus(apzc, tm, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
+    ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
 
     // Send the signal that content has handled and preventDefaulted the touch
     // events. This flushes the event queue.
     apzc->ContentReceivedTouch(true);
     // Run all pending tasks (this should include at least the
     // prevent-default timer).
     EXPECT_LE(1, mcc->RunThroughDelayedTasks());
 
@@ -891,17 +906,17 @@ TEST_F(APZCBasicTester, Fling) {
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // Fling down. Each step scroll further down
-  ApzcPan(apzc, tm, time, touchStart, touchEnd);
+  ApzcPan(apzc, time, touchStart, touchEnd);
   ScreenPoint lastPoint;
   for (int i = 1; i < 50; i+=1) {
     apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &viewTransformOut, pointOut);
     EXPECT_GT(pointOut.y, lastPoint.y);
     lastPoint = pointOut;
   }
 }
 
@@ -914,64 +929,115 @@ protected:
   // is at a slow velocity, and we check that the tap does trigger sending
   // a tap to content. See bug 1022956.
   void DoFlingStopTest(bool aSlow) {
     int time = 0;
     int touchStart = 50;
     int touchEnd = 10;
 
     // Start the fling down.
-    ApzcPan(apzc, tm, time, touchStart, touchEnd);
+    ApzcPan(apzc, time, touchStart, touchEnd);
     // The touchstart from the pan will leave some cancelled tasks in the queue, clear them out
-    EXPECT_EQ(2, mcc->RunThroughDelayedTasks());
+    while (mcc->RunThroughDelayedTasks());
 
     // If we want to tap while the fling is fast, let the fling advance for 10ms only. If we want
     // the fling to slow down more, advance to 2000ms. These numbers may need adjusting if our
     // friction and threshold values change, but they should be deterministic at least.
     int timeDelta = aSlow ? 2000 : 10;
     int tapCallsExpected = aSlow ? 1 : 0;
-    int delayedTasksExpected = aSlow ? 3 : 2;
 
     // Advance the fling animation by timeDelta milliseconds.
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(timeDelta), &viewTransformOut, pointOut);
 
     // Deliver a tap to abort the fling. Ensure that we get a HandleSingleTap
     // call out of it if and only if the fling is slow.
     EXPECT_CALL(*mcc, HandleSingleTap(_, 0, apzc->GetGuid())).Times(tapCallsExpected);
-    ApzcTap(apzc, 10, 10, time, 0, nullptr);
-    EXPECT_EQ(delayedTasksExpected, mcc->RunThroughDelayedTasks());
+    ApzcTap(apzc, 10, 10, time, 0);
+    while (mcc->RunThroughDelayedTasks());
 
     // Verify that we didn't advance any further after the fling was aborted, in either case.
     ScreenPoint finalPointOut;
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(timeDelta + 1000), &viewTransformOut, finalPointOut);
     EXPECT_EQ(pointOut.x, finalPointOut.x);
     EXPECT_EQ(pointOut.y, finalPointOut.y);
 
     apzc->AssertStateIsReset();
   }
+
+  void DoFlingStopWithSlowListener(bool aPreventDefault) {
+    SetMayHaveTouchListeners();
+
+    int time = 0;
+    int touchStart = 50;
+    int touchEnd = 10;
+
+    // Start the fling down.
+    ApzcPan(apzc, time, touchStart, touchEnd);
+    apzc->ContentReceivedTouch(false);
+    while (mcc->RunThroughDelayedTasks());
+
+    // Sample the fling a couple of times to ensure it's going.
+    ScreenPoint point, finalPoint;
+    ViewTransform viewTransform;
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(10), &viewTransform, point);
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(20), &viewTransform, finalPoint);
+    EXPECT_GT(finalPoint.y, point.y);
+
+    // Now we put our finger down to stop the fling
+    ApzcDown(apzc, 10, 10, time);
+
+    // Re-sample to make sure it hasn't moved
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(30), &viewTransform, point);
+    EXPECT_EQ(finalPoint.x, point.x);
+    EXPECT_EQ(finalPoint.y, point.y);
+
+    // respond to the touchdown that stopped the fling.
+    // even if we do a prevent-default on it, the animation should remain stopped.
+    apzc->ContentReceivedTouch(aPreventDefault);
+    while (mcc->RunThroughDelayedTasks());
+
+    // Verify the page hasn't moved
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(100), &viewTransform, point);
+    EXPECT_EQ(finalPoint.x, point.x);
+    EXPECT_EQ(finalPoint.y, point.y);
+
+    // clean up
+    ApzcUp(apzc, 10, 10, time);
+    while (mcc->RunThroughDelayedTasks());
+
+    apzc->AssertStateIsReset();
+  }
 };
 
 TEST_F(APZCFlingStopTester, FlingStop) {
   DoFlingStopTest(false);
 }
 
 TEST_F(APZCFlingStopTester, FlingStopTap) {
   DoFlingStopTest(true);
 }
 
+TEST_F(APZCFlingStopTester, FlingStopSlowListener) {
+  DoFlingStopWithSlowListener(false);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopPreventDefault) {
+  DoFlingStopWithSlowListener(true);
+}
+
 TEST_F(APZCBasicTester, OverScrollPanning) {
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
   // Pan sufficiently to hit overscroll behavior
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
-  ApzcPan(apzc, tm, time, touchStart, touchEnd);
+  ApzcPan(apzc, time, touchStart, touchEnd);
   EXPECT_TRUE(apzc->IsOverscrolled());
 
   // Note that in the calls to SampleContentTransformForFrame below, the time
   // increment used is sufficiently large for the animation to have completed. However,
   // any single call to SampleContentTransformForFrame will not finish an animation
   // *and* also proceed through the following animation, if there is one.
   // Therefore the minimum number of calls to go from an overscroll-inducing pan
   // to a reset state is 3; these are documented further below.
@@ -1001,17 +1067,17 @@ TEST_F(APZCBasicTester, OverScrollPannin
 
 TEST_F(APZCBasicTester, OverScrollAbort) {
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
   // Pan sufficiently to hit overscroll behavior
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
-  ApzcPan(apzc, tm, time, touchStart, touchEnd);
+  ApzcPan(apzc, time, touchStart, touchEnd);
   EXPECT_TRUE(apzc->IsOverscrolled());
 
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // This sample call will run to the end of the non-overscrolling fling animation
   // and will schedule the overscrolling fling animation (see comment in OverScrollPanning
   // above for more explanation).
@@ -1028,51 +1094,57 @@ TEST_F(APZCBasicTester, OverScrollAbort)
 TEST_F(APZCBasicTester, OverScrollPanningAbort) {
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
   // Pan sufficiently to hit overscroll behaviour. Keep the finger down so
   // the pan does not end.
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
-  ApzcPan(apzc, tm, time, touchStart, touchEnd,
+  ApzcPan(apzc, time, touchStart, touchEnd,
           true);                   // keep finger down
   EXPECT_TRUE(apzc->IsOverscrolled());
 
   // Check that calling CancelAnimation() while the user is still panning
   // (and thus no fling or snap-back animation has had a chance to start)
   // clears the overscroll.
   apzc->CancelAnimation();
   EXPECT_FALSE(apzc->IsOverscrolled());
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, ShortPress) {
   MakeApzcUnzoomable();
 
   int time = 0;
-  nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100, mcc.get());
-  EXPECT_EQ(nsEventStatus_eIgnore, status);
+  ApzcTapAndCheckStatus(apzc, 10, 10, time, 100);
+  // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
+  // we want to clear those.
+  mcc->ClearDelayedTask();
+  mcc->ClearDelayedTask();
 
   // This verifies that the single tap notification is sent after the
   // touchdown is fully processed. The ordering here is important.
   mcc->CheckHasDelayedTask();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   mcc->RunDelayedTask();
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, MediumPress) {
   MakeApzcUnzoomable();
 
   int time = 0;
-  nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400, mcc.get());
-  EXPECT_EQ(nsEventStatus_eIgnore, status);
+  ApzcTapAndCheckStatus(apzc, 10, 10, time, 400);
+  // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
+  // we want to clear those.
+  mcc->ClearDelayedTask();
+  mcc->ClearDelayedTask();
 
   // This verifies that the single tap notification is sent after the
   // touchdown is fully processed. The ordering here is important.
   mcc->CheckHasDelayedTask();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   mcc->RunDelayedTask();
 
@@ -1596,17 +1668,19 @@ TEST_F(APZCTreeManagerTester, HitTesting
   // Silence GMock warnings about "uninteresting mock function calls".
   EXPECT_CALL(*mcc, PostDelayedTask(_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
   // Since this paint request is in the queue to Gecko, transformToGecko will
   // take it into account.
-  ApzcPan(apzcroot, manager, time, 100, 50);
+  manager->BuildOverscrollHandoffChain(apzcroot);
+  ApzcPanNoFling(apzcroot, time, 100, 50);
+  manager->ClearOverscrollHandoffChain();
 
   // Hit where layers[3] used to be. It should now hit the root.
   hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
   EXPECT_EQ(apzcroot, hit.get());
   // transformToApzc doesn't unapply the root's own async transform
   EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
   // and transformToGecko unapplies it and then reapplies it, because by the
   // time the event being transformed reaches Gecko the new paint request will
@@ -1622,17 +1696,19 @@ TEST_F(APZCTreeManagerTester, HitTesting
   // transformToGecko reapplies both the css transform and the async transform
   // because we have already issued a paint request with it.
   EXPECT_EQ(gfxPoint(25, 25), transformToGecko.Transform(gfxPoint(12.5, 75)));
 
   // This second pan will move the APZC by another 50 pixels but since the paint
   // request dispatched above has not "completed", we will not dispatch another
   // one yet. Now we have an async transform on top of the pending paint request
   // transform.
-  ApzcPan(apzcroot, manager, time, 100, 50);
+  manager->BuildOverscrollHandoffChain(apzcroot);
+  ApzcPanNoFling(apzcroot, time, 100, 50);
+  manager->ClearOverscrollHandoffChain();
 
   // Hit where layers[3] used to be. It should now hit the root.
   hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
   EXPECT_EQ(apzcroot, hit.get());
   // transformToApzc doesn't unapply the root's own async transform
   EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
   // transformToGecko unapplies the full async transform of -100 pixels, and then
   // reapplies the "D" transform of -50 leading to an overall adjustment of +50
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -602,27 +602,17 @@ gfxUtils::DrawPixelSnapped(gfxContext*  
 
     aFilter = ReduceResamplingFilter(aFilter, aImageRect.Width(), aImageRect.Height(), aSourceRect.Width(), aSourceRect.Height());
 
     gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
 
     // On Mobile, we don't ever want to do this; it has the potential for
     // allocating very large temporary surfaces, especially since we'll
     // do full-page snapshots often (see bug 749426).
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-    // If the pattern translation is large we can get into trouble with pixman's
-    // 16 bit coordinate limits. For now, we only do this on platforms where
-    // we know we have the pixman limits. 16384.0 is a somewhat arbitrary
-    // large number to make sure we avoid the expensive fmod when we can, but
-    // still maintain a safe margin from the actual limit
-    if (doTile && (userSpaceToImageSpace._32 > 16384.0 || userSpaceToImageSpace._31 > 16384.0)) {
-        userSpaceToImageSpace._31 = fmod(userSpaceToImageSpace._31, aImageRect.width);
-        userSpaceToImageSpace._32 = fmod(userSpaceToImageSpace._32, aImageRect.height);
-    }
-#else
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
     // OK now, the hard part left is to account for the subimage sampling
     // restriction. If all the transforms involved are just integer
     // translations, then we assume no resampling will occur so there's
     // nothing to do.
     // XXX if only we had source-clipping in cairo!
     if (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
         aUserSpaceToImageSpace.HasNonIntegerTranslation()) {
         if (doTile || !aSubimage.Contains(aImageRect)) {
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -78,16 +78,18 @@ using namespace mozilla::image;
 static const char *kFeatureLevelPref =
   "gfx.direct3d.last_used_feature_level_idx";
 static const int kSupportedFeatureLevels[] =
   { D3D10_FEATURE_LEVEL_10_1, D3D10_FEATURE_LEVEL_10_0,
     D3D10_FEATURE_LEVEL_9_3 };
 
 class GfxD2DSurfaceReporter MOZ_FINAL : public nsIMemoryReporter
 {
+    ~GfxD2DSurfaceReporter() {}
+
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData, bool aAnonymize)
     {
         nsresult rv;
 
--- a/image/decoders/icon/win/nsIconChannel.h
+++ b/image/decoders/icon/win/nsIconChannel.h
@@ -22,25 +22,26 @@
 #include "nsIIconURI.h"
 
 #include <windows.h>
 
 class nsIFile;
 
 class nsIconChannel MOZ_FINAL : public nsIChannel, public nsIStreamListener
 {
+  ~nsIconChannel();
+
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIREQUEST
   NS_DECL_NSICHANNEL
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
 
   nsIconChannel();
-  ~nsIconChannel();
 
   nsresult Init(nsIURI* uri);
 
 protected:
   nsCOMPtr<nsIURI> mUrl;
   nsCOMPtr<nsIURI> mOriginalURI;
   int64_t          mContentLength;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
--- a/image/src/ClippedImage.cpp
+++ b/image/src/ClippedImage.cpp
@@ -8,22 +8,23 @@
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 
 #include "ClippedImage.h"
 #include "Orientation.h"
 #include "SVGImageContext.h"
 
-using namespace mozilla;
-using namespace mozilla::gfx;
-using mozilla::layers::LayerManager;
-using mozilla::layers::ImageContainer;
 
 namespace mozilla {
+
+using namespace gfx;
+using layers::LayerManager;
+using layers::ImageContainer;
+
 namespace image {
 
 class ClippedImageCachedSurface
 {
 public:
   ClippedImageCachedSurface(TemporaryRef<SourceSurface> aSurface,
                             const nsIntSize& aViewportSize,
                             const SVGImageContext* aSVGContext,
--- a/image/src/ClippedImage.h
+++ b/image/src/ClippedImage.h
@@ -21,31 +21,31 @@ class DrawSingleTileCallback;
  * An Image wrapper that clips an image against a rectangle. Right now only
  * absolute coordinates in pixels are supported.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class ClippedImage : public ImageWrapper
 {
-  typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef gfx::SourceSurface SourceSurface;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE;
   NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE;
-  NS_IMETHOD_(mozilla::TemporaryRef<SourceSurface>)
+  NS_IMETHOD_(TemporaryRef<SourceSurface>)
     GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
-  NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager,
-                               mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE;
+  NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
+                               layers::ImageContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
                   const SVGImageContext* aSVGContext,
                   uint32_t aWhichFrame,
@@ -55,17 +55,17 @@ public:
   NS_IMETHOD_(nsIntRect) GetImageSpaceInvalidationRect(const nsIntRect& aRect) MOZ_OVERRIDE;
 
 protected:
   ClippedImage(Image* aImage, nsIntRect aClip);
 
   virtual ~ClippedImage();
 
 private:
-  mozilla::TemporaryRef<SourceSurface>
+  TemporaryRef<SourceSurface>
     GetFrameInternal(const nsIntSize& aViewportSize,
                      const SVGImageContext* aSVGContext,
                      uint32_t aWhichFrame,
                      uint32_t aFlags);
   bool ShouldClip();
   bool MustCreateSurface(gfxContext* aContext,
                          const gfxMatrix& aTransform,
                          const gfxRect& aSourceRect,
--- a/image/src/DiscardTracker.cpp
+++ b/image/src/DiscardTracker.cpp
@@ -19,17 +19,17 @@ static const char* sDiscardTimeoutPref =
 /* static */ bool DiscardTracker::sInitialized = false;
 /* static */ bool DiscardTracker::sTimerOn = false;
 /* static */ Atomic<bool> DiscardTracker::sDiscardRunnablePending(false);
 /* static */ uint64_t DiscardTracker::sCurrentDecodedImageBytes = 0;
 /* static */ uint32_t DiscardTracker::sMinDiscardTimeoutMs = 10000;
 /* static */ uint32_t DiscardTracker::sMaxDecodedImageKB = 42 * 1024;
 /* static */ uint32_t DiscardTracker::sHardLimitDecodedImageKB = 0;
 /* static */ PRLock * DiscardTracker::sAllocationLock = nullptr;
-/* static */ mozilla::Mutex* DiscardTracker::sNodeListMutex = nullptr;
+/* static */ Mutex* DiscardTracker::sNodeListMutex = nullptr;
 /* static */ Atomic<bool> DiscardTracker::sShutdown(false);
 
 /*
  * When we notice we're using too much memory for decoded images, we enqueue a
  * DiscardRunnable, which runs this code.
  */
 NS_IMETHODIMP
 DiscardTracker::DiscardRunnable::Run()
--- a/image/src/DiscardTracker.h
+++ b/image/src/DiscardTracker.h
@@ -118,23 +118,23 @@ class DiscardTracker
     static void MaybeDiscardSoon();
     static void TimerCallback(nsITimer *aTimer, void *aClosure);
     static void DiscardNow();
 
     static LinkedList<Node> sDiscardableImages;
     static nsCOMPtr<nsITimer> sTimer;
     static bool sInitialized;
     static bool sTimerOn;
-    static mozilla::Atomic<bool> sDiscardRunnablePending;
+    static Atomic<bool> sDiscardRunnablePending;
     static uint64_t sCurrentDecodedImageBytes;
     static uint32_t sMinDiscardTimeoutMs;
     static uint32_t sMaxDecodedImageKB;
     static uint32_t sHardLimitDecodedImageKB;
     // Lock for safegarding the 64-bit sCurrentDecodedImageBytes
     static PRLock *sAllocationLock;
-    static mozilla::Mutex* sNodeListMutex;
+    static Mutex* sNodeListMutex;
     static Atomic<bool> sShutdown;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif /* mozilla_imagelib_DiscardTracker_h_ */
--- a/image/src/FrameAnimator.cpp
+++ b/image/src/FrameAnimator.cpp
@@ -3,18 +3,18 @@
  * 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 "FrameAnimator.h"
 #include "FrameBlender.h"
 
 #include "imgIContainer.h"
 
-using namespace mozilla::image;
-using namespace mozilla;
+namespace mozilla {
+namespace image {
 
 FrameAnimator::FrameAnimator(FrameBlender& aFrameBlender,
                              uint16_t aAnimationMode)
   : mCurrentAnimationFrameIndex(0)
   , mLoopCounter(-1)
   , mFrameBlender(aFrameBlender)
   , mAnimationMode(aAnimationMode)
   , mDoneDecoding(false)
@@ -172,17 +172,17 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
 
   // If we're here, we successfully advanced the frame.
   ret.frameAdvanced = true;
 
   return ret;
 }
 
 FrameAnimator::RefreshResult
-FrameAnimator::RequestRefresh(const mozilla::TimeStamp& aTime)
+FrameAnimator::RequestRefresh(const TimeStamp& aTime)
 {
   // only advance the frame if the current time is greater than or
   // equal to the current frame's end time.
   TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
 
   // By default, an empty RefreshResult.
   RefreshResult ret;
 
@@ -258,9 +258,10 @@ FrameAnimator::GetCurrentAnimationFrameI
 }
 
 nsIntRect
 FrameAnimator::GetFirstFrameRefreshArea() const
 {
   return mFirstFrameRefreshArea;
 }
 
-
+} // namespace image
+} // namespace mozilla
--- a/image/src/FrameAnimator.h
+++ b/image/src/FrameAnimator.h
@@ -56,17 +56,17 @@ public:
 
   /**
    * Re-evaluate what frame we're supposed to be on, and do whatever blending
    * is necessary to get us to that frame.
    *
    * Returns the result of that blending, including whether the current frame
    * changed and what the resulting dirty rectangle is.
    */
-  RefreshResult RequestRefresh(const mozilla::TimeStamp& aTime);
+  RefreshResult RequestRefresh(const TimeStamp& aTime);
 
   /**
    * Call when this image is finished decoding so we know that there aren't any
    * more frames coming.
    */
   void SetDoneDecoding(bool aDone);
 
   /**
@@ -131,24 +131,24 @@ private: // methods
    * lived animation frames.
    *
    * @param aTime the time that the animation should advance to. This will
    *              typically be <= TimeStamp::Now().
    *
    * @returns a RefreshResult that shows whether the frame was successfully
    *          advanced, and its resulting dirty rect.
    */
-  RefreshResult AdvanceFrame(mozilla::TimeStamp aTime);
+  RefreshResult AdvanceFrame(TimeStamp aTime);
 
   /**
    * Get the time the frame we're currently displaying is supposed to end.
    *
    * In the error case, returns an "infinity" timestamp.
    */
-  mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
+  TimeStamp GetCurrentImgFrameEndTime() const;
 
 private: // data
   //! Area of the first frame that needs to be redrawn on subsequent loops.
   nsIntRect mFirstFrameRefreshArea;
 
   //! the time that the animation advanced to the current frame
   TimeStamp mCurrentAnimationFrameTime;
 
--- a/image/src/FrameBlender.cpp
+++ b/image/src/FrameBlender.cpp
@@ -5,19 +5,16 @@
 
 #include "FrameBlender.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "MainThreadUtils.h"
 
 #include "pixman.h"
 
-using namespace mozilla;
-using namespace mozilla::image;
-
 namespace mozilla {
 namespace image {
 
 FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
  : mFrames(aSequenceToUse)
  , mAnim(nullptr)
  , mLoopCount(-1)
 {
--- a/image/src/FrameBlender.h
+++ b/image/src/FrameBlender.h
@@ -7,21 +7,21 @@
 #ifndef mozilla_imagelib_FrameBlender_h_
 #define mozilla_imagelib_FrameBlender_h_
 
 #include "mozilla/MemoryReporting.h"
 #include "gfxTypes.h"
 #include "FrameSequence.h"
 #include "nsCOMPtr.h"
 
-class imgFrame;
-
 namespace mozilla {
 namespace image {
 
+class imgFrame;
+
 /**
  * FrameBlender stores and gives access to imgFrames. It also knows how to
  * blend frames from previous to next, looping if necessary.
  *
  * All logic about when and whether to blend are external to FrameBlender.
  */
 class FrameBlender
 {
@@ -73,17 +73,17 @@ public:
   void SetLoopCount(int32_t aLoopCount);
   int32_t GetLoopCount() const;
 
   void Discard();
 
   void SetSize(nsIntSize aSize) { mSize = aSize; }
 
   size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
-                                                 mozilla::MallocSizeOf aMallocSizeOf) const;
+                                                 MallocSizeOf aMallocSizeOf) const;
 
   void ResetAnimation();
 
   // "Blend" method indicates how the current image is combined with the
   // previous image.
   enum FrameBlendMethod {
     // All color components of the frame, including alpha, overwrite the current
     // contents of the frame's output buffer region
--- a/image/src/FrameSequence.cpp
+++ b/image/src/FrameSequence.cpp
@@ -1,18 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrameSequence.h"
 
-using namespace mozilla;
-using namespace mozilla::image;
-
 namespace mozilla {
 namespace image {
 
 FrameSequence::~FrameSequence()
 {
   ClearFrames();
 }
 
--- a/image/src/FrameSequence.h
+++ b/image/src/FrameSequence.h
@@ -169,17 +169,17 @@ public:
    * Remove (and delete) all frames.
    */
   void ClearFrames();
 
   /* The total number of frames in this image. */
   uint32_t GetNumFrames() const;
 
   size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
-                                                 mozilla::MallocSizeOf aMallocSizeOf) const;
+                                                 MallocSizeOf aMallocSizeOf) const;
 
 private: // data
   //! All the frames of the image
   nsTArray<FrameDataPair> mFrames;
 };
 
 } // namespace image
 } // namespace mozilla
--- a/image/src/FrozenImage.cpp
+++ b/image/src/FrozenImage.cpp
@@ -1,18 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrozenImage.h"
 
-using namespace mozilla::gfx;
+namespace mozilla {
 
-namespace mozilla {
+using namespace gfx;
+
 namespace image {
 
 NS_IMPL_ISUPPORTS_INHERITED0(FrozenImage, ImageWrapper)
 
 nsIntRect
 FrozenImage::FrameRect(uint32_t /* aWhichFrame - ignored */)
 {
   return InnerImage()->FrameRect(FRAME_FIRST);
@@ -81,17 +82,17 @@ FrozenImage::Draw(gfxContext* aContext,
                   uint32_t aFlags)
 {
   return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace,
                             aFill, aSubimage, aViewportSize, aSVGContext,
                             FRAME_FIRST, aFlags);
 }
 
 NS_IMETHODIMP_(void)
-FrozenImage::RequestRefresh(const mozilla::TimeStamp& aTime)
+FrozenImage::RequestRefresh(const TimeStamp& aTime)
 {
   // Do nothing.
 }
 
 NS_IMETHODIMP
 FrozenImage::GetAnimationMode(uint16_t* aAnimationMode)
 {
   *aAnimationMode = kNormalAnimMode;
--- a/image/src/FrozenImage.h
+++ b/image/src/FrozenImage.h
@@ -21,17 +21,17 @@ namespace image {
  * because any imgIContainer method that is affected by animation gets its
  * aWhichFrame argument set to FRAME_FIRST when it passes through FrozenImage.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class FrozenImage : public ImageWrapper
 {
-  typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef gfx::SourceSurface SourceSurface;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
   virtual void DecrementAnimationConsumers() MOZ_OVERRIDE;
 
@@ -45,17 +45,17 @@ public:
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
                   const SVGImageContext* aSVGContext,
                   uint32_t aWhichFrame,
                   uint32_t aFlags) MOZ_OVERRIDE;
-  NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime) MOZ_OVERRIDE;
+  NS_IMETHOD_(void) RequestRefresh(const TimeStamp& aTime) MOZ_OVERRIDE;
   NS_IMETHOD GetAnimationMode(uint16_t* aAnimationMode) MOZ_OVERRIDE;
   NS_IMETHOD SetAnimationMode(uint16_t aAnimationMode) MOZ_OVERRIDE;
   NS_IMETHOD ResetAnimation() MOZ_OVERRIDE;
   NS_IMETHOD_(float) GetFrameIndex(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
 protected:
   FrozenImage(Image* aImage) : ImageWrapper(aImage) { }
   virtual ~FrozenImage() { }
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -75,18 +75,18 @@ public:
    * The size, in bytes, occupied by the significant data portions of the image.
    * This includes both compressed source data and decoded frames.
    */
   virtual uint32_t SizeOfData() = 0;
 
   /**
    * The components that make up SizeOfData().
    */
-  virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
-  virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+  virtual size_t HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const = 0;
+  virtual size_t HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const = 0;
   virtual size_t NonHeapSizeOfDecoded() const = 0;
   virtual size_t OutOfProcessSizeOfDecoded() const = 0;
 
   /**
    * Gets the size of the memory taken up for the parsed vector image's
    * document (e.g. SVGDocument), and returns the document's URL via the
    * aDocURL outparam.
    */
@@ -190,17 +190,17 @@ protected:
    * Helper for RequestRefresh.
    *
    * If we've had a "recent" refresh (i.e. if this image is being used in
    * multiple documents & some other document *just* called RequestRefresh() on
    * this image with a timestamp close to aTime), this method returns true.
    *
    * Otherwise, this method updates mLastRefreshTime to aTime & returns false.
    */
-  bool HadRecentRefresh(const mozilla::TimeStamp& aTime);
+  bool HadRecentRefresh(const TimeStamp& aTime);
 
   /**
    * Decides whether animation should or should not be happening,
    * and makes sure the right thing is being done.
    */
   virtual void EvaluateAnimation();
 
   /**
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -231,17 +231,17 @@ ImageFactory::CreateRasterImage(nsIReque
       if (NS_FAILED(rv) || NS_FAILED(rv2)) {
         NS_WARNING("About to hit OOM in imagelib!");
       }
     }
   }
 
   nsAutoCString ref;
   aURI->GetRef(ref);
-  mozilla::net::nsMediaFragmentURIParser parser(ref);
+  net::nsMediaFragmentURIParser parser(ref);
   if (parser.HasResolution()) {
     newImage->SetRequestedResolution(parser.GetResolution());
   }
 
   if (parser.HasSampleSize()) {
       /* Get our principal */
       nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
       nsCOMPtr<nsIPrincipal> principal;
--- a/image/src/ImageMetadata.cpp
+++ b/image/src/ImageMetadata.cpp
@@ -6,17 +6,18 @@
 
 #include "ImageMetadata.h"
 
 #include "RasterImage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsXPCOMCID.h"
 
-using namespace mozilla::image;
+namespace mozilla {
+namespace image {
 
 void
 ImageMetadata::SetOnImage(RasterImage* image)
 {
   if (mHotspotX != -1 && mHotspotY != -1) {
     nsCOMPtr<nsISupportsPRUint32> intwrapx = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
     nsCOMPtr<nsISupportsPRUint32> intwrapy = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
     intwrapx->SetData(mHotspotX);
@@ -26,8 +27,11 @@ ImageMetadata::SetOnImage(RasterImage* i
   }
 
   image->SetLoopCount(mLoopCount);
 
   for (uint32_t i = 0; i < image->GetNumFrames(); i++) {
     image->SetFrameAsNonPremult(i, mIsNonPremultiplied);
   }
 }
+
+} // namespace image
+} // namespace mozilla
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -5,22 +5,23 @@
 
 #include "ImageWrapper.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "Orientation.h"
 
 #include "mozilla/MemoryReporting.h"
 
-using mozilla::gfx::DataSourceSurface;
-using mozilla::gfx::SourceSurface;
-using mozilla::layers::LayerManager;
-using mozilla::layers::ImageContainer;
+namespace mozilla {
 
-namespace mozilla {
+using gfx::DataSourceSurface;
+using gfx::SourceSurface;
+using layers::LayerManager;
+using layers::ImageContainer;
+
 namespace image {
 
 // Inherited methods from Image.
 
 nsresult
 ImageWrapper::Init(const char* aMimeType, uint32_t aFlags)
 {
   return mInnerImage->Init(aMimeType, aFlags);
@@ -40,23 +41,23 @@ ImageWrapper::FrameRect(uint32_t aWhichF
 
 uint32_t
 ImageWrapper::SizeOfData()
 {
   return mInnerImage->SizeOfData();
 }
 
 size_t
-ImageWrapper::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
+ImageWrapper::HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   return mInnerImage->HeapSizeOfSourceWithComputedFallback(aMallocSizeOf);
 }
 
 size_t
-ImageWrapper::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
+ImageWrapper::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   return mInnerImage->HeapSizeOfDecodedWithComputedFallback(aMallocSizeOf);
 }
 
 size_t
 ImageWrapper::NonHeapSizeOfDecoded() const
 {
   return mInnerImage->NonHeapSizeOfDecoded();
@@ -271,17 +272,17 @@ ImageWrapper::UnlockImage()
 
 NS_IMETHODIMP
 ImageWrapper::RequestDiscard()
 {
   return mInnerImage->RequestDiscard();
 }
 
 NS_IMETHODIMP_(void)
-ImageWrapper::RequestRefresh(const mozilla::TimeStamp& aTime)
+ImageWrapper::RequestRefresh(const TimeStamp& aTime)
 {
   return mInnerImage->RequestRefresh(aTime);
 }
 
 NS_IMETHODIMP
 ImageWrapper::GetAnimationMode(uint16_t* aAnimationMode)
 {
   return mInnerImage->GetAnimationMode(aAnimationMode);
@@ -307,17 +308,17 @@ ImageWrapper::GetFrameIndex(uint32_t aWh
 
 NS_IMETHODIMP_(int32_t)
 ImageWrapper::GetFirstFrameDelay()
 {
   return mInnerImage->GetFirstFrameDelay();
 }
 
 NS_IMETHODIMP_(void)
-ImageWrapper::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
+ImageWrapper::SetAnimationStartTime(const TimeStamp& aTime)
 {
   mInnerImage->SetAnimationStartTime(aTime);
 }
 
 NS_IMETHODIMP_(nsIntRect)
 ImageWrapper::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
 {
   return mInnerImage->GetImageSpaceInvalidationRect(aRect);
--- a/image/src/ImageWrapper.h
+++ b/image/src/ImageWrapper.h
@@ -23,18 +23,18 @@ public:
 
   // Inherited methods from Image.
   virtual nsresult Init(const char* aMimeType, uint32_t aFlags) MOZ_OVERRIDE;
 
   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() MOZ_OVERRIDE;
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   virtual uint32_t SizeOfData() MOZ_OVERRIDE;
-  virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
-  virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t NonHeapSizeOfDecoded() const MOZ_OVERRIDE;
   virtual size_t OutOfProcessSizeOfDecoded() const MOZ_OVERRIDE;
 
   virtual size_t HeapSizeOfVectorImageDocument(nsACString* aDocURL = nullptr) const MOZ_OVERRIDE {
     return mInnerImage->HeapSizeOfVectorImageDocument(aDocURL);
   }
 
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -6,23 +6,24 @@
 #include <algorithm>
 
 #include "gfxDrawable.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 
 #include "OrientedImage.h"
 
-using namespace mozilla::gfx;
-
 using std::swap;
-using mozilla::layers::LayerManager;
-using mozilla::layers::ImageContainer;
 
 namespace mozilla {
+
+using namespace gfx;
+using layers::LayerManager;
+using layers::ImageContainer;
+
 namespace image {
 
 NS_IMPL_ISUPPORTS_INHERITED0(OrientedImage, ImageWrapper)
 
 nsIntRect
 OrientedImage::FrameRect(uint32_t aWhichFrame)
 {
   if (mOrientation.SwapsWidthAndHeight()) {
@@ -102,17 +103,17 @@ OrientedImage::GetFrame(uint32_t aWhichF
   gfx::SurfaceFormat surfaceFormat;
   if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
   }
 
   // Create a surface to draw into.
-  mozilla::RefPtr<DrawTarget> target =
+  RefPtr<DrawTarget> target =
     gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
   if (!target) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
 
--- a/image/src/OrientedImage.h
+++ b/image/src/OrientedImage.h
@@ -18,31 +18,31 @@ namespace image {
  * An Image wrapper that rotates and/or flips an image according to a specified
  * Orientation.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class OrientedImage : public ImageWrapper
 {
-  typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef gfx::SourceSurface SourceSurface;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE;
   NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE;
-  NS_IMETHOD_(mozilla::TemporaryRef<SourceSurface>)
+  NS_IMETHOD_(TemporaryRef<SourceSurface>)
     GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
-  NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager,
-                               mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE;
+  NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
+                               layers::ImageContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
                   const SVGImageContext* aSVGContext,
                   uint32_t aWhichFrame,
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -48,20 +48,22 @@
 #include "GeckoProfiler.h"
 #include "gfx2DGlue.h"
 #include <algorithm>
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
-using namespace mozilla;
-using namespace mozilla::gfx;
-using namespace mozilla::image;
-using namespace mozilla::layers;
+namespace mozilla {
+
+using namespace gfx;
+using namespace layers;
+
+namespace image {
 
 // a mask for flags that will affect the decoding
 #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
 #define DECODE_FLAGS_DEFAULT 0
 
 /* Accounting for compressed data */
 #if defined(PR_LOGGING)
 static PRLogModuleInfo *
@@ -366,19 +368,16 @@ public:
   }
 
   bool IsOK() const { return !!mScaleRequest; }
 
 private:
   nsAutoPtr<ScaleRequest> mScaleRequest;
 };
 
-namespace mozilla {
-namespace image {
-
 /* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
 static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
 
 #ifndef DEBUG
 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
 #else
 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties,
                   imgIContainerDebug)
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -116,28 +116,27 @@ class nsIRequest;
  * prevCompositingFrame.  When DoComposite gets called to do Frame 4, we
  * copy prevCompositingFrame back, and then draw Frame 4 on top.
  *
  * @par
  * The mAnim structure has members only needed for animated images, so
  * it's not allocated until the second frame is added.
  */
 
-class ScaleRequest;
-
 namespace mozilla {
 
 namespace layers {
 class LayerManager;
 class ImageContainer;
 class Image;
 }
 
 namespace image {
 
+class ScaleRequest;
 class Decoder;
 class FrameAnimator;
 
 class RasterImage : public ImageResource
                   , public nsIProperties
                   , public SupportsWeakPtr<RasterImage>
 #ifdef DEBUG
                   , public imgIContainerDebug
--- a/image/src/ScriptedNotificationObserver.cpp
+++ b/image/src/ScriptedNotificationObserver.cpp
@@ -3,17 +3,18 @@
  * 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 "ScriptedNotificationObserver.h"
 #include "imgIScriptedNotificationObserver.h"
 #include "nsCycleCollectionParticipant.h"
 
-using namespace mozilla::image;
+namespace mozilla {
+namespace image {
 
 NS_IMPL_CYCLE_COLLECTION(ScriptedNotificationObserver, mInner)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptedNotificationObserver)
   NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
@@ -42,8 +43,11 @@ ScriptedNotificationObserver::Notify(img
   if (aType == imgINotificationObserver::LOAD_COMPLETE)
     return mInner->LoadComplete(aRequest);
   if (aType == imgINotificationObserver::DISCARD)
     return mInner->Discard(aRequest);
   if (aType == imgINotificationObserver::IS_ANIMATED)
     return mInner->IsAnimated(aRequest);
   return NS_OK;
 }
+
+} // namespace image
+} // namespace mozilla
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -26,19 +26,21 @@
 #include "nsRefPtrHashtable.h"
 #include "nsSize.h"
 #include "nsTArray.h"
 #include "prsystem.h"
 #include "SVGImageContext.h"
 
 using std::max;
 using std::min;
-using namespace mozilla::gfx;
 
 namespace mozilla {
+
+using namespace gfx;
+
 namespace image {
 
 class CachedSurface;
 class SurfaceCacheImpl;
 
 ///////////////////////////////////////////////////////////////////////////////
 // Static Data
 ///////////////////////////////////////////////////////////////////////////////
@@ -213,25 +215,25 @@ public:
   SurfaceCacheImpl(uint32_t aSurfaceCacheExpirationTimeMS,
                    uint32_t aSurfaceCacheSize)
     : mExpirationTracker(MOZ_THIS_IN_INITIALIZER_LIST(),
                          aSurfaceCacheExpirationTimeMS)
     , mMemoryPressureObserver(new MemoryPressureObserver)
     , mMaxCost(aSurfaceCacheSize)
     , mAvailableCost(aSurfaceCacheSize)
   {
-    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os)
       os->AddObserver(mMemoryPressureObserver, "memory-pressure", false);
   }
 
 private:
   virtual ~SurfaceCacheImpl()
   {
-    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os)
       os->RemoveObserver(mMemoryPressureObserver, "memory-pressure");
 
     UnregisterWeakMemoryReporter(this);
   }
 
 public:
   void InitMemoryReporter() {
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -130,19 +130,19 @@ struct SurfaceCache
    * without first calling Lookup to verify that the surface is not already in
    * the cache.
    *
    * @param aTarget      The new surface (in the form of a DrawTarget) to insert
    *                     into the cache.
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
    */
-  static void Insert(mozilla::gfx::DrawTarget* aTarget,
-                     const ImageKey            aImageKey,
-                     const SurfaceKey&         aSurfaceKey);
+  static void Insert(gfx::DrawTarget*  aTarget,
+                     const ImageKey    aImageKey,
+                     const SurfaceKey& aSurfaceKey);
 
   /*
    * Checks if a surface of a given size could possibly be stored in the cache.
    * If CanHold() returns false, Insert() will always fail to insert the
    * surface, but the inverse is not true: Insert() may take more information
    * into account than just image size when deciding whether to cache the
    * surface, so Insert() may still fail even if CanHold() returns true.
    *
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -359,29 +359,29 @@ VectorImage::Init(const char* aMimeType,
 
 nsIntRect
 VectorImage::FrameRect(uint32_t aWhichFrame)
 {
   return nsIntRect::GetMaxSizedIntRect();
 }
 
 size_t
-VectorImage::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
+VectorImage::HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   // We're not storing the source data -- we just feed that directly to
   // our helper SVG document as we receive it, for it to parse.
   // So 0 is an appropriate return value here.
   // If implementing this, we'll need to restructure our callers to make sure
   // any amount we return is attributed to the vector images measure (i.e.
   // "explicit/images/{content,chrome}/vector/{used,unused}/...")
   return 0;
 }
 
 size_t
-VectorImage::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
+VectorImage::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   // If implementing this, we'll need to restructure our callers to make sure
   // any amount we return is attributed to the vector images measure (i.e.
   // "explicit/images/{content,chrome}/vector/{used,unused}/...")
   return 0;
 }
 
 size_t
@@ -498,17 +498,17 @@ VectorImage::StopAnimation()
 
 bool
 VectorImage::ShouldAnimate()
 {
   return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations;
 }
 
 NS_IMETHODIMP_(void)
-VectorImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
+VectorImage::SetAnimationStartTime(const TimeStamp& aTime)
 {
   // We don't care about animation start time.
 }
 
 //------------------------------------------------------------------------------
 // imgIContainer methods
 
 //******************************************************************************
@@ -528,17 +528,17 @@ VectorImage::GetWidth(int32_t* aWidth)
   }
 
   return NS_OK;
 }
 
 //******************************************************************************
 /* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
 NS_IMETHODIMP_(void)
-VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
+VectorImage::RequestRefresh(const TimeStamp& aTime)
 {
   if (HadRecentRefresh(aTime)) {
     return;
   }
 
   EvaluateAnimation();
 
   mSVGDocumentWrapper->TickRefreshDriver();
@@ -750,17 +750,17 @@ VectorImage::GetFrame(uint32_t aWhichFra
   NS_ENSURE_SUCCESS(rv, nullptr);
   return dt->Snapshot();
 }
 
 //******************************************************************************
 /* [noscript] ImageContainer getImageContainer(); */
 NS_IMETHODIMP
 VectorImage::GetImageContainer(LayerManager* aManager,
-                               mozilla::layers::ImageContainer** _retval)
+                               layers::ImageContainer** _retval)
 {
   *_retval = nullptr;
   return NS_OK;
 }
 
 struct SVGDrawingParameters
 {
   SVGDrawingParameters(gfxContext* aContext,
@@ -910,17 +910,17 @@ VectorImage::CreateDrawableAndShow(const
                      // XXX(seth): We may remove this restriction in bug 922893.
                      mHaveAnimations ||
                      // The image is too big to fit in the cache:
                      !SurfaceCache::CanHold(aParams.imageRect.Size());
   if (bypassCache)
     return Show(svgDrawable, aParams);
 
   // Try to create an offscreen surface.
-  mozilla::RefPtr<mozilla::gfx::DrawTarget> target =
+  RefPtr<gfx::DrawTarget> target =
    gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::SurfaceFormat::B8G8R8A8);
 
   // If we couldn't create the draw target, it was probably because it would end
   // up way too big. Generally it also wouldn't fit in the cache, but the prefs
   // could be set such that the cache isn't the limiting factor.
   if (!target)
     return Show(svgDrawable, aParams);
 
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -37,18 +37,18 @@ public:
 
   // (no public constructor - use ImageFactory)
 
   // Methods inherited from Image
   nsresult Init(const char* aMimeType,
                 uint32_t aFlags);
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
-  virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
-  virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
+  virtual size_t HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const;
+  virtual size_t HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const;
   virtual size_t NonHeapSizeOfDecoded() const;
   virtual size_t OutOfProcessSizeOfDecoded() const;
 
   virtual size_t HeapSizeOfVectorImageDocument(nsACString* aDocURL = nullptr) const MOZ_OVERRIDE;
 
   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
                                         nsISupports* aContext,
                                         nsIInputStream* aInStr,
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -18,19 +18,22 @@ static bool gDisableOptimize = false;
 
 #include "GeckoProfiler.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsMargin.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/gfx/Tools.h"
 
-using namespace mozilla;
-using namespace mozilla::gfx;
-using namespace mozilla::image;
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace image {
 
 static UserDataKey kVolatileBuffer;
 
 static void
 VolatileBufferRelease(void *vbuf)
 {
   delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
 }
@@ -745,17 +748,17 @@ void imgFrame::SetCompositingFailed(bool
 {
   mCompositingFailed = val;
 }
 
 // If |aLocation| indicates this is heap memory, we try to measure things with
 // |aMallocSizeOf|.  If that fails (because the platform doesn't support it) or
 // it's non-heap memory, we fall back to computing the size analytically.
 size_t
-imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, mozilla::MallocSizeOf aMallocSizeOf) const
+imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, MallocSizeOf aMallocSizeOf) const
 {
   // aMallocSizeOf is only used if aLocation==gfxMemoryLocation::IN_PROCESS_HEAP.  It
   // should be nullptr otherwise.
   NS_ABORT_IF_FALSE(
     (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&  aMallocSizeOf) ||
     (aLocation != gfxMemoryLocation::IN_PROCESS_HEAP && !aMallocSizeOf),
     "mismatch between aLocation and aMallocSizeOf");
 
@@ -782,8 +785,11 @@ imgFrame::SizeOfExcludingThisWithCompute
   }
 
   if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_NONHEAP) {
     n += mVBuf->NonHeapSizeOfExcludingThis();
   }
 
   return n;
 }
+
+} // namespace image
+} // namespace mozilla
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -8,23 +8,26 @@
 #define imgFrame_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/VolatileBuffer.h"
 #include "gfxDrawable.h"
 #include "imgIContainer.h"
 
+namespace mozilla {
+namespace image {
+
 class imgFrame
 {
-  typedef mozilla::gfx::Color Color;
-  typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
-  typedef mozilla::gfx::IntSize IntSize;
-  typedef mozilla::gfx::SourceSurface SourceSurface;
-  typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+  typedef gfx::Color Color;
+  typedef gfx::DataSourceSurface DataSourceSurface;
+  typedef gfx::IntSize IntSize;
+  typedef gfx::SourceSurface SourceSurface;
+  typedef gfx::SurfaceFormat SurfaceFormat;
 
 public:
   imgFrame();
   ~imgFrame();
 
   nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
   nsresult Optimize();
 
@@ -62,34 +65,34 @@ public:
   bool GetCompositingFailed() const;
   void SetCompositingFailed(bool val);
 
   nsresult LockImageData();
   nsresult UnlockImageData();
 
   void SetDiscardable();
 
-  mozilla::TemporaryRef<SourceSurface> GetSurface();
+  TemporaryRef<SourceSurface> GetSurface();
 
   Color
   SinglePixelColor()
   {
     return mSinglePixelColor;
   }
 
   bool IsSinglePixel()
   {
     return mSinglePixel;
   }
 
-  mozilla::TemporaryRef<SourceSurface> CachedSurface();
+  TemporaryRef<SourceSurface> CachedSurface();
 
   size_t SizeOfExcludingThisWithComputedFallbackIfHeap(
            gfxMemoryLocation aLocation,
-           mozilla::MallocSizeOf aMallocSizeOf) const;
+           MallocSizeOf aMallocSizeOf) const;
 
   uint8_t GetPaletteDepth() const { return mPaletteDepth; }
   uint32_t PaletteDataLength() const {
     if (!mPaletteDepth)
       return 0;
 
     return ((1 << mPaletteDepth) * sizeof(uint32_t));
   }
@@ -112,58 +115,56 @@ private: // methods
                                       gfxMatrix&         aUserSpaceToImageSpace,
                                       gfxRect&           aFill,
                                       gfxRect&           aSubimage,
                                       gfxRect&           aSourceRect,
                                       gfxRect&           aImageRect,
                                       SourceSurface*     aSurface);
 
 private: // data
-  mozilla::RefPtr<DataSourceSurface> mImageSurface;
-  mozilla::RefPtr<SourceSurface> mOptSurface;
+  RefPtr<DataSourceSurface> mImageSurface;
+  RefPtr<SourceSurface> mOptSurface;
 
   IntSize      mSize;
   nsIntPoint   mOffset;
 
   nsIntRect    mDecoded;
 
-  mutable mozilla::Mutex mDecodedMutex;
+  mutable Mutex mDecodedMutex;
 
   // The palette and image data for images that are paletted, since Cairo
   // doesn't support these images.
   // The paletted data comes first, then the image data itself.
   // Total length is PaletteDataLength() + GetImageDataLength().
   uint8_t*     mPalettedImageData;
 
   // Note that the data stored in gfx::Color is *non-alpha-premultiplied*.
   Color        mSinglePixelColor;
 
   int32_t      mTimeout; // -1 means display forever
   int32_t      mDisposalMethod;
 
   /** Indicates how many readers currently have locked this frame */
   int32_t mLockCount;
 
-  mozilla::RefPtr<mozilla::VolatileBuffer> mVBuf;
-  mozilla::VolatileBufferPtr<uint8_t> mVBufPtr;
+  RefPtr<VolatileBuffer> mVBuf;
+  VolatileBufferPtr<uint8_t> mVBufPtr;
 
   SurfaceFormat mFormat;
   uint8_t      mPaletteDepth;
   int8_t       mBlendMethod;
   bool mSinglePixel;
   bool mCompositingFailed;
   bool mNonPremult;
   bool mDiscardable;
 
   /** Have we called DiscardTracker::InformAllocation()? */
   bool mInformedDiscardTracker;
 };
 
-namespace mozilla {
-namespace image {
   // An RAII class to ensure it's easy to balance locks and unlocks on
   // imgFrames.
   class AutoFrameLocker
   {
   public:
     AutoFrameLocker(imgFrame* frame)
       : mFrame(frame)
       , mSucceeded(NS_SUCCEEDED(frame->LockImageData()))
@@ -178,12 +179,13 @@ namespace image {
 
     // Whether the lock request succeeded.
     bool Succeeded() { return mSucceeded; }
 
   private:
     imgFrame* mFrame;
     bool mSucceeded;
   };
-}
-}
+
+} // namespace image
+} // namespace mozilla
 
 #endif /* imgFrame_h */
--- a/intl/locale/src/windows/nsCollationWin.h
+++ b/intl/locale/src/windows/nsCollationWin.h
@@ -10,24 +10,24 @@
 
 #include "nsICollation.h"
 #include "nsCollation.h"  // static library
 #include "plstr.h"
 
 
 
 class nsCollationWin MOZ_FINAL : public nsICollation {
+  ~nsCollationWin();
 
 protected:
   nsCollation   *mCollation;  // XP collation class
   uint32_t      mLCID;        // Windows platform locale ID
 
 public: 
   nsCollationWin();
-  ~nsCollationWin(); 
 
   // nsISupports interface
   NS_DECL_ISUPPORTS
 
   // nsICollation interface
   NS_DECL_NSICOLLATION
 
 };
--- a/intl/locale/src/windows/nsDateTimeFormatWin.h
+++ b/intl/locale/src/windows/nsDateTimeFormatWin.h
@@ -10,16 +10,17 @@
 
 #include "nsIDateTimeFormat.h"
 #include <windows.h>
 
 
 // Locale sensitive date and time format interface
 // 
 class nsDateTimeFormatWin : public nsIDateTimeFormat {
+  virtual ~nsDateTimeFormatWin() {}
 
 public: 
   NS_DECL_THREADSAFE_ISUPPORTS 
 
   // performs a locale sensitive date formatting operation on the time_t parameter
   NS_IMETHOD FormatTime(nsILocale* locale, 
                         const nsDateFormatSelector  dateFormatSelector, 
                         const nsTimeFormatSelector timeFormatSelector, 
@@ -45,18 +46,16 @@ public:
                                   const nsDateFormatSelector  dateFormatSelector, 
                                   const nsTimeFormatSelector timeFormatSelector, 
                                   const PRExplodedTime*  explodedTime, 
                                   nsAString& stringOut); 
 
   nsDateTimeFormatWin() {mLocale.SetLength(0);mAppLocale.SetLength(0);}
 
 
-  virtual ~nsDateTimeFormatWin() {}
-
 private:
   // init this interface to a specified locale
   NS_IMETHOD Initialize(nsILocale* locale);
 
   // call GetTimeFormatW or TimeFormatA
   int nsGetTimeFormatW(DWORD dwFlags, const SYSTEMTIME *lpTime,
                     const char* format, char16_t *timeStr, int cchTime);
 
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -31,16 +31,18 @@ struct RunnableMethodTraits<mozilla::ipc
 };
 
 #define IPC_ASSERT(_cond, ...)                                      \
     do {                                                            \
         if (!(_cond))                                               \
             DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
     } while (0)
 
+static uintptr_t gDispatchingUrgentMessageCount;
+
 namespace mozilla {
 namespace ipc {
 
 const int32_t MessageChannel::kNoTimeout = INT32_MIN;
 
 // static
 bool MessageChannel::sIsPumpingMessages = false;
 
@@ -1094,19 +1096,23 @@ MessageChannel::DispatchSyncMessage(cons
 void
 MessageChannel::DispatchUrgentMessage(const Message& aMsg)
 {
     AssertWorkerThread();
     MOZ_ASSERT(aMsg.is_urgent());
 
     Message *reply = nullptr;
 
+    MOZ_ASSERT(NS_IsMainThread());
+
+    gDispatchingUrgentMessageCount++;
     mDispatchingUrgentMessageCount++;
     Result rv = mListener->OnCallReceived(aMsg, reply);
     mDispatchingUrgentMessageCount--;
+    gDispatchingUrgentMessageCount--;
 
     if (!MaybeHandleError(rv, "DispatchUrgentMessage")) {
         delete reply;
         reply = new Message();
         reply->set_urgent();
         reply->set_reply();
         reply->set_reply_error();
     }
@@ -1747,10 +1753,16 @@ MessageChannel::DumpInterruptStack(const
         const char* dir, *sems, *name;
         mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
 
         printf_stderr("%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
                       i, dir, sems, name, id);
     }
 }
 
+bool
+ProcessingUrgentMessages()
+{
+    return gDispatchingUrgentMessageCount > 0;
+}
+
 } // ipc
 } // mozilla
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -636,12 +636,15 @@ class MessageChannel : HasResultCodes
     HANDLE mEvent;
 #endif
 
     // Should the channel abort the process from the I/O thread when
     // a channel error occurs?
     bool mAbortOnError;
 };
 
+bool
+ProcessingUrgentMessages();
+
 } // namespace ipc
 } // namespace mozilla
 
 #endif  // ifndef ipc_glue_MessageChannel_h
--- a/js/ipc/JavaScriptChild.cpp
+++ b/js/ipc/JavaScriptChild.cpp
@@ -3,39 +3,55 @@
  *
  * 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 "JavaScriptChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "nsContentUtils.h"
 #include "xpcprivate.h"
 #include "jsfriendapi.h"
 #include "nsCxPusher.h"
+#include "AccessCheck.h"
 
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
 
 using mozilla::AutoSafeJSContext;
 
+#ifdef NIGHTLY_BUILD
+static void
+UrgentMessageCheck(JSContext *cx, HandleScript script)
+{
+    // We're only allowed to enter chrome JS code while processing urgent
+    // messages.
+    if (ipc::ProcessingUrgentMessages())
+        MOZ_RELEASE_ASSERT(xpc::AccessCheck::isChrome(js::GetContextCompartment(cx)));
+}
+#endif
+
 static void
 FinalizeChild(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data)
 {
     if (status == JSFINALIZE_GROUP_START) {
         static_cast<JavaScriptChild *>(data)->finalize(fop);
     }
 }
 
 JavaScriptChild::JavaScriptChild(JSRuntime *rt)
   : JavaScriptShared(rt),
     JavaScriptBase<PJavaScriptChild>(rt)
 {
+#ifdef NIGHTLY_BUILD
+    js::SetAssertOnScriptEntryHook(rt, UrgentMessageCheck);
+#endif
 }
 
 JavaScriptChild::~JavaScriptChild()
 {
     JS_RemoveFinalizeCallback(rt_, FinalizeChild);
 }
 
 bool
--- a/js/ipc/moz.build
+++ b/js/ipc/moz.build
@@ -25,10 +25,11 @@ FINAL_LIBRARY = 'xul'
 
 DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/js/ipc',
     '/js/public',
     '/js/xpconnect/src',
+    '/js/xpconnect/wrappers',
 ]
 
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -3452,26 +3452,27 @@ public:
     static int32_t addressImmediate(const void *address) {
 #if WTF_CPU_X86_64
         // x64's 64-bit addresses don't all fit in the 32-bit immediate.
         ASSERT(isAddressImmediate(address));
 #endif
         return static_cast<int32_t>(reinterpret_cast<intptr_t>(address));
     }
 
+    static void setInt32(void* where, int32_t value)
+    {
+        reinterpret_cast<int32_t*>(where)[-1] = value;
+    }
+
 private:
 
     static int32_t getInt32(void* where)
     {
         return reinterpret_cast<int32_t*>(where)[-1];
     }
-    static void setInt32(void* where, int32_t value)
-    {
-        reinterpret_cast<int32_t*>(where)[-1] = value;
-    }
 
     class X86InstructionFormatter {
 
         static const int maxInstructionSize = 16;
 
     public:
 
         // Legacy prefix bytes:
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1036,17 +1036,18 @@ class MOZ_STACK_CLASS ModuleCompiler
     PropertyName *                 moduleFunctionName_;
 
     GlobalMap                      globals_;
     FuncVector                     functions_;
     FuncPtrTableVector             funcPtrTables_;
     ExitMap                        exits_;
     MathNameMap                    standardLibraryMathNames_;
     NonAssertingLabel              stackOverflowLabel_;
-    NonAssertingLabel              interruptLabel_;
+    NonAssertingLabel              asyncInterruptLabel_;
+    NonAssertingLabel              syncInterruptLabel_;
 
     char *                         errorString_;
     uint32_t                       errorOffset_;
     bool                           errorOverRecursed_;
 
     int64_t                        usecBefore_;
     SlowFunctionVector             slowFunctions_;
 
@@ -1139,18 +1140,18 @@ class MOZ_STACK_CLASS ModuleCompiler
 
         uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
         uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
 
         // "use strict" should be added to the source if we are in an implicit
         // strict context, see also comment above addUseStrict in
         // js::FunctionToString.
         bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict();
-
-        module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict);
+        module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict,
+                                         cx_->canUseSignalHandlers());
         if (!module_)
             return false;
 
         return true;
     }
 
     bool failOffset(uint32_t offset, const char *str) {
         JS_ASSERT(!errorString_);
@@ -1218,20 +1219,23 @@ class MOZ_STACK_CLASS ModuleCompiler
 
     /*************************************************** Read-only interface */
 
     ExclusiveContext *cx() const { return cx_; }
     AsmJSParser &parser() const { return parser_; }
     TokenStream &tokenStream() const { return parser_.tokenStream; }
     MacroAssembler &masm() { return masm_; }
     Label &stackOverflowLabel() { return stackOverflowLabel_; }
-    Label &interruptLabel() { return interruptLabel_; }
+    Label &asyncInterruptLabel() { return asyncInterruptLabel_; }
+    Label &syncInterruptLabel() { return syncInterruptLabel_; }
     bool hasError() const { return errorString_ != nullptr; }
     const AsmJSModule &module() const { return *module_.get(); }
     uint32_t srcStart() const { return module_->srcStart(); }
+    bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); }
+    bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); }
 
     ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; }
     PropertyName *moduleFunctionName() const { return moduleFunctionName_; }
 
     const Global *lookupGlobal(PropertyName *name) const {
         if (GlobalMap::Ptr p = globals_.lookup(name))
             return p->value();
         return nullptr;
@@ -1521,17 +1525,17 @@ class MOZ_STACK_CLASS ModuleCompiler
     }
 
     bool finish(ScopedJSDeletePtr<AsmJSModule> *module)
     {
         masm_.finish();
         if (masm_.oom())
             return false;
 
-        if (!module_->finish(cx_, tokenStream(), masm_, interruptLabel_))
+        if (!module_->finish(cx_, tokenStream(), masm_, asyncInterruptLabel_))
             return false;
 
         // Finally, convert all the function-pointer table elements into
         // RelativeLinks that will be patched by AsmJSModule::staticallyLink.
         for (unsigned tableIndex = 0; tableIndex < funcPtrTables_.length(); tableIndex++) {
             FuncPtrTable &table = funcPtrTables_[tableIndex];
             unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset();
             for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) {
@@ -1948,16 +1952,17 @@ class FunctionCompiler
         for (unsigned i = 0; i < varInitializers_.length(); i++) {
             MConstant *ins = MConstant::NewAsmJS(alloc(), varInitializers_[i].value,
                                                  varInitializers_[i].type.toMIRType());
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
             if (!mirGen_->ensureBallast())
                 return false;
         }
+        maybeAddInterruptCheck(fn_);
         return true;
     }
 
     /******************************* For consistency of returns in a function */
 
     bool hasAlreadyReturned() const {
         return !alreadyReturned_.empty();
     }
@@ -2120,28 +2125,28 @@ class FunctionCompiler
     }
 
     MDefinition *loadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk)
     {
         if (inDeadCode())
             return nullptr;
         MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr);
         curBlock_->add(load);
-        if (chk == NO_BOUNDS_CHECK)
+        if (chk == NO_BOUNDS_CHECK || m().usesSignalHandlersForOOB())
             load->setSkipBoundsCheck(true);
         return load;
     }
 
     void storeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
     {
         if (inDeadCode())
             return;
         MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v);
         curBlock_->add(store);
-        if (chk == NO_BOUNDS_CHECK)
+        if (chk == NO_BOUNDS_CHECK || m().usesSignalHandlersForOOB())
             store->setSkipBoundsCheck(true);
     }
 
     MDefinition *loadGlobalVar(const ModuleCompiler::Global &global)
     {
         if (inDeadCode())
             return nullptr;
 
@@ -2158,16 +2163,30 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return;
         JS_ASSERT(!global.isConst());
         unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varOrConstIndex());
         curBlock_->add(MAsmJSStoreGlobalVar::New(alloc(), globalDataOffset, v));
     }
 
+    void maybeAddInterruptCheck(ParseNode *pn)
+    {
+        if (inDeadCode())
+            return;
+
+        if (m().usesSignalHandlersForInterrupt())
+            return;
+
+        unsigned lineno = 0, column = 0;
+        m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &lineno, &column);
+        CallSiteDesc callDesc(lineno, column);
+        curBlock_->add(MAsmJSInterruptCheck::New(alloc(), &m().syncInterruptLabel(), callDesc));
+    }
+
     /***************************************************************** Calls */
 
     // The IonMonkey backend maintains a single stack offset (from the stack
     // pointer to the base of the frame) by adding the total amount of spill
     // space required plus the maximum stack required for argument passing.
     // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must
     // manually accumulate, for the entire function, the maximum required stack
     // space for argument passing. (This is passed to the CodeGenerator via
@@ -2471,16 +2490,17 @@ class FunctionCompiler
                                            MBasicBlock::PENDING_LOOP_HEADER);
         if (!*loopEntry)
             return false;
         mirGraph().addBlock(*loopEntry);
         noteBasicBlockPosition(*loopEntry, bodyStmt);
         (*loopEntry)->setLoopDepth(loopStack_.length());
         curBlock_->end(MGoto::New(alloc(), *loopEntry));
         curBlock_ = *loopEntry;
+        maybeAddInterruptCheck(pn);
         return true;
     }
 
     bool branchAndStartLoopBody(MDefinition *cond, MBasicBlock **afterLoop, ParseNode *bodyPn, ParseNode *afterPn)
     {
         if (inDeadCode()) {
             *afterLoop = nullptr;
             return true;
@@ -6650,30 +6670,30 @@ GenerateStackOverflowExit(ModuleCompiler
     return !masm.oom();
 }
 
 static const RegisterSet AllRegsExceptSP =
     RegisterSet(GeneralRegisterSet(Registers::AllMask &
                                    ~(uint32_t(1) << Registers::StackPointer)),
                 FloatRegisterSet(FloatRegisters::AllDoubleMask));
 
-// The operation-callback exit is called from arbitrarily-interrupted asm.js
+// The async interrupt-callback exit is called from arbitrarily-interrupted asm.js
 // code. That means we must first save *all* registers and restore *all*
 // registers (except the stack pointer) when we resume. The address to resume to
 // (assuming that js::HandleExecutionInterrupt doesn't indicate that the
 // execution should be aborted) is stored in AsmJSActivation::resumePC_.
 // Unfortunately, loading this requires a scratch register which we don't have
 // after restoring all registers. To hack around this, push the resumePC on the
 // stack so that it can be popped directly into PC.
 static bool
-GenerateInterruptExit(ModuleCompiler &m, Label *throwLabel)
+GenerateAsyncInterruptExit(ModuleCompiler &m, Label *throwLabel)
 {
     MacroAssembler &masm = m.masm();
     masm.align(CodeAlignment);
-    masm.bind(&m.interruptLabel());
+    masm.bind(&m.asyncInterruptLabel());
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     // Be very careful here not to perturb the machine state before saving it
     // to the stack. In particular, add/sub instructions may set conditions in
     // the flags register.
     masm.push(Imm32(0));            // space for resumePC
     masm.pushFlags();               // after this we are safe to use sub
     masm.setFramePushed(0);         // set to zero so we can use masm.framePushed() below
@@ -6806,16 +6826,78 @@ GenerateInterruptExit(ModuleCompiler &m,
 
 #else
 # error "Unknown architecture!"
 #endif
 
     return !masm.oom();
 }
 
+static const RegisterSet VolatileRegs =
+    RegisterSet(GeneralRegisterSet(Registers::ArgRegMask),
+                FloatRegisterSet(FloatRegisters::VolatileMask));
+
+static bool
+GenerateSyncInterruptExit(ModuleCompiler &m, Label *throwLabel)
+{
+    MacroAssembler &masm = m.masm();
+
+    masm.setFramePushed(0);
+    masm.align(CodeAlignment);
+    masm.bind(&m.syncInterruptLabel());
+
+    MIRTypeVector argTypes(m.cx());
+    argTypes.infallibleAppend(MIRType_Pointer); // cx
+
+    // See AsmJSFrameSize comment in Assembler-shared.h.
+#if defined(JS_CODEGEN_ARM)
+    masm.push(lr);
+#elif defined(JS_CODEGEN_MIPS)
+    masm.push(ra);
+#endif
+
+    // Record sp in the AsmJSActivation for stack unwinding.
+    Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
+    LoadAsmJSActivationIntoRegister(masm, activation);
+    masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitFP()));
+
+    masm.PushRegsInMask(VolatileRegs);
+
+    unsigned stackDec = StackDecrementForCall(masm, argTypes);
+    masm.reserveStack(stackDec);
+
+    ABIArgMIRTypeIter i(argTypes);
+
+    // argument 0: cx
+    if (i->kind() == ABIArg::GPR) {
+        LoadJSContextFromActivation(masm, activation, i->gpr());
+    } else {
+        LoadJSContextFromActivation(masm, activation, activation);
+        masm.storePtr(activation, Address(StackPointer, i->offsetFromArgBase()));
+    }
+    i++;
+
+    JS_ASSERT(i.done());
+
+    AssertStackAlignment(masm);
+    masm.call(AsmJSImmPtr(AsmJSImm_HandleExecutionInterrupt));
+    masm.branchIfFalseBool(ReturnReg, throwLabel);
+
+    masm.freeStack(stackDec);
+    masm.PopRegsInMask(VolatileRegs);
+
+    // Clear exitFP before the frame is destroyed.
+    LoadAsmJSActivationIntoRegister(masm, activation);
+    masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfExitFP()));
+
+    JS_ASSERT(masm.framePushed() == 0);
+    masm.ret();
+    return !masm.oom();
+}
+
 // If an exception is thrown, simply pop all frames (since asm.js does not
 // contain try/catch). To do this:
 //  1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
 //  2. PopRegsInMask to restore the caller's non-volatile registers.
 //  3. Return (to CallAsmJS).
 static bool
 GenerateThrowExit(ModuleCompiler &m, Label *throwLabel)
 {
@@ -6854,22 +6936,22 @@ GenerateStubs(ModuleCompiler &m)
     // The order of the iterations here is non-deterministic, since
     // m.allExits() is a hash keyed by pointer values!
     for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) {
         GenerateFFIExit(m, r.front().key(), r.front().value(), &throwLabel);
         if (m.masm().oom())
             return false;
     }
 
-    if (m.stackOverflowLabel().used()) {
-        if (!GenerateStackOverflowExit(m, &throwLabel))
-            return false;
-    }
-
-    if (!GenerateInterruptExit(m, &throwLabel))
+    if (m.stackOverflowLabel().used() && !GenerateStackOverflowExit(m, &throwLabel))
+        return false;
+
+    if (!GenerateAsyncInterruptExit(m, &throwLabel))
+        return false;
+    if (m.syncInterruptLabel().used() && !GenerateSyncInterruptExit(m, &throwLabel))
         return false;
 
     if (!GenerateThrowExit(m, &throwLabel))
         return false;
 
     return true;
 }
 
@@ -6966,19 +7048,16 @@ Warn(AsmJSParser &parser, int errorNumbe
 }
 
 static bool
 EstablishPreconditions(ExclusiveContext *cx, AsmJSParser &parser)
 {
     if (!cx->jitSupportsFloatingPoint())
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
 
-    if (!cx->signalHandlersInstalled())
-        return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support");
-
     if (cx->gcSystemPageSize() != AsmJSPageSize)
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
 
     if (!parser.options().asmJSOption)
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
 
     if (!parser.options().compileAndGo)
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Temporarily disabled for event-handler and other cloneable scripts");
@@ -7038,16 +7117,15 @@ js::CompileAsmJS(ExclusiveContext *cx, A
 
 bool
 js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // See EstablishPreconditions.
     bool available = cx->jitSupportsFloatingPoint() &&
-                     cx->signalHandlersInstalled() &&
                      cx->gcSystemPageSize() == AsmJSPageSize &&
                      !cx->compartment()->debugMode() &&
                      cx->runtime()->options().asmJS();
 
     args.rval().set(BooleanValue(available));
     return true;
 }
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -250,17 +250,25 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
             JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the"
                         "largest constant heap access offset rounded up to the next valid "
                         "heap size).",
                         heapLength,
                         module.minHeapLength()));
         return LinkFail(cx, msg.get());
     }
 
-    if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
+    // If we've generated the code with signal handlers in mind (for bounds
+    // checks on x64 and for interrupt callback requesting on all platforms),
+    // we need to be able to use signals at runtime. In particular, a module
+    // can have been created using signals and cached, and executed without
+    // signals activated.
+    if (module.usesSignalHandlersForInterrupt() && !cx->canUseSignalHandlers())
+        return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");
+
+    if (!ArrayBufferObject::prepareForAsmJS(cx, heap, module.usesSignalHandlersForOOB()))
         return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
 
     module.initHeap(heap, cx);
     return true;
 }
 
 static bool
 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -71,17 +71,17 @@ DeallocateExecutableMemory(uint8_t *code
 #ifdef XP_WIN
     JS_ALWAYS_TRUE(VirtualFree(code, 0, MEM_RELEASE));
 #else
     JS_ALWAYS_TRUE(munmap(code, totalBytes) == 0 || errno == ENOMEM);
 #endif
 }
 
 AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
-                         bool strict)
+                         bool strict, bool canUseSignalHandlers)
   : srcStart_(srcStart),
     srcBodyStart_(srcBodyStart),
     scriptSource_(scriptSource),
     globalArgumentName_(nullptr),
     importArgumentName_(nullptr),
     bufferArgumentName_(nullptr),
     code_(nullptr),
     interruptExit_(nullptr),
@@ -89,16 +89,17 @@ AsmJSModule::AsmJSModule(ScriptSource *s
     loadedFromCache_(false),
     codeIsProtected_(false)
 {
     mozilla::PodZero(&pod);
     pod.funcPtrTableAndExitBytes_ = SIZE_MAX;
     pod.functionBytes_ = UINT32_MAX;
     pod.minHeapLength_ = AsmJSAllocationGranularity;
     pod.strict_ = strict;
+    pod.usesSignalHandlers_ = canUseSignalHandlers;
 
     scriptSource_->incref();
 }
 
 AsmJSModule::~AsmJSModule()
 {
     scriptSource_->decref();
 
@@ -513,16 +514,18 @@ RedirectCall(void *fun, ABIFunctionType 
 }
 
 static void *
 AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
 {
     switch (kind) {
       case AsmJSImm_Runtime:
         return cx->runtimeAddressForJit();
+      case AsmJSImm_RuntimeInterrupt:
+        return cx->runtimeAddressOfInterrupt();
       case AsmJSImm_StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case AsmJSImm_ReportOverRecursed:
         return RedirectCall(FuncCast<void (JSContext*)>(js_ReportOverRecursed), Args_General1);
       case AsmJSImm_HandleExecutionInterrupt:
         return RedirectCall(FuncCast(js::HandleExecutionInterrupt), Args_General1);
       case AsmJSImm_InvokeFromAsmJS_Ignore:
         return RedirectCall(FuncCast(InvokeFromAsmJS_Ignore), Args_General4);
@@ -634,16 +637,29 @@ AsmJSModule::initHeap(Handle<ArrayBuffer
         const jit::AsmJSHeapAccess &access = heapAccesses_[i];
         if (access.hasLengthCheck())
             JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
         void *addr = access.patchOffsetAt(code_);
         uint32_t disp = reinterpret_cast<uint32_t>(JSC::X86Assembler::getPointer(addr));
         JS_ASSERT(disp <= INT32_MAX);
         JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
     }
+#elif defined(JS_CODEGEN_X64)
+    int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
+    if (usesSignalHandlersForOOB())
+        return;
+    // If we cannot use the signal handlers, we need to patch the heap length
+    // checks at the right places. All accesses that have been recorded are the
+    // only ones that need bound checks (see also
+    // CodeGeneratorX64::visitAsmJS{Load,Store}Heap)
+    for (size_t i = 0; i < heapAccesses_.length(); i++) {
+        const jit::AsmJSHeapAccess &access = heapAccesses_[i];
+        if (access.hasLengthCheck())
+            JSC::X86Assembler::setInt32(access.patchLengthAt(code_), heapLength);
+    }
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     uint32_t heapLength = heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         jit::Assembler::UpdateBoundsCheck(heapLength,
                                           (jit::Instruction*)(heapAccesses_[i].offset() + code_));
     }
 #endif
 }
@@ -1209,17 +1225,18 @@ class AutoUnprotectCodeForClone
     }
 };
 
 bool
 AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
 {
     AutoUnprotectCodeForClone cloneGuard(cx, *this);
 
-    *moduleOut = cx->new_<AsmJSModule>(scriptSource_, srcStart_, srcBodyStart_, pod.strict_);
+    *moduleOut = cx->new_<AsmJSModule>(scriptSource_, srcStart_, srcBodyStart_, pod.strict_,
+                                       pod.usesSignalHandlers_);
     if (!*moduleOut)
         return false;
 
     AsmJSModule &out = **moduleOut;
 
     // Mirror the order of serialize/deserialize in cloning:
 
     out.pod = pod;
@@ -1669,18 +1686,20 @@ js::LookupAsmJSModuleInCache(ExclusiveCo
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cx, cursor);
     if (!moduleChars.match(parser))
         return true;
 
     uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
     uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
     bool strict = parser.pc->sc->strict && !parser.pc->sc->hasExplicitUseStrict();
+    // usesSignalHandlers will be clobbered when deserializing
     ScopedJSDeletePtr<AsmJSModule> module(
-        cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict));
+        cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict,
+                              /* usesSignalHandlers = */ false));
     if (!module)
         return false;
     cursor = module->deserialize(cx, cursor);
 
     // No need to flush the instruction cache now, it will be flushed when dynamically linking.
     AutoFlushICache afc("LookupAsmJSModuleInCache", /* inhibit= */ true);
     // We already know the exact extent of areas that need to be patched, just make sure we
     // flush all of them at once.
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -489,16 +489,17 @@ class AsmJSModule
         size_t                            totalBytes_;    // function bodies, stubs, and global data
         uint32_t                          minHeapLength_;
         uint32_t                          numGlobalVars_;
         uint32_t                          numFFIs_;
         uint32_t                          srcLength_;
         uint32_t                          srcLengthWithRightBrace_;
         bool                              strict_;
         bool                              hasArrayView_;
+        bool                              usesSignalHandlers_;
     } pod;
 
     // These two fields need to be kept out pod as they depend on the position
     // of the module within the ScriptSource and thus aren't invariant with
     // respect to caching.
     const uint32_t                        srcStart_;
     const uint32_t                        srcBodyStart_;
 
@@ -529,17 +530,17 @@ class AsmJSModule
     bool                                  loadedFromCache_;
 
     // This field is accessed concurrently when requesting an interrupt.
     // Access must be synchronized via the runtime's interrupt lock.
     mutable bool                          codeIsProtected_;
 
   public:
     explicit AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
-                         bool strict);
+                         bool strict, bool canUseSignalHandlers);
     void trace(JSTracer *trc);
     ~AsmJSModule();
 
     // An AsmJSModule transitions monotonically through these states:
     bool isFinishedWithModulePrologue() const { return pod.funcPtrTableAndExitBytes_ != SIZE_MAX; }
     bool isFinishedWithFunctionBodies() const { return pod.functionBytes_ != UINT32_MAX; }
     bool isFinished() const { return !!code_; }
     bool isStaticallyLinked() const { return !!interruptExit_; }
@@ -550,16 +551,26 @@ class AsmJSModule
 
     ScriptSource *scriptSource() const {
         JS_ASSERT(scriptSource_);
         return scriptSource_;
     }
     bool strict() const {
         return pod.strict_;
     }
+    bool usesSignalHandlersForInterrupt() const {
+        return pod.usesSignalHandlers_;
+    }
+    bool usesSignalHandlersForOOB() const {
+#ifdef JS_CODEGEN_X64
+        return usesSignalHandlersForInterrupt();
+#else
+        return false;
+#endif
+    }
     bool loadedFromCache() const {
         return loadedFromCache_;
     }
 
     // srcStart() refers to the offset in the ScriptSource to the beginning of
     // the asm.js module function. If the function has been created with the
     // Function constructor, this will be the first character in the function
     // source. Otherwise, it will be the opening parenthesis of the arguments
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8800,16 +8800,34 @@ CodeGenerator::visitInterruptCheck(LInte
         return false;
 
     AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterrupt());
     masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
+bool
+CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir)
+{
+    Label rejoin;
+    Register scratch = ToRegister(lir->scratch());
+    masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch);
+    masm.branchIfFalseBool(scratch, &rejoin);
+    {
+        uint32_t stackFixup = ComputeByteAlignment(masm.framePushed() + AsmJSFrameSize,
+                                                   StackAlignment);
+        masm.reserveStack(stackFixup);
+        masm.call(lir->funcDesc(), lir->interruptExit());
+        masm.freeStack(stackFixup);
+    }
+    masm.bind(&rejoin);
+    return true;
+}
+
 typedef bool (*RecompileFn)(JSContext *);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
 
 bool
 CodeGenerator::visitRecompileCheck(LRecompileCheck *ins)
 {
     Label done;
     Register tmp = ToRegister(ins->scratch());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -346,16 +346,17 @@ class CodeGenerator : public CodeGenerat
     bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic);
 
     bool visitAssertRangeI(LAssertRangeI *ins);
     bool visitAssertRangeD(LAssertRangeD *ins);
     bool visitAssertRangeF(LAssertRangeF *ins);
     bool visitAssertRangeV(LAssertRangeV *ins);
 
     bool visitInterruptCheck(LInterruptCheck *lir);
+    bool visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir);
     bool visitRecompileCheck(LRecompileCheck *ins);
 
     IonScriptCounts *extractScriptCounts() {
         IonScriptCounts *counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
     }
 
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -100,19 +100,19 @@ CompileRuntime::jitRuntime()
 
 SPSProfiler &
 CompileRuntime::spsProfiler()
 {
     return runtime()->spsProfiler;
 }
 
 bool
-CompileRuntime::signalHandlersInstalled()
+CompileRuntime::canUseSignalHandlers()
 {
-    return runtime()->signalHandlersInstalled();
+    return runtime()->canUseSignalHandlers();
 }
 
 bool
 CompileRuntime::jitSupportsFloatingPoint()
 {
     return runtime()->jitSupportsFloatingPoint;
 }
 
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -60,17 +60,17 @@ class CompileRuntime
 
     const void *addressOfThreadPool();
 
     const JitRuntime *jitRuntime();
 
     // Compilation does not occur off thread when the SPS profiler is enabled.
     SPSProfiler &spsProfiler();
 
-    bool signalHandlersInstalled();
+    bool canUseSignalHandlers();
     bool jitSupportsFloatingPoint();
     bool hadOutOfMemory();
     bool profilingScripts();
 
     const JSAtomState &names();
     const StaticStrings &staticStrings();
     const Value &NaNValue();
     const Value &positiveInfinityValue();
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -703,16 +703,47 @@ class LCheckOverRecursedPar : public LIn
         return getOperand(0);
     }
 
     const LDefinition *getTempReg() {
         return getTemp(0);
     }
 };
 
+class LAsmJSInterruptCheck : public LInstructionHelper<0, 0, 1>
+{
+    Label *interruptExit_;
+    const CallSiteDesc &funcDesc_;
+
+  public:
+    LIR_HEADER(AsmJSInterruptCheck);
+
+    LAsmJSInterruptCheck(const LDefinition &scratch, Label *interruptExit,
+                         const CallSiteDesc &funcDesc)
+      : interruptExit_(interruptExit), funcDesc_(funcDesc)
+    {
+        setTemp(0, scratch);
+    }
+
+    const LDefinition *scratch() {
+        return getTemp(0);
+    }
+
+    bool isCall() const {
+        return true;
+    }
+
+    Label *interruptExit() const {
+        return interruptExit_;
+    }
+    const CallSiteDesc &funcDesc() const {
+        return funcDesc_;
+    }
+};
+
 // Alternative to LInterruptCheck which does not emit an explicit check of the
 // interrupt flag but relies on the loop backedge being patched via a signal
 // handler.
 class LInterruptCheckImplicit : public LInstructionHelper<0, 0, 0>
 {
     Label *oolEntry_;
 
   public:
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -270,16 +270,17 @@
     _(Round)                        \
     _(RoundF)                       \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
+    _(AsmJSInterruptCheck)          \
     _(InterruptCheckImplicit)       \
     _(ProfilerStackOp)              \
     _(GetDOMProperty)               \
     _(GetDOMMember)                 \
     _(SetDOMProperty)               \
     _(CallDOMNative)                \
     _(IsCallable)                   \
     _(IsObject)                     \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2209,26 +2209,37 @@ LIRGenerator::visitGuardThreadExclusive(
     lir->setMir(ins);
     return assignSnapshot(lir, Bailout_GuardThreadExclusive) && add(lir, ins);
 }
 
 bool
 LIRGenerator::visitInterruptCheck(MInterruptCheck *ins)
 {
     // Implicit interrupt checks require asm.js signal handlers to be installed.
-    if (GetIonContext()->runtime->signalHandlersInstalled()) {
+    if (GetIonContext()->runtime->canUseSignalHandlers()) {
         LInterruptCheckImplicit *lir = new(alloc()) LInterruptCheckImplicit();
         return add(lir, ins) && assignSafepoint(lir, ins);
     }
 
     LInterruptCheck *lir = new(alloc()) LInterruptCheck();
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck *ins)
+{
+    gen->setPerformsAsmJSCall();
+
+    LAsmJSInterruptCheck *lir = new(alloc()) LAsmJSInterruptCheck(temp(),
+                                                                  ins->interruptExit(),
+                                                                  ins->funcDesc());
+    return add(lir, ins);
+}
+
+bool
 LIRGenerator::visitInterruptCheckPar(MInterruptCheckPar *ins)
 {
     LInterruptCheckPar *lir =
         new(alloc()) LInterruptCheckPar(useRegister(ins->forkJoinContext()), temp());
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
@@ -3551,19 +3562,18 @@ LIRGenerator::visitAsmJSCall(MAsmJSCall 
 
     for (unsigned i = 0; i < ins->numArgs(); i++)
         args[i] = useFixed(ins->getOperand(i), ins->registerForArg(i));
 
     if (ins->callee().which() == MAsmJSCall::Callee::Dynamic)
         args[ins->dynamicCalleeOperandIndex()] = useFixed(ins->callee().dynamic(), CallTempReg0);
 
     LInstruction *lir = new(alloc()) LAsmJSCall(args, ins->numOperands());
-    if (ins->type() == MIRType_None) {
+    if (ins->type() == MIRType_None)
         return add(lir, ins);
-    }
     return defineReturn(lir, ins);
 }
 
 bool
 LIRGenerator::visitSetDOMProperty(MSetDOMProperty *ins)
 {
     MDefinition *val = ins->value();
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -164,16 +164,17 @@ class LIRGenerator : public LIRGenerator
     bool visitConstantElements(MConstantElements *ins);
     bool visitConvertElementsToDoubles(MConvertElementsToDoubles *ins);
     bool visitMaybeToDoubleElement(MMaybeToDoubleElement *ins);
     bool visitLoadSlot(MLoadSlot *ins);
     bool visitFunctionEnvironment(MFunctionEnvironment *ins);
     bool visitForkJoinContext(MForkJoinContext *ins);
     bool visitGuardThreadExclusive(MGuardThreadExclusive *ins);
     bool visitInterruptCheck(MInterruptCheck *ins);
+    bool visitAsmJSInterruptCheck(MAsmJSInterruptCheck *ins);
     bool visitInterruptCheckPar(MInterruptCheckPar *ins);
     bool visitStoreSlot(MStoreSlot *ins);
     bool visitFilterTypeSet(MFilterTypeSet *ins);
     bool visitTypeBarrier(MTypeBarrier *ins);
     bool visitMonitorTypes(MMonitorTypes *ins);
     bool visitPostWriteBarrier(MPostWriteBarrier *ins);
     bool visitArrayLength(MArrayLength *ins);
     bool visitSetArrayLength(MSetArrayLength *ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3220,18 +3220,22 @@ MSqrt::trySpecializeFloat32(TempAllocato
 }
 
 MDefinition *
 MBoundsCheck::foldsTo(TempAllocator &alloc)
 {
     if (index()->isConstant() && length()->isConstant()) {
        uint32_t len = length()->toConstant()->value().toInt32();
        uint32_t idx = index()->toConstant()->value().toInt32();
-       if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len)
+       if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) {
+           // This bounds check will never fail, so we can clear the Guard flag
+           // and allow it to be deleted.
+           setNotGuard();
            return index();
+       }
     }
 
     return this;
 }
 
 bool
 jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5396,16 +5396,44 @@ class MInterruptCheck : public MNullaryI
     static MInterruptCheck *New(TempAllocator &alloc) {
         return new(alloc) MInterruptCheck();
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+// Check whether we need to fire the interrupt handler at loop headers and
+// function prologues in asm.js. Generated only if we can't use implicit
+// interrupt checks with signal handlers.
+class MAsmJSInterruptCheck : public MNullaryInstruction
+{
+    Label *interruptExit_;
+    CallSiteDesc funcDesc_;
+
+    MAsmJSInterruptCheck(Label *interruptExit, const CallSiteDesc &funcDesc)
+      : interruptExit_(interruptExit), funcDesc_(funcDesc)
+    {}
+
+  public:
+    INSTRUCTION_HEADER(AsmJSInterruptCheck)
+
+    static MAsmJSInterruptCheck *New(TempAllocator &alloc, Label *interruptExit,
+                                     const CallSiteDesc &funcDesc)
+    {
+        return new(alloc) MAsmJSInterruptCheck(interruptExit, funcDesc);
+    }
+    Label *interruptExit() const {
+        return interruptExit_;
+    }
+    const CallSiteDesc &funcDesc() const {
+        return funcDesc_;
+    }
+};
+
 // If not defined, set a global variable to |undefined|.
 class MDefVar : public MUnaryInstruction
 {
     CompilerRootPropertyName name_; // Target name to be defined.
     unsigned attrs_; // Attributes to be set.
 
   private:
     MDefVar(PropertyName *name, unsigned attrs, MDefinition *scopeChain)
@@ -10384,29 +10412,29 @@ class MAsmJSNeg : public MUnaryInstructi
 };
 
 class MAsmJSHeapAccess
 {
     Scalar::Type viewType_;
     bool skipBoundsCheck_;
 
   public:
-    MAsmJSHeapAccess(Scalar::Type vt, bool s)
-      : viewType_(vt), skipBoundsCheck_(s)
+    MAsmJSHeapAccess(Scalar::Type vt)
+      : viewType_(vt), skipBoundsCheck_(false)
     {}
 
     Scalar::Type viewType() const { return viewType_; }
     bool skipBoundsCheck() const { return skipBoundsCheck_; }
     void setSkipBoundsCheck(bool v) { skipBoundsCheck_ = v; }
 };
 
 class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSLoadHeap(Scalar::Type vt, MDefinition *ptr)
-      : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
+      : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt)
     {
         setMovable();
         if (vt == Scalar::Float32)
             setResultType(MIRType_Float32);
         else if (vt == Scalar::Float64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
@@ -10426,17 +10454,17 @@ class MAsmJSLoadHeap : public MUnaryInst
         return AliasSet::Load(AliasSet::AsmJSHeap);
     }
     bool mightAlias(const MDefinition *def) const;
 };
 
 class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v)
-      : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt, false)
+      : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt)
     {}
 
   public:
     INSTRUCTION_HEADER(AsmJSStoreHeap);
 
     static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type vt,
                                 MDefinition *ptr, MDefinition *v)
     {
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -188,16 +188,17 @@ namespace jit {
     _(Rest)                                                                 \
     _(Floor)                                                                \
     _(Ceil)                                                                 \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
+    _(AsmJSInterruptCheck)                                                  \
     _(ProfilerStackOp)                                                      \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsCallable)                                                           \
     _(IsObject)                                                             \
     _(HaveSameClass)                                                        \
     _(HasClass)                                                             \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -272,16 +272,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(RunOncePrologue)
     CUSTOM_OP(Rest)
     SAFE_OP(RestPar)
     SAFE_OP(Floor)
     SAFE_OP(Ceil)
     SAFE_OP(Round)
     UNSAFE_OP(InstanceOf)
     CUSTOM_OP(InterruptCheck)
+    UNSAFE_OP(AsmJSInterruptCheck)
     SAFE_OP(ForkJoinContext)
     SAFE_OP(ForkJoinGetSlice)
     SAFE_OP(NewPar)
     SAFE_OP(NewDenseArrayPar)
     SAFE_OP(NewCallObjectPar)
     SAFE_OP(LambdaPar)
     UNSAFE_OP(ArrayConcat)
     UNSAFE_OP(GetDOMProperty)
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -366,17 +366,17 @@ class TypedRegisterSet
         // When checking to see if a set has a register, we only want that exact
         // register, not worrying about aliasing.
         return !!(bits_ & (SetType(1) << reg.code()));
     }
     void addUnchecked(T reg) {
         bits_ |= (SetType(1) << reg.code());
     }
     void addAllAliasedUnchecked(T reg) {
-        for (int a = 0; a < reg.numAliased(); a++) {
+        for (uint32_t a = 0; a < reg.numAliased(); a++) {
             T tmp;
             reg.aliased(a, &tmp);
             bits_ |= (SetType(1) << tmp.code());
         }
     }
 
     void add(T reg) {
         // Make sure we don't add two overlapping registers.
@@ -412,17 +412,17 @@ class TypedRegisterSet
     void take(T reg) {
         JS_ASSERT(has(reg));
         takeUnchecked(reg);
     }
     void takeUnchecked(T reg) {
         bits_ &= ~(SetType(1) << reg.code());
     }
     void takeAllAliasedUnchecked(T reg) {
-        for (int a = 0; a < reg.numAliased(); a++) {
+        for (uint32_t a = 0; a < reg.numAliased(); a++) {
             T tmp;
             reg.aliased(a, &tmp);
             bits_ &= ~(SetType(1) << tmp.code());
         }
     }
     void take(ValueOperand value) {
 #if defined(JS_NUNBOX32)
         take(value.payloadReg());
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1909,19 +1909,21 @@ MacroAssembler::PopRegsInMaskIgnore(Regi
     const int32_t reservedF = diffF;
 
     // ARM can load multiple registers at once, but only if we want back all
     // the registers we previously saved to the stack.
     if (ignore.empty(true)) {
         diffF -= transferMultipleByRuns(set.fpus(), IsLoad, StackPointer, IA);
         adjustFrame(-reservedF);
     } else {
-        for (FloatRegisterBackwardIterator iter(set.fpus()); iter.more(); iter++) {
-            diffF -= sizeof(double);
-            if (!ignore.has(*iter))
+        TypedRegisterSet<VFPRegister> fpset = set.fpus().reduceSetForPush();
+        TypedRegisterSet<VFPRegister> fpignore = ignore.fpus().reduceSetForPush();
+        for (FloatRegisterBackwardIterator iter(fpset); iter.more(); iter++) {
+            diffF -= (*iter).size();
+            if (!fpignore.has(*iter))
                 loadDouble(Address(StackPointer, diffF), *iter);
         }
         freeStack(reservedF);
     }
     JS_ASSERT(diffF == 0);
 
     if (set.gprs().size() > 1 && ignore.empty(false)) {
         startDataTransferM(IsLoad, StackPointer, IA, WriteBack);
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -1135,17 +1135,17 @@ Simulator::Simulator(SimulatorRuntime *s
         MOZ_ReportAssertionFailure("[unhandlable oom] Simulator stack", __FILE__, __LINE__);
         MOZ_CRASH();
     }
     pc_modified_ = false;
     icount_ = 0L;
     resume_pc_ = 0;
     break_pc_ = nullptr;
     break_instr_ = 0;
-    skipCalleeSavedRegsCheck = true;
+    skipCalleeSavedRegsCheck = false;
 
     // Set up architecture state.
     // All registers are initialized to zero to start with.
     for (int i = 0; i < num_registers; i++)
         registers_[i] = 0;
 
     n_flag_ = false;
     z_flag_ = false;
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -609,17 +609,17 @@ struct CallSite : public CallSiteDesc
     { }
 
     void setReturnAddressOffset(uint32_t r) { returnAddressOffset_ = r; }
     uint32_t returnAddressOffset() const { return returnAddressOffset_; }
 
     // The stackDepth measures the amount of stack space pushed since the
     // function was called. In particular, this includes the pushed return
     // address on all archs (whether or not the call instruction pushes the
-    // return address (x86/x64) or the prologue does (ARM/MIPS).
+    // return address (x86/x64) or the prologue does (ARM/MIPS)).
     uint32_t stackDepth() const { return stackDepth_; }
 };
 
 typedef Vector<CallSite, 0, SystemAllocPolicy> CallSiteVector;
 
 // As an invariant across architectures, within asm.js code:
 //    $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
 // AsmJSFrameSize is 1 word, for the return address pushed by the call (or, in
@@ -630,65 +630,61 @@ static const uint32_t AsmJSFrameSize = s
 // Summarizes a heap access made by asm.js code that needs to be patched later
 // and/or looked up by the asm.js signal handlers. Different architectures need
 // to know different things (x64: offset and length, ARM: where to patch in
 // heap length, x86: where to patch in heap length and base) hence the massive
 // #ifdefery.
 class AsmJSHeapAccess
 {
     uint32_t offset_;
-#if defined(JS_CODEGEN_X86)
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     uint8_t cmpDelta_;  // the number of bytes from the cmp to the load/store instruction
-#endif
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     uint8_t opLength_;  // the length of the load/store instruction
     uint8_t isFloat32Load_;
     AnyRegister::Code loadedReg_ : 8;
 #endif
 
     JS_STATIC_ASSERT(AnyRegister::Total < UINT8_MAX);
 
   public:
     AsmJSHeapAccess() {}
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+    static const uint32_t NoLengthCheck = UINT32_MAX;
+
     // If 'cmp' equals 'offset' or if it is not supplied then the
     // cmpDelta_ is zero indicating that there is no length to patch.
     AsmJSHeapAccess(uint32_t offset, uint32_t after, Scalar::Type vt,
-                    AnyRegister loadedReg, uint32_t cmp = UINT32_MAX)
+                    AnyRegister loadedReg, uint32_t cmp = NoLengthCheck)
       : offset_(offset),
-# if defined(JS_CODEGEN_X86)
-        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
-# endif
+        cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
         opLength_(after - offset),
         isFloat32Load_(vt == Scalar::Float32),
         loadedReg_(loadedReg.code())
     {}
-    AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = UINT32_MAX)
+    AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = NoLengthCheck)
       : offset_(offset),
-# if defined(JS_CODEGEN_X86)
-        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
-# endif
+        cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
         opLength_(after - offset),
         isFloat32Load_(false),
         loadedReg_(UINT8_MAX)
     {}
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     explicit AsmJSHeapAccess(uint32_t offset)
       : offset_(offset)
     {}
 #endif
 
     uint32_t offset() const { return offset_; }
     void setOffset(uint32_t offset) { offset_ = offset; }
 #if defined(JS_CODEGEN_X86)
-    bool hasLengthCheck() const { return cmpDelta_ > 0; }
-    void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
     void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); }
 #endif
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+    bool hasLengthCheck() const { return cmpDelta_ > 0; }
+    void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
     unsigned opLength() const { return opLength_; }
     bool isLoad() const { return loadedReg_ != UINT8_MAX; }
     bool isFloat32Load() const { return isFloat32Load_; }
     AnyRegister loadedReg() const { return AnyRegister::FromCode(loadedReg_); }
 #endif
 };
 
 typedef Vector<AsmJSHeapAccess, 0, SystemAllocPolicy> AsmJSHeapAccessVector;
@@ -704,16 +700,17 @@ struct AsmJSGlobalAccess
 };
 
 // Describes the intended pointee of an immediate to be embedded in asm.js
 // code. By representing the pointee as a symbolic enum, the pointee can be
 // patched after deserialization when the address of global things has changed.
 enum AsmJSImmKind
 {
     AsmJSImm_Runtime,
+    AsmJSImm_RuntimeInterrupt,
     AsmJSImm_StackLimit,
     AsmJSImm_ReportOverRecursed,
     AsmJSImm_HandleExecutionInterrupt,
     AsmJSImm_InvokeFromAsmJS_Ignore,
     AsmJSImm_InvokeFromAsmJS_ToInt32,
     AsmJSImm_InvokeFromAsmJS_ToNumber,
     AsmJSImm_CoerceInPlace_ToInt32,
     AsmJSImm_CoerceInPlace_ToNumber,
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -856,16 +856,20 @@ class AssemblerX86Shared : public Assemb
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
     void cmpl(const Operand &op, ImmPtr imm) {
         cmpl(op, ImmWord(uintptr_t(imm.value)));
     }
+    CodeOffsetLabel cmplWithPatch(Register lhs, Imm32 rhs) {
+        masm.cmpl_ir_force32(rhs.value, lhs.code());
+        return CodeOffsetLabel(masm.currentOffset());
+    }
     void cmpw(Register lhs, Register rhs) {
         masm.cmpw_rr(lhs.code(), rhs.code());
     }
     void setCC(Condition cond, Register r) {
         masm.setCC_r(static_cast<JSC::X86Assembler::Condition>(cond), r.code());
     }
     void testb(Register lhs, Register rhs) {
         JS_ASSERT(GeneralRegisterSet(Registers::SingleByteRegs).has(lhs));
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -23,16 +23,18 @@ using namespace js;
 using namespace js::jit;
 
 using mozilla::Abs;
 using mozilla::FloatingPoint;
 using mozilla::FloorLog2;
 using mozilla::NegativeInfinity;
 using mozilla::SpecificNaN;
 
+using JS::GenericNaN;
+
 namespace js {
 namespace jit {
 
 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
@@ -339,16 +341,32 @@ CodeGeneratorX86Shared::visitAsmJSPassSt
             masm.storePtr(ToRegister(ins->arg()), dst);
         else
             masm.storeDouble(ToFloatRegister(ins->arg()), dst);
     }
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
+{
+    if (ool->dest().isFloat()) {
+        if (ool->isFloat32Load())
+            masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
+        else
+            masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
+    } else {
+        Register destReg = ool->dest().gpr();
+        masm.mov(ImmWord(0), destReg);
+    }
+    masm.jmp(ool->rejoin());
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::generateOutOfLineCode()
 {
     if (!CodeGeneratorShared::generateOutOfLineCode())
         return false;
 
     if (deoptLabel_.used()) {
         // All non-table-based bailouts will go here.
         masm.bind(&deoptLabel_);
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -9,16 +9,17 @@
 
 #include "jit/shared/CodeGenerator-shared.h"
 
 namespace js {
 namespace jit {
 
 class OutOfLineBailout;
 class OutOfLineUndoALUOperation;
+class OutOfLineLoadTypedArrayOutOfBounds;
 class MulNegativeZeroCheck;
 class ModOverflowCheck;
 class ReturnZero;
 class OutOfLineTableSwitch;
 
 class CodeGeneratorX86Shared : public CodeGeneratorShared
 {
     friend class MoveResolverX86;
@@ -26,16 +27,35 @@ class CodeGeneratorX86Shared : public Co
     CodeGeneratorX86Shared *thisFromCtor() {
         return this;
     }
 
     template <typename T>
     bool bailout(const T &t, LSnapshot *snapshot);
 
   protected:
+
+    // Load a NaN or zero into a register for an out of bounds AsmJS or static
+    // typed array load.
+    class OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86Shared>
+    {
+        AnyRegister dest_;
+        bool isFloat32Load_;
+      public:
+        OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
+          : dest_(dest), isFloat32Load_(isFloat32Load)
+        {}
+
+        AnyRegister dest() const { return dest_; }
+        bool isFloat32Load() const { return isFloat32Load_; }
+        bool accept(CodeGeneratorX86Shared *codegen) {
+            return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this);
+        }
+    };
+
     // Label for the common return path.
     NonAssertingLabel returnLabel_;
     NonAssertingLabel deoptLabel_;
 
     inline Operand ToOperand(const LAllocation &a) {
         if (a.isGeneralReg())
             return Operand(a.toGeneralReg()->reg());
         if (a.isFloatReg())
@@ -92,17 +112,17 @@ class CodeGeneratorX86Shared : public Co
         // Same trick as explained in the above comment.
         masm.cvttss2si(src, dest);
         masm.cmp32(dest, Imm32(1));
         return bailoutIf(Assembler::Overflow, snapshot);
     }
 
   protected:
     bool generatePrologue();
-    bool generateAsmJSPrologue(Label *stackOverflowLabe);
+    bool generateAsmJSPrologue(Label *stackOverflowLabel);
     bool generateEpilogue();
     bool generateOutOfLineCode();
 
     void emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right);
 
     // Emits a branch that directs control flow to the true block if |cond| is
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse,
@@ -172,16 +192,18 @@ class CodeGeneratorX86Shared : public Co
     virtual bool visitRoundF(LRoundF *lir);
     virtual bool visitGuardShape(LGuardShape *guard);
     virtual bool visitGuardObjectType(LGuardObjectType *guard);
     virtual bool visitGuardClass(LGuardClass *guard);
     virtual bool visitEffectiveAddress(LEffectiveAddress *ins);
     virtual bool visitUDivOrMod(LUDivOrMod *ins);
     virtual bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);
 
+    bool visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool);
+
     bool visitForkJoinGetSlice(LForkJoinGetSlice *ins);
 
     bool visitNegI(LNegI *lir);
     bool visitNegD(LNegD *lir);
     bool visitNegF(LNegF *lir);
 
     // Out of line visitors.
     bool visitOutOfLineBailout(OutOfLineBailout *ool);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -241,67 +241,81 @@ CodeGeneratorX64::visitStoreTypedArrayEl
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     MAsmJSLoadHeap *mir = ins->mir();
     Scalar::Type vt = mir->viewType();
     const LAllocation *ptr = ins->ptr();
-
-    // No need to note the access if it will never fault.
-    bool skipNote = mir->skipBoundsCheck();
+    const LDefinition *out = ins->output();
     Operand srcAddr(HeapReg);
 
     if (ptr->isConstant()) {
         int32_t ptrImm = ptr->toConstant()->toInt32();
-        // Note only a positive index is accepted here because a negative offset would
-        // not wrap back into the protected area reserved for the heap.
         JS_ASSERT(ptrImm >= 0);
         srcAddr = Operand(HeapReg, ptrImm);
     } else {
         srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
     }
 
+    OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
+    uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
+    if (!mir->skipBoundsCheck()) {
+        bool isFloat32Load = vt == Scalar::Float32;
+        ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
+        if (!addOutOfLineCode(ool))
+            return false;
+
+        CodeOffsetLabel cmp = masm.cmplWithPatch(ToRegister(ptr), Imm32(0));
+        masm.j(Assembler::AboveOrEqual, ool->entry());
+        maybeCmpOffset = cmp.offset();
+    }
+
     uint32_t before = masm.size();
     switch (vt) {
-      case Scalar::Int8:    masm.movsbl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Uint8:   masm.movzbl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Int16:   masm.movswl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Uint16:  masm.movzwl(srcAddr, ToRegister(ins->output())); break;
+      case Scalar::Int8:    masm.movsbl(srcAddr, ToRegister(out)); break;
+      case Scalar::Uint8:   masm.movzbl(srcAddr, ToRegister(out)); break;
+      case Scalar::Int16:   masm.movswl(srcAddr, ToRegister(out)); break;
+      case Scalar::Uint16:  masm.movzwl(srcAddr, ToRegister(out)); break;
       case Scalar::Int32:
-      case Scalar::Uint32:  masm.movl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(ins->output())); break;
-      case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(ins->output())); break;
+      case Scalar::Uint32:  masm.movl(srcAddr, ToRegister(out)); break;
+      case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break;
+      case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
     uint32_t after = masm.size();
-    if (!skipNote)
-        masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
+    if (ool)
+        masm.bind(ool->rejoin());
+    masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), maybeCmpOffset));
     return true;
 }
 
 bool
 CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
     MAsmJSStoreHeap *mir = ins->mir();
     Scalar::Type vt = mir->viewType();
     const LAllocation *ptr = ins->ptr();
-    // No need to note the access if it will never fault.
-    bool skipNote = mir->skipBoundsCheck();
     Operand dstAddr(HeapReg);
 
     if (ptr->isConstant()) {
         int32_t ptrImm = ptr->toConstant()->toInt32();
-        // Note only a positive index is accepted here because a negative offset would
-        // not wrap back into the protected area reserved for the heap.
         JS_ASSERT(ptrImm >= 0);
         dstAddr = Operand(HeapReg, ptrImm);
     } else {
-        dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne);
+        dstAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
+    }
+
+    Label rejoin;
+    uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
+    if (!mir->skipBoundsCheck()) {
+        CodeOffsetLabel cmp = masm.cmplWithPatch(ToRegister(ptr), Imm32(0));
+        masm.j(Assembler::AboveOrEqual, &rejoin);
+        maybeCmpOffset = cmp.offset();
     }
 
     uint32_t before = masm.size();
     if (ins->value()->isConstant()) {
         switch (vt) {
           case Scalar::Int8:
           case Scalar::Uint8:   masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
           case Scalar::Int16:
@@ -319,18 +333,19 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
           case Scalar::Int32:
           case Scalar::Uint32:  masm.movl(ToRegister(ins->value()), dstAddr); break;
           case Scalar::Float32: masm.storeFloat32(ToFloatRegister(ins->value()), dstAddr); break;
           case Scalar::Float64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
           default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
         }
     }
     uint32_t after = masm.size();
-    if (!skipNote)
-        masm.append(AsmJSHeapAccess(before, after));
+    if (rejoin.used())
+        masm.bind(&rejoin);
+    masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
     return true;
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     MAsmJSLoadGlobalVar *mir = ins->mir();
 
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -126,26 +126,33 @@ LIRGeneratorX64::visitAsmJSUnsignedToFlo
     return define(lir, ins);
 }
 
 bool
 LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     JS_ASSERT(ptr->type() == MIRType_Int32);
+    LAllocation ptrAlloc;
 
-    // The X64 does not inline an explicit bounds check so has no need to keep the
-    // index in a register, however only a positive index is accepted because a
-    // negative offset encoded as an offset in the addressing mode would not wrap
-    // back into the protected area reserved for the heap.
-    if (ptr->isConstant() && ptr->toConstant()->value().toInt32() >= 0) {
-        LAsmJSLoadHeap *lir = new(alloc()) LAsmJSLoadHeap(LAllocation(ptr->toConstant()->vp()));
-        return define(lir, ins);
+    bool useConstant = false;
+    if (ptr->isConstant()) {
+        int32_t ptrValue = ptr->toConstant()->value().toInt32();
+        if (ins->skipBoundsCheck() && ptrValue >= 0) {
+            // Only a positive index is accepted because a negative offset
+            // encoded as an offset in the addressing mode would not wrap back
+            // into the protected area reserved for the heap.
+            useConstant = true;
+        }
+        // In other cases, still keep the pointer in a register.
     }
-    return define(new(alloc()) LAsmJSLoadHeap(useRegisterAtStart(ptr)), ins);
+
+    ptrAlloc = (useConstant) ? LAllocation(ptr->toConstant()->vp()) : useRegisterAtStart(ptr);
+    LAsmJSLoadHeap *lir = new(alloc()) LAsmJSLoadHeap(ptrAlloc);
+    return define(lir, ins);
 }
 
 bool
 LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     JS_ASSERT(ptr->type() == MIRType_Int32);
     LAsmJSStoreHeap *lir;
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -338,20 +338,16 @@ class Assembler : public AssemblerX86Sha
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
     void cmpl(AsmJSAbsoluteAddress lhs, Register rhs) {
         masm.cmpl_rm_force32(rhs.code(), (void*)-1);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(masm.currentOffset()), lhs.kind()));
     }
-    CodeOffsetLabel cmplWithPatch(Register lhs, Imm32 rhs) {
-        masm.cmpl_ir_force32(rhs.value, lhs.code());
-        return CodeOffsetLabel(masm.currentOffset());
-    }
 
     void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) {
         JmpSrc src = masm.jmp();
         addPendingJump(src, target, reloc);
     }
     void j(Condition cond, ImmPtr target,
            Relocation::Kind reloc = Relocation::HARDCODED) {
         JmpSrc src = masm.jCC(static_cast<JSC::X86Assembler::Condition>(cond));
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -260,32 +260,16 @@ CodeGeneratorX86::visitAsmJSUInt32ToFloa
     if (input != temp)
         masm.mov(input, temp);
 
     // Beware: convertUInt32ToFloat32 clobbers input.
     masm.convertUInt32ToFloat32(temp, output);
     return true;
 }
 
-// Load a NaN or zero into a register for an out of bounds AsmJS or static
-// typed array load.
-class jit::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86>
-{
-    AnyRegister dest_;
-    bool isFloat32Load_;
-  public:
-    OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
-      : dest_(dest), isFloat32Load_(isFloat32Load)
-    {}
-
-    AnyRegister dest() const { return dest_; }
-    bool isFloat32Load() const { return isFloat32Load_; }
-    bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); }
-};
-
 template<typename T>
 void
 CodeGeneratorX86::loadViewTypeElement(Scalar::Type vt, const T &srcAddr,
                                       const LDefinition *out)
 {
     switch (vt) {
       case Scalar::Int8:    masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
       case Scalar::Uint8Clamped:
@@ -381,32 +365,16 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
     uint32_t before = masm.size();
     loadViewTypeElement(vt, srcAddr, out);
     uint32_t after = masm.size();
     masm.bind(ool->rejoin());
     masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset()));
     return true;
 }
 
-bool
-CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
-{
-    if (ool->dest().isFloat()) {
-        if (ool->isFloat32Load())
-            masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
-        else
-            masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
-    } else {
-        Register destReg = ool->dest().gpr();
-        masm.mov(ImmWord(0), destReg);
-    }
-    masm.jmp(ool->rejoin());
-    return true;
-}
-
 template<typename T>
 void
 CodeGeneratorX86::storeViewTypeElement(Scalar::Type vt, const LAllocation *value,
                                        const T &dstAddr)
 {
     switch (vt) {
       case Scalar::Int8:
       case Scalar::Uint8Clamped:
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -8,17 +8,16 @@
 #define jit_x86_CodeGenerator_x86_h
 
 #include "jit/shared/CodeGenerator-x86-shared.h"
 #include "jit/x86/Assembler-x86.h"
 
 namespace js {
 namespace jit {
 
-class OutOfLineLoadTypedArrayOutOfBounds;
 class OutOfLineTruncate;
 class OutOfLineTruncateFloat32;
 
 class CodeGeneratorX86 : public CodeGeneratorX86Shared
 {
   private:
     CodeGeneratorX86 *thisFromCtor() {
         return this;
@@ -61,17 +60,16 @@ class CodeGeneratorX86 : public CodeGene
     bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins);
     bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins);
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
-    bool visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool);
     bool visitOutOfLineTruncate(OutOfLineTruncate *ool);
     bool visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool);
 
     void postAsmJSCall(LAsmJSCall *lir);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -269,20 +269,22 @@ struct ThreadSafeContext : ContextFriend
     // Accessors for immutable runtime data.
     JSAtomState &names() { return *runtime_->commonNames; }
     StaticStrings &staticStrings() { return *runtime_->staticStrings; }
     AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; }
     const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName *emptyString() { return runtime_->emptyString; }
     FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
     void *runtimeAddressForJit() { return runtime_; }
+    void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
     bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
+    bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
 
     // Thread local data that may be accessed freely.
     DtoaState *dtoaState() {
         return perThreadData->dtoaState;
     }
 };
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -58,16 +58,24 @@ js::SetSourceHook(JSRuntime *rt, UniqueP
 }
 
 JS_FRIEND_API(UniquePtr<SourceHook>)
 js::ForgetSourceHook(JSRuntime *rt)
 {
     return Move(rt->sourceHook);
 }
 
+#ifdef NIGHTLY_BUILD
+JS_FRIEND_API(void)
+js::SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook)
+{
+    rt->assertOnScriptEntryHook_ = hook;
+}
+#endif
+
 JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
 {
     rt->gc.setGrayRootsTracer(traceOp, data);
 }
 
 JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -412,16 +412,23 @@ class SourceHook {
  */
 extern JS_FRIEND_API(void)
 SetSourceHook(JSRuntime *rt, mozilla::UniquePtr<SourceHook> hook);
 
 /* Remove |rt|'s source hook, and return it. The caller now owns the hook. */
 extern JS_FRIEND_API(mozilla::UniquePtr<SourceHook>)
 ForgetSourceHook(JSRuntime *rt);
 
+#ifdef NIGHTLY_BUILD
+typedef void (*AssertOnScriptEntryHook)(JSContext *cx, JS::HandleScript script);
+
+extern JS_FRIEND_API(void)
+SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook);
+#endif
+
 extern JS_FRIEND_API(JS::Zone *)
 GetCompartmentZone(JSCompartment *comp);
 
 typedef bool
 (* PreserveWrapperCallback)(JSContext *cx, JSObject *obj);
 
 typedef enum  {
     CollectNurseryBeforeDump,
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -409,20 +409,48 @@ ArrayBufferObject::changeContents(JSCont
     }
 }
 
 #if defined(JS_CPU_X64)
 // Refer to comment above AsmJSMappedSize in AsmJS.h.
 JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
 #endif
 
+/* static */ bool
+ArrayBufferObject::prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    if (buffer->isAsmJSArrayBuffer())
+        return true;
+
+    if (buffer->isSharedArrayBuffer())
+        return true;
+
+    if (!ensureNonInline(cx, buffer))
+        return false;
+
+    buffer->setIsAsmJSArrayBuffer();
+    return true;
+}
+
+void
+ArrayBufferObject::releaseAsmJSArrayNoSignals(FreeOp *fop)
+{
+    JS_ASSERT(!isAsmJSMappedArrayBuffer());
+    fop->free_(dataPointer());
+}
+
 #if defined(JS_ION) && defined(JS_CPU_X64)
 /* static */ bool
-ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer,
+                                   bool usesSignalHandlers)
 {
+    // If we can't use signal handlers, just do it like on other platforms.
+    if (!usesSignalHandlers)
+        return prepareForAsmJSNoSignals(cx, buffer);
+
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
     // SharedArrayBuffers are already created with AsmJS support in mind.
     if (buffer->isSharedArrayBuffer())
         return true;
 
     // Get the entire reserved region (with all pages inaccessible).
@@ -462,23 +490,29 @@ ArrayBufferObject::prepareForAsmJS(JSCon
 
     // Swap the new elements into the ArrayBufferObject.
     buffer->changeContents(cx, data);
     JS_ASSERT(data == buffer->dataPointer());
 
     // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
     // to js_free the data in the normal way.
     buffer->setIsAsmJSArrayBuffer();
+    buffer->setIsAsmJSMappedArrayBuffer();
 
     return true;
 }
 
 void
 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
 {
+    if (!isAsmJSMappedArrayBuffer()) {
+        releaseAsmJSArrayNoSignals(fop);
+        return;
+    }
+
     void *data = dataPointer();
 
     JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0);
 # ifdef XP_WIN
     VirtualFree(data, 0, MEM_RELEASE);
 # else
     munmap(data, AsmJSMappedSize);
 #   if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
@@ -487,35 +521,30 @@ ArrayBufferObject::releaseAsmJSArray(Fre
     if (AsmJSMappedSize > 0) {
         VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(data, AsmJSMappedSize);
     }
 #   endif
 # endif
 }
 #else  /* defined(JS_ION) && defined(JS_CPU_X64) */
 bool
-ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer,
+                                   bool usesSignalHandlers)
 {
-    if (buffer->isAsmJSArrayBuffer())
-        return true;
-
-    if (buffer->isSharedArrayBuffer())
-        return true;
-
-    if (!ensureNonInline(cx, buffer))
-        return false;
-
-    buffer->setIsAsmJSArrayBuffer();
-    return true;
+    // Platforms other than x64 don't use signalling for bounds checking, so
+    // just use the variant with no signals.
+    JS_ASSERT(!usesSignalHandlers);
+    return prepareForAsmJSNoSignals(cx, buffer);
 }
 
 void
 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
 {
-    fop->free_(dataPointer());
+    // See comment above.
+    releaseAsmJSArrayNoSignals(fop);
 }
 #endif
 
 bool
 ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
 {
     JS_ASSERT(!buffer.isSharedArrayBuffer());
 #ifdef JS_ION
@@ -769,23 +798,22 @@ ArrayBufferObject::stealContents(JSConte
 ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
 {
     ArrayBufferObject &buffer = AsArrayBuffer(obj);
 
     if (!buffer.ownsData())
         return;
 
     if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) {
-#if defined (JS_CPU_X64)
         // On x64, ArrayBufferObject::prepareForAsmJS switches the
         // ArrayBufferObject to use mmap'd storage.
-        sizes->nonHeapElementsAsmJS += buffer.byteLength();
-#else
-        sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
-#endif
+        if (buffer.isAsmJSMappedArrayBuffer())
+            sizes->nonHeapElementsAsmJS += buffer.byteLength();
+        else
+            sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
     } else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) {
         sizes->nonHeapElementsMapped += buffer.byteLength();
     } else if (buffer.dataPointer()) {
         sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
     }
 }
 
 /* static */ void
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -147,21 +147,24 @@ class ArrayBufferObject : public JSObjec
      * Check if the arrayBuffer contains any data. This will return false for
      * ArrayBuffer.prototype and neutered ArrayBuffers.
      */
     bool hasData() const {
         return getClass() == &class_;
     }
 
     bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; }
+    bool isAsmJSMappedArrayBuffer() const { return flags() & ASMJS_MAPPED_BUFFER; }
     bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; }
     bool isMappedArrayBuffer() const { return flags() & MAPPED_BUFFER; }
     bool isNeutered() const { return flags() & NEUTERED_BUFFER; }
 
-    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer,
+                                bool usesSignalHandlers);
+    static bool prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObject*> buffer);
     static bool canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
 
     static void finalize(FreeOp *fop, JSObject *obj);
 
     static void *createMappedContents(int fd, size_t offset, size_t length);
 
     static size_t flagsOffset() {
         return getFixedSlotOffset(FLAGS_SLOT);
@@ -179,56 +182,59 @@ class ArrayBufferObject : public JSObjec
     void setByteLength(size_t length);
 
     ArrayBufferViewObject *viewList() const;
     void setViewList(ArrayBufferViewObject *viewsHead);
     void setViewListNoBarrier(ArrayBufferViewObject *viewsHead);
 
     enum ArrayBufferFlags {
         // In the gcLiveArrayBuffers list.
-        IN_LIVE_LIST       =  0x1,
+        IN_LIVE_LIST        =  0x1,
 
         // The dataPointer() is owned by this buffer and should be released
         // when no longer in use. Releasing the pointer may be done by either
         // freeing or unmapping it, and how to do this is determined by the
         // buffer's other flags.
-        OWNS_DATA          =  0x2,
+        OWNS_DATA           =  0x2,
 
-        ASMJS_BUFFER       =  0x4,
-        SHARED_BUFFER      =  0x8,
-        MAPPED_BUFFER      = 0x10,
-        NEUTERED_BUFFER    = 0x20
+        ASMJS_BUFFER        =  0x4,
+        ASMJS_MAPPED_BUFFER =  0x8,
+        SHARED_BUFFER       = 0x10,
+        MAPPED_BUFFER       = 0x20,
+        NEUTERED_BUFFER     = 0x40,
     };
 
     uint32_t flags() const;
     void setFlags(uint32_t flags);
 
     bool inLiveList() const { return flags() & IN_LIVE_LIST; }
     void setInLiveList(bool value) {
         setFlags(value ? (flags() | IN_LIVE_LIST) : (flags() & ~IN_LIVE_LIST));
     }
 
     bool ownsData() const { return flags() & OWNS_DATA; }
     void setOwnsData(OwnsState owns) {
         setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
     }
 
     void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); }
+    void setIsAsmJSMappedArrayBuffer() { setFlags(flags() | ASMJS_MAPPED_BUFFER); }
     void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); }
     void setIsMappedArrayBuffer() { setFlags(flags() | MAPPED_BUFFER); }
     void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); }
 
     void initialize(size_t byteLength, void *data, OwnsState ownsState) {
         setByteLength(byteLength);
         setFlags(0);
         setViewListNoBarrier(nullptr);
         setDataPointer(data, ownsState);
     }
 
     void releaseAsmJSArray(FreeOp *fop);
+    void releaseAsmJSArrayNoSignals(FreeOp *fop);
     void releaseMappedArray();
 };
 
 /*
  * ArrayBufferViewObject
  *
  * Common definitions shared by all ArrayBufferViews.
  */
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -369,16 +369,21 @@ ExecuteState::pushInterpreterFrame(JSCon
                                                               type_, evalInFrame_);
 }
 
 bool
 js::RunScript(JSContext *cx, RunState &state)
 {
     JS_CHECK_RECURSION(cx, return false);
 
+#ifdef NIGHTLY_BUILD
+    if (AssertOnScriptEntryHook hook = cx->runtime()->assertOnScriptEntryHook_)
+        (*hook)(cx, state.script());
+#endif
+
     SPSEntryMarker marker(cx->runtime(), state.script());
 
     state.script()->ensureNonLazyCanonicalFunction(cx);
 
 #ifdef JS_ION
     if (jit::IsIonEnabled(cx)) {
         jit::MethodStatus status = jit::CanEnter(cx, state);
         if (status == jit::Method_Error)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -198,17 +198,17 @@ class RunState
         JS_ASSERT(isInvoke());
         return (InvokeState *)this;
     }
     GeneratorState *asGenerator() const {
         JS_ASSERT(isGenerator());
         return (GeneratorState *)this;
     }
 
-    JSScript *script() const { return script_; }
+    JS::HandleScript script() const { return script_; }
 
     virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx) = 0;
     virtual void setReturnValue(Value v) = 0;
 
   private:
     RunState(const RunState &other) MOZ_DELETE;
     RunState(const ExecuteState &other) MOZ_DELETE;
     RunState(const InvokeState &other) MOZ_DELETE;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -176,23 +176,25 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     simulatorRuntime_(nullptr),
 #endif
     scriptAndCountsVector(nullptr),
     NaNValue(DoubleNaNValue()),
     negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
     positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
     emptyString(nullptr),
+    assertOnScriptEntryHook_(nullptr),
     debugMode(false),
     spsProfiler(thisFromCtor()),
     profilingScripts(false),
     hadOutOfMemory(false),
     haveCreatedContext(false),
     data(nullptr),
     signalHandlersInstalled_(false),
+    canUseSignalHandlers_(false),
     defaultFreeOp_(thisFromCtor(), false),
     debuggerMutations(0),
     securityCallbacks(const_cast<JSSecurityCallbacks *>(&NullSecurityCallbacks)),
     DOMcallbacks(nullptr),
     destroyPrincipals(nullptr),
     structuredCloneCallbacks(nullptr),
     telemetryCallback(nullptr),
     propertyRemovals(0),
@@ -251,16 +253,24 @@ JitSupportsFloatingPoint()
 #endif
 
     return true;
 #else
     return false;
 #endif
 }
 
+static bool
+SignalBasedTriggersDisabled()
+{
+  // Don't bother trying to cache the getenv lookup; this should be called
+  // infrequently.
+  return !!getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS") || !!getenv("JS_NO_SIGNALS");
+}
+
 bool
 JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
 {
 #ifdef JS_THREADSAFE
     ownerThread_ = PR_GetCurrentThread();
 
     interruptLock = PR_NewLock();
     if (!interruptLock)
@@ -332,16 +342,17 @@ JSRuntime::init(uint32_t maxbytes, uint3
 #endif
 
     nativeStackBase = GetNativeStackBase();
 
     jitSupportsFloatingPoint = JitSupportsFloatingPoint();
 
 #ifdef JS_ION
     signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this);
+    canUseSignalHandlers_ = signalHandlersInstalled_ && !SignalBasedTriggersDisabled();
 #endif
 
     if (!spsProfiler.init())
         return false;
 
     return true;
 }
 
@@ -482,17 +493,17 @@ void
 JSRuntime::resetJitStackLimit()
 {
     AutoLockForInterrupt lock(this);
     mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]);
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     mainThread.setJitStackLimit(js::jit::Simulator::StackLimit());
 #endif
- }
+}
 
 void
 JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes)
 {
     // Several tables in the runtime enumerated below can be used off thread.
     AutoLockForExclusiveAccess lock(this);
 
     rtSizes->object += mallocSizeOf(this);
@@ -540,24 +551,16 @@ JSRuntime::addSizeOfIncludingThis(mozill
 #ifdef JSGC_GENERATIONAL
     rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted();
     rtSizes->gc.nurseryDecommitted += gc.nursery.sizeOfHeapDecommitted();
     rtSizes->gc.nurseryHugeSlots += gc.nursery.sizeOfHugeSlots(mallocSizeOf);
     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
 #endif
 }
 
-static bool
-SignalBasedTriggersDisabled()
-{
-  // Don't bother trying to cache the getenv lookup; this should be called
-  // infrequently.
-  return !!getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS");
-}
-
 void
 JSRuntime::requestInterrupt(InterruptMode mode)
 {
     AutoLockForInterrupt lock(this);
 
     /*
      * Invalidate ionTop to trigger its over-recursion check. Note this must be
      * set before interrupt, to avoid racing with js::InvokeInterruptCallback,
@@ -569,20 +572,20 @@ JSRuntime::requestInterrupt(InterruptMod
     interrupt = true;
 
 #ifdef JS_ION
 #ifdef JS_THREADSAFE
     RequestInterruptForForkJoin(this, mode);
 #endif
 
     /*
-     * asm.js and, optionally, normal Ion code use memory protection and signal
+     * asm.js and normal Ion code optionally use memory protection and signal
      * handlers to halt running code.
      */
-    if (!SignalBasedTriggersDisabled()) {
+    if (canUseSignalHandlers()) {
         RequestInterruptForAsmJSCode(this, mode);
         jit::RequestInterruptForIonCode(this, mode);
     }
 #endif
 }
 
 JSC::ExecutableAllocator *
 JSRuntime::createExecutableAllocator(JSContext *cx)
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1031,16 +1031,18 @@ struct JSRuntime : public JS::shadow::Ru
     mozilla::LinkedList<JSContext> contextList;
 
     bool hasContexts() const {
         return !contextList.isEmpty();
     }
 
     mozilla::UniquePtr<js::SourceHook> sourceHook;
 
+    js::AssertOnScriptEntryHook assertOnScriptEntryHook_;
+
     /* Per runtime debug hooks -- see js/OldDebugAPI.h. */
     JSDebugHooks        debugHooks;
 
     /* If true, new compartments are initially in debug mode. */
     bool                debugMode;
 
     /* SPS profiling metadata */
     js::SPSProfiler     spsProfiler;
@@ -1065,24 +1067,30 @@ struct JSRuntime : public JS::shadow::Ru
 
     /* Client opaque pointers */
     void                *data;
 
 #if defined(XP_MACOSX) && defined(JS_ION)
     js::AsmJSMachExceptionHandler asmJSMachExceptionHandler;
 #endif
<