Merge mc -> pine draft
authorGregor Wagner <anygregor@gmail.com>
Wed, 07 May 2014 07:29:14 -0700
changeset 388092 f6f8c4a8138db29c81ce0bd521fab59a81bc796c
parent 388091 40d0ac5d2e5be9c7420f524c5b780cc82d064cec (current diff)
parent 181978 fef1a56f0237b8facbb2fd3fd624902a7c9f7079 (diff)
child 388093 a43266cd1c49911a9c6ba4a43b23c44cb4a3dd12
push id23132
push userbmo:lissyx+mozillians@lissyx.dyndns.org
push dateFri, 15 Jul 2016 10:07:12 +0000
milestone32.0a1
Merge mc -> pine
browser/base/content/test/social/browser_chat_tearoff.js
browser/devtools/webaudioeditor/test/browser_wa_graph_mouseover.js
configure.in
dom/asmjscache/AsmJSCache.cpp
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/settings/SettingsManager.js
--- 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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
@@ -97,12 +97,12 @@
   <project name="platform/system/netd" path="system/netd" revision="3d298fde142bee3fc4f07f63f16f2d8ce42339c0"/>
   <project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="9abf0ab68376afae3e1c7beefa3e9cbee2fde202"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="2fee3bbbfc236b883ef8507e27d88b17b203fe25"/>
   <project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
-  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="10977f28df27f79534f079c0a9852b21a5a0f6da"/>
+  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="f19ba22ba6973804628781606bc072d18f3f79c2"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="431afac2ebfdd9c1c8402b413ff5914ed448e961"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
 </manifest>
--- 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="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <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="65fba428f8d76336b33ddd9e15900357953600ba">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <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="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <!-- Stock Android things -->
--- 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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
@@ -97,12 +97,12 @@
   <project name="platform/system/netd" path="system/netd" revision="3d298fde142bee3fc4f07f63f16f2d8ce42339c0"/>
   <project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="9abf0ab68376afae3e1c7beefa3e9cbee2fde202"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="2fee3bbbfc236b883ef8507e27d88b17b203fe25"/>
   <project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
-  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="10977f28df27f79534f079c0a9852b21a5a0f6da"/>
+  <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="f19ba22ba6973804628781606bc072d18f3f79c2"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="431afac2ebfdd9c1c8402b413ff5914ed448e961"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
 </manifest>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <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"/>
@@ -124,24 +124,24 @@
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="fa892235a9bd8983f8b591129fc1a9398f64e514"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>
   <project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="dd925f76e4f149c3d5571b80e12f7e24bbe89c59"/>
   <project name="platform/external/dbus" path="external/dbus" revision="ea87119c843116340f5df1d94eaf8275e1055ae8"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="494c177966fdc31183a5f7af82dc9130f523da4b"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="320b05a5761eb2a4816f7529c91ea49422979b55"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="1df6dac11d7370a2fffca8e31d65b80f537faec5"/>
-  <project name="platform/frameworks/base" path="frameworks/base" revision="807d87d5ff66cb5e0664f6924f612fcdb5e2c453"/>
+  <project name="platform/frameworks/base" path="frameworks/base" revision="eed05deb23b612ef6b415a039c9e77483669b0c4"/>
   <project name="platform/frameworks/native" path="frameworks/native" revision="33a2b51f78416536e1bfba0c0b7776db307f3a4f"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="484802559ed106bac4811bd01c024ca64f741e60"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="d30227d7ae5cbe8bac8775358b472f44504a20d2"/>
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="81afa7f775b7559da52f468150d1fe090c3fbdc5"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="e38444b6ce12c7c25883272a439800376d5308eb"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="13312a5577db9261cb0fcee9ccbc58cdb5e6bc55"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
-  <project name="platform/system/core" path="system/core" revision="e284280277c1312017a9450956a9676584441cdf"/>
+  <project name="platform/system/core" path="system/core" revision="81c9921fcf372228aab336ff7cf0076c66505af0"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="f620016437dd2f050e044eccef5e70e3f689ccbe"/>
   <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="b3001d5f1686f89995b7bf70963cf69c8faebd83"/>
 </manifest>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "7bf976de32cf817ed4529ac875755ac6f6895b89", 
+    "revision": "787e0db2b536a3f1105c12819d48a8a584d5d4d6", 
     "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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <!-- 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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <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/inari/sources.xml
+++ b/b2g/config/inari/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="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/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="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="98ca8c55dbe2f21a8661d0eaa87f34d316c3bc98"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="347d0517f0a77122c876d5f62c0942006a7a0bfe"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
   <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/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -114,18 +114,18 @@
       oncommand="OpenBrowserWindow({remote: true});"/>
     <command id="Tools:NonRemoteWindow"
       oncommand="OpenBrowserWindow({remote: false});"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
     <command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
-    <command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
     <command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
+    <command id="Chat:Focus" oncommand="Cu.import('resource:///modules/Chat.jsm', {}).Chat.focus(window);"/>
   </commandset>
 
   <commandset id="placesCommands">
     <command id="Browser:ShowAllBookmarks"
              oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
     <command id="Browser:ShowAllHistory"
              oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
   </commandset>
@@ -375,17 +375,25 @@
     <key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #ifdef XP_WIN
 # Cmd+I is conventially mapped to Info on MacOS X, thus it should not be
 # overridden for other purposes there.
     <key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #endif
 
     <!--<key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>-->
-    <key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
+    <key id="focusChatBar" key="&social.chatBar.commandkey;" command="Chat:Focus"
+#ifdef XP_MACOSX
+# Sadly the devtools uses shift-accel-c on non-mac and alt-accel-c everywhere else
+# So we just use the other
+         modifiers="accel,shift"
+#else
+         modifiers="accel,alt"
+#endif
+    />
 
     <key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
 
 #ifdef XP_MACOSX
     <key id="key_stop_mac" modifiers="accel" key="&stopCmd.macCommandKey;" command="Browser:Stop"/>
 #endif
 
     <key id="key_gotoHistory"
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1,15 +1,14 @@
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 // the "exported" symbols
 let SocialUI,
-    SocialChatBar,
     SocialFlyout,
     SocialMarks,
     SocialShare,
     SocialSidebar,
     SocialStatus;
 
 (function() {
 
@@ -168,17 +167,16 @@ SocialUI = {
       Components.utils.reportError(e + "\n" + e.stack);
       throw e;
     }
   },
 
   _providersChanged: function() {
     SocialSidebar.clearProviderMenus();
     SocialSidebar.update();
-    SocialChatBar.update();
     SocialShare.populateProviderMenu();
     SocialStatus.populateToolbarPalette();
     SocialMarks.populateToolbarPalette();
     SocialShare.update();
   },
 
   // This handles "ActivateSocialFeature" events fired against content documents
   // in this window.
@@ -292,55 +290,16 @@ SocialUI = {
   updateState: function() {
     if (!this.enabled)
       return;
     SocialMarks.update();
     SocialShare.update();
   }
 }
 
-SocialChatBar = {
-  get chatbar() {
-    return document.getElementById("pinnedchats");
-  },
-  // Whether the chatbar is available for this window.  Note that in full-screen
-  // mode chats are available, but not shown.
-  get isAvailable() {
-    return SocialUI.enabled;
-  },
-  // Does this chatbar have any chats (whether minimized, collapsed or normal)
-  get hasChats() {
-    return !!this.chatbar.firstElementChild;
-  },
-  openChat: function(aProvider, aURL, aCallback, aMode) {
-    this.update();
-    if (!this.isAvailable)
-      return false;
-    this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
-    // We only want to focus the chat if it is as a result of user input.
-    let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindowUtils);
-    if (dwu.isHandlingUserInput)
-      this.chatbar.focus();
-    return true;
-  },
-  update: function() {
-    let command = document.getElementById("Social:FocusChat");
-    if (!this.isAvailable) {
-      this.chatbar.hidden = command.hidden = true;
-    } else {
-      this.chatbar.hidden = command.hidden = false;
-    }
-    command.setAttribute("disabled", command.hidden ? "true" : "false");
-  },
-  focus: function SocialChatBar_focus() {
-    this.chatbar.focus();
-  }
-}
-
 SocialFlyout = {
   get panel() {
     return document.getElementById("social-flyout-panel");
   },
 
   get iframe() {
     if (!this.panel.firstChild)
       this._createFrame();
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -25,60 +25,53 @@
                   context="contentAreaContextMenu"
                   disableglobalhistory="true"
                   tooltip="aHTMLTooltip"
                   xbl:inherits="src,origin" type="content"/>
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor><![CDATA[
-        let Social = Components.utils.import("resource:///modules/Social.jsm", {}).Social;
         this.content.__defineGetter__("popupnotificationanchor",
                                       () => document.getAnonymousElementByAttribute(this, "anonid", "notification-icon"));
-        Social.setErrorListener(this.content, function(aBrowser) {
-          aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
-                                 encodeURIComponent(aBrowser.getAttribute("origin")),
-                                 null, null, null, null);
-        });
+
         if (!this.chatbar) {
           document.getAnonymousElementByAttribute(this, "anonid", "minimize").hidden = true;
           document.getAnonymousElementByAttribute(this, "anonid", "close").hidden = true;
         }
         let contentWindow = this.contentWindow;
+        // process this._callbacks, then set to null so the chatbox creator
+        // knows to make new callbacks immediately.
+        if (this._callbacks) {
+          for (let callback of this._callbacks) {
+            callback(this);
+          }
+          this._callbacks = null;
+        }
         this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
           if (event.target != this.contentDocument)
             return;
           this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
           this.isActive = !this.minimized;
-          // process this._callbacks, then set to null so the chatbox creator
-          // knows to make new callbacks immediately.
-          if (this._callbacks) {
-            for (let callback of this._callbacks) {
-              if (callback)
-                callback(contentWindow);
-            }
-            this._callbacks = null;
-          }
-
-          // content can send a socialChatActivity event to have the UI update.
-          let chatActivity = function() {
-            this.setAttribute("activity", true);
-            if (this.chatbar)
-              this.chatbar.updateTitlebar(this);
-          }.bind(this);
-          contentWindow.addEventListener("socialChatActivity", chatActivity);
-          contentWindow.addEventListener("unload", function unload() {
-            contentWindow.removeEventListener("unload", unload);
-            contentWindow.removeEventListener("socialChatActivity", chatActivity);
-          });
+          this._deferredChatLoaded.resolve(this);
         }, true);
         if (this.src)
           this.setAttribute("src", this.src);
       ]]></constructor>
 
+      <field name="_deferredChatLoaded" readonly="true">
+        Promise.defer();
+      </field>
+
+      <property name="promiseChatLoaded">
+        <getter>
+          return this._deferredChatLoaded.promise;
+        </getter>
+      </property>
+
       <field name="content" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "content");
       </field>
 
       <property name="contentWindow">
         <getter>
           return this.content.contentWindow;
         </getter>
@@ -128,40 +121,32 @@
           this.content.docShell.isActive = !!val;
 
           // let the chat frame know if it is being shown or hidden
           let evt = this.contentDocument.createEvent("CustomEvent");
           evt.initCustomEvent(val ? "socialFrameShow" : "socialFrameHide", true, true, {});
           this.contentDocument.documentElement.dispatchEvent(evt);
         </setter>
       </property>
-      
+
       <method name="showNotifications">
         <body><![CDATA[
         PopupNotifications._reshowNotifications(this.content.popupnotificationanchor,
                                                 this.content);
         ]]></body>
       </method>
 
       <method name="swapDocShells">
         <parameter name="aTarget"/>
         <body><![CDATA[
           aTarget.setAttribute('label', this.contentDocument.title);
           aTarget.src = this.src;
           aTarget.content.setAttribute("origin", this.content.getAttribute("origin"));
           aTarget.content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
-          this.content.socialErrorListener.remove();
-          aTarget.content.socialErrorListener.remove();
           this.content.swapDocShells(aTarget.content);
-          Social.setErrorListener(this.content, function(aBrowser) {}); // 'this' will be destroyed soon.
-          Social.setErrorListener(aTarget.content, function(aBrowser) {
-            aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
-                                 encodeURIComponent(aBrowser.getAttribute("origin")),
-                                 null, null, null, null);
-          });
         ]]></body>
       </method>
 
       <method name="onTitlebarClick">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (!this.chatbar)
             return;
@@ -181,41 +166,50 @@
           this.chatbar.remove(this);
         else
           window.close();
         ]]></body>
       </method>
 
       <method name="swapWindows">
         <body><![CDATA[
-        let provider = Social._getProviderFromOrigin(this.content.getAttribute("origin"));
+        let deferred = Promise.defer();
+        let title = this.getAttribute("label");
         if (this.chatbar) {
-          this.chatbar.detachChatbox(this, { "centerscreen": "yes" }, win => {
-            win.document.title = provider.name;
-          });
+          this.chatbar.detachChatbox(this, { "centerscreen": "yes" }).then(
+            chatbox => {
+              chatbox.contentWindow.document.title = title;
+              deferred.resolve(chatbox);
+            }
+          );
         } else {
           // attach this chatbox to the topmost browser window
-          let findChromeWindowForChats = Cu.import("resource://gre/modules/MozSocialAPI.jsm").findChromeWindowForChats;
-          let win = findChromeWindowForChats();
-          let chatbar = win.SocialChatBar.chatbar;
-          chatbar.openChat(provider, "about:blank", win => {
-            let cb = chatbar.selectedChat;
-            this.swapDocShells(cb);
+          let Chat = Cu.import("resource:///modules/Chat.jsm").Chat;
+          let win = Chat.findChromeWindowForChats();
+          let chatbar = win.document.getElementById("pinnedchats");
+          let origin = this.content.getAttribute("origin");
+          let cb = chatbar.openChat(origin, title, "about:blank");
+          cb.promiseChatLoaded.then(
+            () => {
+              this.swapDocShells(cb);
 
-            // chatboxForURL is a map of URL -> chatbox used to avoid opening
-            // duplicate chat windows. Ensure reattached chat windows aren't
-            // registered with about:blank as their URL, otherwise reattaching
-            // more than one chat window isn't possible.
-            chatbar.chatboxForURL.delete("about:blank");
-            chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
+              // chatboxForURL is a map of URL -> chatbox used to avoid opening
+              // duplicate chat windows. Ensure reattached chat windows aren't
+              // registered with about:blank as their URL, otherwise reattaching
+              // more than one chat window isn't possible.
+              chatbar.chatboxForURL.delete("about:blank");
+              chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
 
-            chatbar.focus();
-            this.close();
-          });
+              chatbar.focus();
+              this.close();
+              deferred.resolve(cb);
+            }
+          );
         }
+        return deferred.promise;
         ]]></body>
       </method>
 
       <method name="toggle">
         <body><![CDATA[
           this.minimized = !this.minimized;
         ]]></body>
       </method>
@@ -505,76 +499,70 @@
             this._selectAnotherChat();
           }
         ]]></body>
       </method>
 
       <method name="_remove">
         <parameter name="aChatbox"/>
         <body><![CDATA[
-          aChatbox.content.socialErrorListener.remove();
           this.removeChild(aChatbox);
           // child might have been collapsed.
           let menuitem = this.menuitemMap.get(aChatbox);
           if (menuitem) {
             this.menuitemMap.delete(aChatbox);
             this.menupopup.removeChild(menuitem);
           }
           this.chatboxForURL.delete(aChatbox.src);
         ]]></body>
       </method>
 
-      <method name="removeAll">
-        <body><![CDATA[
-          this.selectedChat = null;
-          while (this.firstElementChild) {
-            this._remove(this.firstElementChild);
-          }
-          // and the nub/popup must also die.
-          this.nub.collapsed = true;
-        ]]></body>
-      </method>
-
       <method name="openChat">
-        <parameter name="aProvider"/>
+        <parameter name="aOrigin"/>
+        <parameter name="aTitle"/>
         <parameter name="aURL"/>
+        <parameter name="aMode"/>
         <parameter name="aCallback"/>
-        <parameter name="aMode"/>
         <body><![CDATA[
           let cb = this.chatboxForURL.get(aURL);
           if (cb) {
             cb = cb.get();
             if (cb.parentNode) {
               this.showChat(cb, aMode);
               if (aCallback) {
                 if (cb._callbacks == null) {
-                  // DOMContentLoaded has already fired, so callback now.
-                  aCallback(cb.contentWindow);
+                  // Chatbox has already been created, so callback now.
+                  aCallback(cb);
                 } else {
-                  // DOMContentLoaded for this chat is yet to fire...
+                  // Chatbox is yet to have bindings created...
                   cb._callbacks.push(aCallback);
                 }
               }
-              return;
+              return cb;
             }
             this.chatboxForURL.delete(aURL);
           }
           cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
-          // _callbacks is a javascript property instead of a <field> as it
-          // must exist before the (possibly delayed) bindings are created.
-          cb._callbacks = [aCallback];
+          cb._callbacks = [];
+          if (aCallback) {
+            // _callbacks is a javascript property instead of a <field> as it
+            // must exist before the (possibly delayed) bindings are created.
+            cb._callbacks.push(aCallback);
+          }
           // src also a javascript property; the src attribute is set in the ctor.
           cb.src = aURL;
           if (aMode == "minimized")
             cb.setAttribute("minimized", "true");
-          cb.setAttribute("origin", aProvider.origin);
+          cb.setAttribute("origin", aOrigin);
+          cb.setAttribute("label", aTitle);
           this.insertBefore(cb, this.firstChild);
           this.selectedChat = cb;
           this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
           this.resize();
+          return cb;
         ]]></body>
       </method>
 
       <method name="resize">
         <body><![CDATA[
         // Checks the current size against the collapsed state of children
         // and collapses or expands as necessary such that as many as possible
         // are shown.
@@ -643,40 +631,42 @@
 
       <method name="_getDragTarget">
         <parameter name="event"/>
         <body><![CDATA[
           return event.target.localName == "chatbox" ? event.target : null;
         ]]></body>
       </method>
 
-      <!-- Moves a chatbox to a new window. -->
+      <!-- Moves a chatbox to a new window. Returns a promise that is resolved
+           once the move to the other window is complete.
+      -->
       <method name="detachChatbox">
         <parameter name="aChatbox"/>
         <parameter name="aOptions"/>
-        <parameter name="aCallback"/>
         <body><![CDATA[
+          let deferred = Promise.defer();
           let options = "";
           for (let name in aOptions)
             options += "," + name + "=" + aOptions[name];
 
           let otherWin = window.openDialog("chrome://browser/content/chatWindow.xul",
                                            "_blank", "chrome,all,dialog=no" + options);
 
           otherWin.addEventListener("load", function _chatLoad(event) {
             if (event.target != otherWin.document)
               return;
 
             otherWin.removeEventListener("load", _chatLoad, true);
             let otherChatbox = otherWin.document.getElementById("chatter");
             aChatbox.swapDocShells(otherChatbox);
             aChatbox.close();
-            if (aCallback)
-              aCallback(otherWin);
+            deferred.resolve(otherChatbox);
           }, true);
+          return deferred.promise;
         ]]></body>
       </method>
 
     </implementation>
 
     <handlers>
       <handler event="popupshown"><![CDATA[
         this.nub.removeAttribute("activity");
@@ -745,19 +735,20 @@
         let winWidth = 400;
         let winHeight = 420;
         // ensure new window entirely within screen
         let left = Math.min(Math.max(eX, sX.value),
                             sX.value + sWidth.value - winWidth);
         let top = Math.min(Math.max(eY, sY.value),
                            sY.value + sHeight.value - winHeight);
 
-        let provider = Social._getProviderFromOrigin(draggedChat.content.getAttribute("origin"));
-        this.detachChatbox(draggedChat, { screenX: left, screenY: top }, win => {
-          win.document.title = provider.name;
-        });
-
+        let title = draggedChat.content.getAttribute("title");
+        this.detachChatbox(draggedChat, { screenX: left, screenY: top }).then(
+          chatbox => {
+            chatbox.contentWindow.document.title = title;
+          }
+        );
         event.stopPropagation();
       ]]></handler>
     </handlers>
   </binding>
 
 </bindings>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/chat/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+  head.js
+  chat.html
+
+[browser_chatwindow.js]
+[browser_focus.js]
+[browser_tearoff.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/chat/browser_chatwindow.js
@@ -0,0 +1,135 @@
+/* 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/. */
+
+let chatbar = document.getElementById("pinnedchats");
+
+add_chat_task(function* testOpenCloseChat() {
+  let chatbox = yield promiseOpenChat("http://example.com");
+  Assert.strictEqual(chatbox, chatbar.selectedChat);
+  // we requested a "normal" chat, so shouldn't be minimized
+  Assert.ok(!chatbox.minimized, "chat is not minimized");
+  Assert.equal(chatbar.childNodes.length, 1, "should be 1 chat open");
+
+
+  // now request the same URL again - we should get the same chat.
+  let chatbox2 = yield promiseOpenChat("http://example.com");
+  Assert.strictEqual(chatbox2, chatbox, "got the same chat");
+  Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+
+  chatbox.toggle();
+  is(chatbox.minimized, true, "chat is now minimized");
+  // was no other chat to select, so selected becomes null.
+  is(chatbar.selectedChat, null);
+
+  // We check the content gets an unload event as we close it.
+  let promiseClosed = promiseOneEvent(chatbox.content, "unload", true);
+  chatbox.close();
+  yield promiseClosed;
+});
+
+// In this case we open a chat minimized, then request the same chat again
+// without specifying minimized.  On that second call the chat should open,
+// selected, and no longer minimized.
+add_chat_task(function* testMinimized() {
+  let chatbox = yield promiseOpenChat("http://example.com", "minimized");
+  Assert.strictEqual(chatbox, chatbar.selectedChat);
+  Assert.ok(chatbox.minimized, "chat is minimized");
+  Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+  yield promiseOpenChat("http://example.com");
+  Assert.ok(!chatbox.minimized, false, "chat is no longer minimized");
+});
+
+// open enough chats to overflow the window, then check
+// if the menupopup is visible
+add_chat_task(function* testManyChats() {
+  Assert.ok(chatbar.menupopup.parentNode.collapsed, "popup nub collapsed at start");
+  // we should *never* find a test box that needs more than this to cause
+  // an overflow!
+  let maxToOpen = 20;
+  let numOpened = 0;
+  for (let i = 0; i < maxToOpen; i++) {
+    yield promiseOpenChat("http://example.com#" + i);
+    if (!chatbar.menupopup.parentNode.collapsed) {
+      info("the menu popup appeared");
+      return;
+    }
+  }
+  Assert.ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
+});
+
+// Check that closeAll works as expected.
+add_chat_task(function* testOpenTwiceCallbacks() {
+  yield promiseOpenChat("http://example.com#1");
+  yield promiseOpenChat("http://example.com#2");
+  yield promiseOpenChat("http://test2.example.com");
+  Assert.equal(numChatsInWindow(window), 3, "should be 3 chats open");
+  Chat.closeAll("http://example.com");
+  Assert.equal(numChatsInWindow(window), 1, "should have closed 2 chats");
+  Chat.closeAll("http://test2.example.com");
+  Assert.equal(numChatsInWindow(window), 0, "should have closed last chat");
+});
+
+// Check that when we open the same chat twice, the callbacks are called back
+// twice.
+add_chat_task(function* testOpenTwiceCallbacks() {
+  yield promiseOpenChatCallback("http://example.com");
+  yield promiseOpenChatCallback("http://example.com");
+});
+
+// Bug 817782 - check chats work in new top-level windows.
+add_chat_task(function* testSecondTopLevelWindow() {
+  const chatUrl = "http://example.com";
+  let secondWindow = OpenBrowserWindow();
+  yield promiseOneEvent(secondWindow, "load");
+  yield promiseOpenChat(chatUrl);
+  // the chat was created - let's make sure it was created in the second window.
+  Assert.equal(numChatsInWindow(window), 0, "main window has no chats");
+  Assert.equal(numChatsInWindow(secondWindow), 1, "second window has 1 chat");
+  secondWindow.close();
+});
+
+// Test that chats are created in the correct window.
+add_chat_task(function* testChatWindowChooser() {
+  let chat = yield promiseOpenChat("http://example.com");
+  Assert.equal(numChatsInWindow(window), 1, "first window has the chat");
+  // create a second window - this will be the "most recent" and will
+  // therefore be the window that hosts the new chat (see bug 835111)
+  let secondWindow = OpenBrowserWindow();
+  yield promiseOneEvent(secondWindow, "load");
+  Assert.equal(numChatsInWindow(secondWindow), 0, "second window starts with no chats");
+  yield promiseOpenChat("http://example.com#2");
+  Assert.equal(numChatsInWindow(secondWindow), 1, "second window now has chats");
+  Assert.equal(numChatsInWindow(window), 1, "first window still has 1 chat");
+  chat.close();
+  Assert.equal(numChatsInWindow(window), 0, "first window now has no chats");
+  // now open another chat - it should still open in the second.
+  yield promiseOpenChat("http://example.com#3");
+  Assert.equal(numChatsInWindow(window), 0, "first window still has no chats");
+  Assert.equal(numChatsInWindow(secondWindow), 2, "second window has both chats");
+
+  // focus the first window, and open yet another chat - it
+  // should open in the first window.
+  window.focus();
+  yield promiseWaitForFocus();
+  chat = yield promiseOpenChat("http://example.com#4");
+  Assert.equal(numChatsInWindow(window), 1, "first window got new chat");
+  chat.close();
+  Assert.equal(numChatsInWindow(window), 0, "first window has no chats");
+
+  let privateWindow = OpenBrowserWindow({private: true});
+  yield promiseOneEvent(privateWindow, "load")
+
+  // open a last chat - the focused window can't accept
+  // chats (it's a private window), so the chat should open
+  // in the window that was selected before. This is known
+  // to be broken on Linux.
+  chat = yield promiseOpenChat("http://example.com#5");
+  let os = Services.appinfo.OS;
+  const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
+  let fn = BROKEN_WM_Z_ORDER ? todo : ok;
+  fn(numChatsInWindow(window) == 1, "first window got the chat");
+  chat.close();
+  privateWindow.close();
+  secondWindow.close();
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/chat/browser_focus.js
@@ -0,0 +1,226 @@
+/* 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/. */
+
+// Tests the focus functionality.
+
+const CHAT_URL = "https://example.com/browser/browser/base/content/test/chat/chat.html";
+
+// Is the currently opened tab focused?
+function isTabFocused() {
+  let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
+  return Services.focus.focusedWindow == tabb.contentWindow;
+}
+
+// Is the specified chat focused?
+function isChatFocused(chat) {
+  return chat.chatbar._isChatFocused(chat);
+}
+
+let chatbar = document.getElementById("pinnedchats");
+
+function* setUp() {
+  // Note that (probably) due to bug 604289, if a tab is focused but the
+  // focused element is null, our chat windows can "steal" focus.  This is
+  // avoided if we explicitly focus an element in the tab.
+  // So we load a page with an <input> field and focus that before testing.
+  let html = '<input id="theinput"><button id="chat-opener"></button>';
+  let url = "data:text/html;charset=utf-8," + encodeURI(html);
+  let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
+  yield promiseOneEvent(tab.linkedBrowser, "load", true);
+  tab.linkedBrowser.contentDocument.getElementById("theinput").focus();
+  registerCleanupFunction(function() {
+    gBrowser.removeTab(tab);
+  });
+}
+
+// Test default focus - not user input.
+add_chat_task(function* testDefaultFocus() {
+  yield setUp();
+  let chat = yield promiseOpenChat("http://example.com");
+  // we used the default focus behaviour, which means that because this was
+  // not the direct result of user action the chat should not be focused.
+  Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+  Assert.ok(isTabFocused(), "the tab should remain focused.");
+  Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+});
+
+// Test default focus via user input.
+add_chat_task(function* testDefaultFocus() {
+  yield setUp();
+  let tab = gBrowser.selectedTab;
+  let deferred = Promise.defer();
+  let button = tab.linkedBrowser.contentDocument.getElementById("chat-opener");
+  button.addEventListener("click", function onclick() {
+    button.removeEventListener("click", onclick);
+    promiseOpenChat("http://example.com").then(
+      chat => deferred.resolve(chat)
+    );
+  })
+  // Note we must use synthesizeMouseAtCenter() rather than calling
+  // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput
+  // to be true.
+  EventUtils.synthesizeMouseAtCenter(button, {}, button.ownerDocument.defaultView);
+  let chat = yield deferred.promise;
+
+  // we use the default focus behaviour but the chat was opened via user input,
+  // so the chat should be focused.
+  Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+  Assert.ok(!isTabFocused(), "the tab should have lost focus.");
+  Assert.ok(isChatFocused(chat), "the chat should have got focus.");
+});
+
+// We explicitly ask for the chat to be focused.
+add_chat_task(function* testExplicitFocus() {
+  yield setUp();
+  let chat = yield promiseOpenChat("http://example.com", undefined, true);
+  // we use the default focus behaviour, which means that because this was
+  // not the direct result of user action the chat should not be focused.
+  Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+  Assert.ok(!isTabFocused(), "the tab should have lost focus.");
+  Assert.ok(isChatFocused(chat), "the chat should have got focus.");
+});
+
+// Open a minimized chat via default focus behaviour - it will open and not
+// have focus.  Then open the same chat without 'minimized' - it will be
+// restored but should still not have grabbed focus.
+add_chat_task(function* testNoFocusOnAutoRestore() {
+  yield setUp();
+  let chat = yield promiseOpenChat("http://example.com", "minimized");
+  Assert.ok(chat.minimized, "chat is minimized");
+  Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+  Assert.ok(isTabFocused(), "the tab should remain focused.");
+  Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+  yield promiseOpenChat("http://example.com");
+  Assert.ok(!chat.minimized, "chat should be restored");
+  Assert.ok(isTabFocused(), "the tab should remain focused.");
+  Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+});
+
+// Here we open a chat, which will not be focused.  Then we minimize it and
+// restore it via a titlebar clock - it should get focus at that point.
+add_chat_task(function* testFocusOnExplicitRestore() {
+  yield setUp();
+  let chat = yield promiseOpenChat("http://example.com");
+  Assert.ok(!chat.minimized, "chat should have been opened restored");
+  Assert.ok(isTabFocused(), "the tab should remain focused.");
+  Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+  chat.minimized = true;
+  Assert.ok(isTabFocused(), "tab should still be focused");
+  Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+
+  let promise = promiseOneEvent(chat.contentWindow, "focus");
+  // pretend we clicked on the titlebar
+  chat.onTitlebarClick({button: 0});
+  yield promise; // wait for focus event.
+  Assert.ok(!chat.minimized, "chat should have been restored");
+  Assert.ok(isChatFocused(chat), "chat should be focused");
+  Assert.strictEqual(chat, chatbar.selectedChat, "chat is marked selected");
+});
+
+// Open 2 chats and give 1 focus.  Minimize the focused one - the second
+// should get focus.
+add_chat_task(function* testMinimizeFocused() {
+  yield setUp();
+  let chat1 = yield promiseOpenChat("http://example.com#1");
+  let chat2 = yield promiseOpenChat("http://example.com#2");
+  Assert.equal(numChatsInWindow(window), 2, "2 chats open");
+  Assert.strictEqual(chatbar.selectedChat, chat2, "chat2 is selected");
+  let promise = promiseOneEvent(chat1.contentWindow, "focus");
+  chatbar.selectedChat = chat1;
+  chatbar.focus();
+  yield promise; // wait for chat1 to get focus.
+  Assert.strictEqual(chat1, chatbar.selectedChat, "chat1 is marked selected");
+  Assert.notStrictEqual(chat2, chatbar.selectedChat, "chat2 is not marked selected");
+  promise = promiseOneEvent(chat2.contentWindow, "focus");
+  chat1.minimized = true;
+  yield promise; // wait for chat2 to get focus.
+  Assert.notStrictEqual(chat1, chatbar.selectedChat, "chat1 is not marked selected");
+  Assert.strictEqual(chat2, chatbar.selectedChat, "chat2 is marked selected");
+});
+
+// Open 2 chats, select and focus the second.  Pressing the TAB key should
+// cause focus to move between all elements in our chat window before moving
+// to the next chat window.
+add_chat_task(function* testTab() {
+  yield setUp();
+
+  function sendTabAndWaitForFocus(chat, eltid) {
+    let doc = chat.contentDocument;
+    EventUtils.sendKey("tab");
+    // ideally we would use the 'focus' event here, but that doesn't work
+    // as expected for the iframe - the iframe itself never gets the focus
+    // event (apparently the sub-document etc does.)
+    // So just poll for the correct element getting focus...
+    let deferred = Promise.defer();
+    let tries = 0;
+    let interval = setInterval(function() {
+      if (tries >= 30) {
+        clearInterval(interval);
+        deferred.reject("never got focus");
+        return;
+      }
+      tries ++;
+      let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
+      if (doc.activeElement == elt) {
+        clearInterval(interval);
+        deferred.resolve();
+      }
+    });
+    return deferred.promise;
+  }
+
+  let chat1 = yield promiseOpenChat(CHAT_URL + "#1");
+  let chat2 = yield promiseOpenChat(CHAT_URL + "#2");
+  chatbar.selectedChat = chat2;
+  let promise = promiseOneEvent(chat2.contentWindow, "focus");
+  chatbar.focus();
+  yield promise;
+
+  // Our chats have 3 focusable elements, so it takes 4 TABs to move
+  // to the new chat.
+  yield sendTabAndWaitForFocus(chat2, "input1");
+  Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
+               "first input field has focus");
+  Assert.ok(isChatFocused(chat2), "new chat still focused after first tab");
+
+  yield sendTabAndWaitForFocus(chat2, "input2");
+  Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
+  Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
+               "second input field has focus");
+
+  yield sendTabAndWaitForFocus(chat2, "iframe");
+  Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
+  Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "iframe",
+               "iframe has focus");
+
+  // this tab now should move to the next chat, but focus the
+  // document element itself (hence the null eltid)
+  yield sendTabAndWaitForFocus(chat1, null);
+  Assert.ok(isChatFocused(chat1), "first chat is focused");
+});
+
+// Open a chat and focus an element other than the first. Move focus to some
+// other item (the tab itself in this case), then focus the chatbar - the
+// same element that was previously focused should still have focus.
+add_chat_task(function* testFocusedElement() {
+  yield setUp();
+
+  // open a chat with focus requested.
+  let chat = yield promiseOpenChat(CHAT_URL, undefined, true);
+
+  chat.contentDocument.getElementById("input2").focus();
+
+  // set focus to the tab.
+  let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
+  let promise = promiseOneEvent(tabb.contentWindow, "focus");
+  Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
+  yield promise;
+
+  promise = promiseOneEvent(chat.contentWindow, "focus");
+  chatbar.focus();
+  yield promise;
+
+  Assert.equal(chat.contentDocument.activeElement.getAttribute("id"), "input2",
+               "correct input field still has focus");
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/chat/browser_tearoff.js
@@ -0,0 +1,128 @@
+/* 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/. */
+
+let chatbar = document.getElementById("pinnedchats");
+
+function promiseNewWindowLoaded() {
+  let deferred = Promise.defer();
+  Services.wm.addListener({
+    onWindowTitleChange: function() {},
+    onCloseWindow: function(xulwindow) {},
+    onOpenWindow: function(xulwindow) {
+      var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                            .getInterface(Components.interfaces.nsIDOMWindow);
+      Services.wm.removeListener(this);
+      // wait for load to ensure the window is ready for us to test
+      domwindow.addEventListener("load", function _load(event) {
+        let doc = domwindow.document;
+        if (event.target != doc)
+            return;
+        domwindow.removeEventListener("load", _load);
+        deferred.resolve(domwindow);
+      });
+    },
+  });
+  return deferred.promise;
+}
+
+add_chat_task(function* testTearoffChat() {
+  let chatbox = yield promiseOpenChat("http://example.com");
+  Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+
+  let chatDoc = chatbox.contentDocument;
+  let chatTitle = chatDoc.title;
+
+  Assert.equal(chatbox.getAttribute("label"), chatTitle,
+               "the new chatbox should show the title of the chat window");
+
+  // mutate the chat document a bit before we tear it off.
+  let div = chatDoc.createElement("div");
+  div.setAttribute("id", "testdiv");
+  div.setAttribute("test", "1");
+  chatDoc.body.appendChild(div);
+
+  // chatbox is open, lets detach. The new chat window will be caught in
+  // the window watcher below
+  let promise = promiseNewWindowLoaded();
+
+  let swap = document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
+  swap.click();
+
+  // and wait for the new window.
+  let domwindow = yield promise;
+
+  Assert.equal(domwindow.document.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
+  Assert.equal(numChatsInWindow(window), 0, "should be no chats in the chat bar");
+
+  // get the chatbox from the new window.
+  chatbox = domwindow.document.getElementById("chatter")
+  Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
+
+  div = chatbox.contentDocument.getElementById("testdiv");
+  Assert.equal(div.getAttribute("test"), "1", "docshell should have been swapped");
+  div.setAttribute("test", "2");
+
+  // swap the window back to the chatbar
+  promise = promiseOneEvent(domwindow, "unload");
+  swap = domwindow.document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
+  swap.click();
+
+  yield promise;
+
+  Assert.equal(numChatsInWindow(window), 1, "chat should be docked back in the window");
+  chatbox = chatbar.selectedChat;
+  Assert.equal(chatbox.getAttribute("label"), chatTitle,
+               "the new chatbox should show the title of the chat window again");
+
+  div = chatbox.contentDocument.getElementById("testdiv");
+  Assert.equal(div.getAttribute("test"), "2", "docshell should have been swapped");
+});
+
+// Similar test but with 2 chats.
+add_chat_task(function* testReattachTwice() {
+  let chatbox1 = yield promiseOpenChat("http://example.com#1");
+  let chatbox2 = yield promiseOpenChat("http://example.com#2");
+  Assert.equal(numChatsInWindow(window), 2, "both chats should be docked in the window");
+
+  info("chatboxes are open, detach from window");
+  let promise = promiseNewWindowLoaded();
+  document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
+  let domwindow1 = yield promise;
+  chatbox1 = domwindow1.document.getElementById("chatter");
+  Assert.equal(numChatsInWindow(window), 1, "only second chat should be docked in the window");
+
+  promise = promiseNewWindowLoaded();
+  document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
+  let domwindow2 = yield promise;
+  chatbox2 = domwindow2.document.getElementById("chatter");
+  Assert.equal(numChatsInWindow(window), 0, "should be no docked chats");
+
+  promise = promiseOneEvent(domwindow2, "unload");
+  domwindow2.document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
+  yield promise;
+  Assert.equal(numChatsInWindow(window), 1, "one chat should be docked back in the window");
+
+  promise = promiseOneEvent(domwindow1, "unload");
+  domwindow1.document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
+  yield promise;
+  Assert.equal(numChatsInWindow(window), 2, "both chats should be docked back in the window");
+});
+
+// Check that Chat.closeAll() also closes detached windows.
+add_chat_task(function* testCloseAll() {
+  let chatbox1 = yield promiseOpenChat("http://example.com#1");
+  let chatbox2 = yield promiseOpenChat("http://example.com#2");
+
+  let promise = promiseNewWindowLoaded();
+  document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
+  let domwindow = yield promise;
+  chatbox1 = domwindow.document.getElementById("chatter");
+
+  let promiseWindowUnload = promiseOneEvent(domwindow, "unload");
+
+  Assert.equal(numChatsInWindow(window), 1, "second chat should still be docked");
+  Chat.closeAll("http://example.com");
+  yield promiseWindowUnload;
+  Assert.equal(numChatsInWindow(window), 0, "should be no chats left");
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/chat/chat.html
@@ -0,0 +1,14 @@
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>test chat window</title>
+  </head>
+  <body>
+    <p>This is a test chat window.</p>
+    <!-- a couple of input fields to help with focus testing -->
+    <input id="input1"/>
+    <input id="input2"/>
+    <!-- an iframe here so this one page generates multiple load events -->
+    <iframe id="iframe" src="data:text/plain:this is an iframe"></iframe>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/chat/head.js
@@ -0,0 +1,74 @@
+/* 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/. */
+
+// Utility functions for Chat tests.
+
+let Chat = Cu.import("resource:///modules/Chat.jsm", {}).Chat;
+
+function promiseOpenChat(url, mode, focus) {
+  let uri = Services.io.newURI(url, null, null);
+  let origin = uri.prePath;
+  let title = origin;
+  let chatbox = Chat.open(null, origin, title, url, mode, focus);
+  return chatbox.promiseChatLoaded;
+}
+
+// Opens a chat, returns a promise resolved when the chat callback fired.
+function promiseOpenChatCallback(url, mode) {
+  let uri = Services.io.newURI(url, null, null);
+  let origin = uri.prePath;
+  let title = origin;
+  let deferred = Promise.defer();
+  let callback = deferred.resolve;
+  Chat.open(null, origin, title, url, mode, undefined, callback);
+  return deferred.promise;
+}
+
+// Opens a chat, returns the chat window's promise which fires when the chat
+// starts loading.
+function promiseOneEvent(target, eventName, capture) {
+  let deferred = Promise.defer();
+  target.addEventListener(eventName, function handler(event) {
+    target.removeEventListener(eventName, handler, capture);
+    deferred.resolve();
+  }, capture);
+  return deferred.promise;
+}
+
+// Return the number of chats in a browser window.
+function numChatsInWindow(win) {
+  let chatbar = win.document.getElementById("pinnedchats");
+  return chatbar.childElementCount;
+}
+
+function promiseWaitForFocus() {
+  let deferred = Promise.defer();
+  waitForFocus(deferred.resolve);
+  return deferred.promise;
+}
+
+// A simple way to clean up after each test.
+function add_chat_task(genFunction) {
+  add_task(function* () {
+    info("Starting chat test " + genFunction.name);
+    try {
+      yield genFunction();
+    } finally {
+      info("Finished chat test " + genFunction.name + " - cleaning up.");
+      // close all docked chats.
+      while (chatbar.childNodes.length) {
+        chatbar.childNodes[0].close();
+      }
+      // and non-docked chats.
+      let winEnum = Services.wm.getEnumerator("Social:Chat");
+      while (winEnum.hasMoreElements()) {
+        let win = winEnum.getNext();
+        if (win.closed) {
+          continue;
+        }
+        win.close();
+      }
+    }
+  });
+}
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -21,17 +21,16 @@ support-files =
   social_sidebar.html
   social_sidebar_empty.html
   social_window.html
   social_worker.js
   unchecked.jpg
 
 [browser_addons.js]
 [browser_blocklist.js]
-[browser_chat_tearoff.js]
 [browser_defaults.js]
 [browser_share.js]
 [browser_social_activation.js]
 [browser_social_chatwindow.js]
 [browser_social_chatwindow_resize.js]
 [browser_social_chatwindowfocus.js]
 [browser_social_errorPage.js]
 [browser_social_flyout.js]
deleted file mode 100644
--- a/browser/base/content/test/social/browser_chat_tearoff.js
+++ /dev/null
@@ -1,308 +0,0 @@
-/* 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/. */
-
-function test() {
-  requestLongerTimeout(2); // only debug builds seem to need more time...
-  waitForExplicitFinish();
-
-  let manifest = { // normal provider
-    name: "provider 1",
-    origin: "https://example.com",
-    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
-    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
-    iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
-  };
-
-  let postSubTest = function(cb) {
-    let chats = document.getElementById("pinnedchats");
-    ok(chats.children.length == 0, "no chatty children left behind");
-    cb();
-  };
-  runSocialTestWithProvider(manifest, function (finishcb) {
-    SocialSidebar.show();
-    ok(SocialSidebar.provider, "sidebar provider exists");
-    runSocialTests(tests, undefined, postSubTest, function() {
-      finishcb();
-    });
-  });
-}
-
-var tests = {
-  testTearoffChat: function(next) {
-    let chats = document.getElementById("pinnedchats");
-    let chatTitle;
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "got-sidebar-message":
-          port.postMessage({topic: "test-chatbox-open"});
-          break;
-        case "got-chatbox-visibility":
-          // chatbox is open, lets detach. The new chat window will be caught in
-          // the window watcher below
-          let doc = chats.selectedChat.contentDocument;
-          // This message is (sometimes!) received a second time
-          // before we start our tests from the onCloseWindow
-          // callback.
-          if (doc.location == "about:blank")
-            return;
-          chatTitle = doc.title;
-          ok(chats.selectedChat.getAttribute("label") == chatTitle,
-             "the new chatbox should show the title of the chat window");
-          let div = doc.createElement("div");
-          div.setAttribute("id", "testdiv");
-          div.setAttribute("test", "1");
-          doc.body.appendChild(div);
-          let swap = document.getAnonymousElementByAttribute(chats.selectedChat, "anonid", "swap");
-          swap.click();
-          port.close();
-          break;
-        case "got-chatbox-message":
-          ok(true, "got chatbox message");
-          ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
-          chats.selectedChat.toggle();
-          break;
-      }
-    }
-
-    Services.wm.addListener({
-      onWindowTitleChange: function() {},
-      onCloseWindow: function(xulwindow) {},
-      onOpenWindow: function(xulwindow) {
-        var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                              .getInterface(Components.interfaces.nsIDOMWindow);
-        Services.wm.removeListener(this);
-        // wait for load to ensure the window is ready for us to test
-        domwindow.addEventListener("load", function _load(event) {
-          let doc = domwindow.document;
-          if (event.target != doc)
-              return;
-
-          domwindow.removeEventListener("load", _load, false);
-
-          domwindow.addEventListener("unload", function _close(event) {
-            if (event.target != doc)
-              return;
-            domwindow.removeEventListener("unload", _close, false);
-            info("window has been closed");
-            waitForCondition(function() {
-              return chats.selectedChat && chats.selectedChat.contentDocument &&
-                     chats.selectedChat.contentDocument.readyState == "complete";
-            },function () {
-              ok(chats.selectedChat, "should have a chatbox in our window again");
-              ok(chats.selectedChat.getAttribute("label") == chatTitle,
-                 "the new chatbox should show the title of the chat window again");
-              let testdiv = chats.selectedChat.contentDocument.getElementById("testdiv");
-              is(testdiv.getAttribute("test"), "2", "docshell should have been swapped");
-              chats.selectedChat.close();
-              waitForCondition(function() {
-                return chats.children.length == 0;
-              },function () {
-                next();
-              });
-            });
-          }, false);
-
-          is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
-          // window is loaded, but the docswap does not happen until after load,
-          // and we have no event to wait on, so we'll wait for document state
-          // to be ready
-          let chatbox = doc.getElementById("chatter");
-          waitForCondition(function() {
-            return chats.selectedChat == null &&
-                   chatbox.contentDocument &&
-                   chatbox.contentDocument.readyState == "complete";
-          },function() {
-            ok(chatbox.getAttribute("label") == chatTitle,
-               "detached window should show the title of the chat window");
-            let testdiv = chatbox.contentDocument.getElementById("testdiv");
-            is(testdiv.getAttribute("test"), "1", "docshell should have been swapped");
-            testdiv.setAttribute("test", "2");
-            // swap the window back to the chatbar
-            let swap = doc.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
-            swap.click();
-          }, domwindow);
-        }, false);
-      }
-    });
-
-    port.postMessage({topic: "test-init", data: { id: 1 }});
-  },
-
-  testCloseOnLogout: function(next) {
-    let chats = document.getElementById("pinnedchats");
-    const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
-    port.postMessage({topic: "test-init"});
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "got-chatbox-visibility":
-          // chatbox is open, lets detach. The new chat window will be caught in
-          // the window watcher below
-          let doc = chats.selectedChat.contentDocument;
-          // This message is (sometimes!) received a second time
-          // before we start our tests from the onCloseWindow
-          // callback.
-          if (doc.location == "about:blank")
-            return;
-          info("chatbox is open, detach from window");
-          let swap = document.getAnonymousElementByAttribute(chats.selectedChat, "anonid", "swap");
-          swap.click();
-          break;
-      }
-    }
-
-    Services.wm.addListener({
-      onWindowTitleChange: function() {},
-      onCloseWindow: function(xulwindow) {},
-      onOpenWindow: function(xulwindow) {
-        let domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                              .getInterface(Components.interfaces.nsIDOMWindow);
-        Services.wm.removeListener(this);
-        // wait for load to ensure the window is ready for us to test, make sure
-        // we're not getting called for about:blank
-        domwindow.addEventListener("load", function _load(event) {
-          let doc = domwindow.document;
-          if (event.target != doc)
-              return;
-          domwindow.removeEventListener("load", _load, false);
-
-          domwindow.addEventListener("unload", function _close(event) {
-            if (event.target != doc)
-              return;
-            domwindow.removeEventListener("unload", _close, false);
-            ok(true, "window has been closed");
-            next();
-          }, false);
-
-          is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
-          // window is loaded, but the docswap does not happen until after load,
-          // and we have no event to wait on, so we'll wait for document state
-          // to be ready
-          let chatbox = doc.getElementById("chatter");
-          waitForCondition(function() {
-            return chats.children.length == 0 &&
-                   chatbox.contentDocument &&
-                   chatbox.contentDocument.readyState == "complete";
-          },function() {
-            // logout, we should get unload next
-            port.postMessage({topic: "test-logout"});
-            port.close();
-          }, domwindow);
-
-        }, false);
-      }
-    });
-
-    port.postMessage({topic: "test-worker-chat", data: chatUrl});
-  },
-
-  testReattachTwice: function(next) {
-    let chats = document.getElementById("pinnedchats");
-    const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
-    let chatBoxCount = 0, reattachCount = 0;
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
-    port.postMessage({topic: "test-init"});
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "got-chatbox-visibility":
-          // chatbox is open, lets detach. The new chat window will be caught in
-          // the window watcher below
-          let doc = chats.selectedChat.contentDocument;
-          // This message is (sometimes!) received a second time
-          // before we start our tests from the onCloseWindow
-          // callback.
-          if (doc.location == "about:blank")
-            return;
-          if (++chatBoxCount != 2) {
-            // open the second chat window
-            port.postMessage({topic: "test-worker-chat", data: chatUrl + "?id=2"});
-            return;
-          }
-          info("chatbox is open, detach from window");
-          let chat1 = chats.firstChild;
-          let chat2 = chat1.nextSibling;
-          document.getAnonymousElementByAttribute(chat1, "anonid", "swap").click();
-          document.getAnonymousElementByAttribute(chat2, "anonid", "swap").click();
-          break;
-      }
-    };
-
-    let firstChatWindowDoc;
-    Services.wm.addListener({
-      onWindowTitleChange: function() {},
-      onCloseWindow: function(xulwindow) {},
-      onOpenWindow: function(xulwindow) {
-        let listener = this;
-        let domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                                 .getInterface(Components.interfaces.nsIDOMWindow);
-        // wait for load to ensure the window is ready for us to test, make sure
-        // we're not getting called for about:blank
-        domwindow.addEventListener("load", function _load(event) {
-          let doc = domwindow.document;
-          if (event.target != doc)
-            return;
-          domwindow.removeEventListener("load", _load, false);
-
-          domwindow.addEventListener("unload", function _close(event) {
-            if (event.target != doc)
-              return;
-            domwindow.removeEventListener("unload", _close, false);
-            ok(true, "window has been closed");
-            waitForCondition(function() {
-              return chats.selectedChat && chats.selectedChat.contentDocument &&
-                     chats.selectedChat.contentDocument.readyState == "complete";
-            }, function () {
-              ++reattachCount;
-              if (reattachCount == 1) {
-                info("reattaching second chat window");
-                let chatbox = firstChatWindowDoc.getElementById("chatter");
-                firstChatWindowDoc.getAnonymousElementByAttribute(chatbox, "anonid", "swap").click();
-                firstChatWindowDoc = null;
-              }
-              else if (reattachCount == 2) {
-                is(chats.children.length, 2, "both chat windows should be reattached");
-                chats.removeAll();
-                waitForCondition(() => chats.children.length == 0, function () {
-                  info("no chat window left");
-                  is(chats.chatboxForURL.size, 0, "chatboxForURL map should be empty");
-                  next();
-                });
-              }
-            }, "waited too long for the window to reattach");
-          }, false);
-
-          is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
-          if (!firstChatWindowDoc) {
-            firstChatWindowDoc = doc;
-            return;
-          }
-          Services.wm.removeListener(listener);
-
-          // window is loaded, but the docswap does not happen until after load,
-          // and we have no event to wait on, so we'll wait for document state
-          // to be ready
-          let chatbox = doc.getElementById("chatter");
-          waitForCondition(function() {
-            return chats.children.length == 0 &&
-                   chatbox.contentDocument &&
-                   chatbox.contentDocument.readyState == "complete";
-          },function() {
-            info("reattaching chat window");
-            doc.getAnonymousElementByAttribute(chatbox, "anonid", "swap").click();
-          }, "waited too long for the chat window to be detached");
-
-        }, false);
-      }
-    });
-
-    port.postMessage({topic: "test-worker-chat", data: chatUrl + "?id=1"});
-  }
-};
--- a/browser/base/content/test/social/browser_social_chatwindow.js
+++ b/browser/base/content/test/social/browser_social_chatwindow.js
@@ -39,16 +39,20 @@ function openChat(provider, callback) {
     }
   }
   let url = chatUrl + "?" + (chatId++);
   port.postMessage({topic: "test-init"});
   port.postMessage({topic: "test-worker-chat", data: url});
   gURLsNotRemembered.push(url);
 }
 
+function windowHasChats(win) {
+  return !!getChatBar().firstElementChild;
+}
+
 function test() {
   requestLongerTimeout(2); // only debug builds seem to need more time...
   waitForExplicitFinish();
 
   let oldwidth = window.outerWidth; // we futz with these, so we restore them
   let oldleft = window.screenX;
   window.moveTo(0, window.screenY)
   let postSubTest = function(cb) {
@@ -102,95 +106,16 @@ var tests = {
           ok(true, "got chatbox message");
           ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
           chats.selectedChat.toggle();
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
-  testOpenMinimized: function(next) {
-    // In this case the sidebar opens a chat (without specifying minimized).
-    // We then minimize it and have the sidebar reopen the chat (again without
-    // minimized).  On that second call the chat should open and no longer
-    // be minimized.
-    let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    let seen_opened = false;
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "test-init-done":
-          port.postMessage({topic: "test-chatbox-open"});
-          break;
-        case "chatbox-opened":
-          is(e.data.result, "ok", "the sidebar says it got a chatbox");
-          if (!seen_opened) {
-            // first time we got the opened message, so minimize the chat then
-            // re-request the same chat to be opened - we should get the
-            // message again and the chat should be restored.
-            ok(!chats.selectedChat.minimized, "chat not initially minimized")
-            chats.selectedChat.minimized = true
-            seen_opened = true;
-            port.postMessage({topic: "test-chatbox-open"});
-          } else {
-            // This is the second time we've seen this message - there should
-            // be exactly 1 chat open and it should no longer be minimized.
-            let chats = document.getElementById("pinnedchats");
-            ok(!chats.selectedChat.minimized, "chat no longer minimized")
-            chats.selectedChat.close();
-            is(chats.selectedChat, null, "should only have been one chat open");
-            port.close();
-            next();
-          }
-      }
-    }
-    port.postMessage({topic: "test-init", data: { id: 1 }});
-  },
-  testManyChats: function(next) {
-    // open enough chats to overflow the window, then check
-    // if the menupopup is visible
-    let port = SocialSidebar.provider.getWorkerPort();
-    let chats = document.getElementById("pinnedchats");
-    ok(port, "provider has a port");
-    ok(chats.menupopup.parentNode.collapsed, "popup nub collapsed at start");
-    port.postMessage({topic: "test-init"});
-    // we should *never* find a test box that needs more than this to cause
-    // an overflow!
-    let maxToOpen = 20;
-    let numOpened = 0;
-    let maybeOpenAnother = function() {
-      if (numOpened++ >= maxToOpen) {
-        ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
-        closeAllChats();
-        next();
-      }
-      port.postMessage({topic: "test-chatbox-open", data: { id: numOpened }});
-    }
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "got-chatbox-message":
-          if (!chats.menupopup.parentNode.collapsed) {
-            maybeOpenAnother();
-            break;
-          }
-          ok(true, "popup nub became visible");
-          // close our chats now
-          while (chats.selectedChat) {
-            chats.selectedChat.close();
-          }
-          ok(!chats.selectedChat, "chats are all closed");
-          port.close();
-          next();
-          break;
-      }
-    }
-    maybeOpenAnother();
-  },
   testWorkerChatWindow: function(next) {
     const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
     let chats = document.getElementById("pinnedchats");
     let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
@@ -234,71 +159,20 @@ var tests = {
           ok(!chat.parentNode, "chat is now closed");
           port.close();
           next();
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
-  testSameChatCallbacks: function(next) {
-    let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    let seen_opened = false;
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "test-init-done":
-          port.postMessage({topic: "test-chatbox-open"});
-          break;
-        case "chatbox-opened":
-          is(e.data.result, "ok", "the sidebar says it got a chatbox");
-          if (seen_opened) {
-            // This is the second time we've seen this message - there should
-            // be exactly 1 chat open.
-            let chats = document.getElementById("pinnedchats");
-            chats.selectedChat.close();
-            is(chats.selectedChat, null, "should only have been one chat open");
-            port.close();
-            next();
-          } else {
-            // first time we got the opened message, so re-request the same
-            // chat to be opened - we should get the message again.
-            seen_opened = true;
-            port.postMessage({topic: "test-chatbox-open"});
-          }
-      }
-    }
-    port.postMessage({topic: "test-init", data: { id: 1 }});
-  },
-
-  // check removeAll does the right thing
-  testRemoveAll: function(next, mode) {
-    let port = SocialSidebar.provider.getWorkerPort();
-    port.postMessage({topic: "test-init"});
-    get3ChatsForCollapsing(mode || "normal", function() {
-      let chatbar = window.SocialChatBar.chatbar;
-      chatbar.removeAll();
-      // should be no evidence of any chats left.
-      is(chatbar.childNodes.length, 0, "should be no chats left");
-      checkPopup();
-      is(chatbar.selectedChat, null, "nothing should be selected");
-      is(chatbar.chatboxForURL.size, 0, "chatboxForURL map should be empty");
-      port.close();
-      next();
-    });
-  },
-
-  testRemoveAllMinimized: function(next) {
-    this.testRemoveAll(next, "minimized");
-  },
 
   // Check what happens when you close the only visible chat.
   testCloseOnlyVisible: function(next) {
-    let chatbar = window.SocialChatBar.chatbar;
+    let chatbar = getChatBar();
     let chatWidth = undefined;
     let num = 0;
     is(chatbar.childNodes.length, 0, "chatbar starting empty");
     is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
 
     makeChat("normal", "first chat", function() {
       // got the first one.
       checkPopup();
@@ -326,81 +200,26 @@ var tests = {
       });
     });
   },
 
   testShowWhenCollapsed: function(next) {
     let port = SocialSidebar.provider.getWorkerPort();
     port.postMessage({topic: "test-init"});
     get3ChatsForCollapsing("normal", function(first, second, third) {
-      let chatbar = window.SocialChatBar.chatbar;
+      let chatbar = getChatBar();
       chatbar.showChat(first);
       ok(!first.collapsed, "first should no longer be collapsed");
       ok(second.collapsed ||  third.collapsed, false, "one of the others should be collapsed");
       closeAllChats();
       port.close();
       next();
     });
   },
 
-  testActivity: function(next) {
-    let port = SocialSidebar.provider.getWorkerPort();
-    port.postMessage({topic: "test-init"});
-    get3ChatsForCollapsing("normal", function(first, second, third) {
-      let chatbar = window.SocialChatBar.chatbar;
-      is(chatbar.selectedChat, third, "third chat should be selected");
-      ok(!chatbar.selectedChat.hasAttribute("activity"), "third chat should have no activity");
-      // send an activity message to the second.
-      ok(!second.hasAttribute("activity"), "second chat should have no activity");
-      let chat2 = second.content;
-      let evt = chat2.contentDocument.createEvent("CustomEvent");
-      evt.initCustomEvent("socialChatActivity", true, true, {});
-      chat2.contentDocument.documentElement.dispatchEvent(evt);
-      // second should have activity.
-      ok(second.hasAttribute("activity"), "second chat should now have activity");
-      // select the second - it should lose "activity"
-      chatbar.selectedChat = second;
-      ok(!second.hasAttribute("activity"), "second chat should no longer have activity");
-      // Now try the first - it is collapsed, so the 'nub' also gets activity attr.
-      ok(!first.hasAttribute("activity"), "first chat should have no activity");
-      let chat1 = first.content;
-      let evt = chat1.contentDocument.createEvent("CustomEvent");
-      evt.initCustomEvent("socialChatActivity", true, true, {});
-      chat1.contentDocument.documentElement.dispatchEvent(evt);
-      ok(first.hasAttribute("activity"), "first chat should now have activity");
-      ok(chatbar.nub.hasAttribute("activity"), "nub should also have activity");
-      // first is collapsed, so use openChat to get it.
-      chatbar.openChat(SocialSidebar.provider, first.getAttribute("src"));
-      ok(!first.hasAttribute("activity"), "first chat should no longer have activity");
-      // The nub should lose the activity flag here too
-      todo(!chatbar.nub.hasAttribute("activity"), "Bug 806266 - nub should no longer have activity");
-      // TODO: tests for bug 806266 should arrange to have 2 chats collapsed
-      // then open them checking the nub is updated correctly.
-      // Now we will go and change the embedded browser in the second chat and
-      // ensure the activity magic still works (ie, check that the unload for
-      // the browser didn't cause our event handlers to be removed.)
-      ok(!second.hasAttribute("activity"), "second chat should have no activity");
-      let subiframe = chat2.contentDocument.getElementById("iframe");
-      subiframe.contentWindow.addEventListener("unload", function subunload() {
-        subiframe.contentWindow.removeEventListener("unload", subunload);
-        // ensure all other unload listeners have fired.
-        executeSoon(function() {
-          let evt = chat2.contentDocument.createEvent("CustomEvent");
-          evt.initCustomEvent("socialChatActivity", true, true, {});
-          chat2.contentDocument.documentElement.dispatchEvent(evt);
-          ok(second.hasAttribute("activity"), "second chat still has activity after unloading sub-iframe");
-          closeAllChats();
-          port.close();
-          next();
-        })
-      })
-      subiframe.setAttribute("src", "data:text/plain:new location for iframe");
-    });
-  },
-
   testOnlyOneCallback: function(next) {
     let chats = document.getElementById("pinnedchats");
     let port = SocialSidebar.provider.getWorkerPort();
     let numOpened = 0;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
@@ -408,121 +227,41 @@ var tests = {
           break;
         case "chatbox-opened":
           numOpened += 1;
           port.postMessage({topic: "ping"});
           break;
         case "pong":
           executeSoon(function() {
             is(numOpened, 1, "only got one open message");
-            chats.removeAll();
+            chats.selectedChat.close();
             port.close();
             next();
           });
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
-  testSecondTopLevelWindow: function(next) {
-    // Bug 817782 - check chats work in new top-level windows.
-    const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
-    let port = SocialSidebar.provider.getWorkerPort();
-    let secondWindow;
-    port.onmessage = function(e) {
-      if (e.data.topic == "test-init-done") {
-        secondWindow = OpenBrowserWindow();
-        secondWindow.addEventListener("load", function loadListener() {
-          secondWindow.removeEventListener("load", loadListener);
-          port.postMessage({topic: "test-worker-chat", data: chatUrl});
-        });
-      } else if (e.data.topic == "got-chatbox-message") {
-        // the chat was created - let's make sure it was created in the second window.
-        is(secondWindow.SocialChatBar.chatbar.childElementCount, 1);
-        secondWindow.close();
-        next();
-      }
-    }
-    port.postMessage({topic: "test-init"});
-  },
-
-  testChatWindowChooser: function(next) {
-    // Tests that when a worker creates a chat, it is opened in the correct
-    // window.
-    // open a chat (it will open in the main window)
-    ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
-    openChat(SocialSidebar.provider, function() {
-      ok(window.SocialChatBar.hasChats, "first window has the chat");
-      // create a second window - this will be the "most recent" and will
-      // therefore be the window that hosts the new chat (see bug 835111)
-      let secondWindow = OpenBrowserWindow();
-      secondWindow.addEventListener("load", function loadListener() {
-        secondWindow.removeEventListener("load", loadListener);
-        ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
-        openChat(SocialSidebar.provider, function() {
-          ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
-          is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
-          window.SocialChatBar.chatbar.removeAll();
-          // now open another chat - it should still open in the second.
-          openChat(SocialSidebar.provider, function() {
-            ok(!window.SocialChatBar.hasChats, "first window has no chats");
-            ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
-
-            // focus the first window, and open yet another chat - it
-            // should open in the first window.
-            waitForFocus(function() {
-              openChat(SocialSidebar.provider, function() {
-                ok(window.SocialChatBar.hasChats, "first window has chats");
-                window.SocialChatBar.chatbar.removeAll();
-                ok(!window.SocialChatBar.hasChats, "first window has no chats");
-
-                let privateWindow = OpenBrowserWindow({private: true});
-                privateWindow.addEventListener("load", function loadListener() {
-                  privateWindow.removeEventListener("load", loadListener);
-
-                  // open a last chat - the focused window can't accept
-                  // chats (it's a private window), so the chat should open
-                  // in the window that was selected before. This is known
-                  // to be broken on Linux.
-                  openChat(SocialSidebar.provider, function() {
-                    let os = Services.appinfo.OS;
-                    const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
-                    let fn = BROKEN_WM_Z_ORDER ? todo : ok;
-                    fn(window.SocialChatBar.hasChats, "first window has a chat");
-                    window.SocialChatBar.chatbar.removeAll();
-
-                    privateWindow.close();
-                    secondWindow.close();
-                    next();
-                  });
-                });
-              });
-            });
-            window.focus();
-          });
-        });
-      })
-    });
-  },
   testMultipleProviderChat: function(next) {
     // test incomming chats from all providers
     openChat(Social.providers[0], function() {
       openChat(Social.providers[1], function() {
         openChat(Social.providers[2], function() {
           let chats = document.getElementById("pinnedchats");
           waitForCondition(function() chats.children.length == Social.providers.length,
             function() {
               ok(true, "one chat window per provider opened");
               // test logout of a single provider
               let provider = Social.providers[2];
               let port = provider.getWorkerPort();
               port.postMessage({topic: "test-logout"});
               waitForCondition(function() chats.children.length == Social.providers.length - 1,
                 function() {
-                  chats.removeAll();
+                  closeAllChats();
                   waitForCondition(function() chats.children.length == 0,
                                    function() {
                                     ok(!chats.selectedChat, "multiprovider chats are all closed");
                                     port.close();
                                     next();
                                    },
                                    "chat windows didn't close");
                 },
--- a/browser/base/content/test/social/browser_social_chatwindowfocus.js
+++ b/browser/base/content/test/social/browser_social_chatwindowfocus.js
@@ -4,17 +4,17 @@
 
 // Is the currently opened tab focused?
 function isTabFocused() {
   let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
   return Services.focus.focusedWindow == tabb.contentWindow;
 }
 
 function isChatFocused(chat) {
-  return SocialChatBar.chatbar._isChatFocused(chat);
+  return getChatBar()._isChatFocused(chat);
 }
 
 function openChatViaUser() {
   let sidebarDoc = document.getElementById("social-sidebar-browser").contentDocument;
   let button = sidebarDoc.getElementById("chat-opener");
   // Note we must use synthesizeMouseAtCenter() rather than calling
   // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput
   // to be true.
@@ -27,30 +27,31 @@ function openChatViaSidebarMessage(port,
       callback();
   }
   port.postMessage({topic: "test-chatbox-open", data: data});
 }
 
 function openChatViaWorkerMessage(port, data, callback) {
   // sadly there is no message coming back to tell us when the chat has
   // been opened, so we wait until one appears.
-  let chatbar = SocialChatBar.chatbar;
+  let chatbar = getChatBar();
   let numExpected = chatbar.childElementCount + 1;
   port.postMessage({topic: "test-worker-chat", data: data});
   waitForCondition(function() chatbar.childElementCount == numExpected,
                    function() {
                       // so the child has been added, but we don't know if it
                       // has been intialized - re-request it and the callback
                       // means it's done.  Minimized, same as the worker.
-                      SocialChatBar.openChat(SocialSidebar.provider,
-                                             data,
-                                             function() {
-                                                callback();
-                                             },
-                                             "minimized");
+                      chatbar.openChat(SocialSidebar.provider.origin,
+                                       SocialSidebar.provider.name,
+                                       data,
+                                       "minimized",
+                                       function() {
+                                          callback();
+                                       });
                    },
                    "No new chat appeared");
 }
 
 
 let isSidebarLoaded = false;
 
 function startTestAndWaitForSidebar(callback) {
@@ -104,17 +105,17 @@ function test() {
     let preSubTest = function(cb) {
       // XXX - when bug 604289 is fixed it should be possible to just do:
       // tab.linkedBrowser.contentWindow.focus()
       // but instead we must do:
       tab.linkedBrowser.contentDocument.getElementById("theinput").focus();
       waitForCondition(function() isTabFocused(), cb, "tab should have focus");
     }
     let postSubTest = function(cb) {
-      window.SocialChatBar.chatbar.removeAll();
+      closeAllChats();
       cb();
     }
     // and run the tests.
     runSocialTestWithProvider(manifest, function (finishcb) {
       SocialSidebar.show();
       runSocialTests(tests, preSubTest, postSubTest, function () {
         finishcb();
       });
@@ -127,235 +128,46 @@ function test() {
 }
 
 var tests = {
   // In this test the worker asks the sidebar to open a chat.  As that means
   // we aren't handling user-input we will not focus the chatbar.
   // Then we do it again - should still not be focused.
   // Then we perform a user-initiated request - it should get focus.
   testNoFocusWhenViaWorker: function(next) {
+    let chatbar = getChatBar();
     startTestAndWaitForSidebar(function(port) {
       openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
         ok(true, "got chatbox message");
-        is(SocialChatBar.chatbar.childElementCount, 1, "exactly 1 chat open");
+        is(chatbar.childElementCount, 1, "exactly 1 chat open");
         ok(isTabFocused(), "tab should still be focused");
         // re-request the same chat via a message.
         openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
-          is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open");
+          is(chatbar.childElementCount, 1, "still exactly 1 chat open");
           ok(isTabFocused(), "tab should still be focused");
           // re-request the same chat via user event.
           openChatViaUser();
-          waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
+          waitForCondition(function() isChatFocused(chatbar.selectedChat),
                            function() {
-            is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open");
-            is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat should be selected");
+            is(chatbar.childElementCount, 1, "still exactly 1 chat open");
+            is(chatbar.selectedChat, chatbar.firstElementChild, "chat should be selected");
             next();
           }, "chat should be focused");
         });
       });
     });
   },
 
   // In this test we arrange for the sidebar to open the chat via a simulated
   // click.  This should cause the new chat to be opened and focused.
   testFocusWhenViaUser: function(next) {
     startTestAndWaitForSidebar(function(port) {
+      let chatbar = getChatBar();
       openChatViaUser();
-      ok(SocialChatBar.chatbar.firstElementChild, "chat opened");
-      waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
+      ok(chatbar.firstElementChild, "chat opened");
+      waitForCondition(function() isChatFocused(chatbar.selectedChat),
                        function() {
-        is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat is selected");
+        is(chatbar.selectedChat, chatbar.firstElementChild, "chat is selected");
         next();
       }, "chat should be focused");
     });
   },
-
-  // Open a chat via the worker - it will open and not have focus.
-  // Then open the same chat via a sidebar message - it will be restored but
-  // should still not have grabbed focus.
-  testNoFocusOnAutoRestore: function(next) {
-    const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html?id=1";
-    let chatbar = SocialChatBar.chatbar;
-    startTestAndWaitForSidebar(function(port) {
-      openChatViaWorkerMessage(port, chatUrl, function() {
-        is(chatbar.childElementCount, 1, "exactly 1 chat open");
-        // bug 865086 opening minimized still sets the window as selected
-        todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected");
-        ok(isTabFocused(), "tab should be focused");
-        openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() {
-          is(chatbar.childElementCount, 1, "still 1 chat open");
-          ok(!chatbar.firstElementChild.minimized, "chat no longer minimized");
-          // bug 865086 because we marked it selected on open, it still is
-          todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected");
-          ok(isTabFocused(), "tab should still be focused");
-          next();
-        });
-      });
-    });
-  },
-
-  // Here we open a chat, which will not be focused.  Then we minimize it and
-  // restore it via a titlebar clock - it should get focus at that point.
-  testFocusOnExplicitRestore: function(next) {
-    startTestAndWaitForSidebar(function(port) {
-      openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
-        ok(true, "got chatbox message");
-        ok(isTabFocused(), "tab should still be focused");
-        let chatbox = SocialChatBar.chatbar.firstElementChild;
-        ok(chatbox, "chat opened");
-        chatbox.minimized = true;
-        ok(isTabFocused(), "tab should still be focused");
-        // pretend we clicked on the titlebar
-        chatbox.onTitlebarClick({button: 0});
-        waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
-                         function() {
-          ok(!chatbox.minimized, "chat should have been restored");
-          ok(isChatFocused(chatbox), "chat should be focused");
-          is(chatbox, SocialChatBar.chatbar.selectedChat, "chat is marked selected");
-          next();
-        }, "chat should have focus");
-      });
-    });
-  },
-
-  // Open 2 chats and give 1 focus.  Minimize the focused one - the second
-  // should get focus.
-  testMinimizeFocused: function(next) {
-    let chatbar = SocialChatBar.chatbar;
-    startTestAndWaitForSidebar(function(port) {
-      openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() {
-        let chat1 = chatbar.firstElementChild;
-        openChatViaSidebarMessage(port, {stealFocus: 1, id: 2}, function() {
-          is(chatbar.childElementCount, 2, "exactly 2 chats open");
-          let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
-          chatbar.selectedChat = chat1;
-          chatbar.focus();
-          waitForCondition(function() isChatFocused(chat1),
-                           function() {
-            is(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is marked selected");
-            isnot(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is not marked selected");
-            chat1.minimized = true;
-            waitForCondition(function() isChatFocused(chat2),
-                             function() {
-              // minimizing the chat with focus should give it to another.
-              isnot(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is not marked selected");
-              is(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is marked selected");
-              next();
-            }, "chat2 should have focus");
-          }, "chat1 should have focus");
-        });
-      });
-    });
-  },
-
-  // Open 2 chats, select (but not focus) one, then re-request it be
-  // opened via a message.  Focus should not move.
-  testReopenNonFocused: function(next) {
-    let chatbar = SocialChatBar.chatbar;
-    startTestAndWaitForSidebar(function(port) {
-      openChatViaSidebarMessage(port, {id: 1}, function() {
-        let chat1 = chatbar.firstElementChild;
-        openChatViaSidebarMessage(port, {id: 2}, function() {
-          let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
-          chatbar.selectedChat = chat2;
-          // tab still has focus
-          ok(isTabFocused(), "tab should still be focused");
-          // re-request the first.
-          openChatViaSidebarMessage(port, {id: 1}, function() {
-            is(chatbar.selectedChat, chat1, "chat1 now selected");
-            ok(isTabFocused(), "tab should still be focused");
-            next();
-          });
-        });
-      });
-    });
-  },
-
-  // Open 2 chats, select and focus the second.  Pressing the TAB key should
-  // cause focus to move between all elements in our chat window before moving
-  // to the next chat window.
-  testTab: function(next) {
-    function sendTabAndWaitForFocus(chat, eltid, callback) {
-      // ideally we would use the 'focus' event here, but that doesn't work
-      // as expected for the iframe - the iframe itself never gets the focus
-      // event (apparently the sub-document etc does.)
-      // So just poll for the correct element getting focus...
-      let doc = chat.contentDocument;
-      EventUtils.sendKey("tab");
-      waitForCondition(function() {
-        let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
-        return doc.activeElement == elt;
-      }, callback, "element " + eltid + " never got focus");
-    }
-
-    let chatbar = SocialChatBar.chatbar;
-    startTestAndWaitForSidebar(function(port) {
-      openChatViaSidebarMessage(port, {id: 1}, function() {
-        let chat1 = chatbar.firstElementChild;
-        openChatViaSidebarMessage(port, {id: 2}, function() {
-          let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
-          chatbar.selectedChat = chat2;
-          chatbar.focus();
-          waitForCondition(function() isChatFocused(chatbar.selectedChat),
-                           function() {
-            // Our chats have 3 focusable elements, so it takes 4 TABs to move
-            // to the new chat.
-            sendTabAndWaitForFocus(chat2, "input1", function() {
-              is(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
-                 "first input field has focus");
-              ok(isChatFocused(chat2), "new chat still focused after first tab");
-              sendTabAndWaitForFocus(chat2, "input2", function() {
-                ok(isChatFocused(chat2), "new chat still focused after tab");
-                is(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
-                   "second input field has focus");
-                sendTabAndWaitForFocus(chat2, "iframe", function() {
-                  ok(isChatFocused(chat2), "new chat still focused after tab");
-                  is(chat2.contentDocument.activeElement.getAttribute("id"), "iframe",
-                     "iframe has focus");
-                  // this tab now should move to the next chat, but focus the
-                  // document element itself (hence the null eltid)
-                  sendTabAndWaitForFocus(chat1, null, function() {
-                    ok(isChatFocused(chat1), "first chat is focused");
-                    next();
-                  });
-                });
-              });
-            });
-          }, "chat should have focus");
-        });
-      });
-    });
-  },
-
-  // Open a chat and focus an element other than the first. Move focus to some
-  // other item (the tab itself in this case), then focus the chatbar - the
-  // same element that was previously focused should still have focus.
-  testFocusedElement: function(next) {
-    let chatbar = SocialChatBar.chatbar;
-    startTestAndWaitForSidebar(function(port) {
-      openChatViaUser();
-      let chat = chatbar.firstElementChild;
-      // need to wait for the content to load before we can focus it.
-      chat.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
-        chat.removeEventListener("DOMContentLoaded", DOMContentLoaded);
-        chat.contentDocument.getElementById("input2").focus();
-        waitForCondition(function() isChatFocused(chat),
-                         function() {
-          is(chat.contentDocument.activeElement.getAttribute("id"), "input2",
-             "correct input field has focus");
-          // set focus to the tab.
-          let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
-          Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
-          waitForCondition(function() isTabFocused(),
-                           function() {
-            chatbar.focus();
-            waitForCondition(function() isChatFocused(chat),
-                             function() {
-              is(chat.contentDocument.activeElement.getAttribute("id"), "input2",
-                 "correct input field still has focus");
-              next();
-            }, "chat took focus");
-          }, "tab has focus");
-        }, "chat took focus");
-      });
-    });
-  },
 };
--- a/browser/base/content/test/social/browser_social_errorPage.js
+++ b/browser/base/content/test/social/browser_social_errorPage.js
@@ -4,16 +4,18 @@
 
 function gc() {
   Cu.forceGC();
   let wu =  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIDOMWindowUtils);
   wu.garbageCollect();
 }
 
+let openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).openChatWindow;
+
 // Support for going on and offline.
 // (via browser/base/content/test/browser_bookmark_titles.js)
 let origProxyType = Services.prefs.getIntPref('network.proxy.type');
 
 function goOffline() {
   // Simulate a network outage with offline mode. (Localhost is still
   // accessible in offline mode, so disable the test proxy as well.)
   if (!Services.io.offline)
@@ -37,19 +39,20 @@ function openPanel(url, panelCallback, l
   SocialFlyout.panel.firstChild.addEventListener("load", function panelLoad() {
     SocialFlyout.panel.firstChild.removeEventListener("load", panelLoad, true);
     loadCallback();
   }, true);
 }
 
 function openChat(url, panelCallback, loadCallback) {
   // open a chat window
-  SocialChatBar.openChat(SocialSidebar.provider, url, panelCallback);
-  SocialChatBar.chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
-    SocialChatBar.chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
+  let chatbar = getChatBar();
+  openChatWindow(null, SocialSidebar.provider, url, panelCallback);
+  chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
+    chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
     loadCallback();
   }, true);
 }
 
 function onSidebarLoad(callback) {
   let sbrowser = document.getElementById("social-sidebar-browser");
   sbrowser.addEventListener("load", function load() {
     sbrowser.removeEventListener("load", load, true);
@@ -149,30 +152,61 @@ var tests = {
           );
         });
       }
     );
   },
 
   testChatWindow: function(next) {
     let panelCallbackCount = 0;
-    // go offline and open a flyout.
+    // go offline and open a chat.
     goOffline();
     openChat(
       "https://example.com/browser/browser/base/content/test/social/social_chat.html",
       function() { // the panel api callback
         panelCallbackCount++;
       },
       function() { // the "load" callback.
         executeSoon(function() {
           todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
-          let chat = SocialChatBar.chatbar.selectedChat;
+          let chat = getChatBar().selectedChat;
           waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
                            function() {
                             chat.close();
                             next();
                             },
                            "error page didn't appear");
         });
       }
     );
+  },
+
+  testChatWindowAfterTearOff: function(next) {
+    // Ensure that the error listener survives the chat window being detached.
+    let url = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
+    let panelCallbackCount = 0;
+    // open a chat while we are still online.
+    openChat(
+      url,
+      null,
+      function() { // the "load" callback.
+        executeSoon(function() {
+          let chat = getChatBar().selectedChat;
+          is(chat.contentDocument.location.href, url, "correct url loaded");
+          // toggle to a detached window.
+          chat.swapWindows().then(
+            chat => {
+              // now go offline and reload the chat - about:socialerror should be loaded.
+              goOffline();
+              chat.contentDocument.location.reload();
+              waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
+                               function() {
+                                chat.close();
+                                next();
+                                },
+                               "error page didn't appear");
+            }
+          );
+        });
+      }
+    );
   }
 }
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -232,18 +232,16 @@ function checkSocialUI(win) {
       is(a, b, msg)
     else
       ++numGoodTests;
   }
   function isbool(a, b, msg) {
     _is(!!a, !!b, msg);
   }
   isbool(win.SocialSidebar.canShow, sidebarEnabled, "social sidebar active?");
-  isbool(win.SocialChatBar.isAvailable, enabled, "chatbar available?");
-  isbool(!win.SocialChatBar.chatbar.hidden, enabled, "chatbar visible?");
 
   let contextMenus = [
     {
       type: "link",
       id: "context-marklinkMenu",
       label: "social.marklinkMenu.label"
     },
     {
@@ -274,18 +272,16 @@ function checkSocialUI(win) {
     }
     for (let m of menus)
       _is(m.parentNode, parent, "menu has correct parent");
   }
 
   // and for good measure, check all the social commands.
   isbool(!doc.getElementById("Social:ToggleSidebar").hidden, sidebarEnabled, "Social:ToggleSidebar visible?");
   isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
-  isbool(!doc.getElementById("Social:FocusChat").hidden, enabled, "Social:FocusChat visible?");
-  isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
 
   // and report on overall success of failure of the various checks here.
   is(numGoodTests, numTests, "The Social UI tests succeeded.")
 }
 
 function waitForNotification(topic, cb) {
   function observer(subject, topic, data) {
     Services.obs.removeObserver(observer, topic);
@@ -398,17 +394,17 @@ function loadIntoTab(tab, url, callback)
 function get3ChatsForCollapsing(mode, cb) {
   // We make one chat, then measure its size.  We then resize the browser to
   // ensure a second can be created fully visible but a third can not - then
   // create the other 2.  first will will be collapsed, second fully visible
   // and the third also visible and the "selected" one.
   // To make our life easier we don't go via the worker and ports so we get
   // more control over creation *and* to make the code much simpler.  We
   // assume the worker/port stuff is individually tested above.
-  let chatbar = window.SocialChatBar.chatbar;
+  let chatbar = getChatBar();
   let chatWidth = undefined;
   let num = 0;
   is(chatbar.childNodes.length, 0, "chatbar starting empty");
   is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
 
   makeChat(mode, "first chat", function() {
     // got the first one.
     checkPopup();
@@ -442,33 +438,31 @@ function get3ChatsForCollapsing(mode, cb
     });
   }, mode);
 }
 
 function makeChat(mode, uniqueid, cb) {
   info("making a chat window '" + uniqueid +"'");
   let provider = SocialSidebar.provider;
   const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
-  let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
+  // Note that we use promiseChatLoaded instead of the callback to ensure the
+  // content has started loading.
+  let chatbox = getChatBar().openChat(provider.origin, provider.name,
+                                      chatUrl + "?id=" + uniqueid, mode);
+  chatbox.promiseChatLoaded.then(
+    () => {
     info("chat window has opened");
-    // we can't callback immediately or we might close the chat during
-    // this event which upsets the implementation - it is only 1/2 way through
-    // handling the load event.
-    chat.document.title = uniqueid;
-    executeSoon(cb);
-  }, mode);
-  if (!isOpened) {
-    ok(false, "unable to open chat window, no provider? more failures to come");
-    executeSoon(cb);
-  }
+    chatbox.contentDocument.title = uniqueid;
+    cb();
+  });
 }
 
 function checkPopup() {
   // popup only showing if any collapsed popup children.
-  let chatbar = window.SocialChatBar.chatbar;
+  let chatbar = getChatBar();
   let numCollapsed = 0;
   for (let chat of chatbar.childNodes) {
     if (chat.collapsed) {
       numCollapsed += 1;
       // and it have a menuitem weakmap
       is(chatbar.menuitemMap.get(chat).nodeName, "menuitem", "collapsed chat has a menu item");
     } else {
       ok(!chatbar.menuitemMap.has(chat), "open chat has no menu item");
@@ -477,17 +471,17 @@ function checkPopup() {
   is(chatbar.menupopup.parentNode.collapsed, numCollapsed == 0, "popup matches child collapsed state");
   is(chatbar.menupopup.childNodes.length, numCollapsed, "popup has correct count of children");
   // todo - check each individual elt is what we expect?
 }
 // Resize the main window so the chat area's boxObject is |desired| wide.
 // Does a callback passing |true| if the window is now big enough or false
 // if we couldn't resize large enough to satisfy the test requirement.
 function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
-  let current = window.SocialChatBar.chatbar.getBoundingClientRect().width;
+  let current = getChatBar().getBoundingClientRect().width;
   let delta = desired - current;
   info(count + ": resizing window so chat area is " + desired + " wide, currently it is "
        + current + ".  Screen avail is " + window.screen.availWidth
        + ", current outer width is " + window.outerWidth);
 
   // WTF?  Sometimes we will get fractional values due to the - err - magic
   // of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference.
   let widthDeltaCloseEnough = function(d) {
@@ -510,17 +504,17 @@ function resizeWindowToChatAreaWidth(des
     info(count + ": skipping this as screen available width is less than necessary");
     executeSoon(function() {
       cb(false);
     });
     return;
   }
   function resize_handler(event) {
     // we did resize - but did we get far enough to be able to continue?
-    let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
+    let newSize = getChatBar().getBoundingClientRect().width;
     let sizedOk = widthDeltaCloseEnough(newSize - desired);
     if (!sizedOk)
       return;
     window.removeEventListener("resize", resize_handler, true);
     info(count + ": resized window width is " + newSize);
     executeSoon(function() {
       cb(sizedOk);
     });
@@ -558,20 +552,27 @@ function resizeAndCheckWidths(first, sec
       let m = new MutationObserver(collapsedObserver);
       m.observe(first, {attributes: true });
       m.observe(second, {attributes: true });
       m.observe(third, {attributes: true });
     }
   }, count);
 }
 
+function getChatBar() {
+  return document.getElementById("pinnedchats");
+}
+
 function getPopupWidth() {
-  let popup = window.SocialChatBar.chatbar.menupopup;
+  let chatbar = getChatBar();
+  let popup = chatbar.menupopup;
   ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
   let cs = document.defaultView.getComputedStyle(popup.parentNode);
   let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
   return popup.parentNode.getBoundingClientRect().width + margins;
 }
 
 function closeAllChats() {
-  let chatbar = window.SocialChatBar.chatbar;
-  chatbar.removeAll();
+  let chatbar = getChatBar();
+  while (chatbar.selectedChat) {
+    chatbar.selectedChat.close();
+  }
 }
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -8,16 +8,17 @@ MOCHITEST_MANIFESTS += [
     'content/test/general/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'content/test/chrome/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
+    'content/test/chat/browser.ini',
     'content/test/general/browser.ini',
     'content/test/newtab/browser.ini',
     'content/test/plugins/browser.ini',
     'content/test/social/browser.ini',
 ]
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR
--- a/browser/devtools/webaudioeditor/test/browser.ini
+++ b/browser/devtools/webaudioeditor/test/browser.ini
@@ -11,15 +11,15 @@ support-files =
 [browser_audionode-actor-get-type.js]
 [browser_audionode-actor-get-params.js]
 [browser_audionode-actor-get-param-flags.js]
 [browser_audionode-actor-is-source.js]
 [browser_webaudio-actor-simple.js]
 
 [browser_wa_first-run.js]
 
-[browser_wa_graph_mouseover.js]
+[browser_wa_graph_click.js]
 [browser_wa_graph_render_01.js]
 [browser_wa_graph_render_02.js]
 
 [browser_wa_params_view_edit.js]
 [browser_wa_params_view_events.js]
 [browser_wa_params_view_mouseover.js]
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
@@ -27,20 +27,16 @@ function spawnTest () {
   ise(freq, 220, "AudioNode:setParam correctly sets a `number` AudioParam");
   is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
 
   resSuccess = yield oscNode.setParam("type", "square");
   let type = yield oscNode.getParam("type");
   ise(type, "square", "AudioNode:setParam correctly sets a `string` non-AudioParam");
   is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
 
-  resSuccess = yield oscNode.setParam("type", "\"triangle\"");
-  type = yield oscNode.getParam("type");
-  ise(type, "triangle", "AudioNode:setParam correctly removes quotes in `string` non-AudioParam");
-
   try {
     yield oscNode.setParam("frequency", "hello");
     ok(false, "setParam with invalid types should throw");
   } catch (e) {
     ok(/is not a finite floating-point/.test(e.message), "AudioNode:setParam returns error with correct message when attempting an invalid assignment");
     is(e.type, "TypeError", "AudioNode:setParam returns error with correct type when attempting an invalid assignment");
     freq = yield oscNode.getParam("frequency");
     ise(freq, 220, "AudioNode:setParam does not modify value when an error occurs");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph_click.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the ParamsList view opens the correct node when clicking
+ * on the node in the GraphView
+ */
+
+function spawnTest() {
+  let [target, debuggee, panel] = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
+  let panelWin = panel.panelWin;
+  let { gFront, $, $$, EVENTS, WebAudioParamView } = panelWin;
+  let gVars = WebAudioParamView._paramsView;
+
+  let started = once(gFront, "start-context");
+
+  reload(target);
+
+  let [_, nodes, _] = yield Promise.all([
+    getN(gFront, "create-node", 8),
+    getNSpread(panel.panelWin, EVENTS.UI_ADD_NODE_LIST, 8),
+    waitForGraphRendered(panel.panelWin, 8, 8)
+  ]);
+
+  let nodeIds = nodes.map(([e, id]) => id);
+
+  for (let i = 0; i < 8; i++) {
+    ok(!isExpanded(gVars, i), "no views expanded on default");
+  }
+
+  click(panel.panelWin, findGraphNode(panelWin, nodeIds[1]));
+  ok(isExpanded(gVars, 1), "params view expanded on click");
+
+  var allClosed = true;
+  for (let i = 0; i < 8; i++) {
+    if (i === 1) continue;
+    if (isExpanded(gVars, i))
+      allClosed = false;
+  }
+  ok(allClosed, "all other param views are still minimized");
+
+  click(panel.panelWin, findGraphNode(panelWin, nodeIds[2]));
+  ok(isExpanded(gVars, 2), "second params view expanded on click");
+
+  click(panel.panelWin, $("rect", findGraphNode(panelWin, nodeIds[3])));
+  ok(isExpanded(gVars, 3), "param view opens when clicking `<rect>`");
+
+  click(panel.panelWin, $("tspan", findGraphNode(panelWin, nodeIds[4])));
+  ok(isExpanded(gVars, 4), "param view opens when clicking `<tspan>`");
+
+  yield teardown(panel);
+  finish();
+}
+
+function isExpanded (view, index) {
+  let scope = view.getScopeAtIndex(index);
+  return scope.expanded;
+}
deleted file mode 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph_mouseover.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the shader editor shows the appropriate UI when opened.
- */
-
-function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
-  let { gFront, $, $$, EVENTS, WebAudioParamView } = panel.panelWin;
-  let gVars = WebAudioParamView._paramsView;
-
-  let started = once(gFront, "start-context");
-
-  reload(target);
-
-  yield Promise.all([
-    getN(gFront, "create-node", 8),
-    getNSpread(panel.panelWin, EVENTS.UI_ADD_NODE_LIST, 8),
-    waitForGraphRendered(panel.panelWin, 8, 8)
-  ]);
-
-  let $items = $$(".variables-view-scope");
-  let $graphNodes = $$(".nodes > g");
-
-  for (let $item of $items) {
-    mouseOver(panel.panelWin, $(".devtools-toolbar", $item));
-    // Get actorID from id of variable scope
-    let id = $item.id.match(/\(([^\)]*)\)/)[1];
-
-    // Go over all graph nodes and check only the selected one is highlighted
-    for (let $node of $graphNodes) {
-      let shouldBeSelected = id === $node.getAttribute("data-id");
-      ok($node.classList.contains("selected") === shouldBeSelected,
-        "graph node correctly " + (shouldBeSelected ? "" : "not ") + "highlighted on param view mouseover");
-    }
-  }
-
-  yield teardown(panel);
-  finish();
-}
-
--- a/browser/devtools/webaudioeditor/test/browser_wa_params_view_edit.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_params_view_edit.js
@@ -14,29 +14,37 @@ function spawnTest() {
 
   reload(target);
 
   let [[dest, osc, gain], [[_, destID], [_, oscID], [_, gainID]]] = yield Promise.all([
     get3(gFront, "create-node"),
     get3Spread(panel.panelWin, EVENTS.UI_ADD_NODE_LIST)
   ]);
 
+  let setAndCheck = setAndCheckVariable(panel.panelWin, gVars);
 
   checkVariableView(gVars, 1, {
     "type": "\"sine\"",
     "frequency": 440,
     "detune": 0
-  });
+  }, "default loaded string");
 
   checkVariableView(gVars, 2, {
     "gain": 0
-  });
-
-  yield modifyVariableView(panel.panelWin, gVars, 1, "type", "square");
+  }, "default loaded number");
 
-  checkVariableView(gVars, 1, {
-    "type": "\"square\""
-  });
+  yield setAndCheck(1, "type", "\"square\"", "\"square\"", "sets string as string");
+
+  yield setAndCheck(2, "gain", 0.005, 0.005, "sets number as number");
 
   yield teardown(panel);
   finish();
 }
 
+function setAndCheckVariable (panelWin, gVars) {
+  return Task.async(function (varNum, prop, value, expected, desc) {
+    yield modifyVariableView(panelWin, gVars, varNum, prop, value);
+    var props = {};
+    props[prop] = expected;
+    checkVariableView(gVars, varNum, props, desc);
+  });
+}
+
--- a/browser/devtools/webaudioeditor/test/head.js
+++ b/browser/devtools/webaudioeditor/test/head.js
@@ -206,25 +206,25 @@ function waitForGraphRendered (front, no
     if (nodes === nodeCount && edges === edgeCount) {
       front.off(eventName, onGraphRendered);
       deferred.resolve();
     }
   });
   return deferred.promise;
 }
 
-function checkVariableView (view, index, hash) {
+function checkVariableView (view, index, hash, description = "") {
   let scope = view.getScopeAtIndex(index);
   let variables = Object.keys(hash);
   variables.forEach(variable => {
     let aVar = scope.get(variable);
     is(aVar.target.querySelector(".name").getAttribute("value"), variable,
       "Correct property name for " + variable);
     is(aVar.target.querySelector(".value").getAttribute("value"), hash[variable],
-      "Correct property value of " + hash[variable] + " for " + variable);
+      "Correct property value of " + hash[variable] + " for " + variable + " " + description);
   });
 }
 
 function modifyVariableView (win, view, index, prop, value) {
   let deferred = Promise.defer();
   let scope = view.getScopeAtIndex(index);
   let aVar = scope.get(prop);
   scope.expand();
--- a/browser/devtools/webaudioeditor/webaudioeditor-view.js
+++ b/browser/devtools/webaudioeditor/webaudioeditor-view.js
@@ -35,25 +35,27 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = 
  */
 let WebAudioGraphView = {
   /**
    * Initialization function, called when the tool is started.
    */
   initialize: function() {
     this._onGraphNodeClick = this._onGraphNodeClick.bind(this);
     this.draw = debounce(this.draw.bind(this), GRAPH_DEBOUNCE_TIMER);
+    $('#graph-target').addEventListener('click', this._onGraphNodeClick, false);
   },
 
   /**
    * Destruction function, called when the tool is closed.
    */
   destroy: function() {
     if (this._zoomBinding) {
       this._zoomBinding.on("zoom", null);
     }
+    $('#graph-target').removeEventListener('click', this._onGraphNodeClick, false);
   },
 
   /**
    * Called when a page is reloaded and waiting for a "start-context" event
    * and clears out old content
    */
   resetUI: function () {
     $("#reload-notice").hidden = true;
@@ -135,17 +137,17 @@ let WebAudioGraphView = {
     let renderer = new dagreD3.Renderer();
 
     // Post-render manipulation of the nodes
     let oldDrawNodes = renderer.drawNodes();
     renderer.drawNodes(function(graph, root) {
       let svgNodes = oldDrawNodes(graph, root);
       svgNodes.attr("class", (n) => {
         let node = graph.node(n);
-        return "type-" + node.label;
+        return "audionode type-" + node.label;
       });
       svgNodes.attr("data-id", (n) => {
         let node = graph.node(n);
         return node.id;
       });
       return svgNodes;
     });
 
@@ -209,22 +211,26 @@ let WebAudioGraphView = {
 
   /**
    * Event handlers
    */
 
   /**
    * Fired when a node in the svg graph is clicked. Used to handle triggering the AudioNodePane.
    *
-   * @param Object AudioNodeView
-   *        The object stored in `AudioNodes` which contains render information, but most importantly,
-   *        the actorID under `id` property.
+   * @param Event e
+   *        Click event.
    */
-  _onGraphNodeClick: function (node) {
-    WebAudioParamView.focusNode(node.id);
+  _onGraphNodeClick: function (e) {
+    let node = findGraphNodeParent(e.target);
+    // If node not found (clicking outside of an audio node in the graph),
+    // then ignore this event
+    if (!node)
+      return;
+    WebAudioParamView.focusNode(node.getAttribute('data-id'));
   }
 };
 
 let WebAudioParamView = {
   _paramsView: null,
 
   /**
    * Initialization function called when the tool starts up.
@@ -264,22 +270,31 @@ let WebAudioParamView = {
 
   /**
    * Executed when an audio param is changed in the UI.
    */
   _onEval: Task.async(function* (variable, value) {
     let ownerScope = variable.ownerView;
     let node = getViewNodeById(ownerScope.actorID);
     let propName = variable.name;
-    let errorMessage = yield node.actor.setParam(propName, value);
+    let error;
+
+    // Cast value to proper type
+    try {
+      value = JSON.parse(value);
+      error = yield node.actor.setParam(propName, value);
+    }
+    catch (e) {
+      error = e;
+    }
 
     // TODO figure out how to handle and display set param errors
     // and enable `test/brorwser_wa_params_view_edit_error.js`
     // Bug 994258
-    if (!errorMessage) {
+    if (!error) {
       ownerScope.get(propName).setGrip(value);
       window.emit(EVENTS.UI_SET_PARAM, node.id, propName, value);
     } else {
       window.emit(EVENTS.UI_SET_PARAM_ERROR, node.id, propName, value);
     }
   }),
 
   /**
@@ -356,8 +371,24 @@ let WebAudioParamView = {
   /**
    * Called when `DESTROY_NODE` is fired to remove the node from params view.
    * TODO bug 994263, dependent on node GC events
    */
   removeNode: Task.async(function* (viewNode) {
 
   })
 };
+
+/**
+ * Takes an element in an SVG graph and iterates over
+ * ancestors until it finds the graph node container. If not found,
+ * returns null.
+ */
+
+function findGraphNodeParent (el) {
+  while (!el.classList.contains("nodes")) {
+    if (el.classList.contains("audionode"))
+      return el;
+    else
+      el = el.parentNode;
+  }
+  return null;
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_output_04.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_04.js
@@ -45,17 +45,17 @@ let inputTests = [
     inspectable: true,
     variablesViewLabel: "TypeError",
   },
 
   // 4
   {
     input: "testDOMException()",
     output: 'DOMException [SyntaxError: "An invalid or illegal string was specified"',
-    printOutput: '[Exception... "An invalid or illegal string was specified"',
+    printOutput: '[object XrayWrapper [object DOMException]]"',
     inspectable: true,
     variablesViewLabel: "SyntaxError",
   },
 
   // 5
   {
     input: "testCSSStyleDeclaration()",
     output: 'CSS2Properties { color: "green", font-size: "2em" }',
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -1160,17 +1160,21 @@ Experiments.Experiments.prototype = {
 
     gPrefs.set(PREF_ACTIVE_EXPERIMENT, activeExperiment != null);
 
     if (activeChanged) {
       Services.obs.notifyObservers(null, EXPERIMENTS_CHANGED_TOPIC, null);
     }
 
     if ("@mozilla.org/toolkit/crash-reporter;1" in Cc && activeExperiment) {
-      gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
+      try {
+        gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
+      } catch (e) {
+        // It's ok if crash reporting is disabled.
+      }
     }
   },
 
   /*
    * Schedule the soonest re-check of experiment applicability that is needed.
    */
   _scheduleNextRun: function () {
     this._checkForShutdown();
new file mode 100644
--- /dev/null
+++ b/browser/modules/Chat.jsm
@@ -0,0 +1,191 @@
+/* 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/. */
+"use strict";
+
+// A module for working with chat windows.
+
+this.EXPORTED_SYMBOLS = ["Chat"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+// A couple of internal helper function.
+function isWindowChromeless(win) {
+  // XXX - stolen from browser-social.js, but there's no obvious place to
+  // put this so it can be shared.
+
+  // Is this a popup window that doesn't want chrome shown?
+  let docElem = win.document.documentElement;
+  // extrachrome is not restored during session restore, so we need
+  // to check for the toolbar as well.
+  let chromeless = docElem.getAttribute("chromehidden").contains("extrachrome") ||
+                   docElem.getAttribute('chromehidden').contains("toolbar");
+  return chromeless;
+}
+
+function isWindowGoodForChats(win) {
+  return !win.closed &&
+         !!win.document.getElementById("pinnedchats") &&
+         !isWindowChromeless(win) &&
+         !PrivateBrowsingUtils.isWindowPrivate(win);
+}
+
+function getChromeWindow(contentWin) {
+  return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIWebNavigation)
+                   .QueryInterface(Ci.nsIDocShellTreeItem)
+                   .rootTreeItem
+                   .QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindow);
+}
+
+/*
+ * The exported Chat object
+ */
+
+let Chat = {
+  /**
+   * Open a new chatbox.
+   *
+   * @param contentWindow [optional]
+   *        The content window that requested this chat.  May be null.
+   * @param origin
+   *        The origin for the chat.  This is primarily used as an identifier
+   *        to help identify all chats from the same provider.
+   * @param title
+   *        The title to be used if a new chat window is created.
+   * @param url
+   *        The URL for the that.  Should be under the origin.  If an existing
+   *        chatbox exists with the same URL, it will be reused and returned.
+   * @param mode [optional]
+   *        May be undefined or 'minimized'
+   * @param focus [optional]
+   *        Indicates if the chatbox should be focused.  If undefined the chat
+   *        will be focused if the window is currently handling user input (ie,
+   *        if the chat is being opened as a direct result of user input)
+
+   * @return A chatbox binding.  This binding has a number of promises which
+   *         can be used to determine when the chatbox is being created and
+   *         has loaded.  Will return null if no chat can be created (Which
+   *         should only happen in edge-cases)
+   */
+  open: function(contentWindow, origin, title, url, mode, focus, callback) {
+    let chromeWindow = this.findChromeWindowForChats(contentWindow);
+    if (!chromeWindow) {
+      Cu.reportError("Failed to open a chat window - no host window could be found.");
+      return null;
+    }
+
+    let chatbar = chromeWindow.document.getElementById("pinnedchats");
+    chatbar.hidden = false;
+    let chatbox = chatbar.openChat(origin, title, url, mode, callback);
+    // getAttention is ignored if the target window is already foreground, so
+    // we can call it unconditionally.
+    chromeWindow.getAttention();
+    // If focus is undefined we want automatic focus handling, and only focus
+    // if a direct result of user action.
+    if (focus === undefined) {
+      let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+      focus = dwu.isHandlingUserInput;
+    }
+    if (focus) {
+      chatbar.focus();
+    }
+    return chatbox;
+  },
+
+  /**
+   * Close all chats from the specified origin.
+   *
+   * @param origin
+   *        The origin from which all chats should be closed.
+   */
+  closeAll: function(origin) {
+    // close all attached chat windows
+    let winEnum = Services.wm.getEnumerator("navigator:browser");
+    while (winEnum.hasMoreElements()) {
+      let win = winEnum.getNext();
+      let chatbar = win.document.getElementById("pinnedchats");
+      if (!chatbar)
+        continue;
+      let chats = [c for (c of chatbar.children) if (c.content.getAttribute("origin") == origin)];
+      [c.close() for (c of chats)];
+    }
+
+    // close all standalone chat windows
+    winEnum = Services.wm.getEnumerator("Social:Chat");
+    while (winEnum.hasMoreElements()) {
+      let win = winEnum.getNext();
+      if (win.closed)
+        continue;
+      let chatOrigin = win.document.getElementById("chatter").content.getAttribute("origin");
+      if (origin == chatOrigin)
+        win.close();
+    }
+  },
+
+  /**
+   * Focus the chatbar associated with a window
+   *
+   * @param window
+   */
+  focus: function(win) {
+    let chatbar = win.document.getElementById("pinnedchats");
+    if (chatbar && !chatbar.hidden) {
+      chatbar.focus();
+    }
+
+  },
+
+  // This is exported as socialchat.xml needs to find a window when a chat
+  // is re-docked.
+  findChromeWindowForChats: function(preferredWindow) {
+    if (preferredWindow) {
+      preferredWindow = getChromeWindow(preferredWindow);
+      if (isWindowGoodForChats(preferredWindow)) {
+        return preferredWindow;
+      }
+    }
+    // no good - we just use the "most recent" browser window which can host
+    // chats (we used to try and "group" all chats in the same browser window,
+    // but that didn't work out so well - see bug 835111
+
+    // Try first the most recent window as getMostRecentWindow works
+    // even on platforms where getZOrderDOMWindowEnumerator is broken
+    // (ie. Linux).  This will handle most cases, but won't work if the
+    // foreground window is a popup.
+
+    let mostRecent = Services.wm.getMostRecentWindow("navigator:browser");
+    if (isWindowGoodForChats(mostRecent))
+      return mostRecent;
+
+    let topMost, enumerator;
+    // *sigh* - getZOrderDOMWindowEnumerator is broken except on Mac and
+    // Windows.  We use BROKEN_WM_Z_ORDER as that is what some other code uses
+    // and a few bugs recommend searching mxr for this symbol to identify the
+    // workarounds - we want this code to be hit in such searches.
+    let os = Services.appinfo.OS;
+    const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
+    if (BROKEN_WM_Z_ORDER) {
+      // this is oldest to newest and no way to change the order.
+      enumerator = Services.wm.getEnumerator("navigator:browser");
+    } else {
+      // here we explicitly ask for bottom-to-top so we can use the same logic
+      // where BROKEN_WM_Z_ORDER is true.
+      enumerator = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", false);
+    }
+    while (enumerator.hasMoreElements()) {
+      let win = enumerator.getNext();
+      if (!win.closed && isWindowGoodForChats(win))
+        topMost = win;
+    }
+    return topMost;
+  },
+}
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test']
 
 EXTRA_JS_MODULES += [
     'BrowserNewTabPreloader.jsm',
     'BrowserUITelemetry.jsm',
+    'Chat.jsm',
     'ContentClick.jsm',
     'ContentLinkHandler.jsm',
     'ContentSearch.jsm',
     'CustomizationTabPreloader.jsm',
     'Feeds.jsm',
     'NetworkPrioritizer.jsm',
     'offlineAppCache.jsm',
     'RemotePrompt.jsm',
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -39,18 +39,19 @@ if test -z "$bucket"; then
 else
     mk_add_options "export SCCACHE_BUCKET=$bucket"
     case "$master" in
     *use1.mozilla.com*|*usw2.mozilla.com*)
         mk_add_options "export SCCACHE_NAMESERVER=169.254.169.253"
         ;;
     esac
     ac_add_options "--with-compiler-wrapper=python2.7 $topsrcdir/sccache/sccache.py"
-    mk_add_options MOZ_PREFLIGHT+=build/sccache.mk
-    mk_add_options MOZ_POSTFLIGHT+=build/sccache.mk
+    mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk
+    mk_add_options MOZ_POSTFLIGHT_ALL+=build/sccache.mk
+    UPLOAD_EXTRA_FILES="sccache.log.gz"
     case "$platform" in
     win*)
         # sccache supports a special flag to create depfiles.
         export _DEPEND_CFLAGS='-deps$(MDDEPDIR)/$(@F).pp'
         # Windows builds have a default wrapper that needs to be overridden
         mk_add_options "export CC_WRAPPER="
         mk_add_options "export CXX_WRAPPER="
         # For now, sccache doesn't support separate PDBs so force debug info to be
--- a/build/sccache.mk
+++ b/build/sccache.mk
@@ -1,7 +1,18 @@
-preflight:
+# 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/.
+
+ifdef OBJDIR
+BASE_DIR = $(OBJDIR)
+else
+# OSX Universal builds only do upload in the first MOZ_BUILD_PROJECTS
+BASE_DIR = $(MOZ_OBJDIR)/$(firstword $(MOZ_BUILD_PROJECTS))
+endif
+
+preflight_all:
 	# Terminate any sccache server that might still be around
 	-python2.7 $(TOPSRCDIR)/sccache/sccache.py > /dev/null 2>&1
 
-postflight:
+postflight_all:
 	# Terminate sccache server. This prints sccache stats.
-	-python2.7 $(TOPSRCDIR)/sccache/sccache.py
+	-python2.7 $(TOPSRCDIR)/sccache/sccache.py 2>&1 | gzip > $(BASE_DIR)/dist/sccache.log.gz
--- a/caps/idl/nsIScriptSecurityManager.idl
+++ b/caps/idl/nsIScriptSecurityManager.idl
@@ -6,17 +6,17 @@
 #include "nsISupports.idl"
 #include "nsIPrincipal.idl"
 #include "nsIXPCSecurityManager.idl"
 interface nsIURI;
 interface nsIChannel;
 interface nsIDocShell;
 interface nsIDomainPolicy;
 
-[scriptable, uuid(4c087cc3-e0cc-4ec3-88df-8d68f3023b45)]
+[scriptable, uuid(2565769a-eaec-47a1-a076-605f5294d286)]
 interface nsIScriptSecurityManager : nsIXPCSecurityManager
 {
     /**
      * Check that the script currently running in context "cx" can load "uri".
      *
      * Will return error code NS_ERROR_DOM_BAD_URI if the load request
      * should be denied.
      *
@@ -94,22 +94,16 @@ interface nsIScriptSecurityManager : nsI
                                       in unsigned long flags);
 
     /**
      * Return true if scripts may be executed in the scope of the given global.
      */
     [noscript,notxpcom] boolean scriptAllowed(in JSObjectPtr aGlobal);
 
     ///////////////// Principals ///////////////////////
-    /**
-     * Return the principal of the innermost frame of the currently
-     * executing script. Will return null if there is no script
-     * currently executing.
-     */
-    [noscript] nsIPrincipal getSubjectPrincipal();
 
     /**
      * Return the all-powerful system principal.
      */
     nsIPrincipal getSystemPrincipal();
 
     /**
      * Return a principal that has the same origin as aURI.
@@ -146,22 +140,16 @@ interface nsIScriptSecurityManager : nsI
     /**
      * Legacy name for getNoAppCodebasePrincipal.
      *
      * @deprecated use getNoAppCodebasePrincipal instead.
      */
     [deprecated] nsIPrincipal getCodebasePrincipal(in nsIURI uri);
 
     /**
-     * Returns true if the principal of the currently running script is the
-     * system principal, false otherwise.
-     */
-    [noscript] boolean subjectPrincipalIsSystem();
-
-    /**
      * Returns OK if aJSContext and target have the same "origin"
      * (scheme, host, and port).
      */
     [noscript] void checkSameOrigin(in JSContextPtr aJSContext,
                                     in nsIURI aTargetURI);
 
     /**
      * Returns OK if aSourceURI and target have the same "origin"
@@ -188,24 +176,16 @@ interface nsIScriptSecurityManager : nsI
 %{C++
     bool IsSystemPrincipal(nsIPrincipal* aPrincipal) {
       bool isSystem = false;
       IsSystemPrincipal(aPrincipal, &isSystem);
       return isSystem;
     }
 %}
 
-    /**
-     * Same as getSubjectPrincipal(), only faster. cx must *never* be
-     * passed null, and it must be the context on the top of the
-     * context stack. Does *not* reference count the returned
-     * principal.
-     */
-    [noscript,notxpcom] nsIPrincipal getCxSubjectPrincipal(in JSContextPtr cx);
-
     const unsigned long NO_APP_ID = 0;
     const unsigned long UNKNOWN_APP_ID = 4294967295; // UINT32_MAX
     const unsigned long SAFEBROWSING_APP_ID = 4294967294; // UINT32_MAX - 1
 
     /**
      * Returns the jar prefix for the app.
      * appId can be NO_APP_ID or a valid app id. appId should not be
      * UNKNOWN_APP_ID.
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -35,28 +35,31 @@ class ClassInfoData;
 { 0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 }}
 
 class nsScriptSecurityManager : public nsIScriptSecurityManager,
                                 public nsIChannelEventSink,
                                 public nsIObserver
 {
 public:
     static void Shutdown();
-    
+
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_SCRIPTSECURITYMANAGER_CID)
-        
+
     NS_DECL_ISUPPORTS
     NS_DECL_NSISCRIPTSECURITYMANAGER
     NS_DECL_NSIXPCSECURITYMANAGER
     NS_DECL_NSICHANNELEVENTSINK
     NS_DECL_NSIOBSERVER
 
     static nsScriptSecurityManager*
     GetScriptSecurityManager();
 
+    // Invoked exactly once, by XPConnect.
+    static void InitStatics();
+
     static nsSystemPrincipal*
     SystemPrincipalSingletonConstructor();
 
     JSContext* GetCurrentJSContext();
 
     JSContext* GetSafeJSContext();
 
     /**
@@ -113,36 +116,25 @@ private:
 
     static bool
     JSPrincipalsSubsume(JSPrincipals *first, JSPrincipals *second);
 
     // Returns null if a principal cannot be found; generally callers
     // should error out at that point.
     static nsIPrincipal* doGetObjectPrincipal(JSObject* obj);
 
-    // Returns null if a principal cannot be found.  Note that rv can be NS_OK
-    // when this happens -- this means that there was no JS running.
-    nsIPrincipal*
-    doGetSubjectPrincipal(nsresult* rv);
-
     nsresult
     GetCodebasePrincipalInternal(nsIURI* aURI, uint32_t aAppId,
                                  bool aInMozBrowser,
                                  nsIPrincipal** result);
 
     nsresult
     CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId, bool aInMozBrowser,
                             nsIPrincipal** result);
 
-    // Returns null if a principal cannot be found.  Note that rv can be NS_OK
-    // when this happens -- this means that there was no script for the
-    // context.  Callers MUST pass in a non-null rv here.
-    nsIPrincipal*
-    GetSubjectPrincipal(JSContext* cx, nsresult* rv);
-
     nsresult
     Init();
 
     nsresult
     InitPrefs();
 
     inline void
     ScriptSecurityPrefChanged();
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -76,21 +76,17 @@ static NS_DEFINE_CID(kZipReaderCID, NS_Z
 nsIIOService    *nsScriptSecurityManager::sIOService = nullptr;
 nsIStringBundle *nsScriptSecurityManager::sStrBundle = nullptr;
 JSRuntime       *nsScriptSecurityManager::sRuntime   = 0;
 bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
 
 bool
 nsScriptSecurityManager::SubjectIsPrivileged()
 {
-    JSContext *cx = GetCurrentJSContext();
-    if (cx && xpc::IsUniversalXPConnectEnabled(cx))
-        return true;
-    bool isSystem = false;
-    return NS_SUCCEEDED(SubjectPrincipalIsSystem(&isSystem)) && isSystem;
+    return nsContentUtils::IsCallerChrome();
 }
 
 ///////////////////////////
 // Convenience Functions //
 ///////////////////////////
 // Result of this function should not be freed.
 static inline const char16_t *
 IDToString(JSContext *cx, jsid id_)
@@ -313,30 +309,16 @@ nsScriptSecurityManager::GetChannelPrinc
 NS_IMETHODIMP
 nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
                                            bool* aIsSystem)
 {
     *aIsSystem = (aPrincipal == mSystemPrincipal);
     return NS_OK;
 }
 
-NS_IMETHODIMP_(nsIPrincipal *)
-nsScriptSecurityManager::GetCxSubjectPrincipal(JSContext *cx)
-{
-    NS_ASSERTION(cx == GetCurrentJSContext(),
-                 "Uh, cx is not the current JS context!");
-
-    nsresult rv = NS_ERROR_FAILURE;
-    nsIPrincipal *principal = GetSubjectPrincipal(cx, &rv);
-    if (NS_FAILED(rv))
-        return nullptr;
-
-    return principal;
-}
-
 /////////////////////////////
 // nsScriptSecurityManager //
 /////////////////////////////
 
 ////////////////////////////////////
 // Methods implementing ISupports //
 ////////////////////////////////////
 NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
@@ -349,36 +331,20 @@ NS_IMPL_ISUPPORTS(nsScriptSecurityManage
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
 
 bool
 nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
 {
-    // Get the security manager
-    nsScriptSecurityManager *ssm =
-        nsScriptSecurityManager::GetScriptSecurityManager();
-
-    NS_ASSERTION(ssm, "Failed to get security manager service");
-    if (!ssm)
-        return false;
-
-    nsresult rv;
-    nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
-
-    NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
-    if (NS_FAILED(rv))
-        return false; // Not just absence of principal, but failure.
-
-    if (!subjectPrincipal)
-        return true;
-
+    MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
+    nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::GetSubjectPrincipal();
     nsCOMPtr<nsIContentSecurityPolicy> csp;
-    rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
+    nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
     NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
 
     // don't do anything unless there's a CSP
     if (!csp)
         return true;
 
     bool evalOK = true;
     bool reportViolation = false;
@@ -419,37 +385,20 @@ nsScriptSecurityManager::JSPrincipalsSub
 {
     return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second));
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CheckSameOrigin(JSContext* cx,
                                          nsIURI* aTargetURI)
 {
-    nsresult rv;
-
-    // Get a context if necessary
-    if (!cx)
-    {
-        cx = GetCurrentJSContext();
-        if (!cx)
-            return NS_OK; // No JS context, so allow access
-    }
+    MOZ_ASSERT_IF(cx, cx == nsContentUtils::GetCurrentJSContext());
 
     // Get a principal from the context
-    nsIPrincipal* sourcePrincipal = GetSubjectPrincipal(cx, &rv);
-    if (NS_FAILED(rv))
-        return rv;
-
-    if (!sourcePrincipal)
-    {
-        NS_WARNING("CheckSameOrigin called on script w/o principals; should this happen?");
-        return NS_OK;
-    }
-
+    nsIPrincipal* sourcePrincipal = nsContentUtils::GetSubjectPrincipal();
     if (sourcePrincipal == mSystemPrincipal)
     {
         // This is a system (chrome) script, so allow access
         return NS_OK;
     }
 
     // Get the original URI from the source principal.
     // This has the effect of ignoring any change to document.domain
@@ -515,27 +464,20 @@ nsScriptSecurityManager::AppAttributesEq
     return ((firstAppId == secondAppId) &&
             (aFirst->GetIsInBrowserElement() == aSecond->GetIsInBrowserElement()));
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
 {
     // Get principal of currently executing script.
-    nsresult rv;
-    nsIPrincipal* principal = GetSubjectPrincipal(cx, &rv);
-    if (NS_FAILED(rv))
-        return rv;
-
-    // Native code can load all URIs.
-    if (!principal)
-        return NS_OK;
-
-    rv = CheckLoadURIWithPrincipal(principal, aURI,
-                                   nsIScriptSecurityManager::STANDARD);
+    MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
+    nsIPrincipal* principal = nsContentUtils::GetSubjectPrincipal();
+    nsresult rv = CheckLoadURIWithPrincipal(principal, aURI,
+                                            nsIScriptSecurityManager::STANDARD);
     if (NS_SUCCEEDED(rv)) {
         // OK to load
         return NS_OK;
     }
 
     // See if we're attempting to load a file: URI. If so, let a
     // UniversalXPConnect capability trump the above check.
     bool isFile = false;
@@ -934,72 +876,25 @@ nsScriptSecurityManager::ScriptAllowed(J
     AutoJSContext cx;
     JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
 
     // Check the bits on the compartment private.
     return xpc::Scriptability::Get(aGlobal).Allowed();
 }
 
 ///////////////// Principals ///////////////////////
-NS_IMETHODIMP
-nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **aSubjectPrincipal)
-{
-    nsresult rv;
-    *aSubjectPrincipal = doGetSubjectPrincipal(&rv);
-    if (NS_SUCCEEDED(rv))
-        NS_IF_ADDREF(*aSubjectPrincipal);
-    return rv;
-}
-
-nsIPrincipal*
-nsScriptSecurityManager::doGetSubjectPrincipal(nsresult* rv)
-{
-    NS_PRECONDITION(rv, "Null out param");
-    JSContext *cx = GetCurrentJSContext();
-    if (!cx)
-    {
-        *rv = NS_OK;
-        return nullptr;
-    }
-    return GetSubjectPrincipal(cx, rv);
-}
 
 NS_IMETHODIMP
 nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
 {
     NS_ADDREF(*result = mSystemPrincipal);
 
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsScriptSecurityManager::SubjectPrincipalIsSystem(bool* aIsSystem)
-{
-    NS_ENSURE_ARG_POINTER(aIsSystem);
-    *aIsSystem = false;
-
-    if (!mSystemPrincipal)
-        return NS_OK;
-
-    nsCOMPtr<nsIPrincipal> subject;
-    nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject));
-    if (NS_FAILED(rv))
-        return rv;
-
-    if(!subject)
-    {
-        // No subject principal means no JS is running;
-        // this is the equivalent of system principal code
-        *aIsSystem = true;
-        return NS_OK;
-    }
-
-    return mSystemPrincipal->Equals(subject, aIsSystem);
-}
-
 nsresult
 nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId,
                                                  bool aInMozBrowser,
                                                  nsIPrincipal **result)
 {
     // I _think_ it's safe to not create null principals here based on aURI.
     // At least all the callers would do the right thing in those cases, as far
     // as I can tell.  --bz
@@ -1098,31 +993,16 @@ nsScriptSecurityManager::GetCodebasePrin
     rv = CreateCodebasePrincipal(aURI, aAppId, aInMozBrowser,
                                  getter_AddRefs(principal));
     NS_ENSURE_SUCCESS(rv, rv);
     NS_IF_ADDREF(*result = principal);
 
     return NS_OK;
 }
 
-nsIPrincipal*
-nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
-                                             nsresult* rv)
-{
-    *rv = NS_OK;
-    JSCompartment *compartment = js::GetContextCompartment(cx);
-
-    // The context should always be in a compartment, either one it has entered
-    // or the one associated with its global.
-    MOZ_ASSERT(!!compartment);
-
-    JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
-    return nsJSPrincipals::get(principals);
-}
-
 // static
 nsIPrincipal*
 nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
 {
     JSCompartment *compartment = js::GetObjectCompartment(aObj);
     JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
     return nsJSPrincipals::get(principals);
 }
@@ -1153,42 +1033,36 @@ nsScriptSecurityManager::CanCreateWrappe
     if (SubjectIsPrivileged())
     {
         return NS_OK;
     }
 
     //-- Access denied, report an error
     NS_ConvertUTF8toUTF16 strName("CreateWrapperDenied");
     nsAutoCString origin;
-    nsresult rv2;
-    nsIPrincipal* subjectPrincipal = doGetSubjectPrincipal(&rv2);
-    if (NS_SUCCEEDED(rv2) && subjectPrincipal) {
-        GetPrincipalDomainOrigin(subjectPrincipal, origin);
-    }
+    nsIPrincipal* subjectPrincipal = nsContentUtils::GetSubjectPrincipal();
+    GetPrincipalDomainOrigin(subjectPrincipal, origin);
     NS_ConvertUTF8toUTF16 originUnicode(origin);
     NS_ConvertUTF8toUTF16 classInfoName(objClassInfo.GetName());
     const char16_t* formatStrings[] = {
         classInfoName.get(),
         originUnicode.get()
     };
     uint32_t length = ArrayLength(formatStrings);
     if (originUnicode.IsEmpty()) {
         --length;
     } else {
         strName.AppendLiteral("ForOrigin");
     }
     nsXPIDLString errorMsg;
-    // We need to keep our existing failure rv and not override it
-    // with a likely success code from the following string bundle
-    // call in order to throw the correct security exception later.
-    rv2 = sStrBundle->FormatStringFromName(strName.get(),
-                                           formatStrings,
-                                           length,
-                                           getter_Copies(errorMsg));
-    NS_ENSURE_SUCCESS(rv2, rv2);
+    nsresult rv = sStrBundle->FormatStringFromName(strName.get(),
+                                                   formatStrings,
+                                                   length,
+                                                   getter_Copies(errorMsg));
+    NS_ENSURE_SUCCESS(rv, rv);
 
     SetPendingException(cx, errorMsg.get());
     return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
                                            const nsCID &aCID)
@@ -1363,37 +1237,30 @@ nsScriptSecurityManager::Shutdown()
 
     NS_IF_RELEASE(sIOService);
     NS_IF_RELEASE(sStrBundle);
 }
 
 nsScriptSecurityManager *
 nsScriptSecurityManager::GetScriptSecurityManager()
 {
-    if (!gScriptSecMan && nsXPConnect::XPConnect())
-    {
-        nsRefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
+    return gScriptSecMan;
+}
 
-        nsresult rv;
-        rv = ssManager->Init();
-        if (NS_FAILED(rv)) {
-            return nullptr;
-        }
- 
-        rv = nsXPConnect::XPConnect()->
-            SetDefaultSecurityManager(ssManager);
-        if (NS_FAILED(rv)) {
-            NS_WARNING("Failed to install xpconnect security manager!");
-            return nullptr;
-        }
+/* static */ void
+nsScriptSecurityManager::InitStatics()
+{
+    nsRefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
+    nsresult rv = ssManager->Init();
+    if (NS_FAILED(rv)) {
+        MOZ_CRASH();
+    }
 
-        ClearOnShutdown(&gScriptSecMan);
-        gScriptSecMan = ssManager;
-    }
-    return gScriptSecMan;
+    ClearOnShutdown(&gScriptSecMan);
+    gScriptSecMan = ssManager;
 }
 
 // Currently this nsGenericFactory constructor is used only from FastLoad
 // (XPCOM object deserialization) code, when "creating" the system principal
 // singleton.
 nsSystemPrincipal *
 nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
 {
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1300,22 +1300,16 @@ endif
 endif
 
 ifneq ($(DIST_FILES),)
 DIST_FILES_PATH := $(FINAL_TARGET)
 DIST_FILES_FLAGS := $(XULAPP_DEFINES)
 PP_TARGETS += DIST_FILES
 endif
 
-ifneq ($(DIST_FILES_NO_PP),)
-_DIST_FILES := $(DIST_FILES_NO_PP)
-_DIST_DEST := $(FINAL_TARGET)
-INSTALL_TARGETS += _DIST
-endif
-
 ifneq ($(XPI_PKGNAME),)
 tools realchrome::
 ifdef STRIP_XPI
 ifndef MOZ_DEBUG
 	@echo 'Stripping $(XPI_PKGNAME) package directory...'
 	@echo $(FINAL_TARGET)
 	@cd $(FINAL_TARGET) && find . ! -type d \
 			! -name '*.js' \
--- a/config/system-headers
+++ b/config/system-headers
@@ -159,54 +159,79 @@ afxpriv.h
 afxtempl.h
 afxwin.h
 algorithm
 Aliases.h
 all.h
 alloca.h
 alloc.h
 alsa/asoundlib.h
+#ifdef ANDROID
+android/ashmem.h
 android/log.h
+android/looper.h
+android/native_window.h
+android_audio/AudioSystem.h
+#endif
 ansi_parms.h
 a.out.h
 app/Cursor.h
 Appearance.h
 AppFileInfo.h
 AppKit.h
 AppleEvents.h
 Application.h
 app/Message.h
 app/MessageRunner.h
 arpa/inet.h
 arpa/nameser.h
+asm/page.h
 asm/sigcontext.h
 asm/signal.h
 ASRegistry.h
 assert.h
 atk/atk.h
 atlcom.h
 atlconv.h
 atlctl.cpp
 atlctl.h
 ATLCTL.H
 atlhost.h
 atlimpl.cpp
 atlwin.cpp
 ATSTypes.h
 ATSUnicode.h
+#ifdef ANDROID
+AudioParameter.h
+AudioSystem.h
+AudioTrack.h
+avc_utils.h
+#endif
 Balloons.h
 base/pblock.h
 base/PCR_Base.h
 base/session.h
 basetyps.h
 be/app/Application.h
 Beep.h
 be/kernel/image.h
 be/kernel/OS.h
 bfd.h
+#ifdef ANDROID
+binder/Binder.h
+binder/BinderService.h
+binder/IBinder.h
+binder/IInterface.h
+binder/IMemory.h
+binder/IPCThreadState.h
+binder/IPermissionController.h
+binder/IServiceManager.h
+binder/Parcel.h
+binder/ProcessState.h
+#endif
 Bitmap.h
 bitset
 blapi.h
 bsd/libc.h
 bsd/syscall.h
 bstring.h
 builtin.h
 Button.h
@@ -278,16 +303,20 @@ direct/trace.h
 direct/modules.h
 direct/log.h
 direct/system.h
 direct/list.h
 dfb_types.h
 directfb_strings.h
 directfb_keyboard.h
 callconv.h
+#ifdef ANDROID
+camera/Camera.h
+camera/CameraParameters.h
+#endif
 Carbon/Carbon.h
 CarbonEvents.h
 Carbon.h
 cassert
 c_asm.h
 cctype
 cderr.h
 cerrno
@@ -303,16 +332,19 @@ CFString.h
 CFURL.h
 CGAffineTransform.h
 CheckBox.h
 climits
 Clipboard.h
 cmplrs/stsupport.h
 Cocoa/Cocoa.h
 CodeFragments.h
+#ifdef ANDROID
+ColorConverter.h
+#endif
 comdef.h
 commctrl.h
 COMMCTRL.H
 commdlg.h
 compat.h
 condapi.h
 ConditionalMacros.h
 config.h
@@ -335,16 +367,25 @@ cstddef
 cstdio
 cstdlib
 cstring
 ctime
 ctype.h
 curl/curl.h
 curl/easy.h
 curses.h
+#ifdef ANDROID
+cutils/android_reboot.h
+cutils/atomic.h
+cutils/compiler.h
+cutils/log.h
+cutils/native_handle.h
+cutils/properties.h
+cutils/sockets.h
+#endif
 cxxabi.h
 DateTimeUtils.h
 dbus/dbus.h
 dbus/dbus-glib.h
 dbus/dbus-glib-lowlevel.h
 ddeml.h
 Debug.h
 deque
@@ -361,16 +402,19 @@ dl.h
 docobj.h
 dos/dosextens.h
 dos.h
 Drag.h
 DriverServices.h
 DriverSynchronization.h
 DropInPanel.h
 dvidef.h
+#ifdef ANDROID
+EffectApi.h
+#endif
 elf.h
 endian.h
 Entry.h
 errno.h
 Errors.h
 Events.h
 exdisp.h
 ExDisp.h
@@ -391,16 +435,29 @@ FinderRegistry.h
 FixMath.h
 float.h
 fnmatch.h
 Folders.h
 fontconfig/fontconfig.h
 fontconfig/fcfreetype.h
 Font.h
 Fonts.h
+#ifdef ANDROID
+foundation/ABase.h
+foundation/ABitReader.h
+foundation/ABuffer.h
+foundation/ADebug.h
+foundation/AHandler.h
+foundation/AHandlerReflector.h
+foundation/ALooper.h
+foundation/AMessage.h
+foundation/AString.h
+foundation/base64.h
+foundation/hexdump.h
+#endif
 fp.h
 fpieee.h
 frame/log.h
 frame/req.h
 freetype/freetype.h
 freetype/ftcache.h
 freetype/ftglyph.h
 freetype/ftsynth.h
@@ -453,19 +510,51 @@ gssapi_generic.h
 gssapi/gssapi_generic.h
 gssapi/gssapi.h
 gssapi.h
 gtk/gtk.h
 gtk/gtkx.h
 gtk/gtkprinter.h
 gtk/gtkprintjob.h
 gtk/gtkprintunixdialog.h
+#ifdef ANDROID
+gui/GraphicBufferAlloc.h
+gui/IConsumerListener.h
+gui/IGraphicBufferAlloc.h
+gui/IGraphicBufferProducer.h
+gui/ISurfaceComposer.h
+gui/ISurfaceComposerClient.h
+gui/ISurfaceTexture.h
+gui/Surface.h
+gui/SurfaceComposerClient.h
+gui/SurfaceTextureClient.h
+hardware/audio.h
+hardware/gralloc.h
+hardware/hardware.h
+hardware/hwcomposer.h
+hardware/lights.h
+hardware/power.h
+hardware_legacy/power.h
+hardware_legacy/uevent.h
+hardware_legacy/vibrator.h
+#endif
 HIToolbox/HIToolbox.h
 hlink.h
+#ifdef ANDROID
+HTTPBase.h
+#endif
 ia64/sys/inline.h
+#ifdef ANDROID
+IAudioFlingerClient.h
+IAudioFlinger.h
+IAudioRecord.h
+IAudioTrack.h
+IEffect.h
+IEffectClient.h
+#endif
 Icons.h
 iconv.h
 ieeefp.h
 ifaddrs.h
 image.h
 imagehlp.h
 imm.h
 initguid.h
@@ -555,16 +644,21 @@ libgnomevfs/gnome-vfs-mime.h
 libgnomevfs/gnome-vfs-mime-handlers.h
 libgnomevfs/gnome-vfs-mime-utils.h
 libgnomevfs/gnome-vfs-ops.h
 libgnomevfs/gnome-vfs-standard-callbacks.h
 lib$routines.h
 limits
 limits.h
 link.h
+#ifdef ANDROID
+linux/android_alarm.h
+linux/ashmem.h
+#endif
+linux/ioprio.h
 linux/kernel.h
 linux/limits.h
 linux/rtc.h
 linux/version.h
 list
 List.h
 Lists.h
 LListBox.h
@@ -641,16 +735,57 @@ mapidefs.h
 mapiguid.h
 mapi.h
 mapitags.h
 mapiutil.h
 mapix.h
 Math64.h
 math.h
 mbstring.h
+#ifdef ANDROID
+media/ICrypto.h
+media/IOMX.h
+media/MediaProfiles.h
+media/MediaRecorderBase.h
+media/openmax/OMX_Audio.h
+media/stagefright/AACWriter.h
+media/stagefright/AMRWriter.h
+media/stagefright/AudioSource.h
+media/stagefright/DataSource.h
+media/stagefright/foundation/ABase.h
+media/stagefright/foundation/ABitReader.h
+media/stagefright/foundation/ABuffer.h
+media/stagefright/foundation/ADebug.h
+media/stagefright/foundation/AHandler.h
+media/stagefright/foundation/AHandlerReflector.h
+media/stagefright/foundation/ALooper.h
+media/stagefright/foundation/AMessage.h
+media/stagefright/foundation/AString.h
+media/stagefright/foundation/base64.h
+media/stagefright/foundation/hexdump.h
+media/stagefright/MediaBuffer.h
+media/stagefright/MediaBufferGroup.h
+media/stagefright/MediaCodec.h
+media/stagefright/MediaDefs.h
+media/stagefright/MediaErrors.h
+media/stagefright/MediaExtractor.h
+media/stagefright/MediaSource.h
+media/stagefright/MediaWriter.h
+media/stagefright/MetaData.h
+media/stagefright/MPEG2TSWriter.h
+media/stagefright/MPEG4Writer.h
+media/stagefright/OMXClient.h
+media/stagefright/OMXCodec.h
+media/stagefright/openmax/OMX_Core.h
+media/stagefright/openmax/OMX_Index.h
+media/stagefright/openmax/OMX_IVCommon.h
+media/stagefright/openmax/OMX_Types.h
+media/stagefright/openmax/OMX_Video.h
+media/stagefright/Utils.h
+#endif
 mem.h
 memory
 memory.h
 Memory.h
 MenuBar.h
 Menu.h
 Menus.h
 Message.h
@@ -682,16 +817,20 @@ nl_types.h
 NodeInfo.h
 nsswitch.h
 objbase.h
 objidl.h
 Objsafe.h
 ojiapitests.h
 ole2.h
 oleidl.h
+#ifdef ANDROID
+OMX.h
+OMX_Component.h
+#endif
 OpenGL/OpenGL.h
 OpenTptInternet.h
 OpenTransport.h
 OS.h
 osreldate.h
 ostream
 OSUtils.h
 Packages.h
@@ -728,17 +867,20 @@ PP_DebugHeaders.cp
 PP_KeyCodes.h
 PP_Macros.h
 PP_Messages.h
 PP_Prefix.h
 PP_Resources.h
 PP_Types.h
 Printing.h
 Print/PMPrintingDialogExtensions.h
+#ifdef ANDROID
+private/android_filesystem_config.h
 private/qucomextra_p.h
+#endif
 Processes.h
 process.h
 Process.h
 proto/dos.h
 proto/exec.h
 psap.h
 Pt.h
 pthread.h
@@ -778,16 +920,17 @@ secrng.h
 security.h
 secutil.h
 semaphore.h
 servprov.h
 set
 setjmp.h
 SFNTLayoutTypes.h
 SFNTTypes.h
+sha1.h
 share.h
 shellapi.h
 shlguid.h
 shlobj.h
 sigcontext.h
 signal.h
 SimpleGameSound.h
 SIOUX.h
@@ -795,16 +938,53 @@ size_t.h
 sndio.h
 someincludefile.h
 Sound.h
 soundcard.h
 sqlite3.h
 ssdef.h
 sstream
 stack
+#ifdef ANDROID
+stagefright/AACWriter.h
+stagefright/AMRWriter.h
+stagefright/AudioSource.h
+stagefright/DataSource.h
+stagefright/foundation/ABase.h
+stagefright/foundation/ABitReader.h
+stagefright/foundation/ABuffer.h
+stagefright/foundation/ADebug.h
+stagefright/foundation/AHandler.h
+stagefright/foundation/AHandlerReflector.h
+stagefright/foundation/ALooper.h
+stagefright/foundation/AMessage.h
+stagefright/foundation/AString.h
+stagefright/foundation/base64.h
+stagefright/foundation/hexdump.h
+stagefright/MediaBuffer.h
+stagefright/MediaBufferGroup.h
+stagefright/MediaCodec.h
+stagefright/MediaDefs.h
+stagefright/MediaErrors.h
+stagefright/MediaExtractor.h
+stagefright/MediaSource.h
+stagefright/MediaWriter.h
+stagefright/MetaData.h
+stagefright/MPEG2TSWriter.h
+stagefright/MPEG4Writer.h
+stagefright/OMXCodec.h
+stagefright/OMXClient.h
+stagefright/openmax/OMX_Component.h
+stagefright/openmax/OMX_Core.h
+stagefright/openmax/OMX_Index.h
+stagefright/openmax/OMX_IVCommon.h
+stagefright/openmax/OMX_Types.h
+stagefright/openmax/OMX_Video.h
+stagefright/Utils.h
+#endif
 StandardFile.h
 starlet.h
 stat.h
 statreg.cpp
 statreg.h
 stdarg.h
 stdbool.h
 stddef.h
@@ -823,40 +1003,46 @@ StringView.h
 stropts.h
 strstrea.h
 structs.h
 stsdef.h
 SupportDefs.h
 support/String.h
 support/SupportDefs.h
 support/TLS.h
+#ifdef ANDROID
+suspend/autosuspend.h
+#endif
 svrcore.h
 symconst.h
 sym.h
 synch.h
 syncmgr.h
 sys/atomic_op.h
 sys/bitypes.h
 sys/byteorder.h
 syscall.h
 sys/cdefs.h
 sys/cfgodm.h
 sys/elf.h
 sys/endian.h
+sys/epoll.h
 sys/errno.h
 sys/fault.h
 sys/fcntl.h
 sys/file.h
 sys/filio.h
 sys/frame.h
 sys/immu.h
+sys/inotify.h
 sys/inttypes.h
 sys/ioccom.h
 sys/ioctl.h
 sys/ipc.h
+sys/klog.h
 sys/ldr.h
 sys/link.h
 sys/locking.h
 syslog.h
 sys/lwp.h
 sys/machine.h
 sys/mman.h
 sys/mmu.h
@@ -905,16 +1091,22 @@ sys/ttycom.h
 sys/types.h
 sys/ucontext.h
 sys/uio.h
 sys/un.h
 sys/unistd.h
 sys/utsname.h
 sys/vfs.h
 sys/wait.h
+#ifdef ANDROID
+sysutils/NetlinkEvent.h
+system/audio.h
+system/graphics.h
+system/window.h
+#endif
 tables.h
 TArray.h
 TArrayIterator.h
 task.h
 tchar.h
 TCHAR.H
 termios.h
 TextCommon.h
@@ -947,16 +1139,25 @@ UDebugging.h
 UDesktop.h
 UDrawingState.h
 UDrawingUtils.h
 UEnvironment.h
 UEventMgr.h
 UException.h
 UExtractFromAEDesc.h
 UGWorld.h
+#ifdef ANDROID
+ui/ANativeObjectBase.h
+ui/egl/android_natives.h
+ui/Fence.h
+ui/FramebufferNativeWindow.h
+ui/GraphicBuffer.h
+ui/Rect.h
+ui/Region.h
+#endif
 UKeyFilters.h
 ulocks.h
 ulserrno.h
 UMemoryMgr.h
 UModalDialogs.h
 UNavServicesDialogs.h
 UnicodeBlockObjects.h
 UnicodeConverter.h
@@ -970,16 +1171,38 @@ unixlib.h
 unknwn.h
 UPrinting.h
 UQuickTime.h
 UReanimator.h
 URegions.h
 URegistrar.h
 UResourceMgr.h
 utility
+#ifdef ANDROID
+utils/BitSet.h
+utils/CallStack.h
+utils/Errors.h
+utils/FileMap.h
+utils/KeyedVector.h
+utils/List.h
+utils/Log.h
+utils/Looper.h
+utils/PropertyMap.h
+utils/RefBase.h
+utils/String16.h
+utils/String8.h
+utils/threads.h
+utils/TextOutput.h
+utils/Timers.h
+utils/Trace.h
+utils/TypeHelpers.h
+utils/Unicode.h
+utils/Vector.h
+utils/VectorImpl.h
+#endif
 urlhist.h
 urlmon.h
 UScrap.h
 UScreenPort.h
 UTCUtils.h
 UTETextAction.h
 UTEViewTextAction.h
 UTextEdit.h
--- a/configure.in
+++ b/configure.in
@@ -8978,16 +8978,18 @@ fi
 if test -n "$JS_SHARED_LIBRARY"; then
   MOZ_JS_LIBS="$MOZ_JS_SHARED_LIBS"
 else
   MOZ_JS_LIBS="$MOZ_JS_STATIC_LIBS"
   AC_DEFINE(MOZ_STATIC_JS)
 fi
 AC_SUBST(JS_SHARED_LIBRARY)
 
+AC_SUBST(UPLOAD_EXTRA_FILES)
+
 MOZ_CREATE_CONFIG_STATUS()
 
 # No need to run subconfigures when building with LIBXUL_SDK_DIR
 if test "$COMPILE_ENVIRONMENT" -a -z "$LIBXUL_SDK_DIR"; then
   MOZ_SUBCONFIGURE_ICU()
   MOZ_SUBCONFIGURE_FFI()
 fi
 
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -2165,16 +2165,17 @@ private:
                               nsIAtom* aAtom, void* aData);
   static void DestroyClassNameArray(void* aData);
   static void* AllocClassMatchingInfo(nsINode* aRootNode,
                                       const nsString* aClasses);
 
   static nsIXPConnect *sXPConnect;
 
   static nsIScriptSecurityManager *sSecurityManager;
+  static nsIPrincipal *sSystemPrincipal;
 
   static nsIParserService *sParserService;
 
   static nsNameSpaceManager *sNameSpaceManager;
 
   static nsIIOService *sIOService;
 
   static bool sImgLoaderInitialized;
--- a/content/base/src/DOMParser.cpp
+++ b/content/base/src/DOMParser.cpp
@@ -381,39 +381,20 @@ DOMParser::Constructor(const GlobalObjec
   }
   return domParser.forget();
 }
 
 /*static */already_AddRefed<DOMParser>
 DOMParser::Constructor(const GlobalObject& aOwner,
                        ErrorResult& rv)
 {
-  nsCOMPtr<nsIPrincipal> prin;
-  nsCOMPtr<nsIURI> documentURI;
-  nsCOMPtr<nsIURI> baseURI;
-  // No arguments; use the subject principal
-  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-  if (!secMan) {
-    rv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
-  }
-
-  rv = secMan->GetSubjectPrincipal(getter_AddRefs(prin));
-  if (rv.Failed()) {
-    return nullptr;
-  }
-
-  // We're called from JS; there better be a subject principal, really.
-  if (!prin) {
-    rv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
-  }
-
   nsRefPtr<DOMParser> domParser = new DOMParser(aOwner.GetAsSupports());
-  rv = domParser->InitInternal(aOwner.GetAsSupports(), prin, documentURI, baseURI);
+  rv = domParser->InitInternal(aOwner.GetAsSupports(),
+                               nsContentUtils::GetSubjectPrincipal(),
+                               nullptr, nullptr);
   if (rv.Failed()) {
     return nullptr;
   }
   return domParser.forget();
 }
 
 nsresult
 DOMParser::InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
@@ -459,34 +440,18 @@ DOMParser::Init(nsIPrincipal* aPrincipal
   if (!cx) {
     rv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
 
   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
-
   if (!principal && !aDocumentURI) {
-    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    if (!secMan) {
-      rv.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
-    if (rv.Failed()) {
-      return;
-    }
-
-    // We're called from JS; there better be a subject principal, really.
-    if (!principal) {
-      rv.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
+    principal = nsContentUtils::GetSubjectPrincipal();
   }
 
   rv = Init(principal, aDocumentURI, aBaseURI,
             scriptContext ? scriptContext->GetGlobalObject() : nullptr);
 }
 
 nsresult
 DOMParser::SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult)
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -190,16 +190,17 @@ using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla;
 
 const char kLoadAsData[] = "loadAsData";
 
 nsIXPConnect *nsContentUtils::sXPConnect;
 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
+nsIPrincipal *nsContentUtils::sSystemPrincipal;
 nsIParserService *nsContentUtils::sParserService = nullptr;
 nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
 nsIIOService *nsContentUtils::sIOService;
 nsIConsoleService *nsContentUtils::sConsoleService;
 nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
 nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
 nsIStringBundleService *nsContentUtils::sStringBundleService;
@@ -371,18 +372,18 @@ nsContentUtils::Init()
 
   sXPConnect = nsXPConnect::XPConnect();
 
   sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
   if(!sSecurityManager)
     return NS_ERROR_FAILURE;
   NS_ADDREF(sSecurityManager);
 
-  // Getting the first context can trigger GC, so do this non-lazily.
-  sXPConnect->InitSafeJSContext();
+  sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
+  MOZ_ASSERT(sSystemPrincipal);
 
   nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
   if (NS_FAILED(rv)) {
     // This makes life easier, but we can live without it.
 
     sIOService = nullptr;
   }
 
@@ -1429,16 +1430,17 @@ nsContentUtils::Shutdown()
   uint32_t i;
   for (i = 0; i < PropertiesFile_COUNT; ++i)
     NS_IF_RELEASE(sStringBundles[i]);
 
   NS_IF_RELEASE(sStringBundleService);
   NS_IF_RELEASE(sConsoleService);
   sXPConnect = nullptr;
   NS_IF_RELEASE(sSecurityManager);
+  NS_IF_RELEASE(sSystemPrincipal);
   NS_IF_RELEASE(sParserService);
   NS_IF_RELEASE(sIOService);
   NS_IF_RELEASE(sLineBreaker);
   NS_IF_RELEASE(sWordBreaker);
   NS_IF_RELEASE(sBidiKeyboard);
 
   delete sAtomEventTable;
   sAtomEventTable = nullptr;
@@ -1509,24 +1511,17 @@ nsContentUtils::CheckSameOrigin(const ns
 }
 
 nsresult
 nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
                                 const nsINode* unTrustedNode)
 {
   MOZ_ASSERT(aTrustedNode);
   MOZ_ASSERT(unTrustedNode);
-
-  bool isSystem = false;
-  nsresult rv = sSecurityManager->SubjectPrincipalIsSystem(&isSystem);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (isSystem) {
-    // we're running as system, grant access to the node.
-
+  if (IsCallerChrome()) {
     return NS_OK;
   }
 
   /*
    * Get hold of each node's principal
    */
 
   nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
@@ -1573,55 +1568,29 @@ nsContentUtils::CanCallerAccess(nsIDOMNo
   NS_ENSURE_TRUE(node, false);
   return CanCallerAccess(node);
 }
 
 // static
 bool
 nsContentUtils::CanCallerAccess(nsINode* aNode)
 {
-  // XXXbz why not check the IsCapabilityEnabled thing up front, and not bother
-  // with the system principal games?  But really, there should be a simpler
-  // API here, dammit.
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  nsresult rv = sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  if (!subjectPrincipal) {
-    // we're running as system, grant access to the node.
-
-    return true;
-  }
-
-  return CanCallerAccess(subjectPrincipal, aNode->NodePrincipal());
+  return CanCallerAccess(GetSubjectPrincipal(), aNode->NodePrincipal());
 }
 
 // static
 bool
 nsContentUtils::CanCallerAccess(nsPIDOMWindow* aWindow)
 {
-  // XXXbz why not check the IsCapabilityEnabled thing up front, and not bother
-  // with the system principal games?  But really, there should be a simpler
-  // API here, dammit.
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  nsresult rv = sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  if (!subjectPrincipal) {
-    // we're running as system, grant access to the node.
-
-    return true;
-  }
-
   nsCOMPtr<nsIScriptObjectPrincipal> scriptObject =
     do_QueryInterface(aWindow->IsOuterWindow() ?
                       aWindow->GetCurrentInnerWindow() : aWindow);
   NS_ENSURE_TRUE(scriptObject, false);
 
-  return CanCallerAccess(subjectPrincipal, scriptObject->GetPrincipal());
+  return CanCallerAccess(GetSubjectPrincipal(), scriptObject->GetPrincipal());
 }
 
 //static
 bool
 nsContentUtils::InProlog(nsINode *aNode)
 {
   NS_PRECONDITION(aNode, "missing node to nsContentUtils::InProlog");
 
@@ -1713,22 +1682,17 @@ nsContentUtils::GetDocumentFromContext()
 
   return nullptr;
 }
 
 bool
 nsContentUtils::IsCallerChrome()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  bool is_caller_chrome = false;
-  nsresult rv = sSecurityManager->SubjectPrincipalIsSystem(&is_caller_chrome);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-  if (is_caller_chrome) {
+  if (GetSubjectPrincipal() == sSystemPrincipal) {
     return true;
   }
 
   // If the check failed, look for UniversalXPConnect on the cx compartment.
   return xpc::IsUniversalXPConnectEnabled(GetCurrentJSContext());
 }
 
 namespace mozilla {
@@ -2348,24 +2312,25 @@ nsContentUtils::GenerateStateKey(nsICont
 
   return NS_OK;
 }
 
 // static
 nsIPrincipal*
 nsContentUtils::GetSubjectPrincipal()
 {
-  nsCOMPtr<nsIPrincipal> subject;
-  sSecurityManager->GetSubjectPrincipal(getter_AddRefs(subject));
-
-  // When the ssm says the subject is null, that means system principal.
-  if (!subject)
-    sSecurityManager->GetSystemPrincipal(getter_AddRefs(subject));
-
-  return subject;
+  JSContext* cx = GetCurrentJSContext();
+  if (!cx) {
+    return GetSystemPrincipal();
+  }
+
+  JSCompartment* compartment = js::GetContextCompartment(cx);
+  MOZ_ASSERT(compartment);
+  JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+  return nsJSPrincipals::get(principals);
 }
 
 // static
 nsIPrincipal*
 nsContentUtils::GetObjectPrincipal(JSObject* aObj)
 {
   // This is duplicated from nsScriptSecurityManager. We don't call through there
   // because the API unnecessarily requires a JSContext for historical reasons.
@@ -3136,21 +3101,17 @@ nsContentUtils::LogMessageToConsole(cons
 }
 
 bool
 nsContentUtils::IsChromeDoc(nsIDocument *aDocument)
 {
   if (!aDocument) {
     return false;
   }
-  
-  nsCOMPtr<nsIPrincipal> systemPrincipal;
-  sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
-
-  return aDocument->NodePrincipal() == systemPrincipal;
+  return aDocument->NodePrincipal() == sSystemPrincipal;
 }
 
 bool
 nsContentUtils::IsChildOfSameType(nsIDocument* aDoc)
 {
   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(aDoc->GetDocShell());
   nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
   if (docShellAsItem) {
@@ -4346,20 +4307,17 @@ nsContentUtils::CheckSecurityBeforeLoad(
                                         bool aAllowData,
                                         uint32_t aContentPolicyType,
                                         nsISupports* aContext,
                                         const nsAFlatCString& aMimeGuess,
                                         nsISupports* aExtra)
 {
   NS_PRECONDITION(aLoadingPrincipal, "Must have a loading principal here");
 
-  bool isSystemPrin = false;
-  if (NS_SUCCEEDED(sSecurityManager->IsSystemPrincipal(aLoadingPrincipal,
-                                                       &isSystemPrin)) &&
-      isSystemPrin) {
+  if (aLoadingPrincipal == sSystemPrincipal) {
     return NS_OK;
   }
   
   // XXXbz do we want to fast-path skin stylesheets loading XBL here somehow?
   // CheckLoadURIWithPrincipal
   nsresult rv = sSecurityManager->
     CheckLoadURIWithPrincipal(aLoadingPrincipal, aURIToLoad, aCheckLoadFlags);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -4388,36 +4346,30 @@ nsContentUtils::CheckSecurityBeforeLoad(
   }
 
   return aLoadingPrincipal->CheckMayLoad(aURIToLoad, true, false);
 }
 
 bool
 nsContentUtils::IsSystemPrincipal(nsIPrincipal* aPrincipal)
 {
-  bool isSystem;
-  nsresult rv = sSecurityManager->IsSystemPrincipal(aPrincipal, &isSystem);
-  return NS_SUCCEEDED(rv) && isSystem;
+  return aPrincipal == sSystemPrincipal;
 }
 
 bool
 nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal)
 {
   nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
   return !!ep;
 }
 
 nsIPrincipal*
 nsContentUtils::GetSystemPrincipal()
 {
-  nsCOMPtr<nsIPrincipal> sysPrin;
-  DebugOnly<nsresult> rv =
-    sSecurityManager->GetSystemPrincipal(getter_AddRefs(sysPrin));
-  MOZ_ASSERT(NS_SUCCEEDED(rv) && sysPrin);
-  return sysPrin;
+  return sSystemPrincipal;
 }
 
 bool
 nsContentUtils::CombineResourcePrincipals(nsCOMPtr<nsIPrincipal>* aResourcePrincipal,
                                           nsIPrincipal* aExtraPrincipal)
 {
   if (!aExtraPrincipal) {
     return false;
@@ -4429,17 +4381,17 @@ nsContentUtils::CombineResourcePrincipal
   if (*aResourcePrincipal == aExtraPrincipal) {
     return false;
   }
   bool subsumes;
   if (NS_SUCCEEDED((*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
       subsumes) {
     return false;
   }
-  sSecurityManager->GetSystemPrincipal(getter_AddRefs(*aResourcePrincipal));
+  *aResourcePrincipal = sSystemPrincipal;
   return true;
 }
 
 /* static */
 void
 nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
                             nsIURI *aLinkURI, const nsString &aTargetSpec,
                             bool aClick, bool aIsUserTriggered,
@@ -6101,30 +6053,20 @@ nsContentUtils::FindInternalContentViewe
   return nullptr;
 }
 
 bool
 nsContentUtils::GetContentSecurityPolicy(JSContext* aCx,
                                          nsIContentSecurityPolicy** aCSP)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // Get the security manager
-  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
-
-  if (!ssm) {
-    NS_ERROR("Failed to get security manager service");
-    return false;
-  }
-
-  nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
-  NS_ASSERTION(subjectPrincipal, "Failed to get subjectPrincipal");
+  MOZ_ASSERT(aCx == GetCurrentJSContext());
 
   nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
+  nsresult rv = GetSubjectPrincipal()->GetCsp(getter_AddRefs(csp));
   if (NS_FAILED(rv)) {
     NS_ERROR("CSP: Failed to get CSP from principal.");
     return false;
   }
 
   csp.forget(aCSP);
   return true;
 }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6330,33 +6330,23 @@ nsIDocument::LoadBindingDocument(const n
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), aURI,
                  mCharacterSet.get(),
                  GetDocBaseURI());
   if (rv.Failed()) {
     return;
   }
 
-  // Figure out the right principal to use
-  nsCOMPtr<nsIPrincipal> subject;
-  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-  if (secMan) {
-    rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
-    if (rv.Failed()) {
-      return;
-    }
-  }
-
-  if (!subject) {
-    // Fall back to our principal.  Or should we fall back to the null
-    // principal?  The latter would just mean no binding loads....
-    subject = NodePrincipal();
-  }
-
-  BindingManager()->LoadBindingDocument(this, uri, subject);
+  // Note - This computation of subjectPrincipal isn't necessarily sensical.
+  // It's just designed to preserve the old semantics during a mass-conversion
+  // patch.
+  nsCOMPtr<nsIPrincipal> subjectPrincipal =
+    nsContentUtils::GetCurrentJSContext() ? nsContentUtils::GetSubjectPrincipal()
+                                          : NodePrincipal();
+  BindingManager()->LoadBindingDocument(this, uri, subjectPrincipal);
 }
 
 NS_IMETHODIMP
 nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   NS_ENSURE_ARG_POINTER(node);
 
--- a/content/base/test/chrome/test_bug391728.html
+++ b/content/base/test/chrome/test_bug391728.html
@@ -113,17 +113,17 @@ function load_frame(nextTest, file) {
   frame.src = file + ".html?" + Math.random();
 }
 
 function next_text() {
   PluginUtils.withTestPlugin(gNextTest);
 }
 
 function frame_loaded() {
-  // We must delay to wait for the plugin sources to be loaded :(
+  SimpleTest.requestFlakyTimeout("We must delay to wait for the plugin sources to be loaded :(");
   setTimeout(next_text, 500);
 }
 
 function test_style(expected) {
   var frame = document.getElementById("testframe");
   for (var i = 1; i <= PLUGIN_COUNT; i++) {
     var tag = frame.contentDocument.getElementById("plugin" + i);
     ok(tag, "Plugin " + i + " did not exist");
--- a/content/base/test/test_CrossSiteXHR_cache.html
+++ b/content/base/test/test_CrossSiteXHR_cache.html
@@ -12,16 +12,17 @@
 </p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.7">
 
 SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("This test needs to generate artificial pauses, hence it uses timeouts.  There is no way around it, unfortunately. :(");
 
 window.addEventListener("message", function(e) {
   gen.send(e.data);
 }, false);
 
 gen = runTest();
 
 function runTest() {
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -174,17 +174,17 @@ function CreateTestWS(ws_location, ws_pr
 
 function forcegc()
 {
   SpecialPowers.forceGC();
   SpecialPowers.gc();
   setTimeout(function()
   {
     SpecialPowers.gc();
-  }, 1);
+  }, 0);
 }
 
 function doTest(number)
 {
   if (number > last_test) {
     ranAllTests = true;
     maybeFinished();
     return;
@@ -1493,16 +1493,18 @@ function maybeFinished()
   }
 }
 
 function testWebSocket ()
 {
   doTest(first_test);
 }
 
+SimpleTest.requestFlakyTimeout("The web socket tests are really fragile, but avoiding timeouts might be hard, since it's testing stuff on the network. " +
+                               "Expect all sorts of flakiness in this test...");
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 
 <div id="feedback">
 </div>
 
--- a/content/canvas/test/webgl-conformance/test_webgl_conformance_test_suite.html
+++ b/content/canvas/test/webgl-conformance/test_webgl_conformance_test_suite.html
@@ -462,16 +462,17 @@ function start() {
         },
         OPTIONS);
     // Make timeout delay much higher when running under valgrind.
     testHarness.setTimeoutDelay(20000);
     window.webglTestHarness = testHarness;
   }
 
   SimpleTest.requestLongerTimeout(requestLongerTimeoutLen);
+  SimpleTest.requestFlakyTimeout("We're embedding the WebGL test harness, which uses timeouts internally, so we have to abide. :(");
 
   var statusElem = document.getElementById("status");
   var statusTextNode = document.createTextNode('');
   statusElem.appendChild(statusTextNode);
 
   var expectedtofailElem = document.getElementById("expectedtofail");
   var expectedtofailTextNode = document.createTextNode('');
   expectedtofailElem.appendChild(expectedtofailTextNode);
--- a/content/html/content/src/HTMLOptionElement.cpp
+++ b/content/html/content/src/HTMLOptionElement.cpp
@@ -238,16 +238,35 @@ HTMLOptionElement::BeforeSetAttr(int32_t
   mIsInSetDefaultSelected = inSetDefaultSelected;
   // mIsSelected might have been changed by SetOptionsSelectedByIndex.  Possibly
   // more than once; make sure our mSelectedChanged state is set correctly.
   mSelectedChanged = mIsSelected != defaultSelected;
 
   return NS_OK;
 }
 
+nsresult
+HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue, bool aNotify)
+{
+  if (aNameSpaceID == kNameSpaceID_None &&
+      aName == nsGkAtoms::value && Selected()) {
+    // Since this option is selected, changing value
+    // may have changed missing validity state of the
+    // Select element
+    HTMLSelectElement* select = GetSelect();
+    if (select) {
+      select->UpdateValueMissingValidityState();
+    }
+  }
+
+  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
+                                            aValue, aNotify);
+}
+
 NS_IMETHODIMP
 HTMLOptionElement::GetText(nsAString& aText)
 {
   nsAutoString text;
 
   nsIContent* child = nsINode::GetFirstChild();
   while (child) {
     if (child->NodeType() == nsIDOMNode::TEXT_NODE ||
--- a/content/html/content/src/HTMLOptionElement.h
+++ b/content/html/content/src/HTMLOptionElement.h
@@ -46,16 +46,18 @@ public:
   bool DefaultSelected() const;
 
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               int32_t aModType) const MOZ_OVERRIDE;
 
   virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                  const nsAttrValueOrString* aValue,
                                  bool aNotify) MOZ_OVERRIDE;
+  virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue, bool aNotify) MOZ_OVERRIDE;
 
   void SetSelectedInternal(bool aValue, bool aNotify);
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) MOZ_OVERRIDE;
--- a/content/html/content/src/HTMLSelectElement.h
+++ b/content/html/content/src/HTMLSelectElement.h
@@ -390,16 +390,17 @@ public:
   {
     return mOptions;
   }
 
   // nsIConstraintValidation
   nsresult GetValidationMessage(nsAString& aValidationMessage,
                                 ValidityStateType aType) MOZ_OVERRIDE;
 
+  void UpdateValueMissingValidityState();
   /**
    * Insert aElement before the node given by aBefore
    */
   void Add(nsGenericHTMLElement& aElement, nsGenericHTMLElement* aBefore,
            ErrorResult& aError);
   void Add(nsGenericHTMLElement& aElement, int32_t aIndex, ErrorResult& aError)
   {
     // If item index is out of range, insert to last.
@@ -506,17 +507,16 @@ protected:
   nsresult RemoveOptionsFromListRecurse(nsIContent* aOptions,
                                         int32_t aRemoveIndex,
                                         int32_t* aNumRemoved,
                                         int32_t aDepth);
 
   // nsIConstraintValidation
   void UpdateBarredFromConstraintValidation();
   bool IsValueMissing();
-  void UpdateValueMissingValidityState();
 
   /**
    * Find out how deep this content is from the select (1=direct child)
    * @param aContent the content to check
    * @return the depth
    */
   int32_t GetContentDepth(nsIContent* aContent);
   /**
--- a/content/html/content/test/forms/mochitest.ini
+++ b/content/html/content/test/forms/mochitest.ini
@@ -69,16 +69,17 @@ skip-if = e10s
 [test_output_element.html]
 [test_pattern_attribute.html]
 [test_progress_element.html]
 [test_required_attribute.html]
 skip-if = e10s
 [test_restore_form_elements.html]
 [test_save_restore_radio_groups.html]
 [test_select_selectedOptions.html]
+[test_select_validation.html]
 [test_set_range_text.html]
 [test_step_attribute.html]
 skip-if = e10s
 [test_stepup_stepdown.html]
 [test_submit_invalid_file.html]
 [test_textarea_attributes_reflection.html]
 [test_validation.html]
 skip-if = buildapp == 'b2g' || e10s # b2g(374 total, bug 901848, no keygen support) b2g-debug(374 total, bug 901848, no keygen support) b2g-desktop(374 total, bug 901848, no keygen support)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/forms/test_select_validation.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=942321
+-->
+<head>
+  <title>Test for Bug 942321</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942321">Mozilla Bug 942321</a>
+<p id="display"></p>
+<form id="form" href="">
+  <select required id="testselect">
+    <option id="placeholder" value="" selected>placeholder</option>
+    <option value="test" id="actualvalue">test</option>
+  <select>
+  <input type="submit" />
+</form>
+<script class="testbody" type="text/javascript">
+/** Test for Bug 942321 **/
+var option = document.getElementById("actualvalue");
+option.selected = true;
+is(form.checkValidity(), true, "Select is required and should be valid");
+
+var placeholder = document.getElementById("placeholder");
+placeholder.selected = true;
+is(form.checkValidity(), false, "Select is required and should be invalid");
+
+placeholder.value = "not-invalid-anymore";
+is(form.checkValidity(), true, "Select is required and should be valid when option's value is changed by javascript");
+</script>
+</pre>
+</body>
+</html>
+
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2844,37 +2844,20 @@ nsHTMLDocument::SetDesignMode(const nsAS
   ErrorResult rv;
   SetDesignMode(aDesignMode, rv);
   return rv.ErrorCode();
 }
 
 void
 nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode, ErrorResult& rv)
 {
-  if (!nsContentUtils::IsCallerChrome()) {
-    nsCOMPtr<nsIPrincipal> subject;
-    nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
-    rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
-    if (rv.Failed()) {
-      return;
-    }
-    if (subject) {
-      bool subsumes;
-      rv = subject->Subsumes(NodePrincipal(), &subsumes);
-      if (rv.Failed()) {
-        return;
-      }
-
-      if (!subsumes) {
-        rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
-        return;
-      }
-    }
+  if (!nsContentUtils::GetSubjectPrincipal()->Subsumes(NodePrincipal())) {
+    rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
+    return;
   }
-
   bool editableMode = HasFlag(NODE_IS_EDITABLE);
   if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
     SetEditableFlag(!editableMode);
 
     rv = EditingStateChanged();
   }
 }
 
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -1444,17 +1444,17 @@ void MediaDecoderStateMachine::Seek(cons
   int64_t seekTime = aTarget.mTime + mStartTime;
   seekTime = std::min(seekTime, mEndTime);
   seekTime = std::max(mStartTime, seekTime);
   NS_ASSERTION(seekTime >= mStartTime && seekTime <= mEndTime,
                "Can only seek in range [0,duration]");
   mSeekTarget = SeekTarget(seekTime, aTarget.mType);
 
   mBasePosition = seekTime - mStartTime;
-  DECODER_LOG(PR_LOG_DEBUG, "Changed state to SEEKING (to %ld)", mSeekTarget.mTime);
+  DECODER_LOG(PR_LOG_DEBUG, "Changed state to SEEKING (to %lld)", mSeekTarget.mTime);
   mState = DECODER_STATE_SEEKING;
   if (mDecoder->GetDecodedStream()) {
     mDecoder->RecreateDecodedStream(seekTime - mStartTime);
   }
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::StopAudioThread()
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -10,17 +10,17 @@
 #include "MediaDecoderReader.h"
 #include "nsRect.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include <ui/GraphicBuffer.h>
 #include <stagefright/MediaSource.h>
 
 namespace android {
 class OmxDecoder;
-class MediaExtractor;
+class MOZ_EXPORT MediaExtractor;
 }
 
 namespace mozilla {
 
 namespace dom {
   class TimeRanges;
 }
 
--- a/content/media/omx/OMXCodecProxy.h
+++ b/content/media/omx/OMXCodecProxy.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef OMX_CODEC_PROXY_DECODER_H_
 #define OMX_CODEC_PROXY_DECODER_H_
 
 
 #include <android/native_window.h>
-#include <IOMX.h>
+#include <media/IOMX.h>
 #include <stagefright/MediaBuffer.h>
 #include <stagefright/MediaSource.h>
 #include <utils/threads.h>
 
 #include "MediaResourceManagerClient.h"
 
 namespace android {
 
--- a/content/media/plugins/MediaPluginReader.cpp
+++ b/content/media/plugins/MediaPluginReader.cpp
@@ -318,18 +318,18 @@ bool MediaPluginReader::DecodeAudioData(
                                      source.mSize,
                                      source.mAudioChannels));
 }
 
 nsresult MediaPluginReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
-  mVideoQueue.Erase();
-  mAudioQueue.Erase();
+  mVideoQueue.Reset();
+  mAudioQueue.Reset();
 
   mAudioSeekTimeUs = mVideoSeekTimeUs = aTarget;
 
   return NS_OK;
 }
 
 MediaPluginReader::ImageBufferCallback::ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer) :
   mImageContainer(aImageContainer)
--- a/content/media/raw/RawReader.cpp
+++ b/content/media/raw/RawReader.cpp
@@ -243,17 +243,17 @@ nsresult RawReader::Seek(int64_t aTime, 
 
   CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize;
   offset += sizeof(RawVideoHeader);
   NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE);
 
   nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mVideoQueue.Erase();
+  mVideoQueue.Reset();
 
   while(mVideoQueue.GetSize() == 0) {
     bool keyframeSkip = false;
     if (!DecodeVideoFrame(keyframeSkip, 0)) {
       mCurrentFrame = frame;
       return NS_ERROR_FAILURE;
     }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1550,18 +1550,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
                 return NS_ERROR_DOM_SECURITY_ERR;
             }
             owner = nullptr;
             inheritOwner = true;
         }
     }
     if (!owner && !inheritOwner && !ownerIsExplicit) {
         // See if there's system or chrome JS code running
-        inheritOwner = nsContentUtils::IsSystemPrincipal(
-          nsContentUtils::GetSubjectPrincipal());
+        inheritOwner = nsContentUtils::IsCallerChrome();
     }
 
     if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) {
         inheritOwner = false;
         owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
     }
 
     uint32_t flags = 0;
@@ -8640,67 +8639,53 @@ nsDocShell::CheckLoadingPermissions()
 {
     // This method checks whether the caller may load content into
     // this docshell. Even though we've done our best to hide windows
     // from code that doesn't have the right to access them, it's
     // still possible for an evil site to open a window and access
     // frames in the new window through window.frames[] (which is
     // allAccess for historic reasons), so we still need to do this
     // check on load.
-    nsresult rv = NS_OK, sameOrigin = NS_OK;
+    nsresult rv = NS_OK;
 
     if (!gValidateOrigin || !IsFrame()) {
         // Origin validation was turned off, or we're not a frame.
         // Permit all loads.
 
         return rv;
     }
 
-    nsCOMPtr<nsIScriptSecurityManager> securityManager =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // We're a frame. Check that the caller has write permission to
-    // the parent before allowing it to load anything into this
-    // docshell.
-    nsCOMPtr<nsIPrincipal> subjPrincipal;
-    rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjPrincipal));
-    NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && subjPrincipal, rv);
+    // Note - The check for a current JSContext here isn't necessarily sensical.
+    // It's just designed to preserve the old semantics during a mass-conversion
+    // patch.
+    NS_ENSURE_TRUE(nsContentUtils::GetCurrentJSContext(), NS_OK);
 
     // Check if the caller is from the same origin as this docshell,
     // or any of its ancestors.
     nsCOMPtr<nsIDocShellTreeItem> item(this);
     do {
         nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(item));
         nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
 
         nsIPrincipal *p;
         if (!sop || !(p = sop->GetPrincipal())) {
             return NS_ERROR_UNEXPECTED;
         }
 
-        // Compare origins
-        bool subsumes;
-        sameOrigin = subjPrincipal->Subsumes(p, &subsumes);
-        if (NS_SUCCEEDED(sameOrigin)) {
-            if (subsumes) {
-                // Same origin, permit load
-
-                return sameOrigin;
-            }
-
-            sameOrigin = NS_ERROR_DOM_PROP_ACCESS_DENIED;
+        if (nsContentUtils::GetSubjectPrincipal()->Subsumes(p)) {
+            // Same origin, permit load
+            return NS_OK;
         }
 
         nsCOMPtr<nsIDocShellTreeItem> tmp;
         item->GetSameTypeParent(getter_AddRefs(tmp));
         item.swap(tmp);
     } while (item);
 
-    return sameOrigin;
+    return NS_ERROR_DOM_PROP_ACCESS_DENIED;
 }
 
 //*****************************************************************************
 // nsDocShell: Site Loading
 //*****************************************************************************   
 namespace
 {
 
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -1624,18 +1624,17 @@ OpenEntryForRead(nsIPrincipal* aPrincipa
 
   // The caller guarnatees a call to CloseEntryForRead (on success or
   // failure) at which point the file will be closed.
   file.Forget(reinterpret_cast<File**>(aFile));
   return true;
 }
 
 void
-CloseEntryForRead(JS::Handle<JSObject*> global,
-                  size_t aSize,
+CloseEntryForRead(size_t aSize,
                   const uint8_t* aMemory,
                   intptr_t aFile)
 {
   File::AutoClose file(reinterpret_cast<File*>(aFile));
 
   MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == file->FileSize());
   MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) == file->MappedMemory());
 }
@@ -1678,18 +1677,17 @@ OpenEntryForWrite(nsIPrincipal* aPrincip
 
   // The caller guarnatees a call to CloseEntryForWrite (on success or
   // failure) at which point the file will be closed
   file.Forget(reinterpret_cast<File**>(aFile));
   return true;
 }
 
 void
-CloseEntryForWrite(JS::Handle<JSObject*> global,
-                   size_t aSize,
+CloseEntryForWrite(size_t aSize,
                    uint8_t* aMemory,
                    intptr_t aFile)
 {
   File::AutoClose file(reinterpret_cast<File*>(aFile));
 
   MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == file->FileSize());
   MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) == file->MappedMemory());
 
--- a/dom/asmjscache/AsmJSCache.h
+++ b/dom/asmjscache/AsmJSCache.h
@@ -104,31 +104,29 @@ struct ReadParams
 bool
 OpenEntryForRead(nsIPrincipal* aPrincipal,
                  const jschar* aBegin,
                  const jschar* aLimit,
                  size_t* aSize,
                  const uint8_t** aMemory,
                  intptr_t *aHandle);
 void
-CloseEntryForRead(JS::Handle<JSObject*> aGlobal,
-                  size_t aSize,
+CloseEntryForRead(size_t aSize,
                   const uint8_t* aMemory,
                   intptr_t aHandle);
 bool
 OpenEntryForWrite(nsIPrincipal* aPrincipal,
                   bool aInstalled,
                   const jschar* aBegin,
                   const jschar* aEnd,
                   size_t aSize,
                   uint8_t** aMemory,
                   intptr_t* aHandle);
 void
-CloseEntryForWrite(JS::Handle<JSObject*> aGlobal,
-                   size_t aSize,
+CloseEntryForWrite(size_t aSize,
                    uint8_t* aMemory,
                    intptr_t aHandle);
 
 bool
 GetBuildId(JS::BuildIdCharVector* aBuildId);
 
 // Called from QuotaManager.cpp:
 
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -226,17 +226,17 @@ URL::SetHref(const nsAString& aHref, Err
   nsCOMPtr<nsIURI> uri;
   rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri));
   if (NS_FAILED(rv)) {
     nsAutoString label(aHref);
     aRv.ThrowTypeError(MSG_INVALID_URL, &label);
     return;
   }
 
-  aRv = mURI->SetSpec(href);
+  mURI = uri;
   UpdateURLSearchParams();
 }
 
 void
 URL::GetOrigin(nsString& aOrigin) const
 {
   nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin);
 }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2009,19 +2009,16 @@ nsGlobalWindow::WouldReuseInnerWindow(ns
 
 void
 nsGlobalWindow::SetInitialPrincipalToSubject()
 {
   FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ());
 
   // First, grab the subject principal.
   nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::GetSubjectPrincipal();
-  if (!newWindowPrincipal) {
-    newWindowPrincipal = nsContentUtils::GetSystemPrincipal();
-  }
 
   // Now, if we're about to use the system principal or an nsExpandedPrincipal,
   // make sure we're not using it for a content docshell.
   if (nsContentUtils::IsSystemOrExpandedPrincipal(newWindowPrincipal) &&
       GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome) {
     newWindowPrincipal = nullptr;
   }
 
@@ -6050,63 +6047,52 @@ nsGlobalWindow::SetTextZoom(float aZoom)
 void
 nsGlobalWindow::MakeScriptDialogTitle(nsAString &aOutTitle)
 {
   aOutTitle.Truncate();
 
   // Try to get a host from the running principal -- this will do the
   // right thing for javascript: and data: documents.
 
-  nsresult rv = NS_OK;
-  NS_ASSERTION(nsContentUtils::GetSecurityManager(),
-    "Global Window has no security manager!");
-  if (nsContentUtils::GetSecurityManager()) {
-    nsCOMPtr<nsIPrincipal> principal;
-    rv = nsContentUtils::GetSecurityManager()->
-      GetSubjectPrincipal(getter_AddRefs(principal));
-
-    if (NS_SUCCEEDED(rv) && principal) {
-      nsCOMPtr<nsIURI> uri;
-      rv = principal->GetURI(getter_AddRefs(uri));
-
-      if (NS_SUCCEEDED(rv) && uri) {
-        // remove user:pass for privacy and spoof prevention
-
-        nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID));
-        if (fixup) {
-          nsCOMPtr<nsIURI> fixedURI;
-          rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
-          if (NS_SUCCEEDED(rv) && fixedURI) {
-            nsAutoCString host;
-            fixedURI->GetHost(host);
-
-            if (!host.IsEmpty()) {
-              // if this URI has a host we'll show it. For other
-              // schemes (e.g. file:) we fall back to the localized
-              // generic string
-
-              nsAutoCString prepath;
-              fixedURI->GetPrePath(prepath);
-
-              NS_ConvertUTF8toUTF16 ucsPrePath(prepath);
-              const char16_t *formatStrings[] = { ucsPrePath.get() };
-              nsXPIDLString tempString;
-              nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
-                                                    "ScriptDlgHeading",
-                                                    formatStrings,
-                                                    tempString);
-              aOutTitle = tempString;
-            }
-          }
+  nsCOMPtr<nsIPrincipal> principal = nsContentUtils::GetSubjectPrincipal();
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = principal->GetURI(getter_AddRefs(uri));
+  // Note - The check for the current JSContext here isn't necessarily sensical.
+  // It's just designed to preserve existing behavior during a mass-conversion
+  // patch.
+  if (NS_SUCCEEDED(rv) && uri && nsContentUtils::GetCurrentJSContext()) {
+    // remove user:pass for privacy and spoof prevention
+
+    nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID));
+    if (fixup) {
+      nsCOMPtr<nsIURI> fixedURI;
+      rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
+      if (NS_SUCCEEDED(rv) && fixedURI) {
+        nsAutoCString host;
+        fixedURI->GetHost(host);
+
+        if (!host.IsEmpty()) {
+          // if this URI has a host we'll show it. For other
+          // schemes (e.g. file:) we fall back to the localized
+          // generic string
+
+          nsAutoCString prepath;
+          fixedURI->GetPrePath(prepath);
+
+          NS_ConvertUTF8toUTF16 ucsPrePath(prepath);
+          const char16_t *formatStrings[] = { ucsPrePath.get() };
+          nsXPIDLString tempString;
+          nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
+                                                "ScriptDlgHeading",
+                                                formatStrings,
+                                                tempString);
+          aOutTitle = tempString;
         }
       }
     }
-    else { // failed to get subject principal
-      NS_WARNING("No script principal? Who is calling alert/confirm/prompt?!");
-    }
   }
 
   if (aOutTitle.IsEmpty()) {
     // We didn't find a host so use the generic heading
     nsXPIDLString tempString;
     nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                        "ScriptDlgGenericHeading",
                                        tempString);
@@ -11688,53 +11674,40 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI
     // now...
     realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
   }
 
   // Get principal of currently executing code, save for execution of timeout.
   // If our principals subsume the subject principal then use the subject
   // principal. Otherwise, use our principal to avoid running script in
   // elevated principals.
-
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  nsresult rv;
-  rv = nsContentUtils::GetSecurityManager()->
-    GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  bool subsumes = false;
-  nsCOMPtr<nsIPrincipal> ourPrincipal = GetPrincipal();
-
+  //
   // Note the direction of this test: We don't allow setTimeouts running with
   // chrome privileges on content windows, but we do allow setTimeouts running
   // with content privileges on chrome windows (where they can't do very much,
   // of course).
-  rv = ourPrincipal->Subsumes(subjectPrincipal, &subsumes);
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (subsumes) {
+  nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::GetSubjectPrincipal();
+  nsCOMPtr<nsIPrincipal> ourPrincipal = GetPrincipal();
+  if (ourPrincipal->Subsumes(subjectPrincipal)) {
     timeout->mPrincipal = subjectPrincipal;
   } else {
     timeout->mPrincipal = ourPrincipal;
   }
 
   ++gTimeoutsRecentlySet;
   TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
 
   if (!IsFrozen() && !mTimeoutsSuspendDepth) {
     // If we're not currently frozen, then we set timeout->mWhen to be the
     // actual firing time of the timer (i.e., now + delta). We also actually
     // create a timer and fire it off.
 
     timeout->mWhen = TimeStamp::Now() + delta;
 
+    nsresult rv;
     timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     nsRefPtr<nsTimeout> copy = timeout;
 
     rv = timeout->InitTimer(TimerCallback, realInterval);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2084,22 +2084,22 @@ nsJSContext::EndCycleCollectionCallback(
     }
 
     nsCString gcMsg;
     if (aResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
     NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
-      MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu waiting for GC)%s\n")
+      MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu waiting for GC)%s\n")
       MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
     nsString msg;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
                                         gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
-                                        gCCStats.mSuspected,
+                                        aResults.mNumSlices, gCCStats.mSuspected,
                                         aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
                                         aResults.mFreedRefCounted, aResults.mFreedGCed,
                                         sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
                                         gcMsg.get(),
                                         sForgetSkippableBeforeCC,
                                         minForgetSkippableTime / PR_USEC_PER_MSEC,
                                         sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
                                         (sTotalForgetSkippableTime / cleanups) /
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -155,17 +155,17 @@ nsLocation::CheckURL(nsIURI* aURI, nsIDo
         rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
                                     &isNullPrincipalScheme);
         if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
           sourceURI = principalURI;
         }
       }
     }
 
-    owner = do_QueryInterface(ssm->GetCxSubjectPrincipal(cx));
+    owner = nsContentUtils::GetSubjectPrincipal();
   }
 
   // Create load info
   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
   docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
   NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
 
   loadInfo->SetOwner(owner);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2323,16 +2323,18 @@ class CGCreateInterfaceObjectsMethod(CGA
         CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
         self.properties = properties
 
     def definition_body(self):
         if len(self.descriptor.prototypeChain) == 1:
             parentProtoType = "Rooted"
             if self.descriptor.interface.getExtendedAttribute("ArrayClass"):
                 getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)"
+            elif self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
+                getParentProto = "aCx, JS_GetErrorPrototype(aCx)"
             else:
                 getParentProto = "aCx, JS_GetObjectPrototype(aCx, aGlobal)"
         else:
             parentProtoName = self.descriptor.prototypeChain[-2]
             parentDesc = self.descriptor.getDescriptor(parentProtoName)
             if parentDesc.workers:
                 parentProtoName += '_workers'
             getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" %
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -13,36 +13,16 @@
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "XPCWrapper.h"
 #include "WorkerPrivate.h"
 #include "nsContentUtils.h"
 
-namespace {
-
-// We can't use nsContentUtils::IsCallerChrome because it might not exist in
-// xpcshell.
-bool
-IsCallerChrome()
-{
-  nsCOMPtr<nsIScriptSecurityManager> secMan;
-  secMan = XPCWrapper::GetSecurityManager();
-
-  if (!secMan) {
-    return false;
-  }
-
-  bool isChrome;
-  return NS_SUCCEEDED(secMan->SubjectPrincipalIsSystem(&isChrome)) && isChrome;
-}
-
-} // anonymous namespace
-
 namespace mozilla {
 namespace dom {
 
 bool
 ThrowExceptionObject(JSContext* aCx, nsIException* aException)
 {
   // See if we really have an Exception.
   nsCOMPtr<Exception> exception = do_QueryInterface(aException);
@@ -75,17 +55,17 @@ ThrowExceptionObject(JSContext* aCx, Exc
 {
   JS::Rooted<JS::Value> thrown(aCx);
 
   // If we stored the original thrown JS value in the exception
   // (see XPCConvert::ConstructException) and we are in a web context
   // (i.e., not chrome), rethrow the original value. This only applies to JS
   // implemented components so we only need to check for this on the main
   // thread.
-  if (NS_IsMainThread() && !IsCallerChrome() &&
+  if (NS_IsMainThread() && !nsContentUtils::IsCallerChrome() &&
       aException->StealJSVal(thrown.address())) {
     if (!JS_WrapValue(aCx, &thrown)) {
       return false;
     }
     JS_SetPendingException(aCx, thrown);
     return true;
   }
 
@@ -168,24 +148,18 @@ CreateException(JSContext* aCx, nsresult
 
 already_AddRefed<nsIStackFrame>
 GetCurrentJSStack()
 {
   // is there a current context available?
   JSContext* cx = nullptr;
 
   if (NS_IsMainThread()) {
-    // Note, in xpcshell nsContentUtils is never initialized, but we still need
-    // to report exceptions.
-    if (nsContentUtils::XPConnect()) {
-      cx = nsContentUtils::XPConnect()->GetCurrentJSContext();
-    } else {
-      nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
-      cx = xpc->GetCurrentJSContext();
-    }
+    MOZ_ASSERT(nsContentUtils::XPConnect());
+    cx = nsContentUtils::GetCurrentJSContext();
   } else {
     cx = workers::GetCurrentThreadJSContext();
   }
 
   if (!cx) {
     return nullptr;
   }
 
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -264,16 +264,24 @@ class IDLScope(IDLObject):
         if (isinstance(originalObject, IDLExternalInterface) or
             isinstance(newObject, IDLExternalInterface)):
             raise WebIDLError(
                 "Name collision between "
                 "interface declarations for identifier '%s' at '%s' and '%s'"
                 % (identifier.name,
                     originalObject.location, newObject.location), [])
 
+        if (isinstance(originalObject, IDLDictionary) or
+            isinstance(newObject, IDLDictionary)):
+            raise WebIDLError(
+                "Name collision between dictionary declarations for "
+                "identifier '%s'.\n%s\n%s"
+                % (identifier.name,
+                   originalObject.location, newObject.location), [])
+
         # We do the merging of overloads here as opposed to in IDLInterface
         # because we need to merge overloads of NamedConstructors and we need to
         # detect conflicts in those across interfaces. See also the comment in
         # IDLInterface.addExtendedAttributes for "NamedConstructor".
         if originalObject.tag == IDLInterfaceMember.Tags.Method and \
            newObject.tag == IDLInterfaceMember.Tags.Method:
             return originalObject.addOverload(newObject)
 
@@ -908,17 +916,17 @@ class IDLInterface(IDLObjectWithScope):
 
                 if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
                     raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
                                       [self.location])
 
                 args = attr.args() if attr.hasArgs() else []
 
                 retType = IDLWrapperType(self.location, self)
-                
+
                 if identifier == "Constructor" or identifier == "ChromeConstructor":
                     name = "constructor"
                     allowForbidden = True
                 else:
                     name = attr.value()
                     allowForbidden = False
 
                 methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
@@ -963,16 +971,24 @@ class IDLInterface(IDLObjectWithScope):
             elif (identifier == "ArrayClass"):
                 if not attr.noArguments():
                     raise WebIDLError("[ArrayClass] must take no arguments",
                                       [attr.location])
                 if self.parent:
                     raise WebIDLError("[ArrayClass] must not be specified on "
                                       "an interface with inherited interfaces",
                                       [attr.location, self.location])
+            elif (identifier == "ExceptionClass"):
+                if not attr.noArguments():
+                    raise WebIDLError("[ExceptionClass] must take no arguments",
+                                      [attr.location])
+                if self.parent:
+                    raise WebIDLError("[ExceptionClass] must not be specified on "
+                                      "an interface with inherited interfaces",
+                                      [attr.location, self.location])
             elif identifier == "Global":
                 if not attr.noArguments():
                     raise WebIDLError("[Global] must take no arguments",
                                       [attr.location])
                 self._isOnGlobalProtoChain = True
             elif (identifier == "NeedNewResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly"):
@@ -1439,17 +1455,17 @@ class IDLType(IDLObject):
         return self
 
     def isDistinguishableFrom(self, other):
         raise TypeError("Can't tell whether a generic type is or is not "
                         "distinguishable from other things")
 
 class IDLUnresolvedType(IDLType):
     """
-        Unresolved types are interface types 
+        Unresolved types are interface types
     """
 
     def __init__(self, location, name):
         IDLType.__init__(self, location, name)
 
     def isComplete(self):
         return False
 
@@ -3654,17 +3670,17 @@ class Parser(Tokenizer):
         return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename)
 
     def globalScope(self):
         return self._globalScope
 
     # The p_Foo functions here must match the WebIDL spec's grammar.
     # It's acceptable to split things at '|' boundaries.
     def p_Definitions(self, p):
-        """ 
+        """
             Definitions : ExtendedAttributeList Definition Definitions
         """
         if p[2]:
             p[0] = [p[2]]
             p[2].addExtendedAttributes(p[1])
         else:
             assert not p[1]
             p[0] = []
@@ -4582,17 +4598,17 @@ class Parser(Tokenizer):
         """
             UnionMemberTypes : OR UnionMemberType UnionMemberTypes
         """
         p[0] = [p[2]]
         p[0].extend(p[3])
 
     def p_UnionMemberTypesEmpty(self, p):
         """
-            UnionMemberTypes : 
+            UnionMemberTypes :
         """
         p[0] = []
 
     def p_NonAnyType(self, p):
         """
             NonAnyType : PrimitiveOrStringType TypeSuffix
                        | ARRAYBUFFER TypeSuffix
                        | OBJECT TypeSuffix
--- a/dom/bluetooth/BluetoothProfileController.cpp
+++ b/dom/bluetooth/BluetoothProfileController.cpp
@@ -64,17 +64,16 @@ BluetoothProfileController::BluetoothPro
 {
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
   MOZ_ASSERT(aRunnable);
   MOZ_ASSERT(aCallback);
 
   mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
   MOZ_ASSERT(mTimer);
 
-  mCheckProfileStatusCallback = new CheckProfileStatusCallback(this);
   mProfiles.Clear();
 
   /**
    * If the service uuid is not specified, either connect multiple profiles
    * based on Cod, or disconnect all connected profiles.
    */
   if (!aServiceUuid) {
     mTarget.cod = aCod;
@@ -220,18 +219,18 @@ BluetoothProfileController::StartSession
 
   if (mProfiles.Length() < 1) {
     BT_LOGR("No queued profile.");
     EndSession();
     return;
   }
 
   if (mTimer) {
-    mTimer->InitWithCallback(mCheckProfileStatusCallback, CONNECTION_TIMEOUT_MS,
-                             nsITimer::TYPE_ONE_SHOT);
+    mTimer->InitWithCallback(new CheckProfileStatusCallback(this),
+                             CONNECTION_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
   }
 
   BT_LOGR("%s", mConnect ? "connecting" : "disconnecting");
 
   Next();
 }
 
 void
--- a/dom/bluetooth/BluetoothProfileController.h
+++ b/dom/bluetooth/BluetoothProfileController.h
@@ -134,14 +134,13 @@ private:
 
   // Either CoD or BluetoothServiceClass is assigned.
   union {
     uint32_t cod;
     BluetoothServiceClass service;
   } mTarget;
 
   nsCOMPtr<nsITimer> mTimer;
-  nsCOMPtr<nsITimerCallback> mCheckProfileStatusCallback;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/camera/GonkRecorder.h
+++ b/dom/camera/GonkRecorder.h
@@ -26,21 +26,21 @@
 #include <system/audio.h>
 
 #include "mozilla/RefPtr.h"
 #include "GonkCameraHwMgr.h"
 
 namespace android {
 
 class GonkCameraSource;
-struct MediaSource;
-struct MediaWriter;
-class MetaData;
-struct AudioSource;
-class MediaProfiles;
+struct MOZ_EXPORT MediaSource;
+struct MOZ_EXPORT MediaWriter;
+class MOZ_EXPORT MetaData;
+struct MOZ_EXPORT AudioSource;
+class MOZ_EXPORT MediaProfiles;
 class GonkCameraHardware;
 
 struct GonkRecorder {
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GonkRecorder)
 
     GonkRecorder();
 
     virtual status_t init();
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -589,19 +589,17 @@ DataTransfer::MozGetDataAt(const nsAStri
   // source of the drag is in a child frame of the caller. In that case,
   // we only allow access to data of the same principal. During other events,
   // only allow access to the data with the same principal.
   nsIPrincipal* principal = nullptr;
   if (mIsCrossDomainSubFrameDrop ||
       (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP &&
        mEventType != NS_PASTE &&
        !nsContentUtils::IsCallerChrome())) {
-    nsresult rv = NS_OK;
-    principal = GetCurrentPrincipal(&rv);
-    NS_ENSURE_SUCCESS(rv, rv);
+    principal = nsContentUtils::GetSubjectPrincipal();
   }
 
   uint32_t count = item.Length();
   for (uint32_t i = 0; i < count; i++) {
     TransferItem& formatitem = item[i];
     if (formatitem.mFormat.Equals(format)) {
       bool subsumes;
       if (formatitem.mPrincipal && principal &&
@@ -620,19 +618,19 @@ DataTransfer::MozGetDataAt(const nsAStri
           nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
           NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR);
           nsIGlobalObject* go = c->GetGlobalObject();
           NS_ENSURE_TRUE(go, NS_ERROR_DOM_SECURITY_ERR);
           nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
           MOZ_ASSERT(sp, "This cannot fail on the main thread.");
           nsIPrincipal* dataPrincipal = sp->GetPrincipal();
           NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR);
-          NS_ENSURE_TRUE(principal || (principal = GetCurrentPrincipal(&rv)),
-                         NS_ERROR_DOM_SECURITY_ERR);
-          NS_ENSURE_SUCCESS(rv, rv);
+          if (!principal) {
+            principal = nsContentUtils::GetSubjectPrincipal();
+          }
           bool equals = false;
           NS_ENSURE_TRUE(NS_SUCCEEDED(principal->Equals(dataPrincipal, &equals)) && equals,
                          NS_ERROR_DOM_SECURITY_ERR);
         }
       }
       *aData = formatitem.mData;
       NS_IF_ADDREF(*aData);
       return NS_OK;
@@ -692,21 +690,18 @@ DataTransfer::MozSetDataAt(const nsAStri
   // don't allow non-chrome to add file data
   // XXX perhaps this should also limit any non-string type as well
   if ((aFormat.EqualsLiteral("application/x-moz-file-promise") ||
        aFormat.EqualsLiteral("application/x-moz-file")) &&
        !nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  nsresult rv = NS_OK;
-  nsIPrincipal* principal = GetCurrentPrincipal(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return SetDataWithPrincipal(aFormat, aData, aIndex, principal);
+  return SetDataWithPrincipal(aFormat, aData, aIndex,
+                              nsContentUtils::GetSubjectPrincipal());
 }
 
 void
 DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                            JS::Handle<JS::Value> aData,
                            uint32_t aIndex, ErrorResult& aRv)
 {
   nsCOMPtr<nsIVariant> data;
@@ -749,22 +744,17 @@ DataTransfer::MozClearDataAtHelper(const
   MOZ_ASSERT(aIndex < mItems.Length());
   MOZ_ASSERT(aIndex == 0 ||
              (mEventType != NS_CUT && mEventType != NS_COPY &&
               mEventType != NS_PASTE));
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
-  nsresult rv = NS_OK;
-  nsIPrincipal* principal = GetCurrentPrincipal(&rv);
-  if (NS_FAILED(rv)) {
-    aRv = rv;
-    return;
-  }
+  nsIPrincipal* principal = nsContentUtils::GetSubjectPrincipal();
 
   // if the format is empty, clear all formats
   bool clearall = format.IsEmpty();
 
   nsTArray<TransferItem>& item = mItems[aIndex];
   // count backwards so that the count and index don't have to be adjusted
   // after removing an element
   for (int32_t i = item.Length() - 1; i >= 0; i--) {
@@ -1084,31 +1074,16 @@ DataTransfer::SetDataWithPrincipal(const
 
   formatitem->mFormat = format;
   formatitem->mPrincipal = aPrincipal;
   formatitem->mData = aData;
 
   return NS_OK;
 }
 
-nsIPrincipal*
-DataTransfer::GetCurrentPrincipal(nsresult* rv)
-{
-  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-
-  nsCOMPtr<nsIPrincipal> currentPrincipal;
-  *rv = ssm->GetSubjectPrincipal(getter_AddRefs(currentPrincipal));
-  NS_ENSURE_SUCCESS(*rv, nullptr);
-
-  if (!currentPrincipal)
-    ssm->GetSystemPrincipal(getter_AddRefs(currentPrincipal));
-
-  return currentPrincipal.get();
-}
-
 void
 DataTransfer::GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat)
 {
   // treat text/unicode as equivalent to text/plain
   nsAutoString lowercaseFormat;
   nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
   if (lowercaseFormat.EqualsLiteral("text") || lowercaseFormat.EqualsLiteral("text/unicode"))
     aOutFormat.AssignLiteral("text/plain");
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -214,19 +214,16 @@ public:
     return mDragImage;
   }
 
   nsresult Clone(nsISupports* aParent, uint32_t aEventType, bool aUserCancelled,
                  bool aIsCrossDomainSubFrameDrop, DataTransfer** aResult);
 
 protected:
 
-  // returns a weak reference to the current principal
-  nsIPrincipal* GetCurrentPrincipal(nsresult* rv);
-
   // converts some formats used for compatibility in aInFormat into aOutFormat.
   // Text and text/unicode become text/plain, and URL becomes text/uri-list
   void GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat);
 
   // caches text and uri-list data formats that exist in the drag service or
   // clipboard for retrieval later.
   void CacheExternalData(const char* aFormat, uint32_t aIndex, nsIPrincipal* aPrincipal);
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -552,21 +552,16 @@ EventStateManager::PreHandleEvent(nsPres
         // then fall through...
       case WidgetMouseEvent::eRightButton:
       case WidgetMouseEvent::eMiddleButton:
         SetClickCount(aPresContext, mouseEvent, aStatus);
         break;
     }
     break;
   }
-  case NS_POINTER_CANCEL:
-  {
-    GenerateMouseEnterExit(mouseEvent);
-    break;
-  }
   case NS_MOUSE_EXIT:
     // If the event is not a top-level window exit, then it's not
     // really an exit --- we may have traversed widget boundaries but
     // we're still in our toplevel window.
     if (mouseEvent->exit != WidgetMouseEvent::eTopLevel) {
       // Treat it as a synthetic move so we don't generate spurious
       // "exit" or "move" events.  Any necessary "out" or "over" events
       // will be generated by GenerateMouseEnterExit
@@ -2859,17 +2854,23 @@ EventStateManager::PostHandleEvent(nsPre
               fm->SetFocusedWindow(mDocument->GetWindow());
             }
           }
         }
       }
       SetActiveManager(this, activeContent);
     }
     break;
-  case NS_POINTER_CANCEL:
+  case NS_POINTER_CANCEL: {
+    if(WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
+      GenerateMouseEnterExit(mouseEvent);
+    }
+    // This break was commented specially
+    // break;
+  }
   case NS_POINTER_UP: {
     WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
     // After UP/Cancel Touch pointers become invalid so we can remove relevant helper from Table
     // Mouse/Pen pointers are valid all the time (not only between down/up)
     if (pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
       mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
     }
     if (pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_MOUSE) {
--- a/dom/imptests/failures/html/dom/test_interfaces.html.json
+++ b/dom/imptests/failures/html/dom/test_interfaces.html.json
@@ -1,10 +1,9 @@
 {
-  "DOMException exception: existence and properties of exception interface prototype object": true,
   "DOMException exception: existence and properties of exception interface prototype object's \"name\" property": true,
   "CustomEvent interface: existence and properties of interface object": true,
   "EventListener interface: existence and properties of interface prototype object": true,
   "EventListener interface: existence and properties of interface prototype object's \"constructor\" property": true,
   "EventListener interface: operation handleEvent(Event)": true,
   "MutationObserver interface: operation observe(Node,MutationObserverInit)": true,
   "Node interface: existence and properties of interface object": true,
   "Document interface: existence and properties of interface object": true,
--- a/dom/indexedDB/test/extensions/Makefile.in
+++ b/dom/indexedDB/test/extensions/Makefile.in
@@ -1,20 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIST_FILES = \
+  bootstrap.js \
   install.rdf \
   $(NULL)
 
-DIST_FILES_NO_PP = \
-  bootstrap.js \
-  $(NULL)
-
 TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
 
 GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -116,16 +116,17 @@ TabChildBase::TabChildBase()
   , mTabChildGlobal(nullptr)
   , mInnerSize(0, 0)
 {
 }
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION(TabChildBase, mTabChildGlobal, mGlobal)
 
 void
 TabChildBase::InitializeRootMetrics()
 {
   // Calculate a really simple resolution that we probably won't
@@ -970,34 +971,33 @@ TabChild::NotifyTabContextUpdated()
         if (IsBrowserElement()) {
           docShell->SetIsBrowserInsideApp(BrowserOwnerAppId());
         } else {
           docShell->SetIsApp(OwnAppId());
         }
     }
 }
 
-NS_INTERFACE_MAP_BEGIN(TabChild)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChild)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
   NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
   NS_INTERFACE_MAP_ENTRY(nsITabChild)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsITooltipListener)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_ADDREF(TabChild)
-NS_IMPL_RELEASE(TabChild)
+NS_INTERFACE_MAP_END_INHERITING(TabChildBase)
+
+NS_IMPL_ADDREF_INHERITED(TabChild, TabChildBase);
+NS_IMPL_RELEASE_INHERITED(TabChild, TabChildBase);
 
 NS_IMETHODIMP
 TabChild::SetStatus(uint32_t aStatusType, const char16_t* aStatus)
 {
   return SetStatusWithContext(aStatusType,
       aStatus ? static_cast<const nsString &>(nsDependentString(aStatus))
               : EmptyString(),
       nullptr);
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -215,30 +215,30 @@ protected:
     float mOldViewportWidth;
     bool mContentDocumentIsDisplayed;
     nsRefPtr<TabChildGlobal> mTabChildGlobal;
     ScreenIntSize mInnerSize;
     mozilla::layers::FrameMetrics mLastRootMetrics;
     mozilla::layout::ScrollingBehavior mScrolling;
 };
 
-class TabChild : public PBrowserChild,
+class TabChild : public TabChildBase,
+                 public PBrowserChild,
                  public nsIWebBrowserChrome2,
                  public nsIEmbeddingSiteWindow,
                  public nsIWebBrowserChromeFocus,
                  public nsIInterfaceRequestor,
                  public nsIWindowProvider,
                  public nsIDOMEventListener,
                  public nsIWebProgressListener,
                  public nsSupportsWeakReference,
                  public nsITabChild,
                  public nsIObserver,
                  public TabContext,
-                 public nsITooltipListener,
-                 public TabChildBase
+                 public nsITooltipListener
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
     typedef mozilla::layers::ActiveElementManager ActiveElementManager;
 
 public:
     /** 
@@ -251,17 +251,17 @@ public:
     /** Return a TabChild with the given attributes. */
     static already_AddRefed<TabChild>
     Create(ContentChild* aManager, const TabContext& aContext, uint32_t aChromeFlags);
 
     virtual ~TabChild();
 
     bool IsRootContentDocument();
 
-    NS_DECL_ISUPPORTS
+    NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIWEBBROWSERCHROME
     NS_DECL_NSIWEBBROWSERCHROME2
     NS_DECL_NSIEMBEDDINGSITEWINDOW
     NS_DECL_NSIWEBBROWSERCHROMEFOCUS
     NS_DECL_NSIINTERFACEREQUESTOR
     NS_DECL_NSIWINDOWPROVIDER
     NS_DECL_NSIDOMEVENTLISTENER
     NS_DECL_NSIWEBPROGRESSLISTENER
--- a/dom/nfc/tests/marionette/manifest.ini
+++ b/dom/nfc/tests/marionette/manifest.ini
@@ -1,9 +1,11 @@
 [DEFAULT]
 b2g=true
 browser=false
 qemu=true
 
 [test_ndef.js]
 [test_nfc_enabled.js]
 [test_nfc_manager_tech_discovered.js]
+disabled = Bug 996426
 [test_nfc_peer.js]
+disabled = Bug 996426
--- a/dom/nfc/tests/marionette/test_nfc_manager_tech_discovered.js
+++ b/dom/nfc/tests/marionette/test_nfc_manager_tech_discovered.js
@@ -13,29 +13,29 @@ function handleTechnologyDiscoveredRE0(m
   log('Received \'nfc-manager-tech-discovered\'');
   is(msg.type, 'techDiscovered', 'check for correct message type');
   is(msg.techList[0], 'P2P', 'check for correct tech type');
   toggleNFC(false, runNextTest);
 }
 
 function activateRE(re) {
   let deferred = Promise.defer();
-  let cmd = 'nfc ntf rf_intf_activated ' + re;
+  let cmd = 'nfc nci rf_intf_activated_ntf ' + re;
 
   emulator.run(cmd, function(result) {
     is(result.pop(), 'OK', 'check activation of RE' + re);
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
 function notifyDiscoverRE(re, type) {
   let deferred = Promise.defer();
-  let cmd = 'nfc ntf rf_discover ' + re + ' ' + type;
+  let cmd = 'nfc nci rf_discover_ntf ' + re + ' ' + type;
 
   emulator.run(cmd, function(result) {
     is(result.pop(), 'OK', 'check discover of RE' + re);
     deferred.resolve();
   });
 
   return deferred.promise;
 }
--- a/dom/nfc/tests/marionette/test_nfc_peer.js
+++ b/dom/nfc/tests/marionette/test_nfc_peer.js
@@ -56,17 +56,17 @@ function handleTechnologyDiscoveredRE0Fo
 
     nfc.onpeerready = null;
     toggleNFC(false, runNextTest);
   }
 }
 
 function activateRE(re) {
   let deferred = Promise.defer();
-  let cmd = "nfc ntf rf_intf_activated " + re;
+  let cmd = "nfc nci rf_intf_activated_ntf " + re;
 
   emulator.run(cmd, function(result) {
     is(result.pop(), "OK", "check activation of RE" + re);
     deferred.resolve();
   });
 
   return deferred.promise;
 }
--- a/dom/src/storage/DOMStorage.cpp
+++ b/dom/src/storage/DOMStorage.cpp
@@ -236,28 +236,22 @@ DOMStorage::CanUseStorage(DOMStorage* aS
     aStorage->mIsSessionOnly = false;
   }
 
   if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
     return false;
   }
 
   // chrome can always use aStorage regardless of permission preferences
-  if (nsContentUtils::IsCallerChrome()) {
+  nsCOMPtr<nsIPrincipal> subjectPrincipal =
+    nsContentUtils::GetSubjectPrincipal();
+  if (nsContentUtils::IsSystemPrincipal(subjectPrincipal)) {
     return true;
   }
 
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  nsresult rv = nsContentUtils::GetSecurityManager()->
-                  GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  // if subjectPrincipal were null we'd have returned after
-  // IsCallerChrome().
-
   nsCOMPtr<nsIPermissionManager> permissionManager =
     services::GetPermissionManager();
   if (!permissionManager) {
     return false;
   }
 
   uint32_t perm;
   permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
--- a/dom/telephony/gonk/TelephonyProvider.js
+++ b/dom/telephony/gonk/TelephonyProvider.js
@@ -208,40 +208,28 @@ TelephonyProvider.prototype = {
            this._activeCall.clientId === aCall.clientId &&
            this._activeCall.callIndex === aCall.callIndex;
   },
 
   /**
    * Track the active call and update the audio system as its state changes.
    */
   _activeCall: null,
-  _updateCallAudioState: function(aCall, aConferenceState) {
+  _updateActiveCall: function(aCall, aConferenceState) {
     if (aConferenceState === nsITelephonyProvider.CALL_STATE_CONNECTED) {
       this._activeCall = new ConferenceCall(aConferenceState);
-      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
-      if (this.speakerEnabled) {
-        gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
-                                     nsIAudioManager.FORCE_SPEAKER);
-      }
-      if (DEBUG) {
-        debug("Active call, put audio system into PHONE_STATE_IN_CALL: " +
-              gAudioManager.phoneState);
-      }
+      this._updateCallAudioState(aCall);
       return;
     }
 
     if (aConferenceState === nsITelephonyProvider.CALL_STATE_UNKNOWN ||
         aConferenceState === nsITelephonyProvider.CALL_STATE_HELD) {
       if (this._activeCall instanceof ConferenceCall) {
         this._activeCall = null;
-        gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
-        if (DEBUG) {
-          debug("No active call, put audio system into PHONE_STATE_NORMAL: " +
-                gAudioManager.phoneState);
-        }
+        this._updateCallAudioState(aCall);
       }
       return;
     }
 
     if (!aCall) {
       return;
     }
 
@@ -253,60 +241,73 @@ TelephonyProvider.prototype = {
     }
 
     switch (aCall.state) {
       case nsITelephonyProvider.CALL_STATE_DIALING: // Fall through...
       case nsITelephonyProvider.CALL_STATE_ALERTING:
       case nsITelephonyProvider.CALL_STATE_CONNECTED:
         aCall.isActive = true;
         this._activeCall = new SingleCall(aCall);
-        gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
-        if (this.speakerEnabled) {
-          gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
-                                       nsIAudioManager.FORCE_SPEAKER);
-        }
-        if (DEBUG) {
-          debug("Active call, put audio system into PHONE_STATE_IN_CALL: " +
-                gAudioManager.phoneState);
-        }
+        this._updateCallAudioState(aCall);
         break;
 
       case nsITelephonyProvider.CALL_STATE_INCOMING:
         aCall.isActive = false;
-        if (!this._activeCall) {
-          // We can change the phone state into RINGTONE only when there's
-          // no active call.
-          gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE;
-          if (DEBUG) {
-            debug("Incoming call, put audio system into PHONE_STATE_RINGTONE: " +
-                  gAudioManager.phoneState);
-          }
-        }
+        this._updateCallAudioState(aCall);
         break;
 
       case nsITelephonyProvider.CALL_STATE_HELD: // Fall through...
       case nsITelephonyProvider.CALL_STATE_DISCONNECTED:
         aCall.isActive = false;
         if (this._matchActiveSingleCall(aCall)) {
           // Previously active call is not active now.
           this._activeCall = null;
-        }
-
-        if (!this._activeCall) {
-          // No active call. Disable the audio.
-          gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
-          if (DEBUG) {
-            debug("No active call, put audio system into PHONE_STATE_NORMAL: " +
-                  gAudioManager.phoneState);
-          }
+          this._updateCallAudioState(aCall);
         }
         break;
     }
   },
 
+  _updateCallAudioState: function(aCall) {
+    // Ignore audio state setting if the call is a placeholder.
+    if (aCall && aCall.callIndex === OUTGOING_PLACEHOLDER_CALL_INDEX) {
+      return;
+    }
+
+    let active = (this._activeCall !== null);
+    let incoming = (aCall &&
+                    aCall.state === nsITelephonyProvider.CALL_STATE_INCOMING);
+
+    if (active) {
+      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
+      if (this.speakerEnabled) {
+        gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
+                                     nsIAudioManager.FORCE_SPEAKER);
+      }
+      if (DEBUG) {
+        debug("Active call, put audio system into PHONE_STATE_IN_CALL: " +
+              gAudioManager.phoneState);
+      }
+    } else if (incoming) {
+      // We can change the phone state into RINGTONE only when there's
+      // no active call.
+      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE;
+      if (DEBUG) {
+        debug("Incoming call, put audio system into PHONE_STATE_RINGTONE: " +
+              gAudioManager.phoneState);
+      }
+    } else {
+      gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
+      if (DEBUG) {
+        debug("No active call, put audio system into PHONE_STATE_NORMAL: " +
+              gAudioManager.phoneState);
+      }
+    }
+  },
+
   _convertRILCallState: function(aState) {
     switch (aState) {
       case RIL.CALL_STATE_UNKNOWN:
         return nsITelephonyProvider.CALL_STATE_UNKNOWN;
       case RIL.CALL_STATE_ACTIVE:
         return nsITelephonyProvider.CALL_STATE_CONNECTED;
       case RIL.CALL_STATE_HOLDING:
         return nsITelephonyProvider.CALL_STATE_HELD;
@@ -742,17 +743,17 @@ TelephonyProvider.prototype = {
       serviceId: aClientId,
       emergency: aCall.isEmergency,
       duration: duration,
       direction: aCall.isOutgoing ? "outgoing" : "incoming"
     };
     gSystemMessenger.broadcastMessage("telephony-call-ended", data);
 
     aCall.clientId = aClientId;
-    this._updateCallAudioState(aCall, null);
+    this._updateActiveCall(aCall, null);
 
     let manualConfStateChange = false;
     let childId = this._currentCalls[aClientId][aCall.callIndex].childId;
     if (childId) {
       // Child cannot live without parent.
       let childCall = this._currentCalls[aClientId][childId];
       this.notifyCallDisconnected(aClientId, childCall);
     } else {
@@ -822,17 +823,17 @@ TelephonyProvider.prototype = {
       aCall.state = this._convertRILCallState(aCall.state);
     }
 
     if (aCall.state == nsITelephonyProvider.CALL_STATE_DIALING) {
       gSystemMessenger.broadcastMessage("telephony-new-call", {});
     }
 
     aCall.clientId = aClientId;
-    this._updateCallAudioState(aCall, null);
+    this._updateActiveCall(aCall, null);
 
     let call = this._currentCalls[aClientId][aCall.callIndex];
     if (call) {
       call.state = aCall.state;
       call.isConference = aCall.isConference;
       call.isEmergency = aCall.isEmergency;
       call.isActive = aCall.isActive;
       call.isSwitchable = aCall.isSwitchable != null ?
@@ -886,17 +887,17 @@ TelephonyProvider.prototype = {
     let notification = this._convertRILSuppSvcNotification(aNotification);
     this._notifyAllListeners("supplementaryServiceNotification",
                              [aClientId, aCallIndex, notification]);
   },
 
   notifyConferenceCallStateChanged: function(aState) {
     if (DEBUG) debug("handleConferenceCallStateChanged: " + aState);
     aState = this._convertRILCallState(aState);
-    this._updateCallAudioState(null, aState);
+    this._updateActiveCall(null, aState);
 
     this._notifyAllListeners("conferenceCallStateChanged", [aState]);
   },
 
   /**
    * nsIObserver interface.
    */
 
--- a/dom/tests/mochitest/chrome/test_cyclecollector.xul
+++ b/dom/tests/mochitest/chrome/test_cyclecollector.xul
@@ -13,33 +13,40 @@ https://bugzilla.mozilla.org/show_bug.cg
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id="
      target="_blank">Mozilla Bug </a>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
   /** Test for Bug  **/
+  let Ci = Components.interfaces;
   var obs = Components.classes["@mozilla.org/observer-service;1"]
-                      .getService(Components.interfaces.nsIObserverService);
+                      .getService(Ci.nsIObserverService);
   var didCall = false;
   var observer = {
     QueryInterface: function QueryInterface(aIID) {
-      if (aIID.equals(Components.interfaces.nsIObserver) ||
-          aIID.equals(Components.interfaces.nsISupports))
+      if (aIID.equals(Ci.nsIObserver) ||
+          aIID.equals(Ci.nsISupports))
         return this;
       throw Components.results.NS_NOINTERFACE;
     },
     observe: function(subject, topic, data) {
       obs.removeObserver(observer, "cycle-collector-begin");
       observer = null;
       didCall = true;
     }
   };
 
+  // Make sure that we call the observer even if we're in the middle
+  // of an ICC when we add the observer. See bug 981033.
+  // XXX This will assert if we try to start an ICC during a IGC.
+  //SpecialPowers.finishCC();
+  //SpecialPowers.ccSlice(1);
+
   obs.addObserver(observer, "cycle-collector-begin", false);
-  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-        .getInterface(Components.interfaces.nsIDOMWindowUtils).cycleCollect();
+
+  SpecialPowers.DOMWindowUtils.cycleCollect();
 
   ok(didCall, "Observer should have been called!");
   ]]>
   </script>
 </window>
--- a/dom/webidl/DOMException.webidl
+++ b/dom/webidl/DOMException.webidl
@@ -13,21 +13,28 @@
 
 // This is the WebIDL version of nsIException.  This is mostly legacy stuff.
 
 interface StackFrame;
 
 [NoInterfaceObject]
 interface ExceptionMembers
 {
-  // A custom message set by the thrower.
+  // A custom message set by the thrower.  LenientThis so it can be
+  // gotten on the prototype, which Error.prototype.toString will do
+  // if someone tries to stringify DOMException.prototype.
+  [LenientThis]
   readonly attribute DOMString               message;
   // The nsresult associated with this exception.
   readonly attribute unsigned long           result;
-  // The name of the error code (ie, a string repr of |result|)
+  // The name of the error code (ie, a string repr of |result|).
+  // LenientThis so it can be gotten on the prototype, which
+  // Error.prototype.toString will do if someone tries to stringify
+  // DOMException.prototype.
+  [LenientThis]
   readonly attribute DOMString               name;
 
   // Filename location.  This is the location that caused the
   // error, which may or may not be a source file location.
   // For example, standard language errors would generally have
   // the same location as their top stack entry.  File
   // parsers may put the location of the file they were parsing,
   // etc.
@@ -44,29 +51,29 @@ interface ExceptionMembers
   // this was only ever usefully available to chrome JS.
   [ChromeOnly]
   readonly attribute StackFrame?             location;
   // An inner exception that triggered this, if available.
   readonly attribute nsISupports?            inner;
 
   // Arbitary data for the implementation.
   readonly attribute nsISupports?            data;
-
-  // A generic formatter - make it suitable to print, etc.
-  stringifier;
 };
 
 [NoInterfaceObject]
 interface Exception {
+  // A generic formatter - make it suitable to print, etc.
+  stringifier;
 };
 
 Exception implements ExceptionMembers;
 
 // XXXkhuey this is an 'exception', not an interface, but we don't have any
 // parser or codegen mechanisms for dealing with exceptions.
+[ExceptionClass]
 interface DOMException {
   const unsigned short INDEX_SIZE_ERR = 1;
   const unsigned short DOMSTRING_SIZE_ERR = 2; // historical
   const unsigned short HIERARCHY_REQUEST_ERR = 3;
   const unsigned short WRONG_DOCUMENT_ERR = 4;
   const unsigned short INVALID_CHARACTER_ERR = 5;
   const unsigned short NO_DATA_ALLOWED_ERR = 6; // historical
   const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7;
--- a/dom/webidl/MozWifiManager.webidl
+++ b/dom/webidl/MozWifiManager.webidl
@@ -242,16 +242,24 @@ interface MozWifiManager : EventTarget {
    *            string as value.
    *            request.result[USAGE] = [CA_NICKNAME1, CA_NICKNAME2, ...]
    *            USAGE string includes: "ServerCert".
    * onerror: We have failed to list certificate.
    */
   DOMRequest getImportedCerts();
 
   /**
+   * Delete an imported certificate.
+   * @param certNickname Nickname of imported to be deleted.
+   * onsuccess: We have successfully deleted certificate.
+   * onerror: We have failed to delete certificate.
+   */
+  DOMRequest deleteCert(DOMString certNickname);
+
+  /**
    * Returns whether or not wifi is currently enabled.
    */
   readonly attribute boolean enabled;
 
   /**
    * Returns the MAC address of the wifi adapter.
    */
   readonly attribute DOMString macAddress;
--- a/dom/wifi/DOMWifiManager.js
+++ b/dom/wifi/DOMWifiManager.js
@@ -91,16 +91,17 @@ DOMWifiManager.prototype = {
                       "WifiManager:associate:Return:OK", "WifiManager:associate:Return:NO",
                       "WifiManager:forget:Return:OK", "WifiManager:forget:Return:NO",
                       "WifiManager:wps:Return:OK", "WifiManager:wps:Return:NO",
                       "WifiManager:setPowerSavingMode:Return:OK", "WifiManager:setPowerSavingMode:Return:NO",
                       "WifiManager:setHttpProxy:Return:OK", "WifiManager:setHttpProxy:Return:NO",
                       "WifiManager:setStaticIpMode:Return:OK", "WifiManager:setStaticIpMode:Return:NO",
                       "WifiManager:importCert:Return:OK", "WifiManager:importCert:Return:NO",
                       "WifiManager:getImportedCerts:Return:OK", "WifiManager:getImportedCerts:Return:NO",
+                      "WifiManager:deleteCert:Return:OK", "WifiManager:deleteCert:Return:NO",
                       "WifiManager:wifiDown", "WifiManager:wifiUp",
                       "WifiManager:onconnecting", "WifiManager:onassociate",
                       "WifiManager:onconnect", "WifiManager:ondisconnect",
                       "WifiManager:onwpstimeout", "WifiManager:onwpsfail",
                       "WifiManager:onwpsoverlap", "WifiManager:connectionInfoUpdate",
                       "WifiManager:onconnectingfailed"];
     this.initDOMRequestHelper(aWindow, messages);
     this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
@@ -283,16 +284,24 @@ DOMWifiManager.prototype = {
       case "WifiManager:getImportedCerts:Return:OK":
         Services.DOMRequest.fireSuccess(request, this._convertWifiCertificateList(msg.data));
         break;
 
       case "WifiManager:getImportedCerts:Return:NO":
         Services.DOMRequest.fireError(request, msg.data);
         break;
 
+      case "WifiManager:deleteCert:Return:OK":
+        Services.DOMRequest.fireSuccess(request, msg.data);
+        break;
+
+      case "WifiManager:deleteCert:Return:NO":
+        Services.DOMRequest.fireError(request, msg.data);
+        break;
+
       case "WifiManager:wifiDown":
         this._enabled = false;
         this._currentNetwork = null;
         this._fireEnabledOrDisabled(false);
         break;
 
       case "WifiManager:wifiUp":
         this._enabled = true;
@@ -447,16 +456,25 @@ DOMWifiManager.prototype = {
   },
 
   getImportedCerts: function nsIDOMWifiManager_getImportedCerts() {
     var request = this.createRequest();
     this._sendMessageForRequest("WifiManager:getImportedCerts", null, request);
     return request;
   },
 
+  deleteCert: function nsIDOMWifiManager_deleteCert(certNickname) {
+    var request = this.createRequest();
+    this._sendMessageForRequest("WifiManager:deleteCert",
+                                {
+                                  certNickname: certNickname
+                                }, request);
+    return request;
+  },
+
   get enabled() {
     return this._enabled;
   },
 
   get macAddress() {
     return this._macAddress;
   },
 
--- a/dom/wifi/WifiCertService.cpp
+++ b/dom/wifi/WifiCertService.cpp
@@ -158,16 +158,69 @@ private:
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsString mPassword;
   WifiCertServiceResultOptions mResult;
 };
 
+class DeleteCertTask MOZ_FINAL: public CryptoTask
+{
+public:
+  DeleteCertTask(int32_t aId, const nsAString& aCertNickname)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mResult.mId = aId;
+    mResult.mStatus = 0;
+    mResult.mUsageFlag = 0;
+    mResult.mNickname = aCertNickname;
+  }
+
+private:
+  virtual void ReleaseNSSResources() {}
+
+  virtual nsresult CalculateResult() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsCString userNickname;
+    CopyUTF16toUTF8(mResult.mNickname, userNickname);
+
+    // Delete server certificate.
+    nsCString serverCertName("WIFI_SERVERCERT_", 16);
+    serverCertName += userNickname;
+
+    ScopedCERTCertificate cert(
+      CERT_FindCertByNickname(CERT_GetDefaultCertDB(), serverCertName.get())
+    );
+    if (!cert) {
+      return MapSECStatus(SECFailure);
+    }
+
+    SECStatus srv = SEC_DeletePermCertificate(cert);
+    if (srv != SECSuccess) {
+      return MapSECStatus(srv);
+    }
+
+    return NS_OK;
+  }
+
+  virtual void CallCallback(nsresult rv)
+  {
+    if (NS_FAILED(rv)) {
+      mResult.mStatus = -1;
+    }
+    gWifiCertService->DispatchResult(mResult);
+  }
+
+  WifiCertServiceResultOptions mResult;
+};
+
 NS_IMPL_ISUPPORTS(WifiCertService, nsIWifiCertService)
 
 NS_IMETHODIMP
 WifiCertService::Start(nsIWifiEventListener* aListener)
 {
   MOZ_ASSERT(aListener);
 
   nsresult rv = NS_NewThread(getter_AddRefs(mRequestThread));
@@ -244,16 +297,23 @@ WifiCertService::ImportCert(int32_t aId,
                             const nsAString& aCertPassword,
                             const nsAString& aCertNickname)
 {
   RefPtr<CryptoTask> task = new ImportCertTask(aId, aCertBlob, aCertPassword,
                                                aCertNickname);
   return task->Dispatch("WifiImportCert");
 }
 
+NS_IMETHODIMP
+WifiCertService::DeleteCert(int32_t aId, const nsAString& aCertNickname)
+{
+  RefPtr<CryptoTask> task = new DeleteCertTask(aId, aCertNickname);
+  return task->Dispatch("WifiDeleteCert");
+}
+
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiCertService,
                                          WifiCertService::FactoryCreate)
 
 NS_DEFINE_NAMED_CID(NS_WIFICERTSERVICE_CID);
 
 static const mozilla::Module::CIDEntry kWifiCertServiceCIDs[] = {
   { &kNS_WIFICERTSERVICE_CID, false, nullptr, WifiCertServiceConstructor },
   { nullptr }
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -1374,16 +1374,25 @@ var WifiManager = (function() {
     if (callback) {
       controlCallbacks[id] = callback;
     }
 
     wifiCertService.importCert(id, caInfo.certBlob, caInfo.certPassword,
                                caInfo.certNickname);
   }
 
+  manager.deleteCert = function(caInfo, callback) {
+    var id = idgen++;
+    if (callback) {
+      controlCallbacks[id] = callback;
+    }
+
+    wifiCertService.deleteCert(id, caInfo.certNickname);
+  }
+
   return manager;
 })();
 
 // Get unique key for a network, now the key is created by escape(SSID)+Security.
 // So networks of same SSID but different security mode can be identified.
 function getNetworkKey(network)
 {
   var ssid = "",
@@ -1642,16 +1651,17 @@ function WifiWorker() {
   const messages = ["WifiManager:getNetworks", "WifiManager:getKnownNetworks",
                     "WifiManager:associate", "WifiManager:forget",
                     "WifiManager:wps", "WifiManager:getState",
                     "WifiManager:setPowerSavingMode",
                     "WifiManager:setHttpProxy",
                     "WifiManager:setStaticIpMode",
                     "WifiManager:importCert",
                     "WifiManager:getImportedCerts",
+                    "WifiManager:deleteCert",
                     "child-process-shutdown"];
 
   messages.forEach((function(msgName) {
     this._mm.addMessageListener(msgName, this);
   }).bind(this));
 
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
 
@@ -2600,16 +2610,19 @@ WifiWorker.prototype = {
         this.setStaticIpMode(msg);
         break;
       case "WifiManager:importCert":
         this.importCert(msg);
         break;
       case "WifiManager:getImportedCerts":
         this.getImportedCerts(msg);
         break;
+      case "WifiManager:deleteCert":
+        this.deleteCert(msg);
+        break;
       case "WifiManager:getState": {
         let i;
         if ((i = this._domManagers.indexOf(msg.manager)) === -1) {
           this._domManagers.push(msg.manager);
         }
 
         let net = this.currentNetwork ? netToDOM(this.currentNetwork) : null;
         return { network: net,
@@ -3152,16 +3165,25 @@ WifiWorker.prototype = {
         continue;
       }
       importedCerts[UsageMapping[certNicknameInfo[1]]].push(certNicknameInfo[2]);
     }
 
     self._sendMessage(message, true, importedCerts, msg);
   },
 
+  deleteCert: function deleteCert(msg) {
+    const message = "WifiManager:deleteCert:Return";
+    let self = this;
+
+    WifiManager.deleteCert(msg.data, function(data) {
+      self._sendMessage(message, data.status === 0, "Delete Cert failed", msg);
+    });
+  },
+
   // This is a bit ugly, but works. In particular, this depends on the fact
   // that RadioManager never actually tries to get the worker from us.
   get worker() { throw "Not implemented"; },
 
   shutdown: function() {
     debug("shutting down ...");
     this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
       this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
--- a/dom/wifi/nsIWifiCertService.idl
+++ b/dom/wifi/nsIWifiCertService.idl
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMBlob;
 interface nsIWifiEventListener;
 
-[scriptable, uuid(49e8e04e-eb05-4c92-84a6-7af32310f0c3)]
+[scriptable, uuid(2712a791-f720-484d-8820-c4085629f657)]
 interface nsIWifiCertService : nsISupports
 {
   const unsigned short WIFI_CERT_USAGE_FLAG_SERVER = 0x01;
   const unsigned short WIFI_CERT_USAGE_FLAG_USER   = 0x02;
 
   void start(in nsIWifiEventListener listener);
   void shutdown();
 
@@ -27,9 +27,20 @@ interface nsIWifiCertService : nsISuppor
    *        Password of certificate.
    * @param certNickname
    *        User assigned nickname for imported certificate.
    */
   void importCert(in long id,
                   in nsIDOMBlob certBlob,
                   in DOMString certPassword,
                   in DOMString certNickname);
+
+  /**
+   * Delete an imported certificate file
+   *
+   * @param id
+   *        Request ID.
+   * @param certNickname
+   *        Certificate nickname to delete.
+   */
+  void deleteCert(in long id,
+                  in DOMString certNickname);
 };
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3712,16 +3712,17 @@ WorkerPrivate::Constructor(const GlobalO
 nsresult
 WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
                            WorkerPrivate* aParent, const nsAString& aScriptURL,
                            bool aIsChromeWorker, LoadInfo* aLoadInfo)
 {
   using namespace mozilla::dom::workers::scriptloader;
 
   MOZ_ASSERT(aCx);
+  MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext());
 
   if (aWindow) {
     AssertIsOnMainThread();
   }
 
   LoadInfo loadInfo;
   nsresult rv;
 
@@ -3908,20 +3909,16 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
         }
       }
       loadInfo.mXHRParamsAllowed = true;
     }
 
     MOZ_ASSERT(loadInfo.mPrincipal);
     MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
 
-    // XXXbent Use subject principal here instead of the one we already have?
-    nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
-    MOZ_ASSERT(subjectPrincipal);
-
     if (!nsContentUtils::GetContentSecurityPolicy(aCx,
                                                getter_AddRefs(loadInfo.mCSP))) {
       NS_WARNING("Failed to get CSP!");
       return NS_ERROR_FAILURE;
     }
 
     if (loadInfo.mCSP) {
       rv = loadInfo.mCSP->GetAllowsEval(&loadInfo.mReportCSPViolations,
--- a/dom/workers/test/extensions/bootstrap/Makefile.in
+++ b/dom/workers/test/extensions/bootstrap/Makefile.in
@@ -1,18 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIST_FILES = \
+  bootstrap.js \
   install.rdf \
-  $(NULL)
-
-DIST_FILES_NO_PP = \
-  bootstrap.js \
   worker.js \
   $(NULL)
 
 TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
 
 GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/workers/test/extensions/traditional/Makefile.in
+++ b/dom/workers/test/extensions/traditional/Makefile.in
@@ -1,17 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIST_FILES = \
   install.rdf \
-  $(NULL)
-
-DIST_FILES_NO_PP = \
   worker.js \
   $(NULL)
 
 TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
 
 GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -1247,25 +1247,18 @@ txMozillaXSLTProcessor::ContentRemoved(n
 {
     mStylesheet = nullptr;
 }
 
 NS_IMETHODIMP
 txMozillaXSLTProcessor::Initialize(nsISupports* aOwner, JSContext* cx,
                                    JSObject* obj, const JS::CallArgs& args)
 {
-    nsCOMPtr<nsIPrincipal> prin;
-    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    NS_ENSURE_TRUE(secMan, NS_ERROR_UNEXPECTED);
-
-    nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(prin));
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED);
-
-    return Init(prin);
+    MOZ_ASSERT(nsContentUtils::GetCurrentJSContext());
+    return Init(nsContentUtils::GetSubjectPrincipal());
 }
 
 NS_IMETHODIMP
 txMozillaXSLTProcessor::Init(nsIPrincipal* aPrincipal)
 {
     NS_ENSURE_ARG_POINTER(aPrincipal);
     mPrincipal = aPrincipal;
 
--- a/embedding/components/commandhandler/src/nsCommandManager.cpp
+++ b/embedding/components/commandhandler/src/nsCommandManager.cpp
@@ -9,16 +9,17 @@
 #include "nsIControllers.h"
 #include "nsIObserver.h"
 
 #include "nsIComponentManager.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsIScriptSecurityManager.h"
 
+#include "nsContentUtils.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsIFocusManager.h"
 
 #include "nsCOMArray.h"
 
 #include "nsCommandManager.h"
@@ -232,47 +233,26 @@ nsCommandManager::DoCommand(const char *
   if (commandController && aCommandParams)
     rv = commandController->DoCommandWithParams(aCommandName, aCommandParams);
   else
     rv = controller->DoCommand(aCommandName);
   return rv;
 }
 
 nsresult
-nsCommandManager::IsCallerChrome(bool *is_caller_chrome)
-{
-  *is_caller_chrome = false;
-  nsresult rv = NS_OK;
-  nsCOMPtr<nsIScriptSecurityManager> secMan = 
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  if (NS_FAILED(rv))
-    return rv;
-  if (!secMan)
-    return NS_ERROR_FAILURE;
-
-  rv = secMan->SubjectPrincipalIsSystem(is_caller_chrome);
-  return rv;
-}
-
-nsresult
 nsCommandManager::GetControllerForCommand(const char *aCommand, 
                                           nsIDOMWindow *aTargetWindow,
                                           nsIController** outController)
 {
   nsresult rv = NS_ERROR_FAILURE;
   *outController = nullptr;
 
   // check if we're in content or chrome
   // if we're not chrome we must have a target window or we bail
-  bool isChrome = false;
-  rv = IsCallerChrome(&isChrome);
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (!isChrome) {
+  if (!nsContentUtils::IsCallerChrome()) {
     if (!aTargetWindow)
       return rv;
 
     // if a target window is specified, it must be the window we expect
     if (aTargetWindow != mWindow)
         return NS_ERROR_FAILURE;
   }
 
--- a/embedding/components/commandhandler/src/nsCommandManager.h
+++ b/embedding/components/commandhandler/src/nsCommandManager.h
@@ -38,18 +38,16 @@ public:
   NS_DECL_NSICOMMANDMANAGER
   
   // nsPICommandUpdater
   NS_DECL_NSPICOMMANDUPDATER
 
 
 protected:
 
-
-  nsresult  IsCallerChrome(bool *aIsCallerChrome);
   nsresult  GetControllerForCommand(const char * aCommand,
                                     nsIDOMWindow *aDirectedToThisWindow,
                                     nsIController** outController);
 
 
 protected:
   nsClassHashtable<nsCharPtrHashKey, ObserverList> mObserversTable;
 
--- a/embedding/components/find/src/nsWebBrowserFind.cpp
+++ b/embedding/components/find/src/nsWebBrowserFind.cpp
@@ -34,16 +34,17 @@
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsFind.h"
 #include "nsError.h"
 #include "nsFocusManager.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/Element.h"
 #include "nsISimpleEnumerator.h"
+#include "nsContentUtils.h"
 
 #if DEBUG
 #include "nsIWebNavigation.h"
 #include "nsXPIDLString.h"
 #endif
 
 //*****************************************************************************
 // nsWebBrowserFind
@@ -655,31 +656,18 @@ nsresult nsWebBrowserFind::SearchInFrame
 
     // Do security check, to ensure that the frame we're searching is
     // acccessible from the frame where the Find is being run.
 
     // get a uri for the window
     nsCOMPtr<nsIDocument> theDoc = do_QueryInterface(domDoc);
     if (!theDoc) return NS_ERROR_FAILURE;
 
-    nsCOMPtr<nsIScriptSecurityManager> secMan =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  
-    nsCOMPtr<nsIPrincipal> subject;
-    rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (subject) {
-        bool subsumes;
-        rv = subject->Subsumes(theDoc->NodePrincipal(), &subsumes);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (!subsumes) {
-            return NS_ERROR_DOM_PROP_ACCESS_DENIED;
-        }
+    if (!nsContentUtils::GetSubjectPrincipal()->Subsumes(theDoc->NodePrincipal())) {
+      return NS_ERROR_DOM_PROP_ACCESS_DENIED;
     }
 
     nsCOMPtr<nsIFind> find = do_CreateInstance(NS_FIND_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     (void) find->SetCaseSensitive(mMatchCase);
     (void) find->SetFindBackwards(mFindBackwards);
 
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -780,25 +780,24 @@ nsWindowWatcher::OpenWindowInternal(nsID
       newDocShellItem->SetName(name);
     } else {
       newDocShellItem->SetName(EmptyString());
     }
   }
 
   // Now we have to set the right opener principal on the new window.  Note
   // that we have to do this _before_ starting any URI loads, thanks to the
-  // sync nature of javascript: loads.  Since this is the only place where we
-  // set said opener principal, we need to do it for all URIs, including
-  // chrome ones.  So to deal with the mess that is bug 79775, just press on in
-  // a reasonable way even if GetSubjectPrincipal fails.  In that case, just
-  // use a null subjectPrincipal.
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  if (NS_FAILED(sm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)))) {
-    subjectPrincipal = nullptr;
-  }
+  // sync nature of javascript: loads.
+  //
+  // Note: The check for the current JSContext isn't necessarily sensical.
+  // It's just designed to preserve old semantics during a mass-conversion
+  // patch.
+  nsCOMPtr<nsIPrincipal> subjectPrincipal =
+    nsContentUtils::GetCurrentJSContext() ? nsContentUtils::GetSubjectPrincipal()
+                                          : nullptr;
 
   if (windowIsNew) {
     // Now set the opener principal on the new window.  Note that we need to do
     // this no matter whether we were opened from JS; if there is nothing on
     // the JS stack, just use the principal of our parent window.  In those
     // cases we do _not_ set the parent window principal as the owner of the
     // load--since we really don't know who the owner is, just leave it null.
     nsCOMPtr<nsPIDOMWindow> newWindow = do_QueryInterface(*_retval);
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -298,16 +298,29 @@ GLContext::~GLContext() {
         tip->SharedContextDestroyed(this);
         tip->ReportOutstandingNames();
     } else {
         ReportOutstandingNames();
     }
 #endif
 }
 
+/*static*/ void
+GLContext::StaticDebugCallback(GLenum source,
+                               GLenum type,
+                               GLuint id,
+                               GLenum severity,
+                               GLsizei length,
+                               const GLchar* message,
+                               const GLvoid* userParam)
+{
+    GLContext* gl = (GLContext*)userParam;
+    gl->DebugCallback(source, type, id, severity, length, message);
+}
+
 bool
 GLContext::InitWithPrefix(const char *prefix, bool trygl)
 {
     ScopedGfxFeatureReporter reporter("GL Context");
 
     if (mInitialized) {
         reporter.SetSuccessful();
         return true;
@@ -1154,32 +1167,132 @@ GLContext::InitWithPrefix(const char *pr
             DetermineCaps();
 
         UpdatePixelFormat();
         UpdateGLFormats(mCaps);
 
         mTexGarbageBin = new TextureGarbageBin(this);
 
         MOZ_ASSERT(IsCurrent());
+
+        if (DebugMode() && IsExtensionSupported(KHR_debug)) {
+            fEnable(LOCAL_GL_DEBUG_OUTPUT);
+            fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
+            fDebugMessageCallback(&StaticDebugCallback, (void*)this);
+            fDebugMessageControl(LOCAL_GL_DONT_CARE,
+                                 LOCAL_GL_DONT_CARE,
+                                 LOCAL_GL_DONT_CARE,
+                                 0, nullptr,
+                                 true);
+        }
     }
 
     if (mInitialized)
         reporter.SetSuccessful();
     else {
         // if initialization fails, ensure all symbols are zero, to avoid hard-to-understand bugs
         mSymbols.Zero();
         NS_WARNING("InitWithPrefix failed!");
     }
 
     mVersionString = nsPrintfCString("%u.%u.%u", mVersion / 100, (mVersion / 10) % 10, mVersion % 10);
 
     return mInitialized;
 }
 
 void
+GLContext::DebugCallback(GLenum source,
+                         GLenum type,
+                         GLuint id,
+                         GLenum severity,
+                         GLsizei length,
+                         const GLchar* message)
+{
+    nsAutoCString sourceStr;
+    switch (source) {
+    case LOCAL_GL_DEBUG_SOURCE_API:
+        sourceStr = NS_LITERAL_CSTRING("SOURCE_API");
+        break;
+    case LOCAL_GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+        sourceStr = NS_LITERAL_CSTRING("SOURCE_WINDOW_SYSTEM");
+        break;
+    case LOCAL_GL_DEBUG_SOURCE_SHADER_COMPILER:
+        sourceStr = NS_LITERAL_CSTRING("SOURCE_SHADER_COMPILER");
+        break;
+    case LOCAL_GL_DEBUG_SOURCE_THIRD_PARTY:
+        sourceStr = NS_LITERAL_CSTRING("SOURCE_THIRD_PARTY");
+        break;
+    case LOCAL_GL_DEBUG_SOURCE_APPLICATION:
+        sourceStr = NS_LITERAL_CSTRING("SOURCE_APPLICATION");
+        break;
+    case LOCAL_GL_DEBUG_SOURCE_OTHER:
+        sourceStr = NS_LITERAL_CSTRING("SOURCE_OTHER");
+        break;
+    default:
+        sourceStr = nsPrintfCString("<source 0x%04x>", source);
+        break;
+    }
+
+    nsAutoCString typeStr;
+    switch (type) {
+    case LOCAL_GL_DEBUG_TYPE_ERROR:
+        typeStr = NS_LITERAL_CSTRING("TYPE_ERROR");
+        break;
+    case LOCAL_GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+        typeStr = NS_LITERAL_CSTRING("TYPE_DEPRECATED_BEHAVIOR");
+        break;
+    case LOCAL_GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+        typeStr = NS_LITERAL_CSTRING("TYPE_UNDEFINED_BEHAVIOR");
+        break;
+    case LOCAL_GL_DEBUG_TYPE_PORTABILITY:
+        typeStr = NS_LITERAL_CSTRING("TYPE_PORTABILITY");
+        break;
+    case LOCAL_GL_DEBUG_TYPE_PERFORMANCE:
+        typeStr = NS_LITERAL_CSTRING("TYPE_PERFORMANCE");
+        break;
+    case LOCAL_GL_DEBUG_TYPE_OTHER:
+        typeStr = NS_LITERAL_CSTRING("TYPE_OTHER");
+        break;
+    case LOCAL_GL_DEBUG_TYPE_MARKER:
+        typeStr = NS_LITERAL_CSTRING("TYPE_MARKER");
+        break;
+    default:
+        typeStr = nsPrintfCString("<type 0x%04x>", type);
+        break;
+    }
+
+    nsAutoCString sevStr;
+    switch (severity) {
+    case LOCAL_GL_DEBUG_SEVERITY_HIGH:
+        sevStr = NS_LITERAL_CSTRING("SEVERITY_HIGH");
+        break;
+    case LOCAL_GL_DEBUG_SEVERITY_MEDIUM:
+        sevStr = NS_LITERAL_CSTRING("SEVERITY_MEDIUM");
+        break;
+    case LOCAL_GL_DEBUG_SEVERITY_LOW:
+        sevStr = NS_LITERAL_CSTRING("SEVERITY_LOW");
+        break;
+    case LOCAL_GL_DEBUG_SEVERITY_NOTIFICATION:
+        sevStr = NS_LITERAL_CSTRING("SEVERITY_NOTIFICATION");
+        break;
+    default:
+        sevStr = nsPrintfCString("<severity 0x%04x>", severity);
+        break;
+    }
+
+    printf_stderr("[KHR_debug: 0x%x] ID %u: %s %s %s:\n    %s",
+                  (uintptr_t)this,
+                  id,
+                  sourceStr.BeginReading(),
+                  typeStr.BeginReading(),
+                  sevStr.BeginReading(),
+                  message);
+}
+
+void
 GLContext::InitExtensions()
 {
     MakeCurrent();
     const char* extensions = (const char*)fGetString(LOCAL_GL_EXTENSIONS);
     if (!extensions)
         return;
 
 #ifdef DEBUG
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -577,16 +577,30 @@ public:
 
 
 #ifdef DEBUG
 private:
 
     GLenum mGLError;
 #endif // DEBUG
 
+    static void GLAPIENTRY StaticDebugCallback(GLenum source,
+                                               GLenum type,
+                                               GLuint id,
+                                               GLenum severity,
+                                               GLsizei length,
+                                               const GLchar* message,
+                                               const GLvoid* userParam);
+    void DebugCallback(GLenum source,
+                       GLenum type,
+                       GLuint id,
+                       GLenum severity,
+                       GLsizei length,
+                       const GLchar* message);
+
 
 // -----------------------------------------------------------------------------
 // MOZ_GL_DEBUG implementation
 private:
 
 #undef BEFORE_GL_CALL
 #undef AFTER_GL_CALL
 
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -200,16 +200,24 @@ public:
     return mCompositionBounds.Size() / GetZoomToParent();
   }
 
   CSSRect CalculateCompositedRectInCssPixels() const
   {
     return mCompositionBounds / GetZoomToParent();
   }
 
+  CSSSize CalculateBoundedCompositedSizeInCssPixels() const
+  {
+    CSSSize size = CalculateCompositedSizeInCssPixels();
+    size.width = std::min(size.width, mRootCompositionSize.width);
+    size.height = std::min(size.height, mRootCompositionSize.height);
+    return size;
+  }
+
   void ScrollBy(const CSSPoint& aPoint)
   {
     mScrollOffset += aPoint;
   }
 
   void ZoomBy(float aFactor)
   {
     mZoom.scale *= aFactor;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1480,21 +1480,17 @@ RedistributeDisplayPortExcess(CSSSize& a
 }
 
 /* static */
 const LayerMargin AsyncPanZoomController::CalculatePendingDisplayPort(
   const FrameMetrics& aFrameMetrics,
   const ScreenPoint& aVelocity,
   double aEstimatedPaintDuration)
 {
-  CSSSize compositionBounds = aFrameMetrics.CalculateCompositedSizeInCssPixels();
-  CSSSize compositionSize = aFrameMetrics.GetRootCompositionSize();
-  compositionSize =
-    CSSSize(std::min(compositionBounds.width, compositionSize.width),
-            std::min(compositionBounds.height, compositionSize.height));
+  CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
   CSSPoint velocity = aVelocity / aFrameMetrics.GetZoom();
   CSSPoint scrollOffset = aFrameMetrics.GetScrollOffset();
   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
 
   // Calculate the displayport size based on how fast we're moving along each axis.
   CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
 
   if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
@@ -1590,20 +1586,17 @@ void AsyncPanZoomController::RequestCont
 }
 
 /*static*/ CSSRect
 GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
 {
   // This computation is based on what happens in CalculatePendingDisplayPort. If that
   // changes then this might need to change too
   CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
-                   CSSSize(std::min(aFrameMetrics.CalculateCompositedSizeInCssPixels().width,
-                                    aFrameMetrics.GetRootCompositionSize().width),
-                           std::min(aFrameMetrics.CalculateCompositedSizeInCssPixels().height,
-                                    aFrameMetrics.GetRootCompositionSize().height)));
+                   aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels());
   baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.LayersPixelsPerCSSPixel());
   return baseRect;
 }
 
 void
 AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics) {
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -236,17 +236,21 @@ SharedFrameMetricsHelper::FindFallbackCo
   aZoom = contentMetrics->GetZoomToParent();  // TODO(botond): double-check this
   return;
 }
 
 bool
 SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
                                               const FrameMetrics& aCompositorMetrics)
 {
-  return !aContentMetrics.mDisplayPort.Contains(aCompositorMetrics.CalculateCompositedRectInCssPixels() - aCompositorMetrics.GetScrollOffset());
+  CSSRect painted =
+        (aContentMetrics.mCriticalDisplayPort.IsEmpty() ? aContentMetrics.mDisplayPort : aContentMetrics.mCriticalDisplayPort)
+        + aContentMetrics.GetScrollOffset();
+  CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(), aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
+  return !painted.Contains(showing);
 }
 
 ClientTiledLayerBuffer::ClientTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer,
                                              CompositableClient* aCompositableClient,
                                              ClientLayerManager* aManager,
                                              SharedFrameMetricsHelper* aHelper)
   : mThebesLayer(aThebesLayer)
   , mCompositableClient(aCompositableClient)
--- a/image/src/imgDecoderObserver.h
+++ b/image/src/imgDecoderObserver.h
@@ -28,21 +28,20 @@ struct nsIntRect;
  * relied upon.
  *
  * Decode notifications may or may not be synchronous, depending on the
  * situation. If imgIDecoder::FLAG_SYNC_DECODE is passed to a function that
  * triggers a decode, all notifications that can be generated from the currently
  * loaded data fire before the call returns. If FLAG_SYNC_DECODE is not passed,
  * all, some, or none of the notifications may fire before the call returns.
  */
-class imgDecoderObserver : public mozilla::RefCounted<imgDecoderObserver>
+class imgDecoderObserver
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(imgDecoderObserver)
-  virtual ~imgDecoderObserver() = 0;
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgDecoderObserver);
 
   /**
    * Load notification.
    *
    * called at the same time that nsIRequestObserver::onStartRequest would be
    * (used only for observers of imgIRequest objects, which are nsIRequests,
    * not imgIDecoder objects)
    */
@@ -121,16 +120,19 @@ public:
    * Called when we are asked to Draw an image that is not locked.
    */
   virtual void OnUnlockedDraw() = 0;
 
   /**
    * Called when an image is realized to be in error state.
    */
   virtual void OnError() = 0;
+
+protected:
+  virtual ~imgDecoderObserver() = 0;
 };
 
 // We must define a destructor because derived classes call our destructor from
 // theirs.  Pure virtual destructors only requires that child classes implement
 // a virtual destructor, not that we can't have one too!
 inline imgDecoderObserver::~imgDecoderObserver()
 {}
 
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -24,18 +24,16 @@ class imgStatusTrackerObserver : public 
 {
 public:
   imgStatusTrackerObserver(imgStatusTracker* aTracker)
   : mTracker(aTracker->asWeakPtr())
   {
     MOZ_ASSERT(aTracker);
   }
 
-  virtual ~imgStatusTrackerObserver() {}
-
   void SetTracker(imgStatusTracker* aTracker)
   {
     MOZ_ASSERT(aTracker);
     mTracker = aTracker->asWeakPtr();
   }
 
   /** imgDecoderObserver methods **/
 
@@ -138,16 +136,19 @@ public:
   virtual void OnError() MOZ_OVERRIDE
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnError");
     nsRefPtr<imgStatusTracker> tracker = mTracker.get();
     if (!tracker) { return; }
     tracker->RecordError();
   }
 
+protected:
+  virtual ~imgStatusTrackerObserver() {}
+
 private:
   WeakPtr<imgStatusTracker> mTracker;
 };
 
 // imgStatusTracker methods
 
 imgStatusTracker::imgStatusTracker(Image* aImage)
   : mImage(aImage),
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1006205.js
@@ -0,0 +1,20 @@
+var lfcode = new Array();
+lfcode.push = loadFile;
+lfcode.push("\
+var g = newGlobal();\
+g.debuggeeGlobal = this;\
+g.eval(\"(\" + function () {\
+        dbg = new Debugger(debuggeeGlobal);\
+    } + \")();\");\
+");
+lfcode.push("gc();");
+lfcode.push("\
+var g = newGlobal();\
+g.debuggeeGlobal = this;\
+g.eval(\"(\" + function () {\
+  dbg = new Debugger(debuggeeGlobal);\
+} + \")();\");\
+");
+function loadFile(lfVarx) {
+function newFunc(x) { new Function(x)(); }; newFunc(lfVarx);
+}
--- a/js/src/jit-test/tests/debug/optimized-out-01.js
+++ b/js/src/jit-test/tests/debug/optimized-out-01.js
@@ -20,19 +20,20 @@ withJitOptions(Opts_Ion2NoParallelCompil
       var frame = dbg.getNewestFrame();
       assertEq(frame.implementation, "ion");
       // x is unused and should be elided.
       assertEq(frame.environment.getVariable("x").optimizedOut, true);
       assertEq(frame.arguments[1].optimizedOut, true);
     }
   };
 
-  g.eval("" + function f(d, x) { g(d, x); });
+  g.eval("" + function f(d, x) { "use strict"; g(d, x); });
 
   g.eval("" + function g(d, x) {
+    "use strict";
     for (var i = 0; i < 200; i++);
     // Hack to prevent inlining.
     function inner() { i = 42; };
     toggle(d);
   });
 
   g.eval("(" + function test() {
     for (i = 0; i < 5; i++)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1001382.js
@@ -0,0 +1,10 @@
+var i = 0;
+var expectedmatch = '';
+var expectedmatches = new Array();
+addThis();
+expectedmatch = null;
+addThis();
+function addThis() {
+  Array(-2147483648, -2147483648);
+  expectedmatches[i] = expectedmatch;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1005458.js
@@ -0,0 +1,5 @@
+(function(x) {
+    for (var y = 0; y < 1; y++) {
+        assertEq(Array.prototype.shift.call(arguments.callee.arguments), 0);
+    }
+})(0)
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -162,20 +162,20 @@ InvokeFromAsmJS_ToInt32(JSContext *cx, i
 int32_t
 InvokeFromAsmJS_ToNumber(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv);
 
 }
 
 #if defined(JS_CODEGEN_ARM)
 extern "C" {
 
-extern int64_t
+extern MOZ_EXPORT int64_t
 __aeabi_idivmod(int, int);
 
-extern int64_t
+extern MOZ_EXPORT int64_t
 __aeabi_uidivmod(int, int);
 
 }
 #endif
 
 template <class F>
 static inline void *
 FuncCast(F *pf)
@@ -1241,17 +1241,17 @@ struct ScopedCacheEntryOpenedForWrite
     intptr_t handle;
 
     ScopedCacheEntryOpenedForWrite(ExclusiveContext *cx, size_t serializedSize)
       : cx(cx), serializedSize(serializedSize), memory(nullptr), handle(-1)
     {}
 
     ~ScopedCacheEntryOpenedForWrite() {
         if (memory)
-            cx->asmJSCacheOps().closeEntryForWrite(cx->global(), serializedSize, memory, handle);
+            cx->asmJSCacheOps().closeEntryForWrite(serializedSize, memory, handle);
     }
 };
 
 bool
 js::StoreAsmJSModuleInCache(AsmJSParser &parser,
                             const AsmJSModule &module,
                             ExclusiveContext *cx)
 {
@@ -1298,17 +1298,17 @@ struct ScopedCacheEntryOpenedForRead
     intptr_t handle;
 
     ScopedCacheEntryOpenedForRead(ExclusiveContext *cx)
       : cx(cx), serializedSize(0), memory(nullptr), handle(0)
     {}
 
     ~ScopedCacheEntryOpenedForRead() {
         if (memory)
-            cx->asmJSCacheOps().closeEntryForRead(cx->global(), serializedSize, memory, handle);
+            cx->asmJSCacheOps().closeEntryForRead(serializedSize, memory, handle);
     }
 };
 
 bool
 js::LookupAsmJSModuleInCache(ExclusiveContext *cx,
                              AsmJSParser &parser,
                              ScopedJSDeletePtr<AsmJSModule> *moduleOut,
                              ScopedJSFreePtr<char> *compilationTimeReport)
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -88,16 +88,20 @@ struct DebugModeOSREntry
 
     bool needsRecompileInfo() const {
         return (frameKind == ICEntry::Kind_CallVM ||
                 frameKind == ICEntry::Kind_DebugTrap ||
                 frameKind == ICEntry::Kind_DebugPrologue ||
                 frameKind == ICEntry::Kind_DebugEpilogue);
     }
 
+    bool recompiled() const {
+        return oldBaselineScript != script->baselineScript();
+    }
+
     BaselineDebugModeOSRInfo *takeRecompInfo() {
         MOZ_ASSERT(recompInfo);
         BaselineDebugModeOSRInfo *tmp = recompInfo;
         recompInfo = nullptr;
         return tmp;
     }
 
     bool allocateRecompileInfo(JSContext *cx) {
@@ -203,28 +207,27 @@ ICEntryKindToString(ICEntry::Kind kind)
     }
 }
 
 static void
 SpewPatchBaselineFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
                        JSScript *script, ICEntry::Kind frameKind, jsbytecode *pc)
 {
     IonSpew(IonSpew_BaselineDebugModeOSR,
-            "Patch return %#016llx -> %#016llx to BaselineJS (%s:%d) from %s at %s",
-            uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress),
-            script->filename(), script->lineno(),
+            "Patch return %p -> %p on BaselineJS frame (%s:%d) from %s at %s",
+            oldReturnAddress, newReturnAddress, script->filename(), script->lineno(),
             ICEntryKindToString(frameKind), js_CodeName[(JSOp)*pc]);
 }
 
 static void
 SpewPatchStubFrame(ICStub *oldStub, ICStub *newStub)
 {
     IonSpew(IonSpew_BaselineDebugModeOSR,
-            "Patch   stub %#016llx -> %#016llx to BaselineStub (%s)",
-            uintptr_t(oldStub), uintptr_t(newStub), ICStub::KindString(newStub->kind()));
+            "Patch   stub %p -> %p on BaselineStub frame (%s)",
+            oldStub, newStub, ICStub::KindString(newStub->kind()));
 }
 
 static void
 PatchBaselineFramesForDebugMode(JSContext *cx, const JitActivationIterator &activation,
                                 DebugModeOSREntryVector &entries, size_t *start)
 {
     //
     // Recompile Patching Overview
@@ -249,18 +252,25 @@ PatchBaselineFramesForDebugMode(JSContex
     //
 
     IonCommonFrameLayout *prev = nullptr;
     size_t entryIndex = *start;
     DebugOnly<bool> expectedDebugMode = cx->compartment()->debugMode();
 
     for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
         DebugModeOSREntry &entry = entries[entryIndex];
+
         switch (iter.type()) {
           case JitFrame_BaselineJS: {
+            // If the script wasn't recompiled, there's nothing to patch.
+            if (!entry.recompiled()) {
+                entryIndex++;
+                break;
+            }
+
             JSScript *script = entry.script;
             uint32_t pcOffset = entry.pcOffset;
             jsbytecode *pc = script->offsetToPC(pcOffset);
 
             MOZ_ASSERT(script == iter.script());
             MOZ_ASSERT(pcOffset < script->length());
             MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
 
@@ -346,16 +356,20 @@ PatchBaselineFramesForDebugMode(JSContex
             prev->setReturnAddress(reinterpret_cast<uint8_t *>(handlerAddr));
             iter.baselineFrame()->setDebugModeOSRInfo(recompInfo);
 
             entryIndex++;
             break;
           }
 
           case JitFrame_BaselineStub: {
+            // If the script wasn't recompiled, there's nothing to patch.
+            if (!entry.recompiled())
+                break;
+
             IonBaselineStubFrameLayout *layout =
                 reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp());
             MOZ_ASSERT(entry.script->baselineScript()->debugMode() == expectedDebugMode);
             MOZ_ASSERT(layout->maybeStubPtr() == entry.oldStub);
 
             // Patch baseline stub frames for case A above.
             //
             // We need to patch the stub frame to point to an ICStub belonging
@@ -534,17 +548,17 @@ static void
 UndoRecompileBaselineScriptsForDebugMode(JSContext *cx,
                                          const DebugModeOSREntryVector &entries)
 {
     // In case of failure, roll back the entire set of active scripts so that
     // we don't have to patch return addresses on the stack.
     for (size_t i = 0; i < entries.length(); i++) {
         JSScript *script = entries[i].script;
         BaselineScript *baselineScript = script->baselineScript();
-        if (baselineScript != entries[i].oldBaselineScript) {
+        if (entries[i].recompiled()) {
             script->setBaselineScript(cx, entries[i].oldBaselineScript);
             BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), baselineScript);
         }
     }
 }
 
 bool
 jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp)
@@ -582,18 +596,20 @@ jit::RecompileOnStackBaselineScriptsForD
         }
     }
 
     // If all recompiles succeeded, destroy the old baseline scripts and patch
     // the live frames.
     //
     // After this point the function must be infallible.
 
-    for (size_t i = 0; i < entries.length(); i++)
-        BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entries[i].oldBaselineScript);
+    for (size_t i = 0; i < entries.length(); i++) {
+        if (entries[i].recompiled())
+            BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entries[i].oldBaselineScript);
+    }
 
     size_t processed = 0;
     for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
         if (iter.activation()->compartment() == comp)
             PatchBaselineFramesForDebugMode(cx, iter, entries, &processed);
     }
     MOZ_ASSERT(processed == entries.length());
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1266,16 +1266,24 @@ ICTypeMonitor_Fallback::addMonitorStubFo
 
     return true;
 }
 
 static bool
 DoTypeMonitorFallback(JSContext *cx, BaselineFrame *frame, ICTypeMonitor_Fallback *stub,
                       HandleValue value, MutableHandleValue res)
 {
+    // It's possible that we arrived here from bailing out of Ion, and that
+    // Ion proved that the value is dead and optimized out. In such cases, do
+    // nothing.
+    if (value.isMagic(JS_OPTIMIZED_OUT)) {
+        res.set(value);
+        return true;
+    }
+
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     TypeFallbackICSpew(cx, stub, "TypeMonitor");
 
     uint32_t argument;
     if (stub->monitorsThis()) {
         JS_ASSERT(pc == script->code());
         types::TypeScript::SetThis(cx, script, value);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6829,16 +6829,17 @@ CodeGenerator::visitStoreFixedSlotT(LSto
 bool
 CodeGenerator::visitCallsiteCloneCache(LCallsiteCloneCache *ins)
 {
     const MCallsiteCloneCache *mir = ins->mir();
     Register callee = ToRegister(ins->callee());
     Register output = ToRegister(ins->output());
 
     CallsiteCloneIC cache(callee, mir->block()->info().script(), mir->callPc(), output);
+    cache.setProfilerLeavePC(mir->profilerLeavePc());
     return addCache(ins, allocateCache(cache));
 }
 
 typedef JSObject *(*CallsiteCloneICFn)(JSContext *, size_t, HandleObject);
 const VMFunction CallsiteCloneIC::UpdateInfo =
     FunctionInfo<CallsiteCloneICFn>(CallsiteCloneIC::update);
 
 bool
@@ -6862,16 +6863,17 @@ bool
 CodeGenerator::visitGetNameCache(LGetNameCache *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register scopeChain = ToRegister(ins->scopeObj());
     TypedOrValueRegister output(GetValueOutput(ins));
     bool isTypeOf = ins->mir()->accessKind() != MGetNameCache::NAME;
 
     NameIC cache(liveRegs, isTypeOf, scopeChain, ins->mir()->name(), output);
+    cache.setProfilerLeavePC(ins->mir()->profilerLeavePc());
     return addCache(ins, allocateCache(cache));
 }
 
 typedef bool (*NameICFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
 const VMFunction NameIC::UpdateInfo = FunctionInfo<NameICFn>(NameIC::update);
 
 bool
 CodeGenerator::visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic)
@@ -6888,94 +6890,103 @@ CodeGenerator::visitNameIC(OutOfLineUpda
 
     masm.jump(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                                    PropertyName *name, TypedOrValueRegister output,
-                                   bool monitoredResult)
+                                   bool monitoredResult, jsbytecode *profilerLeavePc)
 {
     switch (gen->info().executionMode()) {
       case SequentialExecution: {
         GetPropertyIC cache(liveRegs, objReg, name, output, monitoredResult);
+        cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       case ParallelExecution: {
         GetPropertyParIC cache(objReg, name, output);
+        cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
 
 bool
 CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                                    PropertyName *name, ConstantOrRegister value, bool strict,
-                                   bool needsTypeBarrier)
+                                   bool needsTypeBarrier, jsbytecode *profilerLeavePc)
 {
     switch (gen->info().executionMode()) {
       case SequentialExecution: {
           SetPropertyIC cache(liveRegs, objReg, name, value, strict, needsTypeBarrier);
+            cache.setProfilerLeavePC(profilerLeavePc);
           return addCache(ins, allocateCache(cache));
       }
       case ParallelExecution: {
           SetPropertyParIC cache(objReg, name, value, strict, needsTypeBarrier);
+            cache.setProfilerLeavePC(profilerLeavePc);
           return addCache(ins, allocateCache(cache));
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
 
 bool
 CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
                                   Register temp, FloatRegister tempFloat, ValueOperand index,
-                                  ConstantOrRegister value, bool strict, bool guardHoles)
+                                  ConstantOrRegister value, bool strict, bool guardHoles,
+                                  jsbytecode *profilerLeavePc)
 {
     switch (gen->info().executionMode()) {
       case SequentialExecution: {
         SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
                            guardHoles);
+        cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       case ParallelExecution: {
         SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
                               guardHoles);
+        cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
 
 bool
 CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     PropertyName *name = ins->mir()->name();
     bool monitoredResult = ins->mir()->monitoredResult();
     TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
 
-    return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult);
+    return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult,
+                               ins->mir()->profilerLeavePc());
 }
 
 bool
 CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     PropertyName *name = ins->mir()->name();
     bool monitoredResult = ins->mir()->monitoredResult();
     TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->getDef(0)));
 
-    return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult);
+    return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult,
+                               ins->mir()->profilerLeavePc());
 }
 
 typedef bool (*GetPropertyICFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
 const VMFunction GetPropertyIC::UpdateInfo =
     FunctionInfo<GetPropertyICFn>(GetPropertyIC::update);
 
 bool
 CodeGenerator::visitGetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyIC> &ic)
@@ -7021,53 +7032,57 @@ CodeGenerator::visitGetPropertyParIC(Out
 
     masm.jump(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
                                   TypedOrValueRegister output, bool monitoredResult,
-                                  bool allowDoubleResult)
+                                  bool allowDoubleResult, jsbytecode *profilerLeavePc)
 {
     switch (gen->info().executionMode()) {
       case SequentialExecution: {
         RegisterSet liveRegs = ins->safepoint()->liveRegs();
         GetElementIC cache(liveRegs, obj, index, output, monitoredResult, allowDoubleResult);
+        cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       case ParallelExecution: {
         GetElementParIC cache(obj, index, output, monitoredResult, allowDoubleResult);
+        cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 }
 
 bool
 CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
 {
     Register obj = ToRegister(ins->object());
     ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
     TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
     const MGetElementCache *mir = ins->mir();
 
-    return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult());
+    return addGetElementCache(ins, obj, index, output, mir->monitoredResult(),
+                              mir->allowDoubleResult(), mir->profilerLeavePc());
 }
 
 bool
 CodeGenerator::visitGetElementCacheT(LGetElementCacheT *ins)
 {
     Register obj = ToRegister(ins->object());
     ConstantOrRegister index = TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index()));
     TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
     const MGetElementCache *mir = ins->mir();
 
-    return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult());
+    return addGetElementCache(ins, obj, index, output, mir->monitoredResult(),
+                              mir->allowDoubleResult(), mir->profilerLeavePc());
 }
 
 typedef bool (*GetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue);
 const VMFunction GetElementIC::UpdateInfo =
     FunctionInfo<GetElementICFn>(GetElementIC::update);
 
 bool
 CodeGenerator::visitGetElementIC(OutOfLineUpdateCache *ool, DataPtr<GetElementIC> &ic)
@@ -7093,17 +7108,18 @@ CodeGenerator::visitSetElementCacheV(LSe
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
     FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
     ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
 
     return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
-                              ins->mir()->strict(), ins->mir()->guardHoles());
+                              ins->mir()->strict(), ins->mir()->guardHoles(),
+                              ins->mir()->profilerLeavePc());
 }
 
 bool
 CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
@@ -7112,17 +7128,18 @@ CodeGenerator::visitSetElementCacheT(LSe
     ConstantOrRegister value;
     const LAllocation *tmp = ins->value();
     if (tmp->isConstant())
         value = *tmp->toConstant();
     else
         value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
 
     return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
-                              ins->mir()->strict(), ins->mir()->guardHoles());
+                              ins->mir()->strict(), ins->mir()->guardHoles(),
+                              ins->mir()->profilerLeavePc());
 }
 
 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
 const VMFunction SetElementIC::UpdateInfo =
     FunctionInfo<SetElementICFn>(SetElementIC::update);
 
 bool
 CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic)
@@ -7188,16 +7205,17 @@ CodeGenerator::visitGetElementParIC(OutO
 }
 
 bool
 CodeGenerator::visitBindNameCache(LBindNameCache *ins)
 {
     Register scopeChain = ToRegister(ins->scopeChain());
     Register output = ToRegister(ins->output());
     BindNameIC cache(scopeChain, ins->mir()->name(), output);
+    cache.setProfilerLeavePC(ins->mir()->profilerLeavePc());
 
     return addCache(ins, allocateCache(cache));
 }
 
 typedef JSObject *(*BindNameICFn)(JSContext *, size_t, HandleObject);
 const VMFunction BindNameIC::UpdateInfo =
     FunctionInfo<BindNameICFn>(BindNameIC::update);
 
@@ -7282,33 +7300,35 @@ CodeGenerator::visitCallDeleteElement(LC
 bool
 CodeGenerator::visitSetPropertyCacheV(LSetPropertyCacheV *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetPropertyCacheV::Value));
 
     return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
-                               ins->mir()->strict(), ins->mir()->needsTypeBarrier());
+                               ins->mir()->strict(), ins->mir()->needsTypeBarrier(),
+                               ins->mir()->profilerLeavePc());
 }
 
 bool
 CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     ConstantOrRegister value;
 
     if (ins->getOperand(1)->isConstant())
         value = ConstantOrRegister(*ins->getOperand(1)->toConstant());
     else
         value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1)));
 
     return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
-                               ins->mir()->strict(), ins->mir()->needsTypeBarrier());
+                               ins->mir()->strict(), ins->mir()->needsTypeBarrier(),
+                               ins->mir()->profilerLeavePc());
 }
 
 typedef bool (*SetPropertyICFn)(JSContext *, size_t, HandleObject, HandleValue);
 const VMFunction SetPropertyIC::UpdateInfo =
     FunctionInfo<SetPropertyICFn>(SetPropertyIC::update);
 
 bool
 CodeGenerator::visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -348,26 +348,26 @@ class CodeGenerator : public CodeGenerat
     bool visitAssertRangeF(LAssertRangeF *ins);
     bool visitAssertRangeV(LAssertRangeV *ins);
 
     bool visitRecompileCheck(LRecompileCheck *ins);
 
   private:
     bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, TypedOrValueRegister output,
-                             bool monitoredResult);
+                             bool monitoredResult, jsbytecode *profilerLeavePc);
     bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
                             TypedOrValueRegister output, bool monitoredResult,
-                            bool allowDoubleResult);
+                            bool allowDoubleResult, jsbytecode *profilerLeavePc);
     bool addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, ConstantOrRegister value, bool strict,
-                             bool needsTypeBarrier);
+                             bool needsTypeBarrier, jsbytecode *profilerLeavePc);
     bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
                             FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
-                            bool strict, bool guardHoles);
+                            bool strict, bool guardHoles, jsbytecode *profilerLeavePc);
     bool checkForAbortPar(LInstruction *lir);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
     bool emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
                                 Register tempReg1, Register tempReg2,
                                 JSObject *templateObj);
 
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -75,16 +75,25 @@ class InlineScriptTree {
 
     InlineScriptTree *addCallee(TempAllocator *allocator, jsbytecode *callerPc,
                                  JSScript *calleeScript);
 
     InlineScriptTree *caller() const {
         return caller_;
     }
 
+    bool isOutermostCaller() const {
+        return caller_ == nullptr;
+    }
+    InlineScriptTree *outermostCaller() {
+        if (isOutermostCaller())
+            return this;
+        return caller_->outermostCaller();
+    }
+
     jsbytecode *callerPc() const {
         return callerPc_;
     }
 
     JSScript *script() const {
         return script_;
     }
 
@@ -387,16 +396,25 @@ class CompileInfo
     bool executionModeIsAnalysis() const {
         return executionMode_ == DefinitePropertiesAnalysis || executionMode_ == ArgumentsUsageAnalysis;
     }
 
     bool isParallelExecution() const {
         return executionMode_ == ParallelExecution;
     }
 
+    bool canOptimizeOutSlot(uint32_t i) const {
+        if (script()->strict())
+            return true;
+
+        // Function.arguments can be used to access all arguments in
+        // non-strict scripts, so we can't optimize out any arguments.
+        return !(firstArgSlot() <= i && i - firstArgSlot() < nargs());
+    }
+
   private:
     unsigned nimplicit_;
     unsigned nargs_;
     unsigned nfixedvars_;
     unsigned nlocals_;
     unsigned nstack_;
     unsigned nslots_;
     JSScript *script_;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -138,26 +138,22 @@ jit::EliminateDeadResumePointOperands(MI
                     !mrp->instruction() ||
                     mrp->instruction() == *ins ||
                     mrp->instruction()->id() <= maxDefinition)
                 {
                     uses++;
                     continue;
                 }
 
-                // Function.arguments can be used to access all arguments in
-                // non-strict scripts, so we can't optimize out any arguments.
-                CompileInfo &info = block->info();
-                if (!info.script()->strict()) {
-                    uint32_t slot = uses->index();
-                    uint32_t firstArgSlot = info.firstArgSlot();
-                    if (firstArgSlot <= slot && slot - firstArgSlot < info.nargs()) {
-                        uses++;
-                        continue;
-                    }
+                // The operand is an uneliminable slot. This currently
+                // includes argument slots in non-strict scripts (due to being
+                // observable via Function.arguments).
+                if (!block->info().canOptimizeOutSlot(uses->index())) {
+                    uses++;
+                    continue;
                 }
 
                 // Store an optimized out magic value in place of all dead
                 // resume point operands. Making any such substitution can in
                 // general alter the interpreter's behavior, even though the
                 // code is dead, as the interpreter will still execute opcodes
                 // whose effects cannot be observed. If the magic value value
                 // were to flow to, say, a dead property access the
@@ -249,28 +245,21 @@ IsPhiObservable(MPhi *phi, Observability
     // arguments object during bailout. If we've already created an arguments
     // object (or got one via OSR), preserve that as well.
     if (fun && info.hasArguments() &&
         (slot == info.scopeChainSlot() || slot == info.argsObjSlot()))
     {
         return true;
     }
 
-    // If the Phi is one of the formal argument, and we are using an argument
-    // object in the function. The phi might be observable after a bailout.
-    // For inlined frames this is not needed, as they are captured in the inlineResumePoint.
-    if (fun && info.hasArguments()) {
-        uint32_t first = info.firstArgSlot();
-        if (first <= slot && slot - first < info.nargs()) {
-            // If arguments obj aliases formals, then the arg slots will never be used.
-            if (info.argsObjAliasesFormals())
-                return false;
-            return true;
-        }
-    }
+    // The Phi is an uneliminable slot. Currently this includes argument slots
+    // in non-strict scripts (due to being observable via Function.arguments).
+    if (fun && !info.canOptimizeOutSlot(slot))
+        return true;
+
     return false;
 }
 
 // Handles cases like:
 //    x is phi(a, x) --> a
 //    x is phi(a, a) --> a
 static inline MDefinition *
 IsPhiRedundant(MPhi *phi)
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -27,16 +27,18 @@
 #include "vm/Interpreter-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::tl::FloorLog2;
 
+typedef Rooted<TypedArrayObject *> RootedTypedArrayObject;
+
 void
 CodeLocationJump::repoint(JitCode *code, MacroAssembler *masm)
 {
     JS_ASSERT(state_ == Relative);
     size_t new_off = (size_t)raw_;
 #ifdef JS_SMALL_BRANCH
     size_t jumpTableEntryOffset = reinterpret_cast<size_t>(jumpTableEntry_);
 #endif
@@ -1190,33 +1192,35 @@ GetPropertyIC::allowArrayLength(Context 
         if (!bcTypes->hasType(types::Type::Int32Type()))
             return false;
     }
 
     return true;
 }
 
 bool
-GetPropertyIC::tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj,
-                               HandlePropertyName name, void *returnAddr, bool *emitted)
+GetPropertyIC::tryAttachNative(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                               HandleObject obj, HandlePropertyName name,
+                               void *returnAddr, bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
+    JS_ASSERT(outerScript->ionScript() == ion);
 
     RootedShape shape(cx);
     RootedObject holder(cx);
 
     NativeGetPropCacheability type =
         CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape);
     if (type == CanAttachNone)
         return true;
 
     *emitted = true;
 
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
 
     RepatchStubAppender attacher(*this);
     const char *attachKind;
 
     switch (type) {
       case CanAttachReadSlot:
         GenerateReadSlot(cx, ion, masm, attacher, obj, holder,
                          shape, object(), output());
@@ -1239,18 +1243,18 @@ GetPropertyIC::tryAttachNative(JSContext
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Bad NativeGetPropCacheability");
     }
     return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
 }
 
 bool
-GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, IonScript *ion, HandleObject obj,
-                                         HandlePropertyName name, bool *emitted)
+GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                         HandleObject obj, HandlePropertyName name, bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
 
     if (!obj->is<TypedArrayObject>())
         return true;
 
     if (cx->names().length != name)
@@ -1265,17 +1269,17 @@ GetPropertyIC::tryAttachTypedArrayLength
         return true;
     }
 
     if (idempotent())
         return true;
 
     *emitted = true;
 
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
 
     JS_ASSERT(!hasTypedArrayLengthStub_);
     hasTypedArrayLengthStub_ = true;
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array length");
 }
 
@@ -1348,33 +1352,33 @@ EmitCallProxyGet(JSContext *cx, MacroAss
     // masm.leaveExitFrame & pop locals
     masm.adjustStack(IonOOLProxyExitFrameLayout::Size());
 
     masm.icRestoreLive(liveRegs, aic);
     return true;
 }
 
 bool
-GetPropertyIC::tryAttachDOMProxyShadowed(JSContext *cx, IonScript *ion,
+GetPropertyIC::tryAttachDOMProxyShadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                          HandleObject obj, void *returnAddr,
                                          bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
     JS_ASSERT(IsCacheableDOMProxy(obj));
     JS_ASSERT(monitoredResult());
     JS_ASSERT(output().hasValue());
 
     if (idempotent())
         return true;
 
     *emitted = true;
 
     Label failures;
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(object(), JSObject::offsetOfShape()),
                                    ImmGCPtr(obj->lastProperty()),
                                    &failures);
 
@@ -1394,19 +1398,19 @@ GetPropertyIC::tryAttachDOMProxyShadowed
     // Failure.
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "list base shadowed get");
 }
 
 bool
-GetPropertyIC::tryAttachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj,
-                                           HandlePropertyName name, bool resetNeeded,
-                                           void *returnAddr, bool *emitted)
+GetPropertyIC::tryAttachDOMProxyUnshadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                           HandleObject obj, HandlePropertyName name,
+                                           bool resetNeeded, void *returnAddr, bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
     JS_ASSERT(IsCacheableDOMProxy(obj));
     JS_ASSERT(monitoredResult());
     JS_ASSERT(output().hasValue());
 
     RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
@@ -1433,17 +1437,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
         // (if there is one). The generation is a constant in the generated
         // code and we will not have the same generation again for this
         // object, so the generation check in the existing IC would always
         // fail anyway.
         reset();
     }
 
     Label failures;
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(object(), JSObject::offsetOfShape()),
                                    ImmGCPtr(obj->lastProperty()),
                                    &failures);
 
@@ -1494,19 +1498,19 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
     attacher.jumpRejoin(masm);
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "unshadowed proxy get");
 }
 
 bool
-GetPropertyIC::tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj,
-                              HandlePropertyName name, void *returnAddr,
-                              bool *emitted)
+GetPropertyIC::tryAttachProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                              HandleObject obj, HandlePropertyName name,
+                              void *returnAddr, bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
 
     if (!obj->is<ProxyObject>())
         return true;
 
     // TI can't be sure about our properties, so make sure anything
@@ -1516,38 +1520,38 @@ GetPropertyIC::tryAttachProxy(JSContext 
 
     // Skim off DOM proxies.
     if (IsCacheableDOMProxy(obj)) {
         RootedId id(cx, NameToId(name));
         DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
         if (shadows == ShadowCheckFailed)
             return false;
         if (shadows == Shadows)
-            return tryAttachDOMProxyShadowed(cx, ion, obj, returnAddr, emitted);
-
-        return tryAttachDOMProxyUnshadowed(cx, ion, obj, name, shadows == DoesntShadowUnique,
-                                           returnAddr, emitted);
+            return tryAttachDOMProxyShadowed(cx, outerScript, ion, obj, returnAddr, emitted);
+
+        return tryAttachDOMProxyUnshadowed(cx, outerScript, ion, obj, name,
+                                           shadows == DoesntShadowUnique, returnAddr, emitted);
     }
 
-    return tryAttachGenericProxy(cx, ion, obj, name, returnAddr, emitted);
+    return tryAttachGenericProxy(cx, outerScript, ion, obj, name, returnAddr, emitted);
 }
 
 static void
 GenerateProxyClassGuards(MacroAssembler &masm, Register object, Register scratchReg,
                          Label *failures)
 {
     masm.loadObjClass(object, scratchReg);
     masm.branchTest32(Assembler::Zero,
                       Address(scratchReg, Class::offsetOfFlags()),
                       Imm32(JSCLASS_IS_PROXY), failures);
 }
 
 bool
-GetPropertyIC::tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject obj,
-                                     HandlePropertyName name, void *returnAddr,
+GetPropertyIC::tryAttachGenericProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                     HandleObject obj, HandlePropertyName name, void *returnAddr,
                                      bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
     JS_ASSERT(obj->is<ProxyObject>());
     JS_ASSERT(monitoredResult());
     JS_ASSERT(output().hasValue());
 
@@ -1555,17 +1559,17 @@ GetPropertyIC::tryAttachGenericProxy(JSC
         return true;
 
     if (idempotent())
         return true;
 
     *emitted = true;
 
     Label failures;
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     Register scratchReg = output().valueReg().scratchReg();
 
     GenerateProxyClassGuards(masm, object(), scratchReg, &failures);
 
     // Ensure that the incoming object is not a DOM proxy, so that we can get to
     // the specialized stubs
@@ -1585,18 +1589,18 @@ GetPropertyIC::tryAttachGenericProxy(JSC
 
     JS_ASSERT(!hasGenericProxyStub_);
     hasGenericProxyStub_ = true;
 
     return linkAndAttachStub(cx, masm, attacher, ion, "Generic Proxy get");
 }
 
 bool
-GetPropertyIC::tryAttachArgumentsLength(JSContext *cx, IonScript *ion, HandleObject obj,
-                                        HandlePropertyName name, bool *emitted)
+GetPropertyIC::tryAttachArgumentsLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                        HandleObject obj, HandlePropertyName name, bool *emitted)
 {
     JS_ASSERT(canAttachStub());
     JS_ASSERT(!*emitted);
 
     if (name != cx->names().length)
         return true;
     if (!IsOptimizableArgumentsObjectForLength(obj))
         return true;
@@ -1608,17 +1612,17 @@ GetPropertyIC::tryAttachArgumentsLength(
     if (hasArgumentsLengthStub(obj->is<StrictArgumentsObject>()))
         return true;
 
     *emitted = true;
 
     JS_ASSERT(!idempotent());
 
     Label failures;
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     Register tmpReg;
     if (output().hasValue()) {
         tmpReg = output().valueReg().scratchReg();
     } else {
         JS_ASSERT(output().type() == MIRType_Int32);
         tmpReg = output().typedReg().gpr();
@@ -1655,46 +1659,47 @@ GetPropertyIC::tryAttachArgumentsLength(
     }
 
     JS_ASSERT(!hasNormalArgumentsLengthStub_);
     hasNormalArgumentsLengthStub_ = true;
     return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (normal)");
 }
 
 bool
-GetPropertyIC::tryAttachStub(JSContext *cx, IonScript *ion, HandleObject obj,
-                             HandlePropertyName name, void *returnAddr, bool *emitted)
+GetPropertyIC::tryAttachStub(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                             HandleObject obj, HandlePropertyName name,
+                             void *returnAddr, bool *emitted)
 {
     JS_ASSERT(!*emitted);
 
     if (!canAttachStub())
         return true;
 
-    if (!*emitted && !tryAttachArgumentsLength(cx, ion, obj, name, emitted))
+    if (!*emitted && !tryAttachArgumentsLength(cx, outerScript, ion, obj, name, emitted))
         return false;
 
-    if (!*emitted && !tryAttachProxy(cx, ion, obj, name, returnAddr, emitted))
+    if (!*emitted && !tryAttachProxy(cx, outerScript, ion, obj, name, returnAddr, emitted))
         return false;
 
-    if (!*emitted && !tryAttachNative(cx, ion, obj, name, returnAddr, emitted))
+    if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, name, returnAddr, emitted))
         return false;
 
-    if (!*emitted && !tryAttachTypedArrayLength(cx, ion, obj, name, emitted))
+    if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted))
         return false;
 
     return true;
 }
 
 /* static */ bool
 GetPropertyIC::update(JSContext *cx, size_t cacheIndex,
                       HandleObject obj, MutableHandleValue vp)
 {
     void *returnAddr;
-    RootedScript topScript(cx, GetTopIonJSScript(cx, &returnAddr));
-    IonScript *ion = topScript->ionScript();
+    RootedScript outerScript(cx, GetTopIonJSScript(cx, &returnAddr));
+    IonScript *ion = outerScript->ionScript();
 
     GetPropertyIC &cache = ion->getCache(cacheIndex).toGetProperty();
     RootedPropertyName name(cx, cache.name());
 
     AutoFlushCache afc ("GetPropertyCache", cx->runtime()->jitRuntime());
 
     // Override the return value if we are invalidated (bug 728188).
     AutoDetectInvalidation adi(cx, vp.address(), ion);
@@ -1702,36 +1707,36 @@ GetPropertyIC::update(JSContext *cx, siz
     // If the cache is idempotent, we will redo the op in the interpreter.
     if (cache.idempotent())
         adi.disable();
 
     // For now, just stop generating new stubs once we hit the stub count
     // limit. Once we can make calls from within generated stubs, a new call
     // stub will be generated instead and the previous stubs unlinked.
     bool emitted = false;
-    if (!cache.tryAttachStub(cx, ion, obj, name, returnAddr, &emitted))
+    if (!cache.tryAttachStub(cx, outerScript, ion, obj, name, returnAddr, &emitted))
         return false;
 
     if (cache.idempotent() && !emitted) {
         // Invalidate the cache if the property was not found, or was found on
         // a non-native object. This ensures:
         // 1) The property read has no observable side-effects.
         // 2) There's no need to dynamically monitor the return type. This would
         //    be complicated since (due to GVN) there can be multiple pc's
         //    associated with a single idempotent cache.
         IonSpew(IonSpew_InlineCaches, "Invalidating from idempotent cache %s:%d",
-                topScript->filename(), topScript->lineno());
-
-        topScript->setInvalidatedIdempotentCache();
+                outerScript->filename(), outerScript->lineno());
+
+        outerScript->setInvalidatedIdempotentCache();
 
         // Do not re-invalidate if the lookup already caused invalidation.
-        if (!topScript->hasIonScript())
+        if (!outerScript->hasIonScript())
             return true;
 
-        return Invalidate(cx, topScript);
+        return Invalidate(cx, outerScript);
     }
 
     RootedId id(cx, NameToId(name));
     if (!JSObject::getGeneric(cx, obj, obj, id, vp))
         return false;
 
     if (!cache.idempotent()) {
         RootedScript script(cx);
@@ -1809,40 +1814,40 @@ ParallelIonCache::destroy()
 void
 GetPropertyParIC::reset()
 {
     ParallelIonCache::reset();
     hasTypedArrayLengthStub_ = false;
 }
 
 bool
-GetPropertyParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj,
-                                 JSObject *holder, Shape *shape)
+GetPropertyParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
+                                 HandleObject holder, HandleShape shape)
 {
     // Ready to generate the read slot stub.
     DispatchStubPrepender attacher(*this);
     MacroAssembler masm(cx, ion);
     GenerateReadSlot(cx, ion, masm, attacher, obj, holder, shape, object(), output());
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel reading");
 }
 
 bool
-GetPropertyParIC::attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj)
+GetPropertyParIC::attachArrayLength(LockedJSContext &cx, IonScript *ion, HandleObject obj)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     if (!GenerateArrayLength(cx, masm, attacher, obj, object(), output()))
         return false;
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel array length");
 }
 
 bool
-GetPropertyParIC::attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj)
+GetPropertyParIC::attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, HandleObject obj)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
 
     JS_ASSERT(!hasTypedArrayLengthStub_);
     hasTypedArrayLengthStub_ = true;
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array length");
@@ -2000,20 +2005,20 @@ GenerateSetSlot(JSContext *cx, MacroAsse
         masm.pop(object);
     }
 
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 }
 
 bool
-SetPropertyIC::attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj,
-                             HandleShape shape, bool checkTypeset)
+SetPropertyIC::attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                             HandleObject obj, HandleShape shape, bool checkTypeset)
 {
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
                     checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "setting");
 }
 
 static bool
 IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape shape)
@@ -2119,21 +2124,22 @@ EmitCallProxySet(JSContext *cx, MacroAss
     // masm.leaveExitFrame & pop locals
     masm.adjustStack(IonOOLProxyExitFrameLayout::Size());
 
     masm.icRestoreLive(liveRegs, aic);
     return true;
 }
 
 bool
-SetPropertyIC::attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr)
+SetPropertyIC::attachGenericProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                  void *returnAddr)
 {
     JS_ASSERT(!hasGenericProxyStub());
 
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     Label failures;
     {
         Label proxyFailures;
         Label proxySuccess;
 
         RegisterSet regSet(RegisterSet::All());
@@ -2174,23 +2180,23 @@ SetPropertyIC::attachGenericProxy(JSCont
 
     JS_ASSERT(!hasGenericProxyStub_);
     hasGenericProxyStub_ = true;
 
     return linkAndAttachStub(cx, masm, attacher, ion, "generic proxy set");
 }
 
 bool
-SetPropertyIC::attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj,
-                                        void *returnAddr)
+SetPropertyIC::attachDOMProxyShadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                      HandleObject obj, void *returnAddr)
 {
     JS_ASSERT(IsCacheableDOMProxy(obj));
 
     Label failures;
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
                    ImmGCPtr(obj->lastProperty()), &failures);
 
     // Make sure object is a DOMProxy
@@ -2401,23 +2407,23 @@ IsCacheableDOMProxyUnshadowedSetterCall(
         return true;
     }
 
     *isSetter = true;
     return true;
 }
 
 bool
-SetPropertyIC::attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj,
-                                        void *returnAddr)
+SetPropertyIC::attachDOMProxyUnshadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                        HandleObject obj, void *returnAddr)
 {
     JS_ASSERT(IsCacheableDOMProxy(obj));
 
     Label failures;
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
                    ImmGCPtr(obj->lastProperty()), &failures);
 
     // Make sure object is a DOMProxy
@@ -2456,23 +2462,23 @@ SetPropertyIC::attachDOMProxyUnshadowed(
     // Failure.
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy unshadowed set");
 }
 
 bool
-SetPropertyIC::attachCallSetter(JSContext *cx, IonScript *ion,
+SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                 HandleObject obj, HandleObject holder, HandleShape shape,
                                 void *returnAddr)
 {
     JS_ASSERT(obj->isNative());
 
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     Label failure;
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
                    ImmGCPtr(obj->lastProperty()),
                    &failure);
 
@@ -2569,22 +2575,22 @@ GenerateAddSlot(JSContext *cx, MacroAsse
     masm.bind(&failuresPopObject);
     masm.pop(object);
     masm.bind(&failures);
 
     attacher.jumpNextStub(masm);
 }
 
 bool
-SetPropertyIC::attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape,
-                             bool checkTypeset)
+SetPropertyIC::attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                             HandleObject obj, HandleShape oldShape, bool checkTypeset)
 {
     JS_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
 
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value(), checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "adding");
 }
 
 static bool
 CanInlineSetPropTypeCheck(JSObject *obj, jsid id, ConstantOrRegister val, bool *checkTypeset)
 {
@@ -2765,31 +2771,31 @@ SetPropertyIC::update(JSContext *cx, siz
     bool addedSetterStub = false;
     if (inlinable) {
         if (!addedSetterStub && obj->is<ProxyObject>()) {
             if (IsCacheableDOMProxy(obj)) {
                 DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
                 if (shadows == ShadowCheckFailed)
                     return false;
                 if (shadows == Shadows) {
-                    if (!cache.attachDOMProxyShadowed(cx, ion, obj, returnAddr))
+                    if (!cache.attachDOMProxyShadowed(cx, script, ion, obj, returnAddr))
                         return false;
                     addedSetterStub = true;
                 } else {
                     JS_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
                     if (shadows == DoesntShadowUnique)
                         cache.reset();
-                    if (!cache.attachDOMProxyUnshadowed(cx, ion, obj, returnAddr))
+                    if (!cache.attachDOMProxyUnshadowed(cx, script, ion, obj, returnAddr))
                         return false;
                     addedSetterStub = true;
                 }
             }
 
             if (!addedSetterStub && !cache.hasGenericProxyStub()) {
-                if (!cache.attachGenericProxy(cx, ion, returnAddr))
+                if (!cache.attachGenericProxy(cx, script, ion, returnAddr))
                     return false;
                 addedSetterStub = true;
             }
         }
 
         // Make sure the object de-lazifies its type. We do this here so that
         // the parallel IC can share code that assumes that native objects all
         // have a type object.
@@ -2798,23 +2804,23 @@ SetPropertyIC::update(JSContext *cx, siz
 
         RootedShape shape(cx);
         RootedObject holder(cx);
         bool checkTypeset;
         canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
                                           &holder, &shape, &checkTypeset);
 
         if (!addedSetterStub && canCache == CanAttachSetSlot) {
-            if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset))
+            if (!cache.attachSetSlot(cx, script, ion, obj, shape, checkTypeset))
                 return false;
             addedSetterStub = true;
         }
 
         if (!addedSetterStub && canCache == CanAttachCallSetter) {
-            if (!cache.attachCallSetter(cx, ion, obj, holder, shape, returnAddr))
+            if (!cache.attachCallSetter(cx, script, ion, obj, holder, shape, returnAddr))
                 return false;
             addedSetterStub = true;
         }
     }
 
     uint32_t oldSlots = obj->numDynamicSlots();
     RootedShape oldShape(cx, obj->lastProperty());
 
@@ -2823,17 +2829,17 @@ SetPropertyIC::update(JSContext *cx, siz
         return false;
 
     // The property did not exist before, now we can try to inline the property add.
     bool checkTypeset;
     if (!addedSetterStub && canCache == MaybeCanAttachAddSlot &&
         IsPropertyAddInlineable(obj, id, cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
                                 &checkTypeset))
     {
-        if (!cache.attachAddSlot(cx, ion, obj, oldShape, checkTypeset))
+        if (!cache.attachAddSlot(cx, script, ion, obj, oldShape, checkTypeset))
             return false;
     }
 
     return true;
 }
 
 void
 SetPropertyIC::reset()
@@ -2916,29 +2922,29 @@ SetPropertyParIC::update(ForkJoinContext
         if (cache.canAttachStub() && !cache.attachAddSlot(ncx, ion, obj, oldShape, checkTypeset))
             return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
     }
 
     return true;
 }
 
 bool
-SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
-                                bool checkTypeset)
+SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
+                                HandleShape shape, bool checkTypeset)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
                     checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel setting");
 }
 
 bool
-SetPropertyParIC::attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape,
-                                bool checkTypeset)
+SetPropertyParIC::attachAddSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
+                                HandleShape oldShape, bool checkTypeset)
 {
     JS_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
 
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value(), checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel adding");
 }
@@ -2964,18 +2970,18 @@ EqualStringsHelper(JSString *str1, JSStr
 
     const jschar *chars = str2->getChars(nullptr);
     if (!chars)
         return false;
     return mozilla::PodEqual(str1->asAtom().chars(), chars, str1->length());
 }
 
 bool
-GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
-                            const Value &idval, HandlePropertyName name,
+GetElementIC::attachGetProp(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                            HandleObject obj, const Value &idval, HandlePropertyName name,
                             void *returnAddr)
 {
     JS_ASSERT(index().reg().hasValue());
 
     RootedObject holder(cx);
     RootedShape shape(cx);
 
     GetPropertyIC::NativeGetPropCacheability canCache =
@@ -2990,17 +2996,17 @@ GetElementIC::attachGetProp(JSContext *c
         IonSpew(IonSpew_InlineCaches, "GETELEM uncacheable property");
         return true;
     }
 
     JS_ASSERT(idval.isString());
     JS_ASSERT(idval.toString()->length() == name->length());
 
     Label failures;
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
 
     // Ensure the index is a string.
     ValueOperand val = index().reg().valueReg();
     masm.branchTestString(Assembler::NotEqual, val, &failures);
 
     Register scratch = output().valueReg().scratchReg();
     masm.unboxString(val, scratch);
 
@@ -3123,19 +3129,20 @@ GenerateDenseElement(JSContext *cx, Macr
     masm.bind(&failures);
 
     attacher.jumpNextStub(masm);
 
     return true;
 }
 
 bool
-GetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval)
+GetElementIC::attachDenseElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                 HandleObject obj, const Value &idval)
 {
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     if (!GenerateDenseElement(cx, masm, attacher, obj, idval, object(), index(), output()))
         return false;
 
     setHasDenseStub();
     return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
 }
 
@@ -3174,17 +3181,17 @@ GetElementIC::canAttachTypedArrayElement
         return output.hasValue();
     }
 
     return output.hasValue() || !output.typedReg().isFloat();
 }
 
 static void
 GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                             TypedArrayObject *tarr, const Value &idval, Register object,
+                             HandleTypedArrayObject tarr, const Value &idval, Register object,
                              ConstantOrRegister index, TypedOrValueRegister output,
                              bool allowDoubleResult)
 {
     JS_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
 
     Label failures;
 
     // The array type is the object within the table of typed array classes.
@@ -3277,33 +3284,34 @@ GenerateGetTypedArrayElement(JSContext *
     masm.bind(&popAndFail);
     masm.pop(object);
     masm.bind(&failures);
 
     attacher.jumpNextStub(masm);
 }
 
 bool
-GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
-                                      const Value &idval)
+GetElementIC::attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                      HandleTypedArrayObject tarr, const Value &idval)
 {
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
                                  allowDoubleResult());
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
 }
 
 bool
-GetElementIC::attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj)
+GetElementIC::attachArgumentsElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                     HandleObject obj)
 {
     JS_ASSERT(obj->is<ArgumentsObject>());
 
     Label failures;
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     Register tmpReg = output().scratchReg().gpr();
     JS_ASSERT(tmpReg != InvalidReg);
 
     const Class *clasp = obj->is<StrictArgumentsObject>() ? &StrictArgumentsObject::class_
                                                           : &NormalArgumentsObject::class_;
 
@@ -3401,17 +3409,18 @@ GetElementIC::attachArgumentsElement(JSC
     return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (normal)");
 }
 
 bool
 GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
                      HandleValue idval, MutableHandleValue res)
 {
     void *returnAddr;
-    IonScript *ion = GetTopIonJSScript(cx, &returnAddr)->ionScript();
+    RootedScript outerScript(cx, GetTopIonJSScript(cx, &returnAddr));
+    IonScript *ion = outerScript->ionScript();
     GetElementIC &cache = ion->getCache(cacheIndex).toGetElement();
     RootedScript script(cx);
     jsbytecode *pc;
     cache.getScriptedLocation(&script, &pc);
 
     // Override the return value when the script is invalidated (bug 728188).
     AutoDetectInvalidation adi(cx, res.address(), ion);
 
@@ -3433,34 +3442,34 @@ GetElementIC::update(JSContext *cx, size
     if (cache.canAttachStub()) {
         if (IsOptimizableArgumentsObjectForGetElem(obj, idval) &&
             !cache.hasArgumentsStub(obj->is<StrictArgumentsObject>()) &&
             !cache.index().constant() &&
             (cache.index().reg().hasValue() ||
              cache.index().reg().type() == MIRType_Int32) &&
             (cache.output().hasValue() || !cache.output().typedReg().isFloat()))
         {
-            if (!cache.attachArgumentsElement(cx, ion, obj))
+            if (!cache.attachArgumentsElement(cx, outerScript, ion, obj))
                 return false;
             attachedStub = true;
         }
         if (!attachedStub && cache.monitoredResult() && canAttachGetProp(obj, idval, id)) {
             RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
-            if (!cache.attachGetProp(cx, ion, obj, idval, name, returnAddr))
+            if (!cache.attachGetProp(cx, outerScript, ion, obj, idval, name, returnAddr))
                 return false;
             attachedStub = true;
         }
         if (!attachedStub && !cache.hasDenseStub() && canAttachDenseElement(obj, idval)) {
-            if (!cache.attachDenseElement(cx, ion, obj, idval))
+            if (!cache.attachDenseElement(cx, outerScript, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
         if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
             Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
-            if (!cache.attachTypedArrayElement(cx, ion, tarr, idval))
+            if (!cache.attachTypedArrayElement(cx, outerScript, ion, tarr, idval))
                 return false;
             attachedStub = true;
         }
     }
 
     if (!GetObjectElementOperation(cx, JSOp(*pc), obj, /* wasObject = */true, idval, res))
         return false;
 
@@ -3672,19 +3681,20 @@ GenerateSetDenseElement(JSContext *cx, M
     masm.bind(&outOfBounds);
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     return true;
 }
 
 bool
-SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval)
+SetElementIC::attachDenseElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                 HandleObject obj, const Value &idval)
 {
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
                                  guardHoles(), object(), index(),
                                  value(), tempToUnboxIndex(),
                                  temp()))
     {
         return false;
     }
@@ -3693,17 +3703,17 @@ SetElementIC::attachDenseElement(JSConte
     const char *message = guardHoles()            ?
                             "dense array (holes)" :
                             "dense array";
     return linkAndAttachStub(cx, masm, attacher, ion, message);
 }
 
 static bool
 GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                             TypedArrayObject *tarr, Register object,
+                             HandleTypedArrayObject tarr, Register object,
                              ValueOperand indexVal, ConstantOrRegister value,
                              Register tempUnbox, Register temp, FloatRegister tempFloat)
 {
     Label failures, done, popObjectAndFail;
 
     // Guard on the shape.
     Shape *shape = tarr->lastProperty();
     if (!shape)
@@ -3775,47 +3785,49 @@ GenerateSetTypedArrayElement(JSContext *
     }
 
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
     return true;
 }
 
 bool
-SetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr)
+SetElementIC::attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                      HandleTypedArrayObject tarr)
 {
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
                                       object(), index(), value(),
                                       tempToUnboxIndex(), temp(), tempFloat()))
     {
         return false;
     }
 
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
 }
 
 bool
 SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
                      HandleValue idval, HandleValue value)
 {
-    IonScript *ion = GetTopIonJSScript(cx)->ionScript();
+    RootedScript outerScript(cx, GetTopIonJSScript(cx));
+    IonScript *ion = outerScript->ionScript();
     SetElementIC &cache = ion->getCache(cacheIndex).toSetElement();
 
     bool attachedStub = false;
     if (cache.canAttachStub()) {
         if (!cache.hasDenseStub() && IsDenseElementSetInlineable(obj, idval)) {
-            if (!cache.attachDenseElement(cx, ion, obj, idval))
+            if (!cache.attachDenseElement(cx, outerScript, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
         if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
-            TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
-            if (!cache.attachTypedArrayElement(cx, ion, tarr))
+            RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
+            if (!cache.attachTypedArrayElement(cx, outerScript, ion, tarr))
                 return false;
         }
     }
 
     if (!SetObjectElement(cx, obj, idval, value, cache.strict()))
         return false;
     return true;
 }
@@ -3823,17 +3835,17 @@ SetElementIC::update(JSContext *cx, size
 void
 SetElementIC::reset()
 {
     RepatchIonCache::reset();
     hasDenseStub_ = false;
 }
 
 bool
-SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj,
+SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleObject obj,
                                     const Value &idval)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
                                  guardHoles(), object(), index(),
                                  value(), tempToUnboxIndex(),
                                  temp()))
@@ -3845,17 +3857,17 @@ SetElementParIC::attachDenseElement(Lock
                             "parallel dense array (holes)" :
                             "parallel dense array";
 
     return linkAndAttachStub(cx, masm, attacher, ion, message);
 }
 
 bool
 SetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
-                                         TypedArrayObject *tarr)
+                                         HandleTypedArrayObject tarr)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
                                       object(), index(), value(),
                                       tempToUnboxIndex(), temp(), tempFloat()))
     {
         return false;
@@ -3887,60 +3899,60 @@ SetElementParIC::update(ForkJoinContext 
 
             bool attachedStub = false;
             if (IsDenseElementSetInlineable(obj, idval)) {
                 if (!cache.attachDenseElement(ncx, ion, obj, idval))
                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
             if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
-                TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
+                RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
                 if (!cache.attachTypedArrayElement(ncx, ion, tarr))
                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
             }
         }
     }
 
     return SetElementPar(cx, obj, idval, value, cache.strict());
 }
 
 bool
-GetElementParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj,
-                                const Value &idval, PropertyName *name, JSObject *holder,
-                                Shape *shape)
+GetElementParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
+                                const Value &idval, HandlePropertyName name, HandleObject holder,
+                                HandleShape shape)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
 
     // Guard on the index value.
     Label failures;
     ValueOperand val = index().reg().valueReg();
     masm.branchTestValue(Assembler::NotEqual, val, idval, &failures);
 
     GenerateReadSlot(cx, ion, masm, attacher, obj, holder, shape, object(), output(),
                      &failures);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel getelem reading");
 }
 
 bool
-GetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj,
+GetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleObject obj,
                                     const Value &idval)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     if (!GenerateDenseElement(cx, masm, attacher, obj, idval, object(), index(), output()))
         return false;
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel dense element");
 }
 
 bool
 GetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
-                                         TypedArrayObject *tarr, const Value &idval)
+                                         HandleTypedArrayObject tarr, const Value &idval)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
                                  allowDoubleResult());
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
@@ -3999,32 +4011,34 @@ GetElementParIC::update(ForkJoinContext 
             {
                 if (!cache.attachDenseElement(ncx, ion, obj, idval))
                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
             if (!attachedStub &&
                 GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output()))
             {
-                if (!cache.attachTypedArrayElement(ncx, ion, &obj->as<TypedArrayObject>(), idval))
+                RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
+                if (!cache.attachTypedArrayElement(ncx, ion, tarr, idval))
                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
         }
     }
 
     return true;
 }
 
 bool
-BindNameIC::attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain)
+BindNameIC::attachGlobal(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                         HandleObject scopeChain)
 {
     JS_ASSERT(scopeChain->is<GlobalObject>());
 
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the scope chain.
     attacher.branchNextStub(masm, Assembler::NotEqual, scopeChainReg(),
                             ImmGCPtr(scopeChain));
     masm.movePtr(ImmGCPtr(scopeChain), outputReg());
 
     attacher.jumpRejoin(masm);
@@ -4085,21 +4099,22 @@ GenerateScopeChainGuards(MacroAssembler 
 
         // Load the next link.
         tobj = &tobj->as<ScopeObject>().enclosingScope();
         masm.extractObject(Address(outputReg, ScopeObject::offsetOfEnclosingScope()), outputReg);
     }
 }
 
 bool
-BindNameIC::attachNonGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain, JSObject *holder)
+BindNameIC::attachNonGlobal(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                            HandleObject scopeChain, HandleObject holder)
 {
     JS_ASSERT(IsCacheableNonGlobalScope(scopeChain));
 
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the scope chain.
     Label failures;
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(scopeChainReg(), JSObject::offsetOfShape()),
                                    ImmGCPtr(scopeChain->lastProperty()),
                                    holder != scopeChain ? &failures : nullptr);
@@ -4148,51 +4163,52 @@ IsCacheableScopeChain(JSObject *scopeCha
     MOZ_ASSUME_UNREACHABLE("Invalid scope chain");
 }
 
 JSObject *
 BindNameIC::update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain)
 {
     AutoFlushCache afc ("BindNameCache", cx->runtime()->jitRuntime());
 
-    IonScript *ion = GetTopIonJSScript(cx)->ionScript();
+    RootedScript outerScript(cx, GetTopIonJSScript(cx));
+    IonScript *ion = outerScript->ionScript();
     BindNameIC &cache = ion->getCache(cacheIndex).toBindName();
     HandlePropertyName name = cache.name();
 
     RootedObject holder(cx);
     if (scopeChain->is<GlobalObject>()) {
         holder = scopeChain;
     } else {
         if (!LookupNameWithGlobalDefault(cx, name, scopeChain, &holder))
             return nullptr;
     }
 
     // Stop generating new stubs once we hit the stub count limit, see
     // GetPropertyCache.
     if (cache.canAttachStub()) {
         if (scopeChain->is<GlobalObject>()) {
-            if (!cache.attachGlobal(cx, ion, scopeChain))
+            if (!cache.attachGlobal(cx, outerScript, ion, scopeChain))
                 return nullptr;
         } else if (IsCacheableScopeChain(scopeChain, holder)) {
-            if (!cache.attachNonGlobal(cx, ion, scopeChain, holder))
+            if (!cache.attachNonGlobal(cx, outerScript, ion, scopeChain, holder))
                 return nullptr;
         } else {
             IonSpew(IonSpew_InlineCaches, "BINDNAME uncacheable scope chain");
         }
     }
 
     return holder;
 }
 
 bool
-NameIC::attachReadSlot(JSContext *cx, IonScript *ion, HandleObject scopeChain,
-                       HandleObject holderBase, HandleObject holder,
-                       HandleShape shape)
+NameIC::attachReadSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                       HandleObject scopeChain, HandleObject holderBase,
+                       HandleObject holder, HandleShape shape)
 {
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     Label failures;
     RepatchStubAppender attacher(*this);
 
     Register scratchReg = outputReg().valueReg().scratchReg();
 
     // Don't guard the base of the proto chain the name was found on. It will be guarded
     // by GenerateReadSlot().
     masm.mov(scopeChainReg(), scratchReg);
@@ -4242,20 +4258,21 @@ IsCacheableNameReadSlot(JSContext *cx, H
 
         obj2 = obj2->enclosingScope();
     }
 
     return obj == obj2;
 }
 
 bool
-NameIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
-                         HandleShape shape, void *returnAddr)
+NameIC::attachCallGetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                         HandleObject obj, HandleObject holder, HandleShape shape,
+                         void *returnAddr)
 {
-    MacroAssembler masm(cx, ion, script_, pc_);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
 
     RepatchStubAppender attacher(*this);
     if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name(), holder, shape, liveRegs_,
                             scopeChainReg(), outputReg(), returnAddr))
     {
          return false;
     }
 
@@ -4278,37 +4295,38 @@ IsCacheableNameCallGetter(JSObject *scop
 
 bool
 NameIC::update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain,
                MutableHandleValue vp)
 {
     AutoFlushCache afc ("GetNameCache", cx->runtime()->jitRuntime());
 
     void *returnAddr;
-    IonScript *ion = GetTopIonJSScript(cx, &returnAddr)->ionScript();
+    RootedScript outerScript(cx, GetTopIonJSScript(cx, &returnAddr));
+    IonScript *ion = outerScript->ionScript();
 
     NameIC &cache = ion->getCache(cacheIndex).toName();
     RootedPropertyName name(cx, cache.name());
 
     RootedScript script(cx);
     jsbytecode *pc;
     cache.getScriptedLocation(&script, &pc);
 
     RootedObject obj(cx);
     RootedObject holder(cx);
     RootedShape shape(cx);
     if (!LookupName(cx, name, scopeChain, &obj, &holder, &shape))
         return false;
 
     if (cache.canAttachStub()) {
         if (IsCacheableNameReadSlot(cx, scopeChain, obj, holder, shape, pc, cache.outputReg())) {
-            if (!cache.attachReadSlot(cx, ion, scopeChain, obj, holder, shape))
+            if (!cache.attachReadSlot(cx, outerScript, ion, scopeChain, obj, holder, shape))
                 return false;
         } else if (IsCacheableNameCallGetter(scopeChain, obj, holder, shape)) {
-            if (!cache.attachCallGetter(cx, ion, obj, holder, shape, returnAddr))
+            if (!cache.attachCallGetter(cx, outerScript, ion, obj, holder, shape, returnAddr))
                 return false;
         }
     }
 
     if (cache.isTypeOf()) {
         if (!FetchName<true>(cx, obj, holder, name, shape, vp))
             return false;
     } else {
@@ -4318,20 +4336,20 @@ NameIC::update(JSContext *cx, size_t cac
 
     // Monitor changes to cache entry.
     types::TypeScript::Monitor(cx, script, pc, vp);
 
     return true;
 }
 
 bool
-CallsiteCloneIC::attach(JSContext *cx, IonScript *ion, HandleFunction original,
-                        HandleFunction clone)
+CallsiteCloneIC::attach(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                        HandleFunction original, HandleFunction clone)
 {
-    MacroAssembler masm(cx, ion);
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard against object identity on the original.
     attacher.branchNextStub(masm, Assembler::NotEqual, calleeReg(), ImmGCPtr(original));
 
     // Load the clone.
     masm.movePtr(ImmGCPtr(clone), outputReg());
 
@@ -4346,22 +4364,23 @@ CallsiteCloneIC::update(JSContext *cx, s
     AutoFlushCache afc ("CallsiteCloneCache", cx->runtime()->jitRuntime());
 
     // Act as the identity for functions that are not clone-at-callsite, as we
     // generate this cache as long as some callees are clone-at-callsite.
     RootedFunction fun(cx, &callee->as<JSFunction>());
     if (!fun->hasScript() || !fun->nonLazyScript()->shouldCloneAtCallsite())
         return fun;
 
-    IonScript *ion = GetTopIonJSScript(cx)->ionScript();
+    RootedScript outerScript(cx, GetTopIonJSScript(cx));
+    IonScript *ion = outerScript->ionScript();
     CallsiteCloneIC &cache = ion->getCache(cacheIndex).toCallsiteClone();
 
     RootedFunction clone(cx, CloneFunctionAtCallsite(cx, fun, cache.callScript(), cache.callPc()));
     if (!clone)
         return nullptr;
 
     if (cache.canAttachStub()) {
-        if (!cache.attach(cx, ion, fun, clone))
+        if (!cache.attach(cx, outerScript, ion, fun, clone))
             return nullptr;
     }
 
     return clone;
 }
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -15,16 +15,18 @@
 #include "jit/Registers.h"
 #include "jit/shared/Assembler-shared.h"
 
 namespace js {
 
 class LockedJSContext;
 class TypedArrayObject;
 
+typedef Handle<TypedArrayObject *> HandleTypedArrayObject;
+
 namespace jit {
 
 #define IONCACHE_KIND_LIST(_)                                   \
     _(GetProperty)                                              \
     _(SetProperty)                                              \
     _(GetElement)                                               \
     _(SetElement)                                               \
     _(BindName)                                                 \
@@ -163,16 +165,20 @@ class IonCache
     size_t stubCount_ : 5;
 
     CodeLocationLabel fallbackLabel_;
 
     // Location of this operation, nullptr for idempotent caches.
     JSScript *script_;
     jsbytecode *pc_;
 
+    // Location to use when updating profiler pseudostack when leaving this
+    // IC code to enter a callee.
+    jsbytecode *profilerLeavePc_;
+
   private:
     static const size_t MAX_STUBS;
     void incrementStubCount() {
         // The IC should stop generating stubs before wrapping stubCount.
         stubCount_++;
         JS_ASSERT(stubCount_);
     }
 
@@ -180,32 +186,38 @@ class IonCache
 
     IonCache()
       : pure_(false),
         idempotent_(false),
         disabled_(false),
         stubCount_(0),
         fallbackLabel_(),
         script_(nullptr),
-        pc_(nullptr)
+        pc_(nullptr),
+        profilerLeavePc_(nullptr)
     {
     }
 
     virtual void disable();
     inline bool isDisabled() const {
         return disabled_;
     }
 
     // Set the initial 'out-of-line' jump state of the cache. The fallbackLabel is
     // the location of the out-of-line update (slow) path.  This location will
     // be set to the exitJump of the last generated stub.
     void setFallbackLabel(CodeOffsetLabel fallbackLabel) {
         fallbackLabel_ = fallbackLabel;
     }
 
+    void setProfilerLeavePC(jsbytecode *pc) {
+        JS_ASSERT(pc != nullptr);
+        profilerLeavePc_ = pc;
+    }
+
     virtual void emitInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0;
     virtual void bindInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0;
     virtual void updateBaseAddress(JitCode *code, MacroAssembler &masm);
 
     // Initialize the AddCacheState depending on the kind of cache, like
     // setting a scratch register. Defaults to doing nothing.
     virtual void initializeAddCacheState(LInstruction *ins, AddCacheState *addState);
 
@@ -609,34 +621,44 @@ class GetPropertyIC : public RepatchIonC
     // Helpers for CanAttachNativeGetProp
     typedef JSContext * Context;
     bool allowArrayLength(Context cx, HandleObject obj) const;
     bool allowGetters() const {
         return monitoredResult() && !idempotent();
     }
 
     // Attach the proper stub, if possible
-    bool tryAttachStub(JSContext *cx, IonScript *ion, HandleObject obj,
-                       HandlePropertyName name, void *returnAddr, bool *emitted);
-    bool tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj,
-                        HandlePropertyName name, void *returnAddr, bool *emitted);
-    bool tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject obj,
-                               HandlePropertyName name, void *returnAddr, bool *emitted);
-    bool tryAttachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj,
-                                   void *returnAddr, bool *emitted);
-    bool tryAttachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj,
-                                     HandlePropertyName name, bool resetNeeded,
+    bool tryAttachStub(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                       HandleObject obj, HandlePropertyName name,
+                       void *returnAddr, bool *emitted);
+
+    bool tryAttachProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                        HandleObject obj, HandlePropertyName name,
+                        void *returnAddr, bool *emitted);
+
+    bool tryAttachGenericProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                               HandleObject obj, HandlePropertyName name,
+                               void *returnAddr, bool *emitted);
+
+    bool tryAttachDOMProxyShadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                   HandleObject obj, void *returnAddr, bool *emitted);
+
+    bool tryAttachDOMProxyUnshadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                     HandleObject obj, HandlePropertyName name, bool resetNeeded,
                                      void *returnAddr, bool *emitted);
-    bool tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj,
-                         HandlePropertyName name, void *returnAddr, bool *emitted);
-    bool tryAttachTypedArrayLength(JSContext *cx, IonScript *ion, HandleObject obj,
-                                   HandlePropertyName name, bool *emitted);
+
+    bool tryAttachNative(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                         HandleObject obj, HandlePropertyName name,
+                         void *returnAddr, bool *emitted);
 
-    bool tryAttachArgumentsLength(JSContext *cx, IonScript *ion, HandleObject obj,
-                                  HandlePropertyName name, bool *emitted);
+    bool tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                   HandleObject obj, HandlePropertyName name, bool *emitted);
+
+    bool tryAttachArgumentsLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                  HandleObject obj, HandlePropertyName name, bool *emitted);
 
     static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp);
 };
 
 class SetPropertyIC : public RepatchIonCache
 {
   protected:
     // Registers live after the cache, excluding output registers. The initial
@@ -689,30 +711,36 @@ class SetPropertyIC : public RepatchIonC
 
     enum NativeSetPropCacheability {
         CanAttachNone,
         CanAttachSetSlot,
         MaybeCanAttachAddSlot,
         CanAttachCallSetter
     };
 
-    bool attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape,
-                       bool checkTypeset);
-    bool attachCallSetter(JSContext *cx, IonScript *ion, HandleObject obj,
-                          HandleObject holder, HandleShape shape, void *returnAddr);
-    bool attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape,
-                       bool checkTypeset);
-    bool attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr);
-    bool attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj,
-                                void *returnAddr);
-    bool attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj,
-                                  void *returnAddr);
+    bool attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                       HandleObject obj, HandleShape shape, bool checkTypeset);
+
+    bool attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                          HandleObject obj, HandleObject holder, HandleShape shape,
+                          void *returnAddr);
+
+    bool attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                       HandleObject obj, HandleShape oldShape, bool checkTypeset);
 
-    static bool
-    update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value);
+    bool attachGenericProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                            void *returnAddr);
+
+    bool attachDOMProxyShadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                HandleObject obj, void *returnAddr);
+
+    bool attachDOMProxyUnshadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                  HandleObject obj, void *returnAddr);
+
+    static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value);
 };
 
 class GetElementIC : public RepatchIonCache
 {
   protected:
     RegisterSet liveRegs_;
 
     Register object_;
@@ -783,22 +811,28 @@ class GetElementIC : public RepatchIonCa
         return monitoredResult();
     }
 
     static bool canAttachGetProp(JSObject *obj, const Value &idval, jsid id);
     static bool canAttachDenseElement(JSObject *obj, const Value &idval);
     static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval,
                                            TypedOrValueRegister output);
 
-    bool attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, const Value &idval,
-                       HandlePropertyName name, void *returnAddr);
-    bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval);
-    bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
-                                 const Value &idval);
-    bool attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj);
+    bool attachGetProp(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                       HandleObject obj, const Value &idval, HandlePropertyName name,
+                       void *returnAddr);
+
+    bool attachDenseElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                            HandleObject obj, const Value &idval);
+
+    bool attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                 HandleTypedArrayObject tarr, const Value &idval);
+
+    bool attachArgumentsElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                HandleObject obj);
 
     static bool
     update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
            MutableHandleValue vp);
 
     void incFailedUpdates() {
         failedUpdates_++;
     }
@@ -873,18 +907,21 @@ class SetElementIC : public RepatchIonCa
     bool hasDenseStub() const {
         return hasDenseStub_;
     }
     void setHasDenseStub() {
         JS_ASSERT(!hasDenseStub());
         hasDenseStub_ = true;
     }
 
-    bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval);
-    bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr);
+    bool attachDenseElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                            HandleObject obj, const Value &idval);
+
+    bool attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                                 HandleTypedArrayObject tarr);
 
     static bool
     update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
            HandleValue value);
 };
 
 class BindNameIC : public RepatchIonCache
 {
@@ -908,18 +945,21 @@ class BindNameIC : public RepatchIonCach
     }
     HandlePropertyName name() const {
         return HandlePropertyName::fromMarkedLocation(&name_);
     }
     Register outputReg() const {
         return output_;
     }
 
-    bool attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain);
-    bool attachNonGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain, JSObject *holder);
+    bool attachGlobal(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                      HandleObject scopeChain);
+
+    bool attachNonGlobal(JSContext *cx, HandleScript outerScript, IonScript *ion,
+                         HandleObject scopeChain, HandleObject holder);
 
     static JSObject *
     update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain);
 };
 
 class NameIC : public RepatchIonCache
 {
   protected:
@@ -954,20 +994,23 @@ class NameIC : public RepatchIonCache
     }
     TypedOrValueRegister outputReg() const {
         return output_;
     }
     bool isTypeOf() const {
         return typeOf_;
     }